diff -u --recursive --new-file v2.1.52/linux/Documentation/networking/Configurable linux/Documentation/networking/Configurable
--- v2.1.52/linux/Documentation/networking/Configurable	Thu Jun  6 04:57:43 1996
+++ linux/Documentation/networking/Configurable	Thu Sep  4 13:25:28 1997
@@ -4,9 +4,10 @@
 are usually a good choice for 99% of the people 99% of the time, but
 you should be aware they do exist and can be changed.
 
-The current list of parameters can be found in the file:
+The current list of parameters can be found in the files:
 
-	./linux/net/TUNABLE
+	linux/net/TUNABLE
+	linux/Documentation/networking/ip-sysctl.txt
 
 Some of these are accessible via the sysctl interface, and many more are
 scheduled to be added in this way. For example, some parameters related 
diff -u --recursive --new-file v2.1.52/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v2.1.52/linux/Documentation/networking/arcnet-hardware.txt	Mon May  6 02:26:01 1996
+++ linux/Documentation/networking/arcnet-hardware.txt	Thu Sep  4 13:25:28 1997
@@ -10,8 +10,8 @@
 Because so many people (myself included) seem to have obtained ARCnet cards
 without manuals, this file contains a quick introduction to ARCnet hardware,
 some cabling tips, and a listing of all jumper settings I can find. Please
-e-mail apenwarr@foxnet.net with any settings for your particular card, or
-any other information you have!
+e-mail apenwarr@bond.net with any settings for your particular card, or any
+other information you have!
 
 
 INTRODUCTION TO ARCNET
@@ -80,7 +80,7 @@
 This section was rewritten by 
         Vojtech Pavlik     <Vojtech.Pavlik@st.mff.cuni.cz>
 using information from several people, including:
-        Avery Pennraun     <apenwarr@foxnet.net>
+        Avery Pennraun     <apenwarr@bond.net>
  	Stephen A. Wood    <saw@hallc1.cebaf.gov>
  	John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
  	Joachim Koenig     <jojo@repas.de>
@@ -3129,6 +3129,6 @@
 
 I have no information on other models of ARCnet cards at the moment.  Please
 send any and all info to:
-	apenwarr@foxnet.net
+	apenwarr@bond.net
 
 Thanks.
diff -u --recursive --new-file v2.1.52/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v2.1.52/linux/Documentation/networking/arcnet.txt	Sat Nov 30 23:47:41 1996
+++ linux/Documentation/networking/arcnet.txt	Thu Sep  4 13:25:28 1997
@@ -37,7 +37,7 @@
 include the type of card(s) you're using, software, size of network, and
 whether it's working or not.)
 
-My e-mail address is: apenwarr@foxnet.net
+My e-mail address is: apenwarr@bond.net
 
 
 ---------------------------------------------------------------------------
@@ -45,7 +45,15 @@
 			
 These are the ARCnet drivers for Linux.
 
-This new release has resulted from many months of on-and-off effort from me
+
+This new release (2.91) has been put together by David Woodhouse 
+<dwmw2@cam.ac.uk>, in an attempt to tidy up the driver after adding support 
+for yet another chipset. Now the generic support has been separated from the
+individual chipset drivers, and the source files aren't quite so packed with
+#ifdefs! I've changed this file a bit, but kept it in the first person from
+Avery, because I didn't want to completely rewrite it.
+
+The previous release resulted from many months of on-and-off effort from me
 (Avery Pennarun), many bug reports/fixes and suggestions from others, and in
 particular a lot of input and coding from Tomasz Motylewski.  Starting with
 ARCnet 2.10 ALPHA, Tomasz's all-new-and-improved RFC1051 support has been
@@ -71,7 +79,8 @@
 Other Drivers and Info
 ----------------------
 
-You can try my ARCNET page on the World Wide Web at:
+You can (could - foxnet.net is no more - DW.) try my ARCNET page on the 
+World Wide Web at:
 	http://www.foxnet.net/~apenwarr/arcnet/
 
 Also, SMC (one of the companies that makes ARCnet cards) has a WWW site you
@@ -97,18 +106,10 @@
 Installing the Driver
 ---------------------
 
-** Note: the latest version of the driver contains preliminary support for
-	ARCnet RIM I cards.  These are very old cards that don't use I/O
-	ports at all, but rather map the status and command ports into
-	shared memory.  To compile the driver in RIM I mode, you must (for
-	now) edit linux/drivers/net/arcnet.c, find the line that says:
-		#undef RIM_I_MODE
-	and change it to:
-		#define RIM_I_MODE
-
 All you will need to do in order to install the driver is:
 	make config
-		(be sure to choose ARCnet in the network devices)
+		(be sure to choose ARCnet in the network devices 
+		and at least one chipset driver.)
 	make dep
 	make clean
 	make zImage
@@ -120,24 +121,72 @@
 You will know the driver is installed properly if you get some ARCnet
 messages when you reboot into the new Linux kernel.
 
-If you use a RIM I card, you will need to give the kernel boot parameters
-specifying your card's irq, node ID, and shared memory.  For example,
-	LILO boot: linux ether=9,0x42,0xD0000,0,arc0
-if your card is node number 42h, irq 9, with shared memory at 0xD0000.
-
-NOTE that if you aren't using RIM I, the above command will still work but
-you will need to replace the node ID with an I/O port number, for example:
-	LILO boot: linux ether=9,0x300,0xD0000,0,arc0
+There are four chipset options:
+
+ 1. Standard ARCnet COM90xx chipset.
 
-You can add the ether= parameter to /etc/lilo.conf to avoid typing this
-every time.
+This is the normal ARCnet card, which you've probably got. This is the only
+chipset driver which will autoprobe if not told where the card is.
+It following options on the command line:
+ com90xx=[<io>[,<irq>[,<shmem>]]][,<name>] | <name>
+
+If you load the chipset support as a module, the options are:
+ io=<io> irq=<irq> shmem=<shmem> device=<name>
+
+To disable the autoprobe, just specify "com90xx=" on the kernel command line.
+To specify the name alone, but allow autoprobe, just put "com90xx=<name>"
+
+ 2. ARCnet COM20020 chipset.
+
+This is the new chipset from SMC with support for promiscuous mode (packet 
+sniffing), extra diagnostic information, etc. Unfortunately, there is no
+sensible method of autoprobing for these cards. You must specify the I/O
+address on the kernel command line.
+The command line options are:
+ com20020=<io>[,<irq>[,<node_ID>[,backplane[,CKP[,timeout]]]]][,name]
+
+If you load the chipset support as a module, the options are:
+ io=<io> irq=<irq> node=<node_ID> backplane=<backplane> clock=<CKP>
+ timeout=<timeout> device=<name>
+
+The COM20020 chipset allows you to set the node ID in software, overriding the
+default which is still set in DIP switches on the card. If you don't have the
+COM20020 data sheets, and you don't know what the other three options refer
+to, then they won't interest you - forget them.
+
+ 3. ARCnet COM90xx chipset in IO-mapped mode.
+
+This will also work with the normal ARCnet cards, but doesn't use the shared
+memory. It performs less well than the above driver, but is provided in case
+you have a card which doesn't support shared memory, or (strangely) in case
+you have so many ARCnet cards in your machine that you run out of shmem slots.
+If you don't give the IO address on the kernel command line, then the driver
+will not find the card.
+The command line options are:
+ com90io=<io>[,<irq>][,<name>] 
+
+If you load the chipset support as a module, the options are:
+ io=<io> irq=<irq> device=<name>
+
+ 4. ARCnet RIM I cards.
+
+These are COM90xx chips which are _completely_ memory mapped. The support for
+these is not tested. If you have one, please mail the author with a success 
+report. All options must be specified, except the device name.
+Command line options:
+ arcrimi=<shmem>,<irq>,<node_ID>[,<name>]
+
+If you load the chipset support as a module, the options are:
+ shmem=<shmem> irq=<irq> node=<node_ID> device=<name>
 
 
 Loadable Module Support
 -----------------------
 
-Configure and rebuild Linux.  When asked, answer 'm' to "arcnet support" if
-you want to use the loadable module.
+Configure and rebuild Linux.  When asked, answer 'm' to "Generic ARCnet 
+support" and to support for your ARcnet chipset if you want to use the
+loadable module. You can also say 'y' to "Generic ARCnet support" and 'm' 
+to the chipset support if you wish.
 
 	make config
 	make dep
@@ -152,20 +201,18 @@
 
 For example:
 	cd /usr/src/linux/modules
-	insmod arcnet.o io=0x300 irq=2 shmem=0xd0000
-
-You can name the device using something like "device=arc1" (for a second
-card) or "device=eth0" (for weird compatibility reasons) if you like.
-
-If you use RIM I, you don't need to specify io= but you must include node=
-for your ARCnet card's station ID.
+	insmod arcnet.o
+	insmod com90xx.o
+	insmod com20020.o io=0x2e0 device=eth1
 	
 
 Using the Driver
 ----------------
 
-If you build your kernel with ARCnet support included, it should probe for
-your card automatically when you boot.
+If you build your kernel with ARCnet COM90xx support included, it should 
+probe for your card automatically when you boot. If you use a different
+chipset driver complied into the kernel, you must give the necessary options
+on the kernel command line, as detailed above.
 
 Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
 available where you picked up this driver.  Think of your ARCnet as a
@@ -180,13 +227,19 @@
 ------------------------------
 
 Linux has pretty good support for this now, but since I've been busy, the
-ARCnet driver has somewhat suffered in this respect.  For now, the easiest
-way to use multiple ARCnet cards is to build it as a loadable module and
-then do something like this:
-	insmod -o arc0 arcnet
-	insmod -o arc1 arcnet device=arc1
-(Note that in the first line, the default is device=arc0, but it doesn't
-hurt if you want to add it for consistency.)
+ARCnet driver has somewhat suffered in this respect. COM90xx support, if 
+compiled into the kernel, will (try to) autodetect all the installed cards. 
+
+If you have other cards, with support compiled into the kernel, then you can 
+just repeat the options on the kernel command line, e.g.:
+LILO: linux com20020=0x2e0 com20020=0x380 com90io=0x260
+
+If you have the chipset support built as a loadable module, then you need to 
+do something like this:
+	insmod -o arc0 com90xx
+	insmod -o arc1 com20020 io=0x2e0
+	insmod -o arc2 com90xx
+The ARCnet drivers will now sort out their names automatically.
 
 
 How do I get it to work with...?
@@ -441,7 +494,7 @@
 Send mail describing your setup, preferably including driver version, kernel
 version, ARCnet card model, CPU type, number of systems on your network, and
 list of software in use to me at the following address:
-	apenwarr@foxnet.net
+	apenwarr@bond.net
 
 I do send (sometimes automated) replies to all messages I receive.  My email
 can be weird (and also usually gets forwarded all over the place along the
diff -u --recursive --new-file v2.1.52/linux/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt
--- v2.1.52/linux/Documentation/networking/ip-sysctl.txt	Wed Dec 18 01:45:43 1996
+++ linux/Documentation/networking/ip-sysctl.txt	Thu Sep  4 13:25:28 1997
@@ -80,6 +80,10 @@
 	(sort of gated, routed etc. etc.) is confused by such packets,
 	even if they are valid.
 
+	NOTE: this option is turned on per default only when ip_forwarding
+	is on. For non-forwarding hosts it doesn't make much sense and 
+	makes some legal multihoming configurations impossible.
+
 ip_fib_model - INTEGER
 	0 - (DEFAULT) Standard model. All routes are in class MAIN.
 	1 - default routes go to class DEFAULT. This mode should
@@ -88,7 +92,61 @@
 	    Interface routes are in class MAIN.
 	    Gateway routes are in class DEFAULT.
 
+IP Fragmentation:
+
+ipfrag_high_thresh - INTEGER
+	Maximum memory used to reassemble IP fragments. When 
+	ipfrag_high_thresh bytes of memory is allocated for this purpose,
+	the fragment handler will toss packets until ipfrag_low_thresh
+	is reached.
+	
+ipfrag_low_thresh - INTEGER
+	See ipfrag_high_thresh	
+
+ipfrag_time - INTEGER
+	Time in seconds to keep an IP fragment in memory.	
+
+TCP variables: 
+
+tcp_syn_retries - INTEGER
+	Number of times initial SYNs for an TCP connection attempt will
+	be retransmitted. Should not be higher that 255.
 
+tcp_keepalive_time - INTEGER
+	How often TCP sends out keepalive messages when keepalive is enabled.
+	Default: 2hours.
+
+tcp_keepalive_probes - INTEGER
+	How many keepalive probes TCP sends out, until it decides that the
+	connection is broken.
+
+tcp_retries1 - INTEGER
+tcp_retries2 - INTEGER
+tcp_max_delay_acks - INTEGER
+tcp_fin_timeout - INTEGER
+tcp_max_ka_probes - INTEGER
+	Undocumented for now.
+
+tcp_syncookies - BOOLEAN
+	Only valid when the kernel was compiled with CONFIG_SYNCOOKIES
+	Send out syncookies when the syn backlog queue of a socket 
+	overflows. This is to prevent against the common 'syn flood attack'
+	Default: FALSE
+
+tcp_stdurg - BOOLEAN
+	Use the Host requirements interpretation of the TCP urg pointer field.
+	Most hosts use the older BSD interpretation, so if you turn this on
+	Linux might not communicate correctly with them.	
+	Default: FALSE 
+	
+tcp_syn_taildrop  - BOOLEAN
+tcp_max_syn_backlog - INTEGER
+	Undocumented (work in progress)
 
 Alexey Kuznetsov.
 kuznet@ms2.inr.ac.ru
+
+Updated by:
+Andi Kleen
+ak@muc.de
+$Id: ip-sysctl.txt,v 1.3 1997/08/22 19:22:00 freitag Exp $
diff -u --recursive --new-file v2.1.52/linux/Documentation/networking/soundmodem.txt linux/Documentation/networking/soundmodem.txt
--- v2.1.52/linux/Documentation/networking/soundmodem.txt	Mon Aug 11 14:47:04 1997
+++ linux/Documentation/networking/soundmodem.txt	Thu Sep  4 13:25:28 1997
@@ -9,7 +9,9 @@
 not support it (it appeared that the card only provides one DMA channel,
 although the Codec chip would support two channels). The driver needs
 some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise
-interactive performance of the computer may become sluggish.
+interactive performance of the computer may become sluggish. This driver
+does *not* support telephone modem standards, it is intended for radio
+use only.
 
 
 The Interface of the driver
diff -u --recursive --new-file v2.1.52/linux/Documentation/powerpc/00-INDEX linux/Documentation/powerpc/00-INDEX
--- v2.1.52/linux/Documentation/powerpc/00-INDEX	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/powerpc/00-INDEX	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,9 @@
+Index of files in Documentation/powerpc.  If you think something about
+Linux/PPC needs an entry here, needs correction of you've written one
+please mail me.
+                                        Cort Dougan (cort@cs.nmt.edu)
+
+00-INDEX
+	- this file
+ppc_htab.txt
+	- info about the Linux/PPC /proc/ppc_htab entry
diff -u --recursive --new-file v2.1.52/linux/Documentation/powerpc/ppc_htab.txt linux/Documentation/powerpc/ppc_htab.txt
--- v2.1.52/linux/Documentation/powerpc/ppc_htab.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/powerpc/ppc_htab.txt	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,50 @@
+                 Information about /proc/ppc_htab
+=====================================================================
+
+This entry in the proc directory is readable by all users but only
+writable by root.
+
+
+1. Reading
+
+  Reading this file will give you information about the memory management
+  hash table that serves as an extended tlb for page translation on the
+  powerpc.
+
+  Explaination of the fields:
+
+    Size - hash table size in Kb.
+    Buckets -  number of buckets in the table.
+    Addess - the virtual kernel address of the hash table base.
+    Entries - the number of ptes that can be stored in the hash table.
+    User/Kernel - how many pte's are in use by the kernel or user at that time.
+    Overflows - How many of the entries are in their secondary hash location.
+    Percent full - ratio of free pte entries to in use entries.
+
+  Note that calculation of the data displayed from /proc/ppc_htab takes
+  a long time and spends a great deal of time in the kernel.  It would
+  be quite hard on performance to read this file constantly.  In time
+  there may be a counter in the kernel that allows successive reads from
+  this file only after a given amount of time has passed to reduce the
+  possibility of a user slowing the system by reading this file.
+
+2. Writing
+
+  Writing to ppc_htab is not yet allowed.
+
+  Write options to ppc_htab:
+  
+   - To set the size of the hash table to 64Kb:
+
+      echo 'size 64' > /dev/ppc_htab
+
+     The size must be a multiple of 64 and must be greater than or equal to
+     64.
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.52/linux/Makefile linux/Makefile
--- v2.1.52/linux/Makefile	Wed Sep  3 20:52:41 1997
+++ linux/Makefile	Wed Sep  3 20:27:20 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 52
+SUBLEVEL = 53
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/Makefile linux/arch/ppc/Makefile
--- v2.1.52/linux/arch/ppc/Makefile	Mon Aug 18 18:19:43 1997
+++ linux/arch/ppc/Makefile	Thu Sep  4 12:54:48 1997
@@ -1,6 +1,3 @@
-#
-# ppc/Makefile
-#
 # This file is included by the global makefile so that you can add your own
 # architecture-specific flags and dependencies. Remember to do have actions
 # for "archclean" and "archdep" for cleaning up and making dependencies for
@@ -12,7 +9,7 @@
 #
 # Copyright (C) 1994 by Linus Torvalds
 # Changes for PPC by Gary Thomas
-# Modified by Cort Dougan and Paul Mackerras
+# Rewritten by Cort Dougan and Paul Mackerras
 #
 
 ifeq ($(CONFIG_PMAC),y)
@@ -23,18 +20,16 @@
 
 # PowerPC (cross) tools
 ifneq ($(shell uname -m),ppc)
-CROSS_COMPILE 	=powerpc-eabi-
+CROSS_COMPILE 	= ppc-linux-elf-
+else
+CHECKS		= checks
 endif
 
 ASFLAGS		=
 LINKFLAGS	= -T arch/ppc/vmlinux.lds -Ttext $(KERNELBASE) -Bstatic
 CFLAGSINC	= -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__
-CFLAGS		= $(CFLAGSINC) \
-		-Wall -Wstrict-prototypes -Wno-uninitialized \
-		-fno-builtin \
-		-fsigned-char \
-		-msoft-float \
-		-O2 -pipe
+CFLAGS		:= $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \
+		-fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring
 CPP		= $(CC) -E $(CFLAGS)
 
 ifdef CONFIG_601
@@ -64,7 +59,7 @@
 ifdef CONFIG_PMAC
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/coffboot
 else
-# PReP systems
+# PReP and CHRP systems
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 endif
 
@@ -74,7 +69,7 @@
 BOOT_TARGETS = netboot znetboot zImage floppy install \
 	vmlinux.coff znetboot.initrd zImage.initrd
 
-$(BOOT_TARGETS): checks vmlinux
+$(BOOT_TARGETS): $(CHECKS) vmlinux
 	@$(MAKEBOOT) $@
 
 tags:
@@ -83,9 +78,6 @@
 archclean:
 	rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h
 	rm -f arch/ppc/kernel/checks
-	find arch/ppc/ -name '*.o' -exec /bin/rm -f '{}' \;
-	find arch/ppc/ -name '*~' -exec /bin/rm -f '{}' \;
-	find arch/ppc/ -name '*.a' -exec /bin/rm -f '{}' \;
 	@$(MAKEBOOT) clean
 
 archdep:
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/boot/Makefile linux/arch/ppc/boot/Makefile
--- v2.1.52/linux/arch/ppc/boot/Makefile	Mon Aug 18 18:19:43 1997
+++ linux/arch/ppc/boot/Makefile	Thu Sep  4 12:54:48 1997
@@ -26,9 +26,9 @@
 GZIP_FLAGS = -v9
 
 SYSTEM = $(TOPDIR)/vmlinux
-OBJECTS = head.o inflate.o unzip.o misc.o vreset.o #kbd.o
+OBJECTS := head.o inflate.o unzip.o misc.o vreset.o kbd.o
 CFLAGS = -O2 -DSTDC_HEADERS -I$(TOPDIR)/include
-OBJCOPY = objcopy
+OBJCOPY = $(CROSS_COMPILE)objcopy
 OBJCOPY_ARGS = -O elf32-powerpc
 
 
@@ -37,6 +37,9 @@
 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
 
@@ -52,6 +55,25 @@
 znetboot.initrd : zImage.initrd mkprep
 	cp $(TOPDIR)/zImage.initrd /usr/local/tftpboot/vmlinux
 
+#
+# 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
+#
+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
 zImage: zvmlinux mkprep
 	mkprep -pbp zvmlinux $(TOPDIR)/zImage
 
@@ -63,6 +85,7 @@
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz \
 		zvmlinux.tmp $@
 	rm zvmlinux.tmp
+endif
 
 vmlinux.gz: $(TOPDIR)/vmlinux
 	dd bs=64k skip=1 if=$(TOPDIR)/vmlinux | gzip -vf9 - > vmlinux.gz
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/boot/head.S linux/arch/ppc/boot/head.S
--- v2.1.52/linux/arch/ppc/boot/head.S	Mon Aug 18 18:19:43 1997
+++ linux/arch/ppc/boot/head.S	Thu Sep  4 12:54:48 1997
@@ -94,8 +94,8 @@
 	   as ptr to residual -- Cort*/
 	lis	r6,cmd_line@h
 	ori	r6,r6,cmd_line@l
-	subi	r7,r7,1
-00:	lbzu	r2,1(r12)
+	subi	r7,r6,1
+00:	lbzu	r2,1(r7)
 	cmpi	0,r2,0
 	bne	00b
 
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/boot/kbd.c linux/arch/ppc/boot/kbd.c
--- v2.1.52/linux/arch/ppc/boot/kbd.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/boot/kbd.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,166 @@
+/* Keyboard handler */
+
+#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */
+
+#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 */
+
+unsigned char shfts, ctls, alts, caps, num, stp;
+
+#define	KBDATAP		0x60	/* kbd data port */
+#define	KBSTATUSPORT	0x61	/* kbd status */
+#define	KBSTATP		0x64	/* kbd status port */
+#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;
+{
+	unsigned char dt, brk, act;
+	int first = 1;	
+loop:
+	if (noblock) {
+		if ((inb(KBSTATP) & KBINRDY) == 0)
+			return (-1);
+	} else while((inb(KBSTATP) & KBINRDY) == 0) ;
+
+	dt = inb(KBDATAP);
+
+	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 (alts)
+			chr |= 0x80;
+
+		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;
+			}
+		}
+		return (chr);
+	}
+	if (first && brk) return (0);  /* Ignore initial 'key up' codes */
+	goto loop;
+}
+
+static
+scankbd(void) {
+	return (kbd(1) != -1);
+}
+
+static 
+kbdreset(void)
+{
+	unsigned char c;
+	int i;
+
+	/* Send self-test */
+	while (inb(KBSTATP) & KBOUTRDY) ;
+	outb(KBSTATP,0xAA);
+	while ((inb(KBSTATP) & KBINRDY) == 0) ;	/* wait input ready */
+	if ((c = inb(KBDATAP)) != 0x55)
+	{
+		puts("Keyboard self test failed - result:");
+		puthex(c);
+		puts("\n");
+	}
+	/* Enable interrupts and keyboard controller */
+	while (inb(KBSTATP) & KBOUTRDY) ;
+	outb(KBSTATP,0x60);	
+	while (inb(KBSTATP) & KBOUTRDY) ;
+	outb(KBDATAP,0x45);
+	for (i = 0;  i < 10000;  i++) udelay(1);
+	while (inb(KBSTATP) & KBOUTRDY) ;
+	outb(KBSTATP,0xAE);
+}
+
+static int kbd_reset = 0;
+
+CRT_getc(void)
+{
+	int c;
+	if (!kbd_reset) {kbdreset(); kbd_reset++; }
+	while ((c = kbd(0)) == 0) ;
+	return(c);
+}
+
+CRT_tstc(void)
+{
+	if (!kbd_reset) {kbdreset(); kbd_reset++; }
+	return ((inb(KBSTATP) & KBINRDY) != 0);
+}
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/boot/misc.c linux/arch/ppc/boot/misc.c
--- v2.1.52/linux/arch/ppc/boot/misc.c	Mon Aug 18 18:19:43 1997
+++ linux/arch/ppc/boot/misc.c	Thu Sep  4 12:54:48 1997
@@ -126,7 +126,7 @@
 	for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
 		vidmem[i] = ' ';
 }
-#if 0
+
 tstc(void)
 {
 	return (CRT_tstc() );
@@ -138,7 +138,7 @@
 		if (CRT_tstc()) return (CRT_getc());
 	}
 }
-#endif
+
 void 
 putc(const char c)
 {
@@ -378,6 +378,7 @@
   Elf32_Shdr *sh, *strtab_shdr;
   char *strtab;
   unsigned long i;
+  extern unsigned long start(void);
 
   
   output_data = (char *)0x0;	/* Points to 0 */
@@ -405,16 +406,17 @@
   clear_bufs();
   makecrc();
 
+  puts("Cksum: ");  puthex(cksum);  puts("\n");
   puts("Loaded at: ");  puthex(load_addr); puts(" ");  puthex(num_words+load_addr);
   puts("\n");
-  /*puts("Relocated to start: ");  puthex(start); puts(" ");
-  puthex(num_words+start);
-  puts("\n");*/
-  puts("Cksum: ");  puthex(cksum);  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)(residual->ResidualLength + residual));  puts("\n");
+    puthex((unsigned long)((unsigned long)(residual->ResidualLength) + residual));  puts("\n");
+    puts("Residual data relocated to: ");  puthex((unsigned long)&hold_residual); puts("\n");
   }
 
   /*
@@ -487,7 +489,39 @@
 	  puts(" "); puthex(initrd_end); puts("\n");
   }
 
-  /* make the moto firmware print something */
+
+  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");
+       
   
   puts("Uncompressing Linux...");
   method = get_method(0);
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.52/linux/arch/ppc/config.in	Wed Sep  3 20:52:42 1997
+++ linux/arch/ppc/config.in	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.16 1997/08/11 08:37:49 cort Exp $
+# $Id: config.in,v 1.19 1997/09/04 01:54:26 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -8,20 +8,21 @@
 comment 'Platform support'
 define_bool CONFIG_PPC y
 
-if [ "`uname`" != "Linux" ]; then
+if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then
   define_bool CONFIG_CROSSCOMPILE y
 else
   define_bool CONFIG_NATIVE y
 fi
 
-bool 'Build PowerMac Kernel (not PReP)?' CONFIG_PMAC
-bool 'Build PReP Kernel (not PowerMac)?' CONFIG_PREP
+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
 
 choice 'Processor type' \
-	"Common		CONFIG_MCOMMON \
-	 601		CONFIG_M601	\
-	 603		CONFIG_M603	\
-	 604		CONFIG_M604" Common
+	"Common		CONFIG_COMMON \
+	 601		CONFIG_601	\
+	 603		CONFIG_603	\
+	 604		CONFIG_604" Common
 endmenu
 
 mainmenu_option next_comment
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.1.52/linux/arch/ppc/defconfig	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/defconfig	Thu Sep  4 12:54:48 1997
@@ -5,6 +5,7 @@
 #
 # Platform support
 #
+CONFIG_PPC=y
 CONFIG_NATIVE=y
 # CONFIG_PMAC is not set
 CONFIG_PREP=y
@@ -26,6 +27,7 @@
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+CONFIG_VGA_CONSOLE=y
 
 #
 # Plug and Play support
@@ -86,10 +88,14 @@
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_NCR53C406A is not set
-CONFIG_SCSI_NCR53C7xx=y
-# CONFIG_SCSI_NCR53C7xx_sync is not set
-# CONFIG_SCSI_NCR53C7xx_FAST is not set
-# CONFIG_SCSI_NCR53C7xx_DISCONNECT is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
+CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
+CONFIG_SCSI_NCR53C8XX_SYNC=5
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PPA is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
@@ -118,6 +124,7 @@
 # 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
@@ -146,6 +153,7 @@
 # CONFIG_VORTEX is not set
 CONFIG_LANCE=y
 # CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
 CONFIG_PCNET32=y
@@ -185,8 +193,8 @@
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
 # CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
@@ -210,8 +218,14 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
 CONFIG_SERIAL=y
-# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_SERIAL_EXTENDED=y
+# CONFIG_SERIAL_MANY_PORTS is not set
+# CONFIG_SERIAL_SHARE_IRQ is not set
+# CONFIG_SERIAL_MULTIPORT is not set
+# CONFIG_HUB6 is not set
+CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_PRINTER is not set
 CONFIG_MOUSE=y
@@ -233,26 +247,4 @@
 #
 # Sound
 #
-#CONFIG_SOUND=y
-# 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=y
-# CONFIG_MAUI is not set
-# CONFIG_YM3812 is not set
-CS4232_BASE=830
-CS4232_IRQ=10
-CS4232_DMA=6
-CS4232_DMA2=7
-CS4232_MPU_BASE=330
-CS4232_MPU_IRQ=9
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/ignore linux/arch/ppc/ignore
--- v2.1.52/linux/arch/ppc/ignore	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/ignore	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,65 @@
+.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.52/linux/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- v2.1.52/linux/arch/ppc/kernel/Makefile	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/Makefile	Thu Sep  4 12:54:48 1997
@@ -12,20 +12,17 @@
 
 O_TARGET := kernel.o
 O_OBJS := misc.o traps.o process.o signal.o syscalls.o \
-	  align.o ptrace.o irq.o bitops.o ppc_htab.o idle.o prom.o \
-	  time.o prep_time.o pmac_time.o \
-	  setup.o pmac_setup.o pmac_support.o \
-	  pci.o prep_pci.o pmac_pci.o
+	  align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \
+	  time.o prep_time.o pmac_time.o chrp_time.o \
+	  setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
+	  pci.o prep_pci.o pmac_pci.o chrp_pci.o \
+	  residual.o prom.o
 OX_OBJS := ppc_ksyms.o
 
 all: head.o kernel.o
 
 head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h
 
-ifeq ($(CONFIG_PREP),y)
-O_OBJS += prep_setup.o #prep_time.o
-endif
-
 ppc_defs.h: mk_defs.c ppc_defs.head \
 		$(TOPDIR)/include/asm/mmu.h \
 		$(TOPDIR)/include/asm/processor.h \
@@ -37,7 +34,7 @@
 	rm mk_defs.s
 
 checks: checks.c
-	$(HOSTCC) ${CFLAGS} -o checks checks.c
+	$(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
 	./checks
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.1.52/linux/arch/ppc/kernel/chrp_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/chrp_pci.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,156 @@
+/*
+ * CHRP pci routines.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/openpic.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hydra.h>
+
+/* LongTrail */
+#define pci_config_addr(bus, dev, offset) \
+	(0xfec00000 | ((bus)<<16) | ((dev)<<8) | (offset))
+
+int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+				  unsigned char offset, unsigned char *val)
+{
+    if (bus > 7) {
+	*val = 0xff;
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    }
+    *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset));
+    if (offset == PCI_INTERRUPT_LINE) {
+	/* PCI interrupts are controlled by the OpenPIC */
+	if (*val)
+	    *val = openpic_to_irq(*val);
+    }
+    return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+				  unsigned char offset, unsigned short *val)
+{
+    if (bus > 7) {
+	*val = 0xffff;
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    }
+    *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset));
+    return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned int *val)
+{
+    if (bus > 7) {
+	*val = 0xffffffff;
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    }
+    *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset));
+    return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned char val)
+{
+    if (bus > 7)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+				   unsigned char offset, unsigned short val)
+{
+    if (bus > 7)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned int val)
+{
+    if (bus > 7)
+	return PCIBIOS_DEVICE_NOT_FOUND;
+    out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val);
+    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_find_class(unsigned int class_code, unsigned short index,
+			    unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+    int devnr, x, num;
+
+    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;
+	}
+    }
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+__initfunc(volatile struct Hydra *find_hydra(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);
+}
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.1.52/linux/arch/ppc/kernel/chrp_setup.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/chrp_setup.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,180 @@
+/*
+ *  linux/arch/ppc/kernel/setup.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ *  Adapted from 'alpha' version by Gary Thomas
+ *  Modified by Cort Dougan (cort@cs.nmt.edu)
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/ioport.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+/* for the mac fs */
+kdev_t boot_dev;
+
+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
+extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt;		/* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start;	/* starting block # of image */
+#endif
+
+
+extern char saved_command_line[256];
+
+long TotalMemory;
+
+int
+chrp_get_cpuinfo(char *buffer)
+{
+	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;
+	}
+
+	len = sprintf(buffer, "PowerPC %s rev %d.%d\n", model,
+		      (pvr & 0xff00) >> 8, pvr & 0xff);
+
+	len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n",
+		       (loops_per_sec+2500)/500000,
+		       ((loops_per_sec+2500)/5000) % 100);
+
+#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));
+	}
+#endif
+	return len;
+}
+
+__initfunc(void
+chrp_setup_arch(char **cmdline_p, 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;
+	}
+	
+#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 )
+	{
+		if (initrd_end > *memory_end_p)
+		{
+			printk("initrd extends beyond end of memory "
+			       "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+			       initrd_end,*memory_end_p);
+			initrd_start = 0;
+		}
+	}
+#endif
+
+	printk("Boot arguments: %s\n", cmd_line);
+	
+	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");
+}
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/chrp_time.c linux/arch/ppc/kernel/chrp_time.c
--- v2.1.52/linux/arch/ppc/kernel/chrp_time.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/chrp_time.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,132 @@
+/*
+ *  linux/arch/i386/kernel/time.c
+ *
+ *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
+ *
+ * Adapted for PowerPC (PreP) by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ *  copied and modified from intel version
+ *
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/kernel_stat.h>
+#include <linux/mc146818rtc.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/nvram.h>
+
+#include "time.h"
+
+int chrp_cmos_clock_read(int addr)
+{
+	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);
+	return;
+}
+
+/*
+ * Set the hardware clock. -- Cort
+ */
+int chrp_set_rtc_time(unsigned long nowtime)
+{
+	unsigned char save_control, save_freq_select;
+	struct rtc_time tm;
+
+	to_tm(nowtime, &tm);
+
+	save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */
+
+	chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL);
+
+	save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */
+	
+	chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+        tm.tm_year -= 1900;
+	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BIN_TO_BCD(tm.tm_sec);
+		BIN_TO_BCD(tm.tm_min);
+		BIN_TO_BCD(tm.tm_hour);
+		BIN_TO_BCD(tm.tm_mon);
+		BIN_TO_BCD(tm.tm_mday);
+		BIN_TO_BCD(tm.tm_year);
+	}
+	chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS);
+	chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES);
+	chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS);
+	chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH);
+	chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH);
+	chrp_cmos_clock_write(tm.tm_year,RTC_YEAR);
+	
+	/* The following flags have to be released exactly in this order,
+	 * otherwise the DS12887 (popular MC146818A clone with integrated
+	 * battery and quartz) will not reset the oscillator and will not
+	 * update precisely 500 ms later. You won't find this mentioned in
+	 * the Dallas Semiconductor data sheets, but who believes data
+	 * sheets anyway ...                           -- Markus Kuhn
+	 */
+	chrp_cmos_clock_write(save_control, RTC_CONTROL);
+	chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT);
+
+	if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) )
+		time_state = TIME_OK;
+	return 0;
+}
+
+unsigned long chrp_get_rtc_time(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+	int i;
+
+	/* The Linux interpretation of the CMOS clock register contents:
+	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+	 * RTC registers show the second which has precisely just started.
+	 * Let's hope other operating systems interpret the RTC the same way.
+	 */
+	/* read RTC exactly on falling edge of update flag */
+	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
+		if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)
+			break;
+	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
+		if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP))
+			break;
+	do { /* Isn't this overkill ? UIP above should guarantee consistency */
+		sec = chrp_cmos_clock_read(RTC_SECONDS);
+		min = chrp_cmos_clock_read(RTC_MINUTES);
+		hour = chrp_cmos_clock_read(RTC_HOURS);
+		day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH);
+		mon = chrp_cmos_clock_read(RTC_MONTH);
+		year = chrp_cmos_clock_read(RTC_YEAR);
+	} while (sec != chrp_cmos_clock_read(RTC_SECONDS));
+	if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+	  {
+	    BCD_TO_BIN(sec);
+	    BCD_TO_BIN(min);
+	    BCD_TO_BIN(hour);
+	    BCD_TO_BIN(day);
+	    BCD_TO_BIN(mon);
+	    BCD_TO_BIN(year);
+	  }
+	if ((year += 1900) < 1970)
+		year += 100;
+	return mktime(year, mon, day, hour, min, sec);
+}
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.1.52/linux/arch/ppc/kernel/head.S	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/head.S	Thu Sep  4 12:54:48 1997
@@ -175,36 +175,27 @@
  * 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
-	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
-
-#if 0	
-/*
- * Now we have the 1st 8M of RAM mapped at KERNELBASE, so we can
- * refer to addresses of data items, procedures, etc. normally.
- */
-	lis	r10,start_here@ha	/* jump up to our copy at KERNELBASE */
-	addi	r10,r10,start_here@l
-	mtlr	r10
-	blr
-#endif
+        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
 /*
  * 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.
@@ -220,7 +211,6 @@
 	SYNC
 	rfi				/* enables MMU */
 		
-
 /*
  * GCC sometimes accesses words at negative offsets from the stack
  * pointer, although the SysV ABI says it shouldn't.  To cope with
@@ -834,15 +824,13 @@
 	bne	3f			/* don't invalidate the D-cache */
 	ori	r8,r8,HID0_DCI		/* unless it wasn't enabled */
 3:
-	/* I haven't tested this yet so it's off now - Cort */
 	/* turn on dpm for 603 */
 	cmpi	0,r9,3
 	bne	10f
 	oris	r11,r11,HID0_DPM@h
-10:	
+10:
 	sync
 	mtspr	HID0,r8			/* enable and invalidate caches */
-	sync
 	mtspr	HID0,r11		/* enable caches */
 	sync
 	isync
@@ -954,6 +942,30 @@
 	mtspr	SRR1,r4
 	rfi			/* enable MMU and jump to start_kernel */
 
+
+	.globl reset_SDR1
+reset_SDR1:
+	lis	r6,_SDR1@ha
+	lwz	r6,_SDR1@l(r6)
+	mfmsr	r3
+	li	r4,MSR_IR|MSR_DR
+	andc	r3,r3,r4
+	lis	r4,2f@h
+	addis	r4,r4,-KERNELBASE@h
+	ori	r4,r4,2f@l
+	mtspr	SRR0,r4
+	mtspr	SRR1,r3
+	rfi
+2:	/* load new SDR1 */
+	tlbia			
+	mtspr	SDR1,r6
+	/* turn the mmu back on */
+	li	r4,MSR_KERNEL
+	mflr	r3
+	mtspr	SRR0,r3
+	mtspr	SRR1,r4
+	rfi
+	
 /*
  * FP unavailable trap from kernel - print a message, but let
  * the task use FP in the kernel until it returns to user mode.
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.1.52/linux/arch/ppc/kernel/idle.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/idle.c	Thu Sep  4 12:54:48 1997
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.3 1997/08/10 04:49:08 davem Exp $
+ * $Id: idle.c,v 1.4 1997/08/23 22:46:01 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -276,3 +276,4 @@
 		schedule();
 	}
 }
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.1.52/linux/arch/ppc/kernel/irq.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/irq.c	Thu Sep  4 12:54:48 1997
@@ -28,18 +28,30 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/malloc.h>
+#include <linux/openpic.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/openpic.h>
 
+#include <asm/hydra.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
 
 #undef SHOW_IRQ
+#define OPENPIC_DEBUG
 
 unsigned lost_interrupts = 0;
-
 unsigned int local_irq_count[NR_CPUS];
-static struct irqaction irq_action[32];
+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) { }
+
+#define cached_21	(((char *)(&cached_irq_mask))[3])
+#define cached_A1	(((char *)(&cached_irq_mask))[2])
 
 /*
  * These are set to the appropriate functions by init_IRQ()
@@ -47,11 +59,6 @@
 void (*mask_and_ack_irq)(int irq_nr);
 void (*set_irq_mask)(int irq_nr);
 
-static unsigned int cached_irq_mask = 0xffffffff;
-#define cached_21	(((char *)(&cached_irq_mask))[3])
-#define cached_A1	(((char *)(&cached_irq_mask))[2])
-static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-int __ppc_bh_counter;
 
 /* prep */
 #define PREP_IRQ_MASK	(((unsigned int)cached_A1)<<8) | (unsigned int)cached_21
@@ -65,7 +72,10 @@
 #define KEYBOARD_IRQ	20	/* irq number for command-power interrupt */
 #define PMAC_IRQ_MASK	(~ld_le32(IRQ_ENABLE))
 
-void prep_mask_and_ack_irq(int irq_nr)
+/* chrp */
+volatile struct Hydra *Hydra = NULL;
+
+void i8259_mask_and_ack_irq(int irq_nr)
 {
 	spin_lock(&irq_controller_lock);
 	cached_irq_mask |= 1 << irq_nr;
@@ -94,7 +104,16 @@
 	spin_unlock(&irq_controller_lock);
 }
 
-void prep_set_irq_mask(int irq_nr)
+void chrp_mask_and_ack_irq(int irq_nr)
+{
+	/* 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)
 {
 	if (irq_nr > 7) {
 		outb(cached_A1,0xA1);
@@ -116,6 +135,21 @@
 		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));
+	}
+}
+
 /*
  * These have to be protected by the spinlock
  * before being called.
@@ -171,6 +205,8 @@
 	len+=sprintf(buf+len, "IPI: %8lu received\n",
 		ipi_count);
 #endif
+	len += sprintf(buf+len, "99: %10u    spurious or short\n",
+		       spurious_interrupts);
 	return len;
 }
 
@@ -184,13 +220,56 @@
 
 	hardirq_enter(cpu);
 
-	if ( _machine == _MACH_Pmac )
+
+	/*
+	 * I'll put this ugly mess of code into a function
+	 * such as get_pending_irq() or some such clear thing
+	 * so we don't have a switch in the irq code and
+	 * the chrp code is merged a bit with the prep.
+	 * -- Cort
+	 */
+	switch ( _machine )
 	{
+	case _MACH_Pmac:
 		bits = ld_le32(IRQ_FLAG) | lost_interrupts;
 		lost_interrupts = 0;
-	}
-	else /* prep */
-	{
+		for (irq = NR_IRQS - 1; irq >= 0; --irq)
+			if (bits & (1U << irq))
+				break;
+		break;
+	case _MACH_chrp:
+		irq = openpic_irq(0);		
+		if (irq == IRQ_8259_CASCADE)
+		{
+			/*
+			 * This magic address generates a PCI IACK cycle.
+			 * 
+			 * This should go in the above mask/ack code soon. -- Cort
+			 */
+			irq = (*(volatile unsigned char *)0xfec80000) & 0x0f;
+		}
+		else if (irq >= 64)
+		{
+			/*
+			 *  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)
+				spurious_interrupts++;
+		}
+		else {
+			/*
+			 *  Here we should process IPI timer
+			 *  for now the interrupt is dismissed.
+			 */
+			goto out;
+		}
+		break;
+	case _MACH_IBM:
+	case _MACH_Motorola:
 #if 1
 		outb(0x0C, 0x20);
 		irq = inb(0x20) & 7;
@@ -221,13 +300,10 @@
 		}
 		/* ignore masked irqs */
 		bits &= ~cached_irq_mask;
-#endif
+#endif		
+		break;
 	}
 	
-	for (irq = NR_IRQS - 1; irq >= 0; --irq)
-		if (bits & (1U << irq))
-			break;
-
 	if (irq < 0) {
 		printk("Bogus interrupt from PC = %lx\n", regs->nip);
 		goto out;
@@ -238,12 +314,12 @@
 	status = 0;
 	action = irq_action + irq;
 	kstat.interrupts[irq]++;
-	if ( action )
+	if ( action && 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 */
@@ -251,11 +327,13 @@
 		unmask_irq(irq);
 		spin_unlock(&irq_controller_lock);
 	} else {
+		if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
+			spurious_interrupts++;
 		disable_irq( irq );
 	}
 	
 	/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
-	if ( (_machine != _MACH_Pmac)/*prep*/ && (irq > 7) )
+	if ( is_prep && (irq > 7) )
 		goto retry_cascade;
 	/* do_bottom_half is called if necessary from int_return in head.S */
 out:
@@ -329,12 +407,37 @@
 	return 0;
 }
 
+__initfunc(static void i8259_init(void))
+{
+	/* 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(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(0x01, 0xA1); /* Select 8086 mode */
+	outb(0xFF, 0xA1); /* Mask all */
+	outb(cached_A1, 0xA1);
+	outb(cached_21, 0x21);
+	if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0)
+		panic("Could not allocate cascade IRQ!");
+	enable_irq(2);  /* Enable cascade interrupt */
+}
+
 __initfunc(void init_IRQ(void))
 {
 	extern void xmon_irq(int, void *, struct pt_regs *);
 
-	if ( _machine == _MACH_Pmac )
+	switch (_machine)
 	{
+	case _MACH_Pmac:
 		mask_and_ack_irq = pmac_mask_and_ack_irq;
 		set_irq_mask = pmac_set_irq_mask;
 		
@@ -342,28 +445,14 @@
 #ifdef CONFIG_XMON
 		request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0);
 #endif	/* CONFIG_XMON */
-	}
-	else /* prep */
-	{
-		mask_and_ack_irq = prep_mask_and_ack_irq;
-		set_irq_mask = prep_set_irq_mask;
+		break;
+	case _MACH_Motorola:
+	case _MACH_IBM:
+		mask_and_ack_irq = i8259_mask_and_ack_irq;
+		set_irq_mask = i8259_set_irq_mask;
 		
-		/* 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(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(0x01, 0xA1); /* Select 8086 mode */
-		outb(0xFF, 0xA1); /* Mask all */
-
+		i8259_init();
+		route_pci_interrupts();
 		/*
 		 * According to the Carolina spec from ibm irq's 0,1,2, and 8
 		 * must be edge triggered.  Also, the pci intrs must be level
@@ -389,17 +478,40 @@
 			 * Sound on the Powerstack reportedly needs to be edge triggered
 			 */
 			if ( _machine == _MACH_Motorola )
-			{}
+			{
+				/*irq_mode2 &= ~0x04L;
+				outb( irq_mode1 , 0x4d0 );
+				outb( irq_mode2 , 0x4d1 );*/
+			}
 
-			/*outb( irq_mode1 , 0x4d0 );
-			  outb( irq_mode2 , 0x4d1 );*/
 		}
-		outb(cached_A1, 0xA1);
-		outb(cached_21, 0x21);
-		if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0)
-			panic("Could not allocate cascade IRQ!");
-		enable_irq(2);  /* Enable cascade interrupt */
-
-		route_pci_interrupts();
+		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.52/linux/arch/ppc/kernel/openpic.c linux/arch/ppc/kernel/openpic.c
--- v2.1.52/linux/arch/ppc/kernel/openpic.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/openpic.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,546 @@
+/*
+ *  arch/ppc/kernel/openpic.c -- OpenPIC Interrupt Handling
+ *
+ *  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.
+ */
+
+
+/*
+ *  Note: Interprocessor Interrupt (IPI) and Timer support is incomplete
+ */
+
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/openpic.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+
+#define REGISTER_DEBUG
+#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;
+
+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;
+
+
+    /*
+     *  Accesses to the current processor's registers
+     */
+
+#ifndef __powerpc__
+#define THIS_CPU		Private
+#define CHECK_THIS_CPU		do {} while (0)
+#else
+#define THIS_CPU		Processor[cpu]
+#define CHECK_THIS_CPU		check_arg_cpu(cpu)
+#endif
+
+
+    /*
+     *  Sanity checks
+     */
+
+#if 1
+#define check_arg_ipi(ipi) \
+    if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
+	printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi);
+#define check_arg_timer(timer) \
+    if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
+	printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer);
+#define check_arg_vec(vec) \
+    if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
+	printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec);
+#define check_arg_pri(pri) \
+    if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
+	printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri);
+#define check_arg_irq(irq) \
+    if (irq < 0 || irq >= NumSources) \
+	printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq);
+#define check_arg_cpu(cpu) \
+    if (cpu < 0 || cpu >= NumProcessors) \
+	printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu);
+#else
+#define check_arg_ipi(ipi)	do {} while (0)
+#define check_arg_timer(timer)	do {} while (0)
+#define check_arg_vec(vec)	do {} while (0)
+#define check_arg_pri(pri)	do {} while (0)
+#define check_arg_irq(irq)	do {} while (0)
+#define check_arg_cpu(cpu)	do {} while (0)
+#endif
+
+
+    /*
+     *  Dummy interrupt handler
+     */
+
+static void no_action(int ir1, void *dev, struct pt_regs *regs)
+{}
+
+
+    /*
+     *  I/O functions
+     */
+
+#ifdef __i386__
+static inline u_int ld_le32(volatile u_int *addr)
+{
+    return *addr;
+}
+
+static inline void out_le32(volatile u_int *addr, u_int val)
+{
+    *addr = val;
+}
+#endif
+
+static inline u_int openpic_read(volatile u_int *addr)
+{
+    u_int val;
+
+    val = ld_le32(addr);
+#ifdef REGISTER_DEBUG
+    printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val);
+#endif
+    return val;
+}
+
+static inline void openpic_write(volatile u_int *addr, u_int val)
+{
+#ifdef REGISTER_DEBUG
+    printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val);
+#endif
+    out_le32(addr, val);
+}
+
+
+static inline u_int openpic_readfield(volatile u_int *addr, u_int mask)
+{
+    u_int val = openpic_read(addr);
+    return val & mask;
+}
+
+static inline void openpic_writefield(volatile u_int *addr, u_int mask,
+				      u_int field)
+{
+    u_int val = openpic_read(addr);
+    openpic_write(addr, (val & ~mask) | (field & mask));
+}
+
+static inline void openpic_clearfield(volatile u_int *addr, u_int mask)
+{
+    openpic_writefield(addr, mask, 0);
+}
+
+static inline void openpic_setfield(volatile u_int *addr, u_int mask)
+{
+    openpic_writefield(addr, mask, mask);
+}
+
+
+    /*
+     *  Update a Vector/Priority register in a safe manner. The interrupt will
+     *  be disabled.
+     */
+
+static void openpic_safe_writefield(volatile u_int *addr, u_int mask,
+				    u_int field)
+{
+    openpic_setfield(addr, OPENPIC_MASK);
+    /* wait until it's not in use */
+    while (openpic_read(addr) & OPENPIC_ACTIVITY);
+    openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
+}
+
+
+/* -------- Global Operations ---------------------------------------------- */
+
+
+    /*
+     *  Initialize the OpenPIC
+     */
+
+void openpic_init(void)
+{
+    u_int t, i;
+    const char *version, *vendor, *device;
+
+    if (!OpenPIC) {
+	printk("No OpenPIC present\n");
+	return;
+    }
+
+    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) {
+	case 1:
+	    version = "1.0";
+	    break;
+	case 2:
+	    version = "1.2";
+	    break;
+	default:
+	    version = "?.?";
+	    break;
+    }
+    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) >>
+	       OPENPIC_VENDOR_ID_STEPPING_SHIFT;
+    switch (VendorID) {
+	case OPENPIC_VENDOR_ID_APPLE:
+	    vendor = "Apple";
+	    break;
+	default:
+	    vendor = "Unknown";
+	    break;
+    }
+    switch (DeviceID) {
+	case OPENPIC_DEVICE_ID_APPLE_HYDRA:
+	    device = "Hydra";
+	    break;
+	default:
+	    device = "Unknown";
+	    break;
+    }
+    printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", VendorID,
+	   vendor, DeviceID, device, Stepping);
+
+    TimerFrequency = openpic_read(&OpenPIC->Global.Timer_Frequency);
+    printk("OpenPIC timer frequency is ");
+    if (TimerFrequency)
+	printk("%d Hz\n", TimerFrequency);
+    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);
+	/* No processor */
+	openpic_maptimer(i, 0);
+    }
+
+    /* Initialize IPI interrupts */
+    for (i = 0; i < OPENPIC_NUM_IPI; i++) {
+	/* Disabled, Priority 0 */
+	openpic_initipi(i, 0, VEC_IPI+i);
+    }
+
+    /* Initialize external interrupts */
+    /* SIOint (8259 cascade) is special */
+    openpic_initirq(0, 8, VEC_SOURCE, 1, 1); /* 0,1 gives interrupt storm */
+    /* 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);
+	/* Processor 0 */
+	openpic_mapirq(i, 1<<0);
+    }
+
+    /* Initialize the spurious interrupt */
+    openpic_set_spurious(VEC_SPURIOUS);
+
+    if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT,
+		    "OpenPIC cascade", NULL))
+	printk("Unable to get OpenPIC IRQ 0 for cascade\n");
+    openpic_set_priority(0, 0);
+    openpic_disable_8259_pass_through();
+}
+
+
+    /*
+     *  Reset the OpenPIC
+     */
+
+void openpic_reset(void)
+{
+    openpic_setfield(&OpenPIC->Global.Global_Configuration0,
+    		       OPENPIC_CONFIG_RESET);
+}
+
+
+    /*
+     *  Enable/disable 8259 Pass Through Mode
+     */
+
+void openpic_enable_8259_pass_through(void)
+{
+    openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
+    		       OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+
+void openpic_disable_8259_pass_through(void)
+{
+    openpic_setfield(&OpenPIC->Global.Global_Configuration0,
+    		     OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+
+
+#ifndef __i386__
+    /*
+     *  Find out the current interrupt
+     */
+
+u_int openpic_irq(u_int cpu)
+{
+    u_int vec;
+
+    check_arg_cpu(cpu);
+    vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge,
+			    OPENPIC_VECTOR_MASK);
+#if 0
+if (vec != 22 /* SCSI */)
+printk("++openpic_irq: %d\n", vec);
+#endif
+    return vec;
+}
+#endif
+
+
+    /*
+     *  Signal end of interrupt (EOI) processing
+     */
+
+#ifndef __powerpc__
+void openpic_eoi(void)
+#else
+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);
+}
+
+
+    /*
+     *  Get/set the current task priority
+     */
+
+#ifndef __powerpc__
+u_int openpic_get_priority(void)
+#else
+u_int openpic_get_priority(u_int cpu)
+#endif
+{
+    CHECK_THIS_CPU;
+    return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
+			     OPENPIC_CURRENT_TASK_PRIORITY_MASK);
+}
+
+#ifndef __powerpc__
+void openpic_set_priority(u_int pri)
+#else
+void openpic_set_priority(u_int cpu, u_int pri)
+#endif
+{
+    CHECK_THIS_CPU;
+    check_arg_pri(pri);
+    openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority,
+    		       OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
+}
+
+    /*
+     *  Get/set the spurious vector
+     */
+
+u_int openpic_get_spurious(void)
+{
+    return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
+    			     OPENPIC_VECTOR_MASK);
+}
+
+void openpic_set_spurious(u_int vec)
+{
+    check_arg_vec(vec);
+    openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
+    		       vec);
+}
+
+
+    /*
+     *  Initialize one or more CPUs
+     */
+
+void openpic_init_processor(u_int cpumask)
+{
+    openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask);
+}
+
+
+/* -------- Interprocessor Interrupts -------------------------------------- */
+
+
+    /*
+     *  Initialize an interprocessor interrupt (and disable it)
+     *
+     *  ipi: OpenPIC interprocessor interrupt number
+     *  pri: interrupt source priority
+     *  vec: the vector it will produce
+     */
+
+void openpic_initipi(u_int ipi, u_int pri, u_int vec)
+{
+    check_arg_timer(ipi);
+    check_arg_pri(pri);
+    check_arg_vec(vec);
+    openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi),
+    			    OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
+    			    (pri << OPENPIC_PRIORITY_SHIFT) | vec);
+}
+
+
+    /*
+     *  Send an IPI to one or more CPUs
+     */
+
+#ifndef __powerpc__
+void openpic_cause_IPI(u_int ipi, u_int cpumask)
+#else
+void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask)
+#endif
+{
+    CHECK_THIS_CPU;
+    check_arg_ipi(ipi);
+    openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask);
+}
+
+
+/* -------- Timer Interrupts ----------------------------------------------- */
+
+
+    /*
+     *  Initialize a timer interrupt (and disable it)
+     *
+     *  timer: OpenPIC timer number
+     *  pri: interrupt source priority
+     *  vec: the vector it will produce
+     */
+
+void openpic_inittimer(u_int timer, u_int pri, u_int vec)
+{
+    check_arg_timer(timer);
+    check_arg_pri(pri);
+    check_arg_vec(vec);
+    openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority,
+    			    OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
+    			    (pri << OPENPIC_PRIORITY_SHIFT) | vec);
+}
+
+
+    /*
+     *  Map a timer interrupt to one or more CPUs
+     */
+
+void openpic_maptimer(u_int timer, u_int cpumask)
+{
+    check_arg_timer(timer);
+    openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask);
+}
+
+
+/* -------- Interrupt Sources ---------------------------------------------- */
+
+
+    /*
+     *  Enable/disable an interrupt source
+     */
+
+void openpic_enable_irq(u_int irq)
+{
+    check_arg_irq(irq);
+    openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK);
+}
+
+void openpic_disable_irq(u_int irq)
+{
+    check_arg_irq(irq);
+    openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK);
+}
+
+
+    /*
+     *  Initialize an interrupt source (and disable it!)
+     *
+     *  irq: OpenPIC interrupt number
+     *  pri: interrupt source priority
+     *  vec: the vector it will produce
+     *  pol: polarity (1 for positive, 0 for negative)
+     *  sense: 1 for level, 0 for edge
+     */
+
+void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
+{
+    check_arg_irq(irq);
+    check_arg_pri(pri);
+    check_arg_vec(vec);
+    openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority,
+    			    OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
+    			    OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL,
+    			    (pri << OPENPIC_PRIORITY_SHIFT) | vec |
+			    (pol ? OPENPIC_SENSE_POLARITY : 0) |
+			    (sense ? OPENPIC_SENSE_LEVEL : 0));
+}
+
+
+    /*
+     *  Map an interrupt source to one or more CPUs
+     */
+
+void openpic_mapirq(u_int irq, u_int cpumask)
+{
+    check_arg_irq(irq);
+    openpic_write(&OpenPIC->Source[irq].Destination, cpumask);
+}
+
+
+    /*
+     *  Set the sense for an interrupt source (and disable it!)
+     *
+     *  sense: 1 for level, 0 for edge
+     */
+
+void openpic_set_sense(u_int irq, int sense)
+{
+    check_arg_irq(irq);
+    openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority,
+    			    OPENPIC_SENSE_LEVEL,
+			    (sense ? OPENPIC_SENSE_LEVEL : 0));
+}
+
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- v2.1.52/linux/arch/ppc/kernel/pci.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/pci.c	Thu Sep  4 12:54:48 1997
@@ -1,6 +1,6 @@
 /*
- * $Id: pci.c,v 1.11 1997/08/13 03:06:14 cort Exp $
- * Common pmac/prep pci routines. -- Cort
+ * $Id: pci.c,v 1.12 1997/08/27 05:05:28 cort Exp $
+ * Common pmac/prep/chrp pci routines. -- Cort
  */
 
 #include <linux/kernel.h>
@@ -16,6 +16,7 @@
 #include <asm/pci-bridge.h>
 
 unsigned long io_base;
+unsigned long pci_dram_offset;
 
 /*
  * It would be nice if we could create a include/asm/pci.h and have just
@@ -63,6 +64,24 @@
 extern int pmac_pcibios_find_class(unsigned int class_code, unsigned short index,
 		       unsigned char *bus_ptr, unsigned char *dev_fn_ptr);
 
+extern int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+			     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);
+extern int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+			      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);
+extern int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+			      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);
+
 extern int prep_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
 			     unsigned char offset, unsigned char *val);
 extern int prep_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
@@ -132,19 +151,9 @@
 __initfunc(unsigned long
 pcibios_init(unsigned long mem_start,unsigned long mem_end))
 {
-	if ( _machine == _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;
-	}
-	else /* prep */
-	{
+	switch (_machine) {
+	    case _MACH_Motorola:
+	    case _MACH_IBM:
 		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;
@@ -153,6 +162,27 @@
 		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:
+		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:
+		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;
 }
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- v2.1.52/linux/arch/ppc/kernel/pmac_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/pmac_pci.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,423 @@
+/*
+ * Support for PCI bridges found on Power Macintoshes.
+ * At present the "bandit" and "chaos" bridges are supported.
+ * Fortunately you access configuration space in the same
+ * way with either bridge.
+ *
+ * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au)
+ *
+ * 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/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+struct bridge_data {
+	volatile unsigned int *cfg_addr;
+	volatile unsigned char *cfg_data;
+	void *io_base;
+	int bus_number;
+	int max_bus;
+	struct bridge_data *next;
+	struct device_node *node;
+};
+
+static struct bridge_data **bridges, *bridge_list;
+static int max_bus;
+
+static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
+
+/*
+ * Magic constants for enabling cache coherency in the bandit/PSX bridge.
+ */
+#define APPLE_VENDID	0x106b
+#define BANDIT_DEVID	1
+#define BANDIT_REVID	3
+
+#define BANDIT_DEVNUM	11
+#define BANDIT_MAGIC	0x50
+#define BANDIT_COHERENT	0x40
+
+/*
+ * For a bandit bridge, turn on cache coherency if necessary.
+ * N.B. we can't use pcibios_*_config_* here because bridges[]
+ * is not initialized yet.
+ */
+static void init_bandit(struct bridge_data *bp)
+{
+	unsigned int vendev, magic;
+	int rev;
+
+	/* read the word at offset 0 in config space for device 11 */
+	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID);
+	udelay(2);
+	vendev = in_le32((volatile unsigned int *)bp->cfg_data);
+	if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) {
+		printk(KERN_WARNING "bandit isn't? (%x)\n", vendev);
+		return;
+	}
+
+	/* read the revision id */
+	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID);
+	udelay(2);
+	rev = in_8(bp->cfg_data);
+	if (rev != BANDIT_REVID)
+		printk(KERN_WARNING "Unknown revision %d for bandit at %p\n",
+		       rev, bp->io_base);
+
+	/* read the word at offset 0x50 */
+	out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC);
+	udelay(2);
+	magic = in_le32((volatile unsigned int *)bp->cfg_data);
+	if ((magic & BANDIT_COHERENT) != 0)
+		return;
+	magic |= BANDIT_COHERENT;
+	udelay(2);
+	out_le32((volatile unsigned int *)bp->cfg_data, magic);
+	printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n",
+	       bp->io_base);
+}
+
+unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end)
+{
+	int bus;
+	struct bridge_data *bridge;
+
+	bridge_list = 0;
+	max_bus = 0;
+	add_bridges(find_devices("bandit"), &mem_start);
+	add_bridges(find_devices("chaos"), &mem_start);
+	bridges = (struct bridge_data **) mem_start;
+	mem_start += (max_bus + 1) * sizeof(struct bridge_data *);
+	memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *));
+	for (bridge = bridge_list; bridge != NULL; bridge = bridge->next)
+		for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus)
+			bridges[bus] = bridge;
+
+	return mem_start;
+}
+
+static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
+{
+	int *bus_range;
+	int len;
+	struct bridge_data *bp;
+
+	for (; dev != NULL; dev = dev->next) {
+		if (dev->n_addrs < 1) {
+			printk(KERN_WARNING "Can't use %s: no address\n",
+			       dev->full_name);
+			continue;
+		}
+		bus_range = (int *) get_property(dev, "bus-range", &len);
+		if (bus_range == NULL || len < 2 * sizeof(int)) {
+			printk(KERN_WARNING "Can't get bus-range for %s\n",
+			       dev->full_name);
+			continue;
+		}
+		if (bus_range[1] == bus_range[0])
+			printk(KERN_INFO "PCI bus %d", bus_range[0]);
+		else
+			printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
+			       bus_range[1]);
+		printk(" controlled by %s at %x\n",
+		       dev->name, dev->addrs[0].address);
+		bp = (struct bridge_data *) *mem_ptr;
+		*mem_ptr += sizeof(struct bridge_data);
+		bp->cfg_addr = (volatile unsigned int *)
+			(dev->addrs[0].address + 0x800000);
+		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);
+		bp->bus_number = bus_range[0];
+		bp->max_bus = bus_range[1];
+		bp->next = bridge_list;
+		bp->node = dev;
+		bridge_list = bp;
+		if (bp->max_bus > max_bus)
+			max_bus = bp->max_bus;
+
+		if (strcmp(dev->name, "bandit") == 0)
+			init_bandit(bp);
+	}
+}
+
+void *pci_io_base(unsigned int bus)
+{
+	struct bridge_data *bp;
+
+	if (bus > max_bus || (bp = bridges[bus]) == 0)
+		return 0;
+	return bp->io_base;
+}
+
+int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
+		   unsigned char *devfn_ptr)
+{
+	unsigned int *reg;
+	int len;
+
+	reg = (unsigned int *) get_property(dev, "reg", &len);
+	if (reg == 0 || len < 5 * sizeof(unsigned int)) {
+		/* doesn't look like a PCI device */
+		*bus_ptr = 0xff;
+		*devfn_ptr = 0xff;
+		return -1;
+	}
+	*bus_ptr = reg[0] >> 16;
+	*devfn_ptr = reg[0] >> 8;
+	return 0;
+}
+
+int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+			     unsigned char offset, unsigned char *val)
+{
+	struct bridge_data *bp;
+
+	*val = 0xff;
+	if (bus > max_bus || (bp = bridges[bus]) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + (offset & ~3));
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+	}
+	udelay(2);
+	*val = in_8(bp->cfg_data + (offset & 3));
+
+	if (offset == PCI_INTERRUPT_LINE) {
+		/*
+		 * Open Firmware often doesn't initialize this
+		 * register properly, so we find the node and see
+		 * if it has an AAPL,interrupts property.
+		 */
+		struct device_node *node;
+		unsigned int *reg;
+
+		for (node = bp->node->child; node != 0; node = node->sibling) {
+			reg = (unsigned int *) get_property(node, "reg", 0);
+			if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
+				continue;
+			/* this is the node, see if it has interrupts */
+			if (node->n_intrs > 0)
+				*val = node->intrs[0];
+			break;
+		}
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+			     unsigned char offset, unsigned short *val)
+{
+	struct bridge_data *bp;
+
+	*val = 0xffff;
+	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + (offset & ~3));
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+	}
+	udelay(2);
+	*val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)));
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+			      unsigned char offset, unsigned int *val)
+{
+	struct bridge_data *bp;
+
+	*val = 0xffffffff;
+	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + offset);
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+	}
+	udelay(2);
+	*val = in_le32((volatile unsigned int *)bp->cfg_data);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+			      unsigned char offset, unsigned char val)
+{
+	struct bridge_data *bp;
+
+	if (bus > max_bus || (bp = bridges[bus]) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + (offset & ~3));
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+	}
+	udelay(2);
+	out_8(bp->cfg_data + (offset & 3), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+			      unsigned char offset, unsigned short val)
+{
+	struct bridge_data *bp;
+
+	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + (offset & ~3));
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1);
+	}
+	udelay(2);
+	out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val);
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+			       unsigned char offset, unsigned int val)
+{
+	struct bridge_data *bp;
+
+	if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (bus == bp->bus_number) {
+		if (dev_fn < (11 << 3))
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		out_le32(bp->cfg_addr,
+			 (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8)
+			 + offset);
+	} else {
+		out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1);
+	}
+	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.52/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.1.52/linux/arch/ppc/kernel/pmac_setup.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/pmac_setup.c	Thu Sep  4 12:54:48 1997
@@ -193,7 +193,7 @@
 	}
 }
 
-void 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)
 {
 	struct device_node *np;
 	int i;
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/ppc_htab.c linux/arch/ppc/kernel/ppc_htab.c
--- v2.1.52/linux/arch/ppc/kernel/ppc_htab.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/ppc_htab.c	Thu Sep  4 12:54:48 1997
@@ -1,5 +1,6 @@
 /*
- * $Id: ppc_htab.c,v 1.4 1997/08/12 04:24:54 cort Exp $
+ * $Id: ppc_htab.c,v 1.7 1997/08/24 19:33:32 cort Exp $
+ *
  * PowerPC hash table management proc entry.  Will show information
  * about the current hash table and will allow changes to it.
  *
@@ -33,6 +34,7 @@
 
 extern PTE *Hash, *Hash_end;
 extern unsigned long Hash_size, Hash_mask;
+extern unsigned long _SDR1;
 
 static struct file_operations ppc_htab_operations = {
     ppc_htab_lseek,	/* lseek   */
@@ -83,6 +85,7 @@
 	unsigned int kptes = 0, overflow = 0, uptes = 0;
 	PTE *ptr;
 	struct task_struct *p;
+	char buffer[128];
 	
 	if (nbytes < 0)
 		return -EINVAL;
@@ -94,7 +97,7 @@
 	 * due to the way tlb invalidation is handled on the ppc
 	 * -- Cort
 	 */
-	for ( ptr = Hash ; ptr < (PTE *)(Hash+Hash_size) ; ptr+=sizeof(PTE))
+	for ( ptr = Hash ; ptr < Hash_end ; ptr += sizeof(PTE))
 	{
 		if (ptr->v)
 		{
@@ -118,7 +121,8 @@
 				overflow++;
 		}
 	}
-	n += sprintf( buf,
+	
+	n += sprintf( buffer,
 		      "Size\t\t: %luKb\n"
 		      "Buckets\t\t: %lu\n"
  		      "Address\t\t: %08lx\n"
@@ -136,9 +140,12 @@
 		      overflow,
 		      ((kptes+uptes)*100) / (Hash_size/sizeof(PTE))
 		);
-	/* if we're trying to read part of the file that isn't there */
-	if ( file->f_pos > n )
-		return -ENOMEM;
+
+	if (file->f_pos >= 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;
 	return n;
 }
@@ -150,11 +157,47 @@
 ppc_htab_write(struct inode * inode, struct file * file,
 		const char * buffer, unsigned long count)
 {
+	unsigned long size;
+	extern void reset_SDR1(void);
+	
 	if ( current->uid != 0 )
 		return -EACCES;
-	else
-		return -ENOSYS;
-	return 0;
+	
+	/* only know how to set size right now */
+	if ( strncmp( buffer, "size ", 5) )
+		return -EINVAL;
+
+	size = simple_strtoul( &buffer[5], NULL, 10 );
+	
+	/* only allow to shrink */
+	if ( size >= Hash_size>>10 )
+		return -EINVAL;
+
+	/* minimum size of htab */
+	if ( size < 64 )
+		return -EINVAL;
+	
+	/* make sure it's a multiple of 64k */
+	if ( size % 64 )
+		return -EINVAL;
+	
+	printk("Hash table resize to %luk\n", size);
+	/*
+	 * We need to rehash all kernel entries for the new htab size.
+	 * Kernel only since we do a flush_tlb_all().  Since it's kernel
+	 * we only need to bother with vsids 0-15.  To avoid problems of
+	 * clobbering un-rehashed values we put the htab at a new spot
+	 * and put everything there.
+	 * -- Cort
+	 */
+	Hash_size = size<<10;
+	Hash_mask = (Hash_size >> 6) - 1;
+        _SDR1 = __pa(Hash) | (Hash_mask >> 10);
+	flush_tlb_all();
+
+	reset_SDR1();
+	printk("done\n");
+	return count;
 }
 
 
@@ -176,21 +219,3 @@
     }
 }
 
-
-#if 0
-/*
-  for root.c
-  */
-static struct proc_dir_entry proc_root_ppc_htab = {
-	PROC_PPC_HTAB, 8, "ppc_htab",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_ppc_htab_inode_operations
-};
-#ifdef __powerpc__
-	proc_register(&proc_root, &proc_root_ppc_htab);
-#endif
-
-
-/* add to proc_fs.h
-   	PROC_PPC_HTAB,*/
-#endif
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.1.52/linux/arch/ppc/kernel/prep_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/prep_pci.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,436 @@
+/*
+ * $Id: prep_pci.c,v 1.7 1997/08/23 22:46:02 cort Exp $
+ * PReP pci functions.
+ * Originally by Gary Thomas
+ * rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
+ *
+ * The motherboard routes/maps will disappear shortly. -- Cort
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+
+#define MAX_DEVNR 22
+
+/* Which PCI interrupt line does a given device [slot] use? */
+/* Note: This really should be two dimensional based in slot/pin used */
+unsigned char *Motherboard_map;
+unsigned char *Motherboard_map_name;
+
+/* How is the 82378 PIRQ mapping setup? */
+unsigned char *Motherboard_routes;
+
+/* Tables for known hardware */   
+
+/* Motorola PowerStack */
+static char Blackhawk_pci_IRQ_map[16] =
+{
+  	0,	/* Slot 0  - unused */
+  	0,	/* Slot 1  - unused */
+  	0,	/* Slot 2  - unused */
+  	0,	/* Slot 3  - unused */
+  	0,	/* Slot 4  - unused */
+  	0,	/* Slot 5  - unused */
+  	0,	/* Slot 6  - unused */
+  	0,	/* Slot 7  - unused */
+  	0,	/* Slot 8  - unused */
+  	0,	/* Slot 9  - unused */
+  	0,	/* Slot 10 - unused */
+  	0,	/* Slot 11 - unused */
+  	3,	/* Slot 12 - SCSI */
+  	0,	/* Slot 13 - unused */
+  	1,	/* Slot 14 - Ethernet */
+  	0,	/* Slot 15 - unused */
+};
+
+static char Blackhawk_pci_IRQ_routes[] =
+{
+   	0,	/* Line 0 - Unused */
+   	9,	/* Line 1 */
+   	11,	/* Line 2 */
+   	14,	/* Line 3 */
+   	15	/* Line 4 */
+};
+   
+/* Motorola MVME16xx */
+static char Genesis_pci_IRQ_map[16] =
+{
+  	0,	/* Slot 0  - unused */
+  	0,	/* Slot 1  - unused */
+  	0,	/* Slot 2  - unused */
+  	0,	/* Slot 3  - unused */
+  	0,	/* Slot 4  - unused */
+  	0,	/* Slot 5  - unused */
+  	0,	/* Slot 6  - unused */
+  	0,	/* Slot 7  - unused */
+  	0,	/* Slot 8  - unused */
+  	0,	/* Slot 9  - unused */
+  	0,	/* Slot 10 - unused */
+  	0,	/* Slot 11 - unused */
+  	3,	/* Slot 12 - SCSI */
+  	0,	/* Slot 13 - unused */
+  	1,	/* Slot 14 - Ethernet */
+  	0,	/* Slot 15 - unused */
+};
+
+static char Genesis_pci_IRQ_routes[] =
+{
+   	0,	/* Line 0 - Unused */
+   	10,	/* Line 1 */
+   	11,	/* Line 2 */
+   	14,	/* Line 3 */
+   	15	/* Line 4 */
+};
+   
+/* Motorola Series-E */
+static char Comet_pci_IRQ_map[16] =
+{
+  	0,	/* Slot 0  - unused */
+  	0,	/* Slot 1  - unused */
+  	0,	/* Slot 2  - unused */
+  	0,	/* Slot 3  - unused */
+  	0,	/* Slot 4  - unused */
+  	0,	/* Slot 5  - unused */
+  	0,	/* Slot 6  - unused */
+  	0,	/* Slot 7  - unused */
+  	0,	/* Slot 8  - unused */
+  	0,	/* Slot 9  - unused */
+  	0,	/* Slot 10 - unused */
+  	0,	/* Slot 11 - unused */
+  	3,	/* Slot 12 - SCSI */
+  	0,	/* Slot 13 - unused */
+  	1,	/* Slot 14 - Ethernet */
+  	0,	/* Slot 15 - unused */
+};
+
+static char Comet_pci_IRQ_routes[] =
+{
+   	0,	/* Line 0 - Unused */
+   	10,	/* Line 1 */
+   	11,	/* Line 2 */
+   	14,	/* Line 3 */
+   	15	/* Line 4 */
+};
+
+/*
+ * ibm 830 (and 850?).
+ * This is actually based on the Carolina motherboard
+ * -- Cort
+ */
+static char ibm8xx_pci_IRQ_map[23] = {
+        0, /* Slot 0  - unused */
+        0, /* Slot 1  - unused */
+        0, /* Slot 2  - unused */
+        0, /* Slot 3  - unused */
+        0, /* Slot 4  - unused */
+        0, /* Slot 5  - unused */
+        0, /* Slot 6  - unused */
+        0, /* Slot 7  - unused */
+        0, /* Slot 8  - unused */
+        0, /* Slot 9  - unused */
+        0, /* Slot 10 - unused */
+        0, /* Slot 11 - FireCoral */
+        4, /* Slot 12 - Ethernet  PCIINTD# */
+        2, /* Slot 13 - PCI Slot #2 */
+        2, /* Slot 14 - S3 Video PCIINTD# */
+        0, /* Slot 15 - onboard SCSI (INDI) [1] */
+        3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */
+        0, /* Slot 17 - unused */
+        2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */
+        0, /* Slot 19 - unused */
+        0, /* Slot 20 - unused */
+        0, /* Slot 21 - unused */
+        2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */
+};
+static char ibm8xx_pci_IRQ_routes[] = {
+        0,      /* Line 0 - unused */
+        13,     /* Line 1 */
+        10,     /* Line 2 */
+        15,     /* Line 3 */
+        15,     /* Line 4 */
+};
+
+/* IBM Nobis and 850 */
+static char Nobis_pci_IRQ_map[23] ={
+        0, /* Slot 0  - unused */
+        0, /* Slot 1  - unused */
+        0, /* Slot 2  - unused */
+        0, /* Slot 3  - unused */
+        0, /* Slot 4  - unused */
+        0, /* Slot 5  - unused */
+        0, /* Slot 6  - unused */
+        0, /* Slot 7  - unused */
+        0, /* Slot 8  - unused */
+        0, /* Slot 9  - unused */
+        0, /* Slot 10 - unused */
+        0, /* Slot 11 - unused */
+        3, /* Slot 12 - SCSI */
+        0, /* Slot 13 - unused */
+        0, /* Slot 14 - unused */
+        0, /* Slot 15 - unused */
+};
+
+static char Nobis_pci_IRQ_routes[] = {
+        0, /* Line 0 - Unused */
+        13, /* Line 1 */
+        13, /* Line 2 */
+        13, /* Line 3 */
+        13      /* Line 4 */
+};
+
+/* We have to turn on LEVEL mode for changed IRQ's */
+/* All PCI IRQ's need to be level mode, so this should be something
+ * other than hard-coded as well... IRQ's are individually mappable
+ * to either edge or level.
+ */
+#define CAROLINA_IRQ_EDGE_MASK_LO   0x00  /* IRQ's 0-7  */
+#define CAROLINA_IRQ_EDGE_MASK_HI   0xA4  /* IRQ's 8-15 [10,13,15] */
+
+int
+prep_pcibios_read_config_dword (unsigned char bus,
+			   unsigned char dev, unsigned char offset, unsigned int *val)
+{
+	unsigned long _val;
+	unsigned long *ptr;
+	dev >>= 3;
+	
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		*val = 0xFFFFFFFF;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset);
+		_val = le32_to_cpu(*ptr);
+	}
+	*val = _val;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+prep_pcibios_read_config_word (unsigned char bus,
+			  unsigned char dev, unsigned char offset, unsigned short *val)
+{
+	unsigned short _val;
+	unsigned short *ptr;
+	dev >>= 3;
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		*val = (unsigned short)0xFFFFFFFF;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset);
+		_val = le16_to_cpu(*ptr);
+	}
+	*val = _val;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+prep_pcibios_read_config_byte (unsigned char bus,
+			  unsigned char dev, unsigned char offset, unsigned char *val)
+{
+	unsigned char _val;
+	volatile unsigned char *ptr;
+	dev >>= 3;
+	/* Note: the configuration registers don't always have this right! */
+	if (offset == PCI_INTERRUPT_LINE)
+	{
+		*val = Motherboard_routes[Motherboard_map[dev]];
+/*printk("dev %d map %d route %d on board %d\n",
+  dev,Motherboard_map[dev],
+  Motherboard_routes[Motherboard_map[dev]],
+  *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/
+		return PCIBIOS_SUCCESSFUL;
+	}
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		*(unsigned long *)val = (unsigned long) 0xFFFFFFFF;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1));
+		_val = *ptr;
+	}
+	*val = _val;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+prep_pcibios_write_config_dword (unsigned char bus,
+			    unsigned char dev, unsigned char offset, unsigned int val)
+{
+	unsigned long _val;
+	unsigned long *ptr;
+	dev >>= 3;
+	_val = le32_to_cpu(val);
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset);
+		*ptr = _val;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+prep_pcibios_write_config_word (unsigned char bus,
+			   unsigned char dev, unsigned char offset, unsigned short val)
+{
+	unsigned short _val;
+	unsigned short *ptr;
+	dev >>= 3;
+	_val = le16_to_cpu(val);
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset);
+		*ptr = _val;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int
+prep_pcibios_write_config_byte (unsigned char bus,
+			   unsigned char dev, unsigned char offset, unsigned char val)
+{
+	unsigned char _val;
+	unsigned char *ptr;
+	dev >>= 3;
+	_val = val;
+	if ((bus != 0) || (dev > MAX_DEVNR))
+	{
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else
+	{
+		ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset^1));
+		*ptr = _val;
+	}
+	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)
+	{ 
+		switch (inb(0x800) & 0xF0)
+		{
+		case 0x10: /* MVME16xx */
+			Motherboard_map_name = "Genesis";
+			Motherboard_map = Genesis_pci_IRQ_map;
+			Motherboard_routes = Genesis_pci_IRQ_routes;
+			break;
+		case 0x20: /* Series E */
+			Motherboard_map_name = "Series E";
+			Motherboard_map = Comet_pci_IRQ_map;
+			Motherboard_routes = Comet_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 )
+	{
+		unsigned char pl_id;
+		
+		if (inb(0x0852) == 0xFF) {
+			Motherboard_map_name = "IBM 850/860 Portable\n";
+			Motherboard_map = Nobis_pci_IRQ_map;
+			Motherboard_routes = Nobis_pci_IRQ_routes;
+		} else {
+			Motherboard_map_name = "IBM 8xx (Carolina)";
+			Motherboard_map = ibm8xx_pci_IRQ_map;
+			Motherboard_routes = ibm8xx_pci_IRQ_routes;
+		}
+		/*printk("Changing IRQ mode\n");*/
+		pl_id=inb(0x04d0);
+		/*printk("Low mask is %#0x\n", pl_id);*/
+		outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0);
+		
+		pl_id=inb(0x04d1);
+		/*printk("Hi mask is  %#0x\n", pl_id);*/
+		outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1);
+		pl_id=inb(0x04d1);
+		/*printk("Hi mask now %#0x\n", pl_id);*/
+	} else
+	{
+		printk("No known machine pci routing!\n");
+		return -1;
+	}
+	
+	/* Set up mapping from slots */
+	for (i = 1;  i <= 4;  i++)
+	{
+		ibc_pirq[i-1] = Motherboard_routes[i];
+	}
+	/* Enable PCI interrupts */
+	*ibc_pcicon |= 0x20;
+	return 0;
+}
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- v2.1.52/linux/arch/ppc/kernel/prep_setup.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/prep_setup.c	Thu Sep  4 12:54:48 1997
@@ -34,6 +34,7 @@
 #include <asm/residual.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
+#include <asm/ide.h>
 
 /* for the mac fs */
 kdev_t boot_dev;
@@ -55,34 +56,19 @@
 
 extern char saved_command_line[256];
 
-struct screen_info screen_info = {
-	0, 25,			/* orig-x, orig-y */
-	{ 0, 0 },		/* unused */
-	0,			/* orig-video-page */
-	0,			/* orig-video-mode */
-	80,			/* orig-video-cols */
-	0,0,0,			/* ega_ax, ega_bx, ega_cx */
-	25,			/* orig-video-lines */
-	1,			/* orig-video-isVGA */
-	16			/* orig-video-points */
-};
-
-
-/*
- * these are here to get by until the pmac/prep merge is done
- */
-int pmac_display_supported(char *name)
-{
-	return 0;
-}
-int sd_find_target(void *a, int b)
-{
-	return 0;
-}
-void pmac_find_display(void)
+void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
+	ide_ioreg_t port = base;
+	int i = 8;
+
+	while (i--)
+		*p++ = port++;
+	*p++ = base + 0x206;
+	if (irq != NULL)
+		*irq = 0;
 }
 
+
 int
 prep_get_cpuinfo(char *buffer)
 {
@@ -237,6 +223,7 @@
 	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 );
@@ -257,6 +244,12 @@
 	init_task.mm->brk = (unsigned long) _end;	
 	
 	aux_device_present = 0xaa;
+	/* Set up floppy in PS/2 mode */
+	outb(0x09, SIO_CONFIG_RA);
+	reg = inb(SIO_CONFIG_RD);
+	reg = (reg & 0x3F) | 0x40;
+	outb(reg, SIO_CONFIG_RD);
+	outb(reg, SIO_CONFIG_RD);	/* Have to write twice to change! */
 	
 	switch ( _machine )
 	{
@@ -289,6 +282,8 @@
 #endif
 
 	printk("Boot arguments: %s\n", cmd_line);
+
+	print_residual_device_info();
 	
 	request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/prep_time.c linux/arch/ppc/kernel/prep_time.c
--- v2.1.52/linux/arch/ppc/kernel/prep_time.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/prep_time.c	Thu Sep  4 12:54:48 1997
@@ -18,7 +18,6 @@
 #include <linux/interrupt.h>
 #include <linux/timex.h>
 #include <linux/kernel_stat.h>
-#include <linux/mc146818rtc.h>
 
 #include <asm/segment.h>
 #include <asm/io.h>
@@ -27,9 +26,6 @@
 
 #include "time.h"
 
-inline unsigned long mktime(unsigned int, unsigned int,unsigned int,
-				   unsigned int, unsigned int, unsigned int);
-
 /*
  * The motorola uses the m48t18 rtc (includes DS1643) whose registers
  * are at a higher end of nvram (1ff8-1fff) than the ibm mc146818
@@ -90,108 +86,6 @@
 	printk("Unknown machine in prep_cmos_clock_write()!\n");
 }
 
-#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
-};
-
-#if 0
-static unsigned long do_slow_gettimeoffset(void)
-{
-	int count;
-	unsigned long offset = 0;
-
-	/* timer count may underflow right here */
-	outb_p(0x00, 0x43);	/* latch the count ASAP */
-	count = inb_p(0x40);	/* read the latched count */
-	count |= inb(0x40) << 8;
-	/* we know probability of underflow is always MUCH less than 1% */
-	if (count > (LATCH - LATCH/100)) {
-		/* check for pending timer interrupt */
-		outb_p(0x0a, 0x20);
-		if (inb(0x20) & 1)
-			offset = TICK_SIZE;
-	}
-	count = ((LATCH-1) - count) * TICK_SIZE;
-	count = (count + LATCH/2) / LATCH;
-	return offset + count;
-}
-static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
-
-/*
- * This version of gettimeofday has near microsecond resolution.
- */
-void do_gettimeofday(struct timeval *tv)
-{
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	*tv = xtime;
-	tv->tv_usec += do_gettimeoffset();
-	if (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
-	}
-	restore_flags(flags);
-}
-
-void do_settimeofday(struct timeval *tv)
-{
-	cli();
-	tv->tv_usec -= do_gettimeoffset();
-  
-	if (tv->tv_usec < 0) {
-		tv->tv_usec += 1000000;
-		tv->tv_sec--;
-	}
-  
-	xtime = *tv;
-	time_state = TIME_ERROR;
-	time_maxerror = 0x70000000;
-	time_esterror = 0x70000000;
-	sti();
-}
-
-#endif
-
-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;
-}
-
 /*
  * Set the hardware clock. -- Cort
  */
@@ -278,38 +172,6 @@
 	if ((year += 1900) < 1970)
 		year += 100;
 	return mktime(year, mon, day, hour, min, sec);
-}
-
-/* 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
- * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
- *
- * [For the Julian calendar (which was used in Russia before 1917,
- * Britain & colonies before 1752, anywhere else before 1582,
- * and is still in use by some communities) leave out the
- * -year/100+year/400 terms, and add 10.]
- *
- * This algorithm was first published by Gauss (I think).
- *
- * WARNING: this function will overflow on 2106-02-07 06:28:16 on
- * 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)
-{
-	
-	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
-		mon += 12;	/* Puts Feb last since it has leap day */
-		year -= 1;
-	}
-	return (((
-		(unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
-		year*365 - 719499
-		)*24 + hour /* now have hours */
-		)*60 + min /* now have minutes */
-		)*60 + sec; /* finally seconds */
 }
 
 #if 0
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- v2.1.52/linux/arch/ppc/kernel/prom.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/prom.c	Thu Sep  4 12:54:48 1997
@@ -16,6 +16,7 @@
 #include <linux/blk.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)))
@@ -128,7 +129,7 @@
 prom_init(char *params, int unused, void (*pp)(void *))
 {
 	/* First get a handle for the stdout device */
-	if ( _machine != _MACH_Pmac ) /* prep */
+	if ( ! have_of() )
 		return;
 	prom_entry = pp;
 	prom_chosen = call_prom("finddevice", 1, 1, "/chosen");
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.1.52/linux/arch/ppc/kernel/residual.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/kernel/residual.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,144 @@
+/*
+ * $Id: residual.c,v 1.2 1997/08/25 06:54:56 cort Exp $
+ *
+ * Code to deal with the PReP residual data.
+ *
+ * Written by: Cort Dougan (cort@cs.nmt.edu)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/ide.h>
+#include <asm/pnp.h>
+
+
+/*
+ * Spit out some info about residual data
+ */
+void print_residual_device_info(void)
+{
+	int i;
+	union _PnP_TAG_PACKET *pkt;
+	PPC_DEVICE *dev;
+#define did dev->DeviceId
+	
+	/* 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++)
+	{
+		dev = &res.Devices[i];
+		/*
+		 * pci devices
+		 */
+		if ( did.BusId & PCIDEVICE )
+		{
+			printk("PCI Device:");
+			/* unknown vendor */
+			if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) )
+				printk(" id %08lx types %d/%d", did.DevId,
+				       did.BaseType, did.SubType);
+			/* known vendor */
+			else
+				printk(" %s %s",
+				       pci_strvendor(did.DevId>>16),
+				       pci_strdev(did.DevId>>16,
+						  did.DevId&0xffff)
+					);
+			
+			if ( did.BusId & PNPISADEVICE )
+			{
+				printk(" pnp:");
+				/* get pnp info on the device */
+				pkt = (union _PnP_TAG_PACKET *)
+					&res.DevicePnPHeap[dev->AllocatedOffset];
+				for (; pkt->S1_Pack.Tag != DF_END_TAG;
+				     pkt++ )
+				{
+					if ( (pkt->S1_Pack.Tag == S4_Packet) ||
+					     (pkt->S1_Pack.Tag == S4_Packet_flags) )
+						printk(" irq %02x%02x",
+						       pkt->S4_Pack.IRQMask[0],
+						       pkt->S4_Pack.IRQMask[1]);
+				}
+			}
+			printk("\n");
+			continue;
+		}
+		/*
+		 * isa devices
+		 */
+		if ( did.BusId & ISADEVICE )
+		{
+			printk("ISA Device: basetype: %d subtype: %d",
+			       did.BaseType, did.SubType);
+			printk("\n");
+			continue;
+		}		
+		/*
+		 * eisa devices
+		 */
+		if ( did.BusId & EISADEVICE )
+		{
+			printk("EISA Device: basetype: %d subtype: %d",
+			       did.BaseType, did.SubType);
+			printk("\n");
+			continue;
+		}		
+		/*
+		 * proc bus devices
+		 */
+		if ( did.BusId & PROCESSORDEVICE )
+		{
+			printk("ProcBus Device: basetype: %d subtype: %d",
+			       did.BaseType, did.SubType);
+			printk("\n");
+			continue;
+		}
+		/*
+		 * pcmcia devices
+		 */
+		if ( did.BusId & PCMCIADEVICE )
+		{
+			printk("PCMCIA Device: basetype: %d subtype: %d",
+			       did.BaseType, did.SubType);
+			printk("\n");
+			continue;
+		}		
+		printk("Unknown bus access device: busid %lx\n",
+		       did.BusId);
+	}
+}
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- v2.1.52/linux/arch/ppc/kernel/setup.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/setup.c	Thu Sep  4 12:54:48 1997
@@ -1,5 +1,5 @@
 /*
- * $Id: setup.c,v 1.12 1997/08/13 03:06:17 cort Exp $
+ * $Id: setup.c,v 1.16 1997/08/27 22:06:54 cort Exp $
  * Common prep/pmac boot and setup code.
  */
 
@@ -8,10 +8,12 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/reboot.h>
+#include <linux/openpic.h>
 
 #include <asm/cuda.h>
 #include <asm/residual.h>
 #include <asm/io.h>
+#include <asm/ide.h>
 
 char saved_command_line[256];
 unsigned char aux_device_present;
@@ -21,6 +23,42 @@
 int _machine;
 
 /*
+ * Perhaps we can put the pmac screen_info[] here
+ * on pmac as well so we don't need the ifdef's.
+ * Until we get multiple-console support in here
+ * that is.  -- Cort
+ */ 
+#if defined(CONFIG_CHRP) || defined(CONFIG_PREP )
+struct screen_info screen_info = {
+	0, 25,			/* orig-x, orig-y */
+	{ 0, 0 },		/* unused */
+	0,			/* orig-video-page */
+	0,			/* orig-video-mode */
+	80,			/* orig-video-cols */
+	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+	25,			/* orig-video-lines */
+	1,			/* orig-video-isVGA */
+	16			/* orig-video-points */
+};
+
+/*
+ * I really need to add multiple-console support... -- Cort
+ */
+int pmac_display_supported(char *name)
+{
+	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() )
  */
@@ -33,28 +71,36 @@
 	_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 */
-	
-	if ( _machine == _MACH_Pmac )
+#ifdef CONFIG_CHRP 
+	_machine = _MACH_chrp;
+#endif /* CONFIG_CHRP */
+
+	switch (_machine)
 	{
+	case _MACH_Pmac:
 		io_base = 0;
-	}
-	else if ( is_prep ) /* prep */
-	{
+		pci_dram_offset = 0;
+		break;
+	case _MACH_IBM:
+	case _MACH_Motorola:
 		io_base = 0x80000000;
-		/* make a copy of residual data */
-		if ( r3 )
-			memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL) );
+		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 )
 		{
@@ -62,9 +108,23 @@
 			*(char *)(r7+KERNELBASE) = 0;
 			strcpy(cmd_line, (char *)(r6+KERNELBASE));
 		}
-	}
-	else
-	{
+		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;
@@ -77,14 +137,15 @@
 	unsigned long flags;
 	unsigned long i = 10000;
 
-	if ( _machine == _MACH_Pmac )
+	switch(_machine)
 	{
+	case _MACH_Pmac:
 		cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
 		for (;;)
 			cuda_poll();
-	}
-	else /* prep */
-	{
+		break;
+	case _MACH_IBM:
+	case _MACH_Motorola:
 		_disable_interrupts();
 		
 		/* set exception prefix high - to the prom */
@@ -98,8 +159,12 @@
 		
 		while ( i != 0 ) i++;
 		panic("restart failed\n");
-	}
+		break;
 
+	case _MACH_chrp:
+		openpic_init_processor(1<<0);
+		break;
+	}
 }
 
 void machine_power_off(void)
@@ -112,7 +177,7 @@
 		for (;;)
 			cuda_poll();
 	}
-	else /* prep */
+	else /* prep or chrp */
 	{
 		machine_restart(NULL);
 	}
@@ -128,16 +193,18 @@
 		machine_power_off();	/* for now */
 #endif
 	}
-	else /* prep */
+	else /* prep or chrp */
 		machine_restart(NULL);
 
 }
 
-
-__initfunc(unsigned long
-bios32_init(unsigned long memory_start, unsigned long memory_end))
+void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
-	return memory_start;
+	if ( _machine == _MACH_Pmac )
+		pmac_ide_init_hwif_ports(p,base,irq);
+	else /* prep */
+		prep_ide_init_hwif_ports(p,base,irq);
+	
 }
 
 /*
@@ -145,29 +212,57 @@
  */
 int get_cpuinfo(char *buffer)
 {
-	extern int pmac_get_cpuinfo(char *);	
+	extern int pmac_get_cpuinfo(char *);
+	extern int chrp_get_cpuinfo(char *);	
 	extern int prep_get_cpuinfo(char *);
 	
-	if ( _machine == _MACH_Pmac )
+
+	switch (_machine)
+	{
+	case _MACH_Pmac:
 		return pmac_get_cpuinfo(buffer);
-#ifdef CONFIG_PREP
-	else /* prep */
+		break;
+	case _MACH_Motorola:
+	case _MACH_IBM:
 		return prep_get_cpuinfo(buffer);
-#endif /* CONFIG_PREP */
+		break;
+	case _MACH_chrp:
+		return chrp_get_cpuinfo(buffer);
+		break;
+	}
+	printk("Unknown machine %d in get_cpuinfo()\n",_machine);
+	return 0;
+}
+
+
+__initfunc(unsigned long
+bios32_init(unsigned long memory_start, unsigned long memory_end))
+{
+	return memory_start;
 }
 
 __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 *);
 	
-	if ( _machine == _MACH_Pmac )
+	switch (_machine)
+	{
+	case _MACH_Pmac:
 		pmac_setup_arch(cmdline_p,memory_start_p,memory_end_p);
-#ifdef CONFIG_PREP
-	else /* prep */
+		break;
+	case _MACH_Motorola:
+	case _MACH_IBM:
 		prep_setup_arch(cmdline_p,memory_start_p,memory_end_p);
-#endif /* CONFIG_PREP */
+		break;
+	case _MACH_chrp:
+		return chrp_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+		break;
+	}
+	printk("Unknown machine %d in setup_arch()\n",_machine);
 }
+
 
 
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.1.52/linux/arch/ppc/kernel/time.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/time.c	Thu Sep  4 12:54:48 1997
@@ -1,5 +1,5 @@
 /*
- * $Id: time.c,v 1.8 1997/08/11 08:37:51 cort Exp $
+ * $Id: time.c,v 1.10 1997/08/27 22:06:56 cort Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -26,7 +26,7 @@
 
 #include "time.h"
 
-/* this is set to the appropriate pmac/prep func in init_IRQ() */
+/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */
 int (*set_rtc_time)(unsigned long);
 
 /* keep track of when we need to update the rtc */
@@ -125,11 +125,14 @@
 void
 time_init(void)
 {
-#ifdef CONFIG_PREP
 	/* pmac hasn't yet called via_cuda_init() */
-	if ( _machine != _MACH_Pmac ) /* prep */
+	if ( _machine != _MACH_Pmac )
 	{
-		xtime.tv_sec = prep_get_rtc_time();
+
+		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
@@ -137,35 +140,33 @@
 		 */
 		last_rtc_update = xtime.tv_sec;
 	}
-#endif /* CONFIG_PREP */
+
 	if ((_get_PVR() >> 16) == 1) {
 		/* 601 processor: dec counts down by 128 every 128ns */
 		decrementer_count = DECREMENTER_COUNT_601;
 		count_period_num = COUNT_PERIOD_NUM_601;
 		count_period_den = COUNT_PERIOD_DEN_601;
-	} else {
-		/*
-		 * These should setup decrementer_count 
-		 */
-		if ( _machine == _MACH_Pmac )
-			pmac_calibrate_decr();
-#ifdef CONFIG_PREP
-		else /* PReP */
-			prep_calibrate_decr();
-#endif /* CONFIG_PREP */
 	}
 
-	if ( _machine == _MACH_Pmac )
+	switch (_machine)
+	{
+	case _MACH_Pmac:
+		pmac_calibrate_decr();
 		set_rtc_time = pmac_set_rtc_time;
-#ifdef CONFIG_PREP
-	else /* prep */
+		break;
+	case _MACH_IBM:
+	case _MACH_Motorola:
+		prep_calibrate_decr();
 		set_rtc_time = prep_set_rtc_time;
-#endif /* CONFIG_PREP */
-		
+		break;
+	case _MACH_chrp:
+		chrp_calibrate_decr();
+		set_rtc_time = chrp_set_rtc_time;
+		break;
+	}
 	set_dec(decrementer_count);
 }
 
-#ifdef CONFIG_PREP
 /*
  * Uses the on-board timer to calibrate the on-chip decrementer register
  * for prep systems.  On the pmac the OF tells us what the frequency is
@@ -218,4 +219,50 @@
 		*done_ptr = 1;
 	}
 }
-#endif /* CONFIG_PREP */
+
+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
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * 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)
+{
+	
+	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
+		mon += 12;	/* Puts Feb last since it has leap day */
+		year -= 1;
+	}
+	return (((
+		(unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+		year*365 - 719499
+		)*24 + hour /* now have hours */
+		)*60 + min /* now have minutes */
+		)*60 + sec; /* finally seconds */
+}
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/kernel/time.h linux/arch/ppc/kernel/time.h
--- v2.1.52/linux/arch/ppc/kernel/time.h	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/kernel/time.h	Thu Sep  4 12:54:48 1997
@@ -1,25 +1,74 @@
 /*
- * $Id: time.h,v 1.3 1997/08/12 08:22:14 cort Exp $
+ * $Id: time.h,v 1.5 1997/08/27 22:06:58 cort Exp $
  * Common time prototypes and such for all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
  * Paul Mackerras' version and mine for PReP and Pmac.
  */
 
+#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);
 
-/* pmac/prep_time.c */
+/* pmac/prep/chrp_time.c */
 unsigned long prep_get_rtc_time(void);
 unsigned long pmac_get_rtc_time(void);
+unsigned long chrp_get_rtc_time(void);
 int prep_set_rtc_time(unsigned long nowtime);
 int pmac_set_rtc_time(unsigned long nowtime);
+int chrp_set_rtc_time(unsigned long nowtime);
 void pmac_read_rtc_time(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)
+{
+	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.52/linux/arch/ppc/mm/extable.c linux/arch/ppc/mm/extable.c
--- v2.1.52/linux/arch/ppc/mm/extable.c	Mon Aug  4 16:25:36 1997
+++ linux/arch/ppc/mm/extable.c	Thu Sep  4 12:54:48 1997
@@ -16,10 +16,17 @@
 		 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)
@@ -29,6 +36,7 @@
                 else
                         last = mid-1;
         }
+#endif	
         return 0;
 }
 
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.1.52/linux/arch/ppc/mm/fault.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/mm/fault.c	Thu Sep  4 12:54:48 1997
@@ -145,8 +145,8 @@
 	if (xmon_kernel_faults)
 		xmon(regs);
 #endif
-	panic("kernel access of bad area\n pc %lx address %lX tsk %s/%d",
-	      regs->nip,address,current->comm,current->pid);
+	panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d",
+	      regs->nip,regs->link,address,current->comm,current->pid);
 }
 
 unsigned long va_to_phys(unsigned long address)
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.1.52/linux/arch/ppc/mm/init.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/mm/init.c	Thu Sep  4 12:54:48 1997
@@ -54,6 +54,16 @@
 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 */
+
 
 void *find_mem_piece(unsigned, unsigned);
 static void mapin_ram(void);
@@ -264,7 +274,7 @@
 unsigned long *pmac_find_end_of_memory(void)
 {
 	unsigned long a, total;
-	unsigned long h, kstart, ksize;
+	unsigned long kstart, ksize;
 	extern char _stext[], _end[];
 	int i;
 
@@ -314,20 +324,6 @@
 	remove_mem_piece(&phys_avail, kstart, ksize, 0);
 	remove_mem_piece(&prom_mem, kstart, ksize, 0);
 
-	/*
-	 * Allow 64k of hash table for every 16MB of memory,
-	 * up to a maximum of 2MB.
-	 */
-	for (h = 64<<10; h < total / 256 && h < 2<<20; h *= 2)
-		;
-	Hash_size = h;
-	Hash_mask = (h >> 6) - 1;
-
-	/* Find some memory for the hash table. */
-	Hash = find_mem_piece(Hash_size, Hash_size);
-	printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n",
-	       total >> 20, Hash_size >> 10, Hash);
-
 	return __va(total);
 }
 
@@ -572,13 +568,13 @@
 BAT BAT0 =
 {
   {
-    0x80000000>>17, 	/* bepi */
+    MMU_SEGMENT_1>>17, 	/* bepi */
     BL_256M,		/* bl */
     1,			/* vs -- supervisor mode valid */
     1,			/* vp -- user mode valid */
   },
   {
-    0x80000000>>17,		/* brpn */
+    MMU_SEGMENT_1>>17,		/* brpn */
     1,			/* write-through */
     1,			/* cache-inhibited */
     0,			/* memory coherence */
@@ -589,13 +585,13 @@
 BAT BAT1 =
 {
   {
-    0xC0000000>>17, 	/* bepi */
+    MMU_SEGMENT_2>>17, 	/* bepi */
     BL_256M,		/* bl */
     1,			/* vs */
     1,			/* vp */
   },
   {
-    0xC0000000>>17,		/* brpn */
+    MMU_SEGMENT_2>>17,		/* brpn */
     1,			/* w */
     1,			/* i (cache disabled) */
     0,			/* m */
@@ -660,13 +656,13 @@
 P601_BAT BAT1_601 =
 {
   {
-    0xC0000000>>17, 	/* bepi */
+    MMU_SEGMENT_2>>17, 	/* bepi */
     1,1,0, /* wim */
     1, 0, /* vs, vp */
     BPP_RW, /* pp */
   },
   {
-    0xC0000000>>17,		/* brpn */
+    MMU_SEGMENT_2>>17,		/* brpn */
     1, /* v */
     BL_8M, /* bl */
   }
@@ -711,8 +707,7 @@
 unsigned long *prep_find_end_of_memory(void)
 {
 	int i;
-	unsigned long h;
-	/* setup the hash table */
+	
 	if (res.TotalMemory == 0 )
 	{
 		/*
@@ -723,34 +718,10 @@
 		res.TotalMemory = 0x03000000;
 		printk("Ramsize default to be %ldM\n", res.TotalMemory>>20);
 	}
-	
-#if 0
-	/* linux has trouble with > 64M ram -- Cort */
-	if ( res.TotalMemory > 0x04000000 /* 64M */ )
-	{
-		printk("Only using first 64M of ram.\n");
-		res.TotalMemory = 0x04000000;
-	}
-#endif	
-	
-	/* setup the bat2 mapping to cover physical ram */
-	BAT2.batu.bl = 0x1; /* 256k mapping */
-	for ( h = 256*1024 /* 256k */ ; (h <= res.TotalMemory) && (h <= 256*1024*1024);
-	      h *= 2 )
-		BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl;
-	/*
-	 * Allow 64k of hash table for every 16MB of memory,
-	 * up to a maximum of 2MB.
-	 */
-	for (h = 64<<10; h < res.TotalMemory / 256 && h < 2<<20; h *= 2)
-		;
-	Hash_size = h;
-	Hash_mask = (h >> 6) - 1;
-	
-	/* align htab on a Hash_size boundry above _end[] */
-	Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size);
-	memset(Hash, Hash_size, 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
@@ -790,9 +761,7 @@
 	}
 #endif /* MAP_RAM_WITH_SEGREGS */
 	
-	printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n",
-	       res.TotalMemory >> 20, Hash_size >> 10, Hash);
-	return ((unsigned long *)res.TotalMemory);
+	return (__va(res.TotalMemory));
 }
 
 
@@ -809,20 +778,39 @@
     int i;
     unsigned long v, p, s, f;
 
-    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;
-	}
+    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;
+	    
     }
 }
 
@@ -878,10 +866,29 @@
 static void hash_init(void)
 {
 	int Hash_bits;
+	unsigned long h;
 
 	extern unsigned int hash_page_patch_A[], hash_page_patch_B[],
 		hash_page_patch_C[];
 
+	/*
+	 * 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)
+		;
+	Hash_size = h;
+	Hash_mask = (h >> 6) - 1;
+
+	/* 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 */
+		Hash = find_mem_piece(Hash_size, Hash_size);
+	
+	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);
 
@@ -924,19 +931,17 @@
 {
 	if ( _machine == _MACH_Pmac )
 		end_of_DRAM = pmac_find_end_of_memory();
-	else /* prep */
+	else /* prep and chrp */
 		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 )
-	{
-		/* Map in all of RAM starting at KERNELBASE */
-		mapin_ram();
 		/* Copy mappings from the prom */
 		inherit_prom_translations();
-	}
 }
 
 static void *
@@ -949,7 +954,7 @@
 		if (p == 0)
 			panic("couldn't get a page in MMU_get_page");
 	} else {
-		if ( is_prep )
+		if ( is_prep || (_machine == _MACH_chrp) )
 		{
 			mmu_pages_count++;
 			if ( mmu_pages_count > MAX_MMU_PAGES )
@@ -971,11 +976,32 @@
 {
 	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);
 	return (void *) addr;
 }
 
+extern 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 */
+}
+
 void
 map_page(struct task_struct *tsk, unsigned long va,
 	 unsigned long pa, int flags)
@@ -1091,3 +1117,4 @@
 	current->mm->context = MUNGE_CONTEXT(++next_mmu_context);
 	set_context(current->mm->context);
 }
+
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/prep_defconfig linux/arch/ppc/prep_defconfig
--- v2.1.52/linux/arch/ppc/prep_defconfig	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/prep_defconfig	Thu Sep  4 12:54:48 1997
@@ -5,6 +5,7 @@
 #
 # Platform support
 #
+CONFIG_PPC=y
 CONFIG_NATIVE=y
 # CONFIG_PMAC is not set
 CONFIG_PREP=y
@@ -26,6 +27,7 @@
 CONFIG_KERNEL_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_BINFMT_JAVA is not set
+CONFIG_VGA_CONSOLE=y
 
 #
 # Plug and Play support
@@ -86,10 +88,14 @@
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_NCR53C406A is not set
-CONFIG_SCSI_NCR53C7xx=y
-# CONFIG_SCSI_NCR53C7xx_sync is not set
-# CONFIG_SCSI_NCR53C7xx_FAST is not set
-# CONFIG_SCSI_NCR53C7xx_DISCONNECT is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y
+CONFIG_SCSI_NCR53C8XX_IOMAPPED=y
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4
+CONFIG_SCSI_NCR53C8XX_SYNC=5
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PPA is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
@@ -118,6 +124,7 @@
 # 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
@@ -146,6 +153,7 @@
 # CONFIG_VORTEX is not set
 CONFIG_LANCE=y
 # CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
 CONFIG_PCNET32=y
@@ -185,8 +193,8 @@
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
-# CONFIG_FAT_FS is not set
-# CONFIG_MSDOS_FS is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
 # CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
@@ -210,8 +218,14 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
+# CONFIG_SOFTCURSOR is not set
 CONFIG_SERIAL=y
-# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_SERIAL_EXTENDED=y
+# CONFIG_SERIAL_MANY_PORTS is not set
+# CONFIG_SERIAL_SHARE_IRQ is not set
+# CONFIG_SERIAL_MULTIPORT is not set
+# CONFIG_HUB6 is not set
+CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_PRINTER is not set
 CONFIG_MOUSE=y
@@ -233,26 +247,4 @@
 #
 # Sound
 #
-#CONFIG_SOUND=y
-# 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=y
-# CONFIG_MAUI is not set
-# CONFIG_YM3812 is not set
-CS4232_BASE=830
-CS4232_IRQ=10
-CS4232_DMA=6
-CS4232_DMA2=7
-CS4232_MPU_BASE=330
-CS4232_MPU_IRQ=9
-# CONFIG_LOWLEVEL_SOUND is not set
+# CONFIG_SOUND is not set
diff -u --recursive --new-file v2.1.52/linux/arch/ppc/vmlinux.lds linux/arch/ppc/vmlinux.lds
--- v2.1.52/linux/arch/ppc/vmlinux.lds	Mon Aug 18 18:19:44 1997
+++ linux/arch/ppc/vmlinux.lds	Thu Sep  4 12:54:48 1997
@@ -57,6 +57,12 @@
     CONSTRUCTORS
   }
   _edata  =  .;
+  PROVIDE (edata = .);
+
+  .fixup   : { *(.fixup) }
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
 
   . = ALIGN(4096);
   __init_begin = .;
@@ -65,7 +71,6 @@
   . = ALIGN(4096);
   __init_end = .;
 
-  PROVIDE (edata = .);
   __bss_start = .;
   .bss       :
   {
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.52/linux/arch/sparc/config.in	Wed Sep  3 20:52:42 1997
+++ linux/arch/sparc/config.in	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.36 1997/06/17 03:54:47 davem Exp $
+# $Id: config.in,v 1.38 1997/09/04 01:54:33 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -145,6 +145,10 @@
 
 source fs/Config.in
 
+mainmenu_option next_comment
+comment 'Watchdog'
+
+bool 'Software watchdog' CONFIG_SOFT_WATCHDOG
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- v2.1.52/linux/arch/sparc/kernel/devices.c	Mon Mar 17 14:54:20 1997
+++ linux/arch/sparc/kernel/devices.c	Thu Sep  4 12:54:48 1997
@@ -53,8 +53,33 @@
 			}
 		};
 		if(cpu_ctr == 0) {
+			if (sparc_cpu_model == sun4d) {
+				scan = prom_getchild(prom_root_node);
+				for (scan = prom_searchsiblings(scan, "cpu-unit"); scan;
+				     scan = prom_searchsiblings(prom_getsibling(scan), "cpu-unit")) {
+					int node = prom_getchild(scan);
+					
+					prom_getstring(node, "device_type", node_str, sizeof(node_str));
+					if (strcmp(node_str, "cpu") == 0) {
+						prom_getproperty(node, "cpu-id", (char *) &thismid, sizeof(thismid));
+						if (cpu_ctr < NCPUS) {
+							cpu_nds[cpu_ctr] = node;
+							linux_cpus[cpu_ctr].prom_node = node;
+							linux_cpus[cpu_ctr].mid = thismid;
+						}
+						prom_printf("Found CPU %d <node=%08lx,mid=%d>\n",
+							    cpu_ctr, (unsigned long) node,
+							    thismid);
+						cpu_ctr++;
+					}
+				}
+			}
+			if (cpu_ctr > NCPUS)
+				cpu_ctr = NCPUS;
+		}
+		if(cpu_ctr == 0) {
 			printk("No CPU nodes found, cannot continue.\n");
-			/* Probably a sun4d or sun4e, Sun is trying to trick us ;-) */
+			/* Probably a sun4e, Sun is trying to trick us ;-) */
 			halt();
 		}
 		printk("Found %d CPU prom device tree node(s).\n", cpu_ctr);
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.1.52/linux/arch/sparc/kernel/head.S	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/head.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.82 1997/05/01 01:40:38 davem Exp $
+/* $Id: head.S,v 1.83 1997/08/28 11:10:39 jj Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -827,22 +827,18 @@
 
 		cmp	%l1, 'c'		! We already know we are not
 		be	1f			! on a plain sun4 because of
-		 nop				! the check for 0x4000 in %o0
-
-		cmp	%l1, 'm'		! at start
+		 				! the check for 0x4000 in %o0
+		 cmp	%l1, 'm'		! at start
 		be	1f
-		 nop
-
-		cmp	%l1, 'e'
+		 cmp	%l1, 'd'
+		be	1f
+		 cmp	%l1, 'e'
 		be	no_sun4e_here		! Could be a sun4e.
 		 nop
-
-		b	no_sun4u_here		! AIEEE, a V9 sun4u...
+		b	no_sun4u_here		! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
 		 nop
 
-
-1:
-		set	C_LABEL(cputypval), %l1
+1:		set	C_LABEL(cputypval), %l1
 		ldub	[%l1 + 0x4], %l1
 		cmp	%l1, 'm'		! Test for sun4d, sun4e ?
 		be	sun4m_init
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.1.52/linux/arch/sparc/kernel/process.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/process.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.99 1997/07/17 02:20:13 davem Exp $
+/*  $Id: process.c,v 1.100 1997/08/10 04:49:23 davem Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.1.52/linux/arch/sparc/kernel/sys_sunos.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/kernel/sys_sunos.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.80 1997/07/17 02:20:22 davem Exp $
+/* $Id: sys_sunos.c,v 1.81 1997/07/20 05:59:31 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.52/linux/arch/sparc/mm/srmmu.c	Mon Aug 18 18:19:44 1997
+++ linux/arch/sparc/mm/srmmu.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.150 1997/07/25 23:06:17 davem Exp $
+/* $Id: srmmu.c,v 1.151 1997/08/28 11:10:54 jj Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -2103,14 +2103,18 @@
 	sparc_iobase_vaddr = 0xfd000000;    /* 16MB of IOSPACE on all sun4m's. */
 	physmem_mapped_contig = 0;	    /* for init.c:taint_real_pages()   */
 
-	/* Find the number of contexts on the srmmu. */
-	cpunode = prom_getchild(prom_root_node);
-	num_contexts = 0;
-	while((cpunode = prom_getsibling(cpunode)) != 0) {
-		prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
-		if(!strcmp(node_str, "cpu")) {
-			num_contexts = prom_getintdefault(cpunode, "mmu-nctx", 0x8);
-			break;
+	if (sparc_cpu_model == sun4d)
+		num_contexts = 65536; /* We now it is Viking */
+	else {
+		/* Find the number of contexts on the srmmu. */
+		cpunode = prom_getchild(prom_root_node);
+		num_contexts = 0;
+		while((cpunode = prom_getsibling(cpunode)) != 0) {
+			prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
+			if(!strcmp(node_str, "cpu")) {
+				num_contexts = prom_getintdefault(cpunode, "mmu-nctx", 0x8);
+				break;
+			}
 		}
 	}
 
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.52/linux/arch/sparc64/Makefile	Thu Jul 17 10:06:03 1997
+++ linux/arch/sparc64/Makefile	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.20 1997/07/11 11:05:29 jj Exp $
+# $Id: Makefile,v 1.22 1997/08/29 15:51:53 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -17,7 +17,7 @@
 NM		= sparc64-linux-nm
 AR		= sparc64-linux-ar
 RANLIB		= sparc64-linux-ranlib
-ELF2AOUT64	= elf2aout64
+ELFTOAOUT	= elftoaout
 
 #
 # Uncomment the first CFLAGS if you are doing kgdb source level
@@ -34,13 +34,21 @@
 SUBDIRS := $(SUBDIRS) arch/sparc64/kernel arch/sparc64/lib arch/sparc64/mm \
 	arch/sparc64/prom
 
+ifneq ($(CONFIG_SOLARIS_EMUL),n)
+  SUBDIRS += arch/sparc64/solaris
+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
+
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \
 	$(TOPDIR)/arch/sparc64/lib/lib.a
 
 vmlinux.aout: vmlinux
-	$(ELF2AOUT64) -o $(TOPDIR)/vmlinux.aout $(TOPDIR)/vmlinux
+	$(ELFTOAOUT) -o $(TOPDIR)/vmlinux.aout $(TOPDIR)/vmlinux
 
 archclean:
 	rm -f $(TOPDIR)/vmlinux.aout
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/boot/Makefile linux/arch/sparc64/boot/Makefile
--- v2.1.52/linux/arch/sparc64/boot/Makefile	Thu Jul 17 10:06:04 1997
+++ linux/arch/sparc64/boot/Makefile	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.1 1997/07/11 11:05:25 jj Exp $
+# $Id: Makefile,v 1.3 1997/08/29 11:08:34 davem Exp $
 # Makefile for the Sparc64 boot stuff.
 #
 # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.52/linux/arch/sparc64/config.in	Wed Sep  3 20:52:42 1997
+++ linux/arch/sparc64/config.in	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.10 1997/08/11 14:35:45 davem Exp $
+# $Id: config.in,v 1.17 1997/09/04 01:54:43 davem Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -61,6 +61,9 @@
   tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
   tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
 fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL
+fi
 endmenu
 
 mainmenu_option next_comment
@@ -82,6 +85,19 @@
 
 tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 
+if [ "$CONFIG_PCI" = "y" ]; then
+	tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
+	if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+	  dep_tristate '   Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+	  dep_tristate '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+	  dep_tristate '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+	  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
+	fi
+fi
+
 endmenu
 
 if [ "$CONFIG_NET" = "y" ]; then
@@ -115,6 +131,21 @@
 
 	bool 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI
 	tristate 'PTI Qlogic,ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI
+
+	if [ "$CONFIG_PCI" != "n" ]; then
+	  dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+	  if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+	    bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
+	    dep_tristate '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
+	    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
+	      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+	    fi
+	    bool '   Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
+	    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
+	fi
+
 	endmenu
 fi
 endmenu
@@ -140,6 +171,9 @@
 		tristate 'Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
 		tristate 'Sun QuadEthernet support' CONFIG_SUNQE
 		tristate 'MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS
+		if [ "$CONFIG_PCI" = "y" ]; then
+			tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
+		fi
 #		bool 'FDDI driver support' CONFIG_FDDI
 #		if [ "$CONFIG_FDDI" = "y" ]; then
 #		fi
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.52/linux/arch/sparc64/defconfig	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/defconfig	Thu Sep  4 12:54:48 1997
@@ -63,6 +63,7 @@
 CONFIG_BINFMT_AOUT32=y
 CONFIG_BINFMT_JAVA=m
 CONFIG_BINFMT_MISC=m
+CONFIG_SOLARIS_EMUL=m
 
 #
 # Floppy, IDE, and other block devices
@@ -74,6 +75,14 @@
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_IDE=y
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+CONFIG_IDE_CHIPSETS=y
+CONFIG_BLK_DEV_NS87415=y
 
 #
 # Networking options
@@ -93,7 +102,7 @@
 # (it is safe to leave these untouched)
 #
 # CONFIG_INET_PCTCP is not set
-# CONFIG_INET_RARP is not set
+CONFIG_INET_RARP=m
 CONFIG_PATH_MTU_DISCOVERY=y
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
@@ -139,6 +148,12 @@
 #
 CONFIG_SCSI_SUNESP=y
 CONFIG_SCSI_QLOGICPTI=m
+CONFIG_SCSI_AIC7XXX=y
+# CONFIG_AIC7XXX_TAGGED_QUEUEING is not set
+# CONFIG_OVERRIDE_CMDS is not set
+# CONFIG_AIC7XXX_PAGE_ENABLE is not set
+# CONFIG_AIC7XXX_PROC_STATS is not set
+CONFIG_AIC7XXX_RESET_DELAY=5
 
 #
 # Network device support
@@ -158,6 +173,7 @@
 CONFIG_HAPPYMEAL=y
 CONFIG_SUNQE=m
 CONFIG_MYRI_SBUS=m
+CONFIG_DE4X5=y
 
 #
 # Filesystems
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/auxio.c linux/arch/sparc64/kernel/auxio.c
--- v2.1.52/linux/arch/sparc64/kernel/auxio.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/auxio.c	Thu Sep  4 12:54:48 1997
@@ -10,6 +10,7 @@
 #include <linux/smp.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/ioport.h>
 
 #include <asm/oplib.h>
 #include <asm/io.h>
@@ -39,18 +40,25 @@
 #ifdef CONFIG_PCI
 		struct linux_ebus *ebus;
 		struct linux_ebus_device *edev = 0;
+		unsigned long led_auxio;
 
 		for_all_ebusdev(edev, ebus)
 			if (!strcmp(edev->prom_name, "auxio"))
 				break;
 
 		if (edev) {
-			auxio_register = (unsigned char *)
-				sparc_alloc_io(edev->regs[0].phys_addr, 0,
-					       edev->regs[0].reg_size,
-					       "auxiliaryIO",
-					       edev->regs[0].which_io, 0x0);
-			*(auxio_register) = 0x01;
+			if (check_region(edev->base_address[0],
+					 sizeof(unsigned int))) {
+				prom_printf("%s: Can't get region %lx, %d\n",
+					    __FUNCTION__, edev->base_address[0],
+					    sizeof(unsigned int));
+				prom_halt();
+			}
+			request_region(edev->base_address[0],
+				       sizeof(unsigned int), "LED auxio");
+
+			led_auxio = edev->base_address[0];
+			outl(0x01, led_auxio);
 			return;
 		}
 #endif
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/binfmt_elf32.c linux/arch/sparc64/kernel/binfmt_elf32.c
--- v2.1.52/linux/arch/sparc64/kernel/binfmt_elf32.c	Thu Jun 26 12:33:38 1997
+++ linux/arch/sparc64/kernel/binfmt_elf32.c	Thu Sep  4 12:54:48 1997
@@ -8,6 +8,9 @@
 
 #define elf_check_arch(x)	(((x) == EM_SPARC) || ((x) == EM_SPARC32PLUS))
 
+#define ELF_ET_DYN_BASE         0x60000000
+
+
 #include <asm/processor.h>
 #include <linux/module.h>
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/central.c linux/arch/sparc64/kernel/central.c
--- v2.1.52/linux/arch/sparc64/kernel/central.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/central.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: central.c,v 1.3 1997/08/12 14:51:55 davem Exp $
+/* $Id: central.c,v 1.4 1997/08/19 14:17:49 jj Exp $
  * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -29,15 +29,13 @@
 	char namebuf[128];
 	int cnode, fnode, err;
 
-	prom_printf("CENTRAL: ");
 	printk("CENTRAL: ");
 	cnode = prom_finddevice("/central");
 	if(cnode == 0 || cnode == -1) {
-		prom_printf("no central found.\n");
 		printk("no central found.\n");
 		return memory_start;
 	}
-	prom_printf("found central PROM node.\n");
+	prom_printf("CENTRAL: found central PROM node.\n");
 	printk("found central PROM node.\n");
 
 	/* Ok we got one, grab some memory for software state. */
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c
--- v2.1.52/linux/arch/sparc64/kernel/ebus.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/ebus.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.2 1997/08/15 06:44:13 davem Exp $
+/* $Id: ebus.c,v 1.7 1997/08/28 02:23:17 ecd Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -6,20 +6,23 @@
 
 #include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/types.h>
 #include <linux/init.h>
 #include <linux/string.h>
 
 #include <asm/system.h>
+#include <asm/page.h>
 #include <asm/pbm.h>
 #include <asm/ebus.h>
 #include <asm/oplib.h>
 #include <asm/bpp.h>
 
-struct linux_ebus *ebus_chain = 0;
+#undef DEBUG_FILL_EBUS_DEV
 
-static char lbuf[128];
+struct linux_ebus *ebus_chain = 0;
 
 extern void prom_ebus_ranges_init(struct linux_ebus *);
+extern unsigned long pci_console_init(unsigned long memory_start);
 
 #ifdef CONFIG_SUN_OPENPROMIO
 extern int openprom_init(void);
@@ -34,57 +37,74 @@
 extern void auxio_probe(void);
 #endif
 
+extern unsigned int psycho_irq_build(unsigned int full_ino);
+
 __initfunc(void fill_ebus_device(int node, struct linux_ebus_device *dev))
 {
+	struct linux_prom_registers regs[PROMREG_MAX];
 	int irqs[PROMINTR_MAX];
-	int i, len;
+	char lbuf[128];
+	int i, n, len;
 
+#ifndef CONFIG_PCI
+	return;
+#endif
 	dev->prom_node = node;
 	prom_getstring(node, "name", lbuf, sizeof(lbuf));
 	strcpy(dev->prom_name, lbuf);
 
-	len = prom_getproperty(node, "reg", (void *)dev->regs,
-			       sizeof(dev->regs));
+	len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
 	if (len % sizeof(struct linux_prom_registers)) {
 		prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
 			    dev->prom_name, len,
 			    (int)sizeof(struct linux_prom_registers));
 		panic(__FUNCTION__);
 	}
-	dev->num_registers = len / sizeof(struct linux_prom_registers);
+	dev->num_addrs = len / sizeof(struct linux_prom_registers);
 
-	prom_apply_ebus_ranges(dev->parent, dev->regs, dev->num_registers);
-#if 0 /* XXX No longer exists/needed in new framework... */
-	prom_apply_pbm_ranges(dev->parent->parent, dev->regs,
-			      dev->num_registers);
-#endif
+	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] += (unsigned long)regs[i].phys_addr;
+	}
 
 	len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
 	if ((len == -1) || (len == 0)) {
-		dev->irqs[0].pri = 0;
 		dev->num_irqs = 0;
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
 		for (i = 0; i < dev->num_irqs; i++)
-			dev->irqs[i].pri = irqs[i];
+			dev->irqs[i] = psycho_irq_build(irqs[i]);
 	}
 
-	printk("Found '%s' at %x.%08x", dev->prom_name,
-	       dev->regs[0].which_io, dev->regs[0].phys_addr);
+#ifdef DEBUG_FILL_EBUS_DEV
+	printk("'%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]);
 	if (dev->num_irqs) {
-		printk(" IRQ%s", dev->num_irqs > 1 ? "s" : "");
+		printk("  IRQ%s", dev->num_irqs > 1 ? "s" : "");
 		for (i = 0; i < dev->num_irqs; i++)
-			printk(" %03x", dev->irqs[i].pri);
+			printk(" %08x", dev->irqs[i]);
+		printk("\n");
 	}
-	printk("\n");
+#endif
 }
 
 __initfunc(unsigned long ebus_init(unsigned long memory_start,
 				   unsigned long memory_end))
 {
+	struct linux_prom_pci_registers regs[PROMREG_MAX];
+	struct linux_pbm_info *pbm;
 	struct linux_ebus_device *dev;
 	struct linux_ebus *ebus;
-	int nd, ebusnd, topnd;
+	struct pci_dev *pdev;
+	char lbuf[128];
+	unsigned long addr, *base;
+	int nd, len, ebusnd, topnd;
+	int reg, rng, nreg;
+	int devfn;
 	int num_ebus = 0;
 
 #ifndef CONFIG_PCI
@@ -108,12 +128,55 @@
 	ebus->next = 0;
 
 	while (ebusnd) {
-		printk("ebus%d: ", num_ebus);
+		printk("ebus%d:\n", num_ebus);
 
 		prom_getstring(ebusnd, "name", lbuf, sizeof(lbuf));
 		ebus->prom_node = ebusnd;
 		strcpy(ebus->prom_name, lbuf);
-		ebus->parent = &psycho_root->pbm_B;
+		ebus->parent = pbm = &psycho_root->pbm_B;
+
+		len = prom_getproperty(ebusnd, "reg", (void *)regs,
+				       sizeof(regs));
+		if (len == 0 || len == -1) {
+			prom_printf("%s: can't find reg property\n",
+				    __FUNCTION__);
+			prom_halt();
+		}
+		nreg = len / sizeof(struct linux_prom_pci_registers);
+
+		devfn = (regs[0].phys_hi >> 8) & 0xff;
+		for (pdev = pbm->pci_bus.devices; pdev; pdev = pdev->sibling)
+			if (pdev->devfn == devfn)
+				break;
+		if (!pdev) {
+			prom_printf("%s: can't find PCI device\n",
+				    __FUNCTION__);
+			prom_halt();
+		}
+		ebus->self = pdev;
+
+		base = &ebus->self->base_address[0];
+		for (reg = 0; reg < nreg; reg++) {
+			if (!(regs[reg].phys_hi & 0x03000000))
+				continue;
+
+			for (rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+				struct linux_prom_pci_ranges *rp =
+						&pbm->pbm_ranges[rng];
+
+				if ((rp->child_phys_hi ^ regs[reg].phys_hi)
+								& 0x03000000)
+					continue;
+
+				addr = (u64)regs[reg].phys_lo;
+				addr += (u64)regs[reg].phys_mid << 32UL;
+				addr += (u64)rp->parent_phys_lo;
+				addr += (u64)rp->parent_phys_hi << 32UL;
+				*base++ = (unsigned long)__va(addr);
+
+				break;
+			}
+		}
 
 		prom_ebus_ranges_init(ebus);
 
@@ -139,6 +202,8 @@
 		ebusnd = prom_searchsiblings(prom_getsibling(ebusnd), "ebus");
 		++num_ebus;
 	}
+
+	memory_start = pci_console_init(memory_start);
 
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/entry.S linux/arch/sparc64/kernel/entry.S
--- v2.1.52/linux/arch/sparc64/kernel/entry.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/entry.S	Thu Sep  4 12:54:48 1997
@@ -1,9 +1,9 @@
-/* $Id: entry.S,v 1.61 1997/08/15 06:44:16 davem Exp $
+/* $Id: entry.S,v 1.65 1997/08/29 15:51:29 jj Exp $
  * arch/sparc64/kernel/entry.S:  Sparc64 trap low-level entry points.
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
- * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Eddie C. Dost        (ecd@skynet.be)
+ * Copyright (C) 1996 Miguel de Icaza      (miguel@nuclecu.unam.mx)
  * Copyright (C) 1996,1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  */
 
@@ -17,6 +17,7 @@
 #include <asm/page.h>
 #include <asm/signal.h>
 #include <asm/pgtable.h>
+#include <asm/processor.h>
 
 /* #define SYSCALL_TRACING */
 
@@ -115,24 +116,33 @@
 	stxa		%g5, [%g0] ASI_ITLB_DATA_IN		! TLB load
 	retry
 
+#define FPDIS_OFF (((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))
 	/* This is trivial with the new code... */
 	.align		32
 	.globl		do_fpdis
 do_fpdis:
-	wr		%g0, FPRS_FEF, %fprs
-	ldx		[%g6 + AOFF_task_flags], %g2
-	sethi		%hi(0x00100000), %g4		! XXX PF_USEDFPU
-	andcc		%g2, %g4, %g0
-
-	bne,a,pt	%xcc, fpload_fromkstk
-	 sethi		%hi((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2
-	fzero		%f0
-	fzero		%f2
+	ldx		[%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
+	andcc		%g5, SPARC_FLAG_USEDFPU, %g0				! IEU1	Group
+	or		%g3, %lo(FPDIS_OFF), %g2				! IEU0
+	sethi		%hi(empty_zero_page), %g1				! IEU0	Group
+	add		%g6, %g2, %g2						! IEU1
+	be,a,pn		%icc, 1f						! CTI
+	 clr		%g7							! IEU0	Group
+	add		%g2, 0x100, %g1						! IEU1
+	ldx		[%g2 + 0x108], %g7					! Load
+1:	andcc		%g5, SPARC_FLAG_USEDFPUL, %g0				! IEU1	Group
+	bne,pn		%icc, 2f						! CTI
+	 fzero		%f0							! FPA
+	andcc		%g5, SPARC_FLAG_USEDFPUU, %g0				! IEU1  Group
+	bne,pn		%icc, 1f						! CTI
+	 fzero		%f2							! FPA
 	faddd		%f0, %f2, %f4
 	fmuld		%f0, %f2, %f6
 	faddd		%f0, %f2, %f8
 	fmuld		%f0, %f2, %f10
-
 	faddd		%f0, %f2, %f12
 	fmuld		%f0, %f2, %f14
 	faddd		%f0, %f2, %f16
@@ -141,7 +151,6 @@
 	fmuld		%f0, %f2, %f22
 	faddd		%f0, %f2, %f24
 	fmuld		%f0, %f2, %f26
-
 	faddd		%f0, %f2, %f28
 	fmuld		%f0, %f2, %f30
 	faddd		%f0, %f2, %f32
@@ -150,52 +159,89 @@
 	fmuld		%f0, %f2, %f38
 	faddd		%f0, %f2, %f40
 	fmuld		%f0, %f2, %f42
-
 	faddd		%f0, %f2, %f44
 	fmuld		%f0, %f2, %f46
-	ldx		[%g6 + AOFF_task_flags], %g2
 	faddd		%f0, %f2, %f48
 	fmuld		%f0, %f2, %f50
-	or		%g2, %g4, %g2
 	faddd		%f0, %f2, %f52
 	fmuld		%f0, %f2, %f54
-
-	stx		%g2, [%g6 + AOFF_task_flags]
 	faddd		%f0, %f2, %f56
-	sethi		%hi(empty_zero_page), %g3
 	fmuld		%f0, %f2, %f58
-
-	faddd		%f0, %f2, %f60
-	ldx		[%g3], %fsr			! wheee, empty_zero_page
+	b,pt		%xcc, fpdis_exit2
+	 faddd		%f0, %f2, %f60
+1:	mov		SECONDARY_CONTEXT, %g3
+	faddd		%f0, %f2, %f4
+	fmuld		%f0, %f2, %f6
+	ldxa		[%g3] ASI_DMMU, %g5
+	stxa		%g0, [%g3] ASI_DMMU
+	faddd		%f0, %f2, %f8
+	fmuld		%f0, %f2, %f10
+	flush		%g2
+	wr		%g0, ASI_BLK_S, %asi	! grrr, where is ASI_BLK_NUCLEUS 8-(
+	membar		#StoreLoad | #LoadLoad
+	ldda		[%g2 + 0x080] %asi, %f32
+	ldda		[%g2 + 0x0c0] %asi, %f48
+	faddd		%f0, %f2, %f12
+	fmuld		%f0, %f2, %f14
+	faddd		%f0, %f2, %f16
+	fmuld		%f0, %f2, %f18
+	faddd		%f0, %f2, %f20
+	fmuld		%f0, %f2, %f22
+	faddd		%f0, %f2, %f24
+	fmuld		%f0, %f2, %f26
+	faddd		%f0, %f2, %f28
+	fmuld		%f0, %f2, %f30
 	b,pt		%xcc, fpdis_exit
-	 wr		%g0, 0, %gsr
-
-fpload_fromkstk:
-	or		%g2, %lo((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2
-	add		%g6, %g2, %g2
+	 membar		#Sync
+2:	andcc		%g5, SPARC_FLAG_USEDFPUU, %g0
+	bne,pt		%icc, 3f
+	 fzero		%f32
 	mov		SECONDARY_CONTEXT, %g3
-	ldxa		[%g3] ASI_DMMU, %g7
+	fzero		%f34
+	ldxa		[%g3] ASI_DMMU, %g5
+	stxa		%g0, [%g3] ASI_DMMU
+	faddd		%f32, %f34, %f36
+	fmuld		%f32, %f34, %f38
+	flush		%g2
+	wr		%g0, ASI_BLK_S, %asi	! grrr, where is ASI_BLK_NUCLEUS 8-(
+	membar		#StoreLoad | #LoadLoad
+	ldda		[%g2 + 0x000] %asi, %f0
+	ldda		[%g2 + 0x040] %asi, %f16
+	faddd		%f32, %f34, %f40
+	fmuld		%f32, %f34, %f42
+	faddd		%f32, %f34, %f44
+	fmuld		%f32, %f34, %f46
+	faddd		%f32, %f34, %f48
+	fmuld		%f32, %f34, %f50
+	faddd		%f32, %f34, %f52
+	fmuld		%f32, %f34, %f54
+	faddd		%f32, %f34, %f56
+	fmuld		%f32, %f34, %f58
+	faddd		%f32, %f34, %f60
+	fmuld		%f32, %f34, %f62
+	b,pt		%xcc, fpdis_exit
+	 membar		#Sync
+3:	mov		SECONDARY_CONTEXT, %g3
+	ldxa		[%g3] ASI_DMMU, %g5
 	stxa		%g0, [%g3] ASI_DMMU
 	flush		%g2
 	wr		%g0, ASI_BLK_S, %asi	! grrr, where is ASI_BLK_NUCLEUS 8-(
 	membar		#StoreLoad | #LoadLoad
-
 	ldda		[%g2 + 0x000] %asi, %f0
 	ldda		[%g2 + 0x040] %asi, %f16
 	ldda		[%g2 + 0x080] %asi, %f32
 	ldda		[%g2 + 0x0c0] %asi, %f48
-	ldx		[%g2 + 0x100], %fsr
-	ldx		[%g2 + 0x108], %g4
 	membar		#Sync
-	wr		%g4, 0, %gsr
-
-	stxa		%g7, [%g3] ASI_DMMU
-	flush		%g2
 fpdis_exit:
+	stxa		%g5, [%g3] ASI_DMMU
+	flush		%g2
+fpdis_exit2:
+	wr		%g7, 0, %gsr
+	ldx		[%g1], %fsr
 	rdpr		%tstate, %g3
-	sethi		%hi(TSTATE_PEF), %g4
 	or		%g3, %g4, %g3		! anal...
 	wrpr		%g3, %tstate
+	wr		%g0, FPRS_FEF, %fprs	! clean DU/DL bits
 	retry
 
 	/* The registers for cross calls will be:
@@ -232,7 +278,10 @@
 	sllx		%g3, 3, %g3
 	ldx		[%g1 + %g3], %g2
 	brz,pn		%g2, do_ivec_spurious
-	 nop
+	 sethi		%hi(0x80000000), %g5
+
+	or		%g2, %g5, %g2
+	stx		%g2, [%g1 + %g3]
 
 	/* No branches, worse case we don't know about this interrupt
 	 * yet, so we would just write a zero into the softint register
@@ -386,10 +435,8 @@
 #endif /* CONFIG_BLK_DEV_FD */
 
 	/* XXX Here is stuff we still need to write... -DaveM XXX */
-	.globl		indirect_syscall, netbsd_syscall, solaris_syscall
-indirect_syscall:
+	.globl		netbsd_syscall
 netbsd_syscall:
-solaris_syscall:
 	retl
 	 nop
 
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S
--- v2.1.52/linux/arch/sparc64/kernel/etrap.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/etrap.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.34 1997/08/08 08:33:40 jj Exp $
+/* $Id: etrap.S,v 1.37 1997/08/21 09:13:18 davem Exp $
  * etrap.S: Preparing for entry into the kernel on Sparc V9.
  *
  * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -15,6 +15,7 @@
 #define		FPUREG_SZ		((64 * 4) + (2 * 8))
 #define		TASK_REGOFF		((((PAGE_SIZE<<1)-FPUREG_SZ)&~(64-1)) - \
 					 TRACEREG_SZ-REGWIN_SZ)
+#define		FPU_OFF			(STACK_BIAS + REGWIN_SZ + TRACEREG_SZ)
 
 /*
  * On entry, %g7 is return address - 0x4.
@@ -23,120 +24,118 @@
 
 		.text
 		.align			32
+etrap_priv:	or			%g1, %g3, %g1						! IEU0		Group
+		rd			%fprs, %g3						! Single	Group+4bubbles
+		sub			%sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2		! IEU0		Group
+		andcc			%g3, FPRS_FEF, %g0					! IEU1
+		add			%g2, REGWIN_SZ + TRACEREG_SZ - FPUREG_SZ, %g3		! IEU0		Group
+		be,pt			%icc, 1f						! CTI
+		 andcc			%g1, TSTATE_PRIV, %g0					! IEU1
+		andn			%g3, (64 - 1), %g3					! IEU0		Group
+		ba,pt			%xcc, 1f						! CTI
+		 sub			%g3, REGWIN_SZ + TRACEREG_SZ, %g2			! IEU0		Group
+		
+		.align			32
 		.globl			etrap, etrap_irq, etraptl1
 
-etrap:		rdpr			%pil, %g2
-etrap_irq:	rdpr			%tstate, %g1
-		sllx			%g2, 20, %g2
-		or			%g1, %g2, %g1
-		andcc			%g1, TSTATE_PRIV, %g0
-		bne,pn			%xcc, etrap_maybe_fpu
-		 sub			%sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
-		sethi			%hi(TASK_REGOFF), %g2
-
-		or			%g2, %lo(TASK_REGOFF), %g2
-		add			%g6, %g2, %g2
-etrap_maybe_fpu:rd			%fprs, %g3
-		brnz,pn			%g3, etrap_save_fpu
-		 st			%g0, [%g2 + REGWIN_SZ + PT_V9_FPRS]
-etrap_after_fpu:rdpr			%tpc, %g3
-		stx			%g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE]
-		rdpr			%tnpc, %g1
-
-		stx			%g3, [%g2 + REGWIN_SZ + PT_V9_TPC]
-		rd			%y, %g3
-		stx			%g1, [%g2 + REGWIN_SZ + PT_V9_TNPC]
-		st			%g3, [%g2 + REGWIN_SZ + PT_V9_Y]
-		save			%g2, -STACK_BIAS, %sp	! The ordering here is
-		rdpr			%pstate, %g1		! critical, see winfixup
-		bne,pn			%xcc, 2f
-		 rdpr			%canrestore, %g3
-
-		rdpr			%wstate, %g2
-		wrpr			%g0, 7, %cleanwin
-		wrpr			%g0, 0, %canrestore
-		sll			%g2, 3, %g2
-		wrpr			%g3, 0, %otherwin
-		wrpr			%g2, 0, %wstate
-		wr			%g0, ASI_DMMU, %asi
-		ldxa			[%g0 + PRIMARY_CONTEXT] %asi, %g2
-
-		stxa			%g0, [%g0 + PRIMARY_CONTEXT] %asi
-		stxa			%g2, [%g0 + SECONDARY_CONTEXT] %asi
-		flush			%g6
-2:		wrpr			%g0, 0x0, %tl
-		or			%g1, 0, %l1
-		add			%g4, 0, %l4
-		or			%g5, 0, %l5
-		add			%g7, 0, %l2
-
-		or			%g6, 0, %l6
-		wrpr			%l1, (PSTATE_AG|PSTATE_RMO), %pstate
-		stx			%g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1]
-		stx			%g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2]
-		stx			%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3]
-		stx			%g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4]
-		stx			%g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5]
-		stx			%g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6]
-
-		stx			%g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7]
-		stx			%i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
-		stx			%i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
-		stx			%i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
-		stx			%i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3]
-		stx			%i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4]
-		sethi			%uhi(PAGE_OFFSET), %g4
-		stx			%i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5]
-
-		stx			%i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6]
-		sllx			%g4, 32, %g4
-		stx			%i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7]
-		wrpr			%l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate
-		jmpl			%l2 + 0x4, %g0
-		 mov			%l6, %g6
-etrap_save_fpu:	and			%g3, FPRS_FEF, %g3
-		brz,pn			%g3, 2f
-
-		 nop
-		be,a,pt			%xcc, 3f
-		 add			%g2, (TRACEREG_SZ + REGWIN_SZ), %g2
-		wr			%g0, ASI_BLK_P, %asi
-		add			%g2, ((TRACEREG_SZ+REGWIN_SZ)-FPUREG_SZ), %g2
-		andn			%g2, (64 - 1), %g2
-1:		st			%g3, [%g2 - 0x4 /*REGWIN_SZ + PT_V9_FPRS*/]
-		rd			%gsr, %g3
-
-		stx			%fsr, [%g2 + 0x100]
-		stx			%g3,  [%g2 + 0x108]
-		membar			#StoreStore | #LoadStore
-		stda			%f0,  [%g2 + 0x000] %asi
-		stda			%f16, [%g2 + 0x040] %asi
-		stda			%f32, [%g2 + 0x080] %asi
-		stda			%f48, [%g2 + 0x0c0] %asi
-		membar			#Sync
-
-		sub			%g2, (TRACEREG_SZ + REGWIN_SZ), %g2
-2:		bne,pn			%xcc, etrap_after_fpu
-		 wr			%g0, 0, %fprs
-		ld			[%g6 + AOFF_task_tss + AOFF_thread_ctx], %g3
-		wr			%g0, ASI_DMMU, %asi
-		nop
-		stxa			%g3, [%g0 + SECONDARY_CONTEXT] %asi
-		flush			%g2
-
-		b,pt			%xcc, etrap_after_fpu
-		 nop
-3:		mov			SECONDARY_CONTEXT, %g3
-		stxa			%g0, [%g3] ASI_DMMU
-		flush			%g2
-		wr			%g0, ASI_BLK_S, %asi
-		b,pt			%xcc, 1b
-		 mov			FPRS_FEF, %g3
-
-etraptl1:	rdpr			%tstate, %g1
-		sub			%sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
-		ba,pt			%xcc, etrap_maybe_fpu
-		 andcc			%g1, TSTATE_PRIV, %g0
-		nop
+etrap:		rdpr			%pil, %g2						! Single 	Group
+etrap_irq:	rdpr			%tstate, %g1						! Single 	Group
+		sllx			%g2, 20, %g3						! IEU0		Group
+		andcc			%g1, TSTATE_PRIV, %g0					! IEU1
+		bne,pn			%xcc, etrap_priv					! CTI
+		 sethi			%hi(TASK_REGOFF), %g2					! IEU0		Group
+		or			%g1, %g3, %g1						! IEU1
+		or			%g2, %lo(TASK_REGOFF), %g2				! IEU0		Group
+		add			%g6, %g2, %g2						! IEU0		Group
+1:		rdpr			%tpc, %g3						! Single	Group
+		stx			%g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE]			! Store		Group
+		rdpr			%tnpc, %g1						! Single	Group
+		stx			%g3, [%g2 + REGWIN_SZ + PT_V9_TPC]			! Store		Group
+		rd			%y, %g3							! Single	Group+4bubbles
+		stx			%g1, [%g2 + REGWIN_SZ + PT_V9_TNPC]			! Store		Group
+		st			%g3, [%g2 + REGWIN_SZ + PT_V9_Y]			! Store		Group
+		save			%g2, -STACK_BIAS, %sp	! The ordering here is		! Single	Group
+		rdpr			%pstate, %g1		! critical, see winfixup	! Single	Group+9bubbles
+		bne,pn			%xcc, 2f						! CTI		Group
+		 sethi			%hi(TSTATE_PEF), %l2					! IEU0
+		mov			PRIMARY_CONTEXT, %l4					! IEU1
+		rdpr			%canrestore, %g3					! Single	Group+4bubbles
+		rdpr			%wstate, %g2						! Single	Group+4bubbles
+		wrpr			%g0, 7, %cleanwin					! Single	Group+4bubbles
+		wrpr			%g0, 0, %canrestore					! Single	Group+4bubbles
+		sll			%g2, 3, %g2						! IEU0		Group
+		mov			SECONDARY_CONTEXT, %l5					! IEU1
+		wrpr			%g3, 0, %otherwin					! Single	Group+4bubbles
+		wrpr			%g2, 0, %wstate						! Single	Group+4bubbles
+		rdpr			%tstate, %l3						! Single	Group
+		ldxa			[%l4] ASI_DMMU, %g2					! Load		Group
+		stxa			%g0, [%l4] ASI_DMMU					! Store		Group
+		stxa			%g2, [%l5] ASI_DMMU					! Store		Group
+		flush			%g6							! Single	Group+9bubbles
+		andcc			%l3, %l2, %g0						! IEU1		Group
+		be,a,pt			%icc, 6f						! CTI
+		 st			%g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]	! Store
+		rd			%fprs, %l0						! Single	Group+4bubbles
+		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
+		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
+2:		rd			%fprs, %l0						! Single	Group+4bubbles
+		andcc			%l0, FPRS_FEF, %g0					! IEU1		Group
+		be,pn			%icc, 6f						! CTI
+		 st			%l0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]	! Store
+		stx			%fsr, [%sp + FPU_OFF + 0x100] 				! Single	Group
+3:		rd			%gsr, %l7						! Single	Group+4bubbles
+		cmp			%l0, FPRS_FEF						! IEU1		Group
+		be,pn			%icc, 6f						! CTI
+		 stx			%l7, [%sp + FPU_OFF + 0x108]				! Store		
+		wr			%g0, ASI_BLK_P, %asi					! Singe		Group+4bubbles
+		andcc			%l0, FPRS_DL, %g0					! IEU1		Group
+		be,pn			%icc, 4f						! CTI
+		 membar			#StoreStore | #LoadStore				! Memory	
+		stda			%f0, [%sp + FPU_OFF + 0x000] %asi			! Store		Group
+		stda			%f16, [%sp + FPU_OFF + 0x040] %asi			! Store		Group
+		andcc			%l0, FPRS_DU, %g0					! IEU1
+		be,pn			%icc, 5f						! CTI
+		 nop										! IEU0		Group
+4:		stda			%f32, [%sp + FPU_OFF + 0x080] %asi			! Store		Group
+		stda			%f48, [%sp + FPU_OFF + 0x0c0] %asi			! Store		Group
+5:		membar			#Sync							! Memory	
+6:		wr			%g0, 0x0, %fprs						! Single	Group+4bubbles
+		wrpr			%g0, 0x0, %tl						! Single	Group+4bubbles
+		mov			%g1, %l1						! IEU0		Group
+		mov			%g4, %l4						! IEU1
+		mov			%g5, %l5						! IEU0		Group
+		mov			%g7, %l2						! IEU1
+		mov			%g6, %l6						! IEU0		Group
+		wrpr			%l1, (PSTATE_AG|PSTATE_RMO), %pstate			! Single	Group+4bubbles
+		stx			%g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1]		! Store		Group
+		stx			%g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2]		! Store		Group
+		stx			%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3]		! Store		Group
+		stx			%g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4]		! Store		Group
+		stx			%g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5]		! Store		Group
+		stx			%g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6]		! Store		Group
+		stx			%g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7]		! Store		Group
+		stx			%i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]		! Store		Group
+		stx			%i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]		! Store		Group
+		stx			%i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]		! Store		Group
+		stx			%i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3]		! Store		Group
+		stx			%i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4]		! Store		Group
+		sethi			%uhi(PAGE_OFFSET), %g4					! IEU0
+		stx			%i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5]		! Store		Group
+		stx			%i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6]		! Store		Group
+		sllx			%g4, 32, %g4						! IEU0
+		stx			%i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7]		! Store		Group
+		wrpr			%l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate		! Single	Group+4bubbles
+		jmpl			%l2 + 0x4, %g0						! CTI		Group
+		 mov			%l6, %g6						! IEU0
+
+etraptl1:	rdpr			%tstate, %g1						! Single	Group+4bubbles
+		ba,pt			%xcc, etrap_priv					! CTI		Group
+		 clr			%g3							! IEU0
 #undef TASK_REGOFF
 #undef FPUREG_SZ
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.1.52/linux/arch/sparc64/kernel/ioctl32.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/ioctl32.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.13 1997/07/17 02:20:38 davem Exp $
+/* $Id: ioctl32.c,v 1.17 1997/09/03 11:54:49 ecd Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -23,6 +23,7 @@
 #include <linux/vt.h>
 #include <linux/fs.h>
 #include <linux/fd.h>
+#include <linux/if_ppp.h>
 
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -32,6 +33,13 @@
 #include <asm/rtc.h>
 #include <asm/openpromio.h>
 
+/*
+ * XXX: for DaveM:
+ * This is the kludge to know what size of buffer to
+ * copy back to the user... (ecd)
+ */
+int ifr_data_len;
+
 /* As gcc will warn about casting u32 to some ptr, we have to cast it to
  * unsigned long first, and that's what is A() for.
  * You just do (void *)A(x), instead of having to type (void *)((unsigned long)x)
@@ -102,6 +110,7 @@
 
 	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;
@@ -145,7 +154,8 @@
 	unsigned long old_fs;
 	int err;
 	
-	if (cmd == SIOCSIFMAP) {
+	switch (cmd) {
+	case SIOCSIFMAP:
 		if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(ifr.ifr_name)) ||
 		    __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) ||
 		    __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_end)) ||
@@ -154,9 +164,20 @@
 		    __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.dma)) ||
 		    __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.port)))
 			return -EFAULT;
-	} else {
+		break;
+	case SIOCGPPPSTATS:
+	case SIOCGPPPCSTATS:
+	case SIOCGPPPVER:
+		if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32)))
+			return -EFAULT;
+		ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
+		if (!ifr.ifr_data)
+			return -EAGAIN;
+		break;
+	default:
 		if (copy_from_user(&ifr, (struct ifreq32 *)A(arg), sizeof(struct ifreq32)))
 			return -EFAULT;
+		break;
 	}
 	old_fs = get_fs();
 	set_fs (KERNEL_DS);
@@ -177,6 +198,21 @@
 			if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(struct ifreq32)))
 				return -EFAULT;
 			break;
+		case SIOCGPPPSTATS:
+		case SIOCGPPPCSTATS:
+		case SIOCGPPPVER:
+		{
+			u32 data;
+			__get_user(data, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_data));
+			/*
+			 * XXX: for DaveM:
+			 * Here we use 'ifr_data_len' to know what size of buffer to
+			 * copy back to the user... (ecd)
+			 */
+			if (copy_to_user((char *)A(data), ifr.ifr_data, ifr_data_len))
+				return -EFAULT;
+			break;
+		}
 		case SIOCGIFMAP:
 			if (copy_to_user((struct ifreq32 *)A(arg), &ifr, sizeof(ifr.ifr_name)) ||
 			    __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)A(arg))->ifr_ifru.ifru_map.mem_start)) ||
@@ -481,6 +517,74 @@
 	return ret;
 }
 	
+static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	unsigned long old_fs = get_fs();
+	unsigned long kval;
+	unsigned int *uvp;
+	int error;
+
+	set_fs(KERNEL_DS);
+	error = sys_ioctl(fd, cmd, (long)&kval);
+	set_fs(old_fs);
+
+	if(error == 0) {
+		uvp = (unsigned int *)A(arg);
+		if(put_user(kval, uvp))
+			error = -EFAULT;
+	}
+	return error;
+}
+
+struct ppp_option_data32 {
+	__kernel_caddr_t32	ptr;
+	__u32			length;
+	int			transmit;
+};
+#define PPPIOCSCOMPRESS32	_IOW('t', 77, struct ppp_option_data32)
+
+static int ppp_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	unsigned long old_fs = get_fs();
+	struct ppp_option_data32 data32;
+	struct ppp_option_data data;
+	int err;
+
+	switch (cmd) {
+	case PPPIOCSCOMPRESS32:
+		if (copy_from_user(&data32, (struct ppp_option_data32 *)A(arg), sizeof(struct ppp_option_data32)))
+			return -EFAULT;
+		data.ptr = kmalloc (data32.length, GFP_KERNEL);
+		if (!data.ptr)
+			return -ENOMEM;
+		if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) {
+			err = -EFAULT;
+			goto out;
+		}
+		data.length = data32.length;
+		data.transmit = data32.transmit;
+		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);
+	set_fs (old_fs);
+	if (err)
+		goto out;
+	switch (cmd) {
+	case PPPIOCSCOMPRESS32:
+	default:
+		break;
+	}
+out:
+	kfree(data.ptr);
+	return err;
+}
+
 asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
 {
 	struct file * filp;
@@ -527,6 +631,9 @@
 	case SIOCSIFDSTADDR:
 	case SIOCGIFNETMASK:
 	case SIOCSIFNETMASK:
+	case SIOCGPPPSTATS:
+	case SIOCGPPPCSTATS:
+	case SIOCGPPPVER:
 		error = dev_ifsioc(fd, cmd, arg);
 		goto out;
 		
@@ -557,6 +664,16 @@
 		error = fbiogscursor(fd, cmd, arg);
 		goto out;
 
+	case HDIO_GET_KEEPSETTINGS:
+	case HDIO_GET_UNMASKINTR:
+	case HDIO_GET_DMA:
+	case HDIO_GET_32BIT:
+	case HDIO_GET_MULTCOUNT:
+	case HDIO_GET_NOWERR:
+	case HDIO_GET_NICE:
+		error = hdio_ioctl_trans(fd, cmd, arg);
+		goto out;
+
 	/* List here exlicitly which ioctl's are known to have
 	 * compatable types passed or none at all...
 	 */
@@ -619,6 +736,23 @@
 	case FIBMAP:
 	case FIGETBSZ:
 	
+	/* 0x03 -- HD/IDE ioctl's used by hdparm and friends.
+	 *         Some need translations, these do not.
+	 */
+	case HDIO_GET_IDENTITY:
+	case HDIO_SET_DMA:
+	case HDIO_SET_KEEPSETTINGS:
+	case HDIO_SET_UNMASKINTR:
+	case HDIO_SET_NOWERR:
+	case HDIO_SET_32BIT:
+	case HDIO_SET_MULTCOUNT:
+	case HDIO_DRIVE_CMD:
+	case HDIO_SET_PIO_MODE:
+	case HDIO_SCAN_HWIF:
+	case HDIO_SET_NICE:
+	case BLKROSET:
+	case BLKROGET:
+
 	/* 0x02 -- Floppy ioctls */
 	case FDSETEMSGTRESH:
 	case FDFLUSH:
@@ -729,11 +863,40 @@
 	case SIOCSARP:
 	case SIOCGARP:
 	case SIOCDARP:
+	case OLD_SIOCSARP:
+	case OLD_SIOCGARP:
+	case OLD_SIOCDARP:
+	case SIOCSRARP:
+	case SIOCGRARP:
+	case SIOCDRARP:
 	case SIOCADDDLCI:
 	case SIOCDELDLCI:
+
+	/* PPP stuff */
+	case PPPIOCGFLAGS:
+	case PPPIOCSFLAGS:
+	case PPPIOCGASYNCMAP:
+	case PPPIOCSASYNCMAP:
+	case PPPIOCGUNIT:
+	case PPPIOCGRASYNCMAP:
+	case PPPIOCSRASYNCMAP:
+	case PPPIOCGMRU:
+	case PPPIOCSMRU:
+	case PPPIOCSMAXCID:
+	case PPPIOCGXASYNCMAP:
+	case PPPIOCSXASYNCMAP:
+	case PPPIOCXFERUNIT:
+	case PPPIOCGNPMODE:
+	case PPPIOCSNPMODE:
+	case PPPIOCGDEBUG:
+	case PPPIOCSDEBUG:
+	case PPPIOCGIDLE:
 		error = sys_ioctl (fd, cmd, (unsigned long)arg);
 		goto out;
-		break;
+
+	case PPPIOCSCOMPRESS32:
+		error = ppp_ioctl (fd, cmd, (unsigned long)arg);
+		goto out;
 
 	default:
 		printk("sys32_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n",
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/ioport.c linux/arch/sparc64/kernel/ioport.c
--- v2.1.52/linux/arch/sparc64/kernel/ioport.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/ioport.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: ioport.c,v 1.12 1997/08/08 05:07:02 davem Exp $
+/* $Id: ioport.c,v 1.13 1997/08/18 01:20:22 davem Exp $
  * ioport.c:  Simple io mapping allocator.
  *
  * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
@@ -88,10 +88,10 @@
 	unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
 	unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK;
 	
+	release_region(vaddr, plen);
+
 	if (((unsigned long)virtual) >= PAGE_OFFSET + 0x10000000000UL)
 		return;
-
-	release_region(vaddr, plen);
 
 	for (; plen != 0;) {
 		plen -= PAGE_SIZE;
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/irq.c linux/arch/sparc64/kernel/irq.c
--- v2.1.52/linux/arch/sparc64/kernel/irq.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/irq.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.34 1997/08/15 06:44:18 davem Exp $
+/* $Id: irq.c,v 1.39 1997/08/31 03:11:18 davem Exp $
  * irq.c: UltraSparc IRQ handling/init/registry.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -112,6 +112,13 @@
 		}
 		len += sprintf(buf + len, "\n");
 	}
+#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);
+#endif
+#endif
 	return len;
 }
 
@@ -216,24 +223,24 @@
 	6, 4, 3, 1,			/* PCI B slot 1  Int A, B, C, D */
 	6, 4, 3, 1,			/* PCI B slot 2  Int A, B, C, D */
 	6, 4, 3, 1,			/* PCI B slot 3  Int A, B, C, D */
-	3, /* SCSI */
-	3, /* Ethernet */
-	2, /* Parallel Port */
-	8, /* Audio Record */
-	7, /* Audio Playback */
-	8, /* PowerFail */
-	7, /* Keyboard/Mouse/Serial */
-	8, /* Floppy */
-	2, /* Spare Hardware */
-	4, /* Keyboard */
-	4, /* Mouse */
-	7, /* Serial */
-	6, /* Timer 0 */
-	6, /* Timer 1 */
-	8, /* Uncorrectable ECC */
-	8, /* Correctable ECC */
-	8, /* PCI Bus A Error */
-	7, /* PCI Bus B Error */
+	3,  /* SCSI */
+	5,  /* Ethernet */
+	8,  /* Parallel Port */
+	13, /* Audio Record */
+	14, /* Audio Playback */
+	15, /* PowerFail */
+	12, /* Keyboard/Mouse/Serial */
+	11, /* Floppy */
+	2,  /* Spare Hardware */
+	12, /* Keyboard */
+	4,  /* Mouse */
+	12, /* Serial */
+	10, /* Timer 0 */
+	11, /* Timer 1 */
+	15, /* Uncorrectable ECC */
+	15, /* Correctable ECC */
+	15, /* PCI Bus A Error */
+	15, /* PCI Bus B Error */
 	1, /* Power Management */
 };
 
@@ -254,10 +261,15 @@
 /* Now these are always passed a true fully specified sun4u INO. */
 void enable_irq(unsigned int ino)
 {
-	struct ino_bucket *bucket = ino_lookup(ino);
+	struct ino_bucket *bucket;
 	unsigned long tid;
 	unsigned int *imap;
 
+#ifdef CONFIG_PCI
+	if(PCI_IRQ_P(ino))
+		ino &= (PCI_IRQ_IGN | PCI_IRQ_INO);
+#endif
+	bucket = ino_lookup(ino);
 	if(!bucket)
 		return;
 
@@ -281,9 +293,14 @@
 /* This now gets passed true ino's as well. */
 void disable_irq(unsigned int ino)
 {
-	struct ino_bucket *bucket = ino_lookup(ino);
+	struct ino_bucket *bucket;
 	unsigned int *imap;
 
+#ifdef CONFIG_PCI
+	if(PCI_IRQ_P(ino))
+		ino &= (PCI_IRQ_IGN | PCI_IRQ_INO);
+#endif
+	bucket = ino_lookup(ino);
 	if(!bucket)
 		return;
 
@@ -427,6 +444,37 @@
 	panic("Bad IRQ bus type...");
 }
 
+#ifdef CONFIG_PCI
+static void pci_irq_frobnicate(int *cpu_irq, int *ivindex_fixup,
+			       unsigned int **imap, unsigned int **iclr,
+			       unsigned int irq)
+{
+	struct linux_psycho *psycho = psycho_root;
+	struct psycho_regs *pregs = psycho->psycho_regs;
+	unsigned long addr, imoff;
+
+	addr = (unsigned long) &pregs->imap_a_slot0;
+	imoff = (irq & PCI_IRQ_IMAP_OFF) >> PCI_IRQ_IMAP_OFF_SHFT;
+	addr = addr + imoff;
+
+	*imap = ((unsigned int *)addr) + 1;
+
+	addr = (unsigned long) pregs;
+	addr += psycho_iclr_offset(irq & (PCI_IRQ_INO));
+	*iclr = ((unsigned int *)addr) + 1;
+
+	*cpu_irq = psycho_ino_to_pil[irq & (PCI_IRQ_INO)];
+	if(*cpu_irq == 0) {
+		printk("get_irq_translations: BAD PSYCHO INO[%x]\n", irq);
+		panic("Bad PSYCHO IRQ frobnication...");
+	}
+
+	/* IVINDEX fixup only needed for PCI slot irq lines. */
+	if(!(irq & 0x20))
+		*ivindex_fixup = irq & 0x03;
+}
+#endif
+
 /* Once added, they are never removed. */
 static struct ino_bucket *add_ino_hash(unsigned int ivindex,
 				       unsigned int *imap, unsigned int *iclr,
@@ -475,6 +523,11 @@
 	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_irq: SA_DCOOKIE but dev_id is NULL!\n");
@@ -588,10 +641,16 @@
 	unsigned int cpu_irq;
 	int ivindex = -1;
 
-	if(irq == 14)
+	if(irq == 14) {
 		cpu_irq = irq;
-	else
-		cpu_irq = sysio_ino_to_pil[irq];
+	} else {
+#ifdef CONFIG_PCI
+		if(PCI_IRQ_P(irq))
+			cpu_irq = psycho_ino_to_pil[irq & PCI_IRQ_INO];
+		else
+#endif
+			cpu_irq = sysio_ino_to_pil[irq];
+	}
 	action = *(cpu_irq + irq_action);
 	if(!action->handler) {
 		printk("Freeing free IRQ %d\n", irq);
@@ -804,10 +863,12 @@
 void report_spurious_ivec(struct pt_regs *regs)
 {
 	extern unsigned long ivec_spurious_cookie;
+	static int times = 0;
 
 	printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n",
 	       ivec_spurious_cookie, regs->tpc);
-	return;
+	if(times++ > 1)
+		prom_halt();
 }
 
 void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
@@ -850,13 +911,21 @@
 		unexpected_irq(irq, 0, regs);
 	} else {
 		do {
-			action->handler(irq, action->dev_id, regs);
+			struct ino_bucket *bucket = NULL;
+			unsigned int ino = 0;
+
 			if(action->flags & SA_IMAP_MASKED) {
-				struct ino_bucket *bucket =
-					(struct ino_bucket *)action->mask;
+				bucket = (struct ino_bucket *)action->mask;
+
+				ino = bucket->ino;
+				if(!(ivector_to_mask[ino] & 0x80000000))
+					continue;
+			}
 
+			action->handler(irq, action->dev_id, regs);
+			if(bucket) {
+				ivector_to_mask[ino] &= ~(0x80000000);
 				*(bucket->iclr) = SYSIO_ICLR_IDLE;
-				membar("#MemIssue");
 			}
 		} while((action = action->next) != NULL);
 	}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.1.52/linux/arch/sparc64/kernel/process.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/process.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.29 1997/07/17 02:20:40 davem Exp $
+/*  $Id: process.c,v 1.42 1997/08/19 14:17:55 jj Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -385,7 +385,8 @@
 
 	/* No new signal delivery by default. */
 	current->tss.new_signal = 0;
-	current->flags &= ~PF_USEDFPU;
+	current->tss.flags &= ~(SPARC_FLAG_USEDFPU | SPARC_FLAG_USEDFPUL |
+			        SPARC_FLAG_USEDFPUU);
 	
 	/* Now, this task is no longer a kernel thread. */
 	current->tss.current_ds = USER_DS;
@@ -642,6 +643,7 @@
 	if(!error) {
 		fprs_write(0);
 		regs->fprs = 0;
+		regs->tstate &= ~TSTATE_PEF;
 	}
 out:
 	unlock_kernel();
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
--- v2.1.52/linux/arch/sparc64/kernel/psycho.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/psycho.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.5 1997/08/15 06:44:18 davem Exp $
+/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $
  * psycho.c: Ultra/AX U2P PCI controller support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -62,6 +62,33 @@
 		~(sizeof(unsigned long) - 1));
 }
 
+static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
+				       unsigned long memory_start)
+{
+	unsigned long tsbbase = PAGE_ALIGN(memory_start);
+	unsigned long control, i;
+	unsigned long *iopte;
+
+	memory_start = (tsbbase + ((32 * 1024) * 8));
+	iopte = (unsigned long *)tsbbase;
+
+	for(i = 0; i < (65536 / 2); i++) {
+		*iopte = (IOPTE_VALID | IOPTE_64K |
+			  IOPTE_CACHE | IOPTE_WRITE);
+		*iopte |= (i << 16);
+		iopte++;
+	}
+
+	psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase);
+
+	control = psycho->psycho_regs->iommu_control;
+	control &= ~(IOMMU_CTRL_TSBSZ);
+	control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+	psycho->psycho_regs->iommu_control = control;
+
+	return memory_start;
+}
+
 extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
 
 unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
@@ -71,7 +98,6 @@
 	u32 portid;
 	int node;
 
-	/* prom_printf("PSYCHO: Probing for controllers.\n"); */
 	printk("PSYCHO: Probing for controllers.\n");
 
 	memory_start = long_align(memory_start);
@@ -126,10 +152,13 @@
 			prom_halt();
 		}
 
-		prom_printf("PSYCHO: Found controller, main regs at %p\n",
-			    psycho->psycho_regs);
 		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);
+#endif
+		psycho->psycho_regs->irq_retry = 0xff;
 
 		/* Now map in PCI config space for entire PSYCHO. */
 		psycho->pci_config_space =
@@ -142,34 +171,17 @@
 			prom_halt();
 		}
 
-		/* Finally map in I/O space for both PBM's.  This is essentially
-		 * backwards compatability for non-conformant PCI cards which
-		 * do not map themselves into the PCI memory space.
-		 */
-		psycho->pbm_B.pbm_IO = __va(pr_regs[2].phys_addr + 0x02000000UL);
-		psycho->pbm_A.pbm_IO = __va(pr_regs[2].phys_addr + 0x02010000UL);
-
-		/* Now record MEM space for both PBM's.
-		 *
-		 * XXX Eddie, these can be reversed if BOOT_BUS pin is clear, is
-		 * XXX there some way to find out what value of BOOT_BUS pin is?
-		 */
-		psycho->pbm_B.pbm_mem = __va(pr_regs[2].phys_addr + 0x180000000UL);
-		psycho->pbm_A.pbm_mem = __va(pr_regs[2].phys_addr + 0x100000000UL);
-
 		/* Report some more info. */
-		prom_printf("PSYCHO: PCI config space at %p\n",
-			    psycho->pci_config_space);
-		prom_printf("PSYCHO: PBM A I/O space at %p, PBM B I/O at %p\n",
-			    psycho->pbm_A.pbm_IO, psycho->pbm_B.pbm_IO);
-		prom_printf("PSYCHO: PBM A MEM at %p, PBM B MEM at %p\n");
-
 		printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space);
-		printk("PSYCHO: PBM A I/O space at %p, PBM B I/O at %p\n",
-		       psycho->pbm_A.pbm_IO, psycho->pbm_B.pbm_IO);
+
+		memory_start = psycho_iommu_init(psycho, memory_start);
 
 		is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
 
+		/* Enable arbitration for all PCI slots. */
+		psycho->psycho_regs->pci_a_control |= 0x3f;
+		psycho->psycho_regs->pci_b_control |= 0x3f;
+
 	other_pbm:
 		if(is_pbm_a)
 			pbm = &psycho->pbm_A;
@@ -177,6 +189,8 @@
 			pbm = &psycho->pbm_B;
 
 		pbm->parent = psycho;
+		pbm->IO_assignments = NULL;
+		pbm->MEM_assignments = NULL;
 		pbm->prom_node = node;
 
 		prom_getstring(node, "name", namebuf, sizeof(namebuf));
@@ -256,6 +270,97 @@
 	return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
+static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm,
+					   unsigned long start,
+					   int io)
+{
+	struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
+
+	while(vp) {
+		if(vp->end > start)
+			break;
+		vp = vp->next;
+	}
+	return vp;
+}
+
+static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new, int io)
+{
+	struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
+
+	if(!vp) {
+		new->next = NULL;
+		if(io)
+			pbm->IO_assignments = new;
+		else
+			pbm->MEM_assignments = new;
+	} else {
+		struct pci_vma *prev = NULL;
+
+		while(vp && (vp->end < new->end)) {
+			prev = vp;
+			vp = vp->next;
+		}
+		new->next = vp;
+		if(!prev) {
+			if(io)
+				pbm->IO_assignments = new;
+			else
+				pbm->MEM_assignments = new;
+		} else {
+			prev->next = new;
+		}
+
+		/* Check for programming errors. */
+		if(vp &&
+		   ((vp->start >= new->start && vp->start < new->end) ||
+		    ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) {
+			prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n",
+				    io ? "IO" : "MEM");
+			prom_printf("pci_add_vma: vp[%016lx:%016lx] "
+				    "new[%016lx:%016lx]\n",
+				    vp->start, vp->end,
+				    new->start, new->end);
+		}
+	}
+}
+
+static unsigned long *pci_alloc_arena = NULL;
+
+static inline void pci_init_alloc_init(unsigned long *mstart)
+{
+	pci_alloc_arena = mstart;
+}
+
+static inline void pci_init_alloc_fini(void)
+{
+	pci_alloc_arena = NULL;
+}
+
+static void *pci_init_alloc(int size)
+{
+	unsigned long start = long_align(*pci_alloc_arena);
+	void *mp = (void *)start;
+
+	if(!pci_alloc_arena) {
+		prom_printf("pci_init_alloc: pci_vma arena not init'd\n");
+		prom_halt();
+	}
+	start += size;
+	*pci_alloc_arena = start;
+	return mp;
+}
+
+static inline struct pci_vma *pci_vma_alloc(void)
+{
+	return pci_init_alloc(sizeof(struct pci_vma));
+}
+
+static inline struct pcidev_cookie *pci_devcookie_alloc(void)
+{
+	return pci_init_alloc(sizeof(struct pcidev_cookie));
+}
+
 static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
 {
 	struct pci_bus *pbus = &pbm->pci_bus;
@@ -270,6 +375,51 @@
 	pbus->subordinate = pci_scan_bus(pbus, mstart);
 }
 
+static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+				     struct pci_dev *pdev,
+				     int node)
+{
+	struct linux_prom_pci_registers pregs[PROMREG_MAX];
+	int err;
+
+	while(node) {
+		int child;
+
+		child = prom_getchild(node);
+		if(child != 0 && child != -1) {
+			int res;
+
+			res = pdev_to_pnode_sibtraverse(pbm, pdev, child);
+			if(res != 0 && res != -1)
+				return res;
+		}
+		err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
+		if(err != 0 && err != -1) {
+			u32 devfn = (pregs[0].phys_hi >> 8) & 0xff;
+
+			if(devfn == pdev->devfn)
+				return node; /* Match */
+		}
+
+		node = prom_getsibling(node);
+	}
+	return 0;
+}
+
+static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
+{
+	struct pcidev_cookie *pcp;
+	int node = prom_getchild(pbm->prom_node);
+
+	node = pdev_to_pnode_sibtraverse(pbm, pdev, node);
+	if(node == 0)
+		node = -1;
+	pcp = pci_devcookie_alloc();
+	pcp->pbm = pbm;
+	pcp->prom_node = node;
+	pdev->sysdata = pcp;
+}
+
 static void fill_in_pbm_cookies(struct linux_pbm_info *pbm)
 {
 	struct pci_bus *pbtmp, *pbus = &pbm->pci_bus;
@@ -280,42 +430,638 @@
 
 	for( ; pbus; pbus = pbus->children)
 		for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
-			pdev->sysdata = pbm;
+			pdev_cookie_fillin(pbm, pdev);
 }
 
-static void fixup_pci_dev(struct pci_dev *pdev,
-			  struct pci_bus *pbus,
-			  struct linux_pbm_info *pbm)
+/* #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.
+ */
+static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
 {
-	struct linux_prom_pci_registers pregs[PROMREG_MAX];
-	int node;
-#if 0
-	int nregs, busno = pbus->number;
-#endif
+	struct linux_prom_ebus_ranges erng[PROMREG_MAX];
+	int err, iter;
+
+	err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng));
+	if(err == 0 || err == -1) {
+		prom_printf("EBUS: fatal error, no range property.\n");
+		prom_halt();
+	}
+	err = (err / sizeof(struct linux_prom_ebus_ranges));
+	for(iter = 0; iter < err; iter++) {
+		struct linux_prom_ebus_ranges *ep = &erng[iter];
+		struct linux_prom_pci_registers *ap = &aregs[iter];
+
+		ap->phys_hi = ep->parent_phys_hi;
+		ap->phys_mid = ep->parent_phys_mid;
+		ap->phys_lo = ep->parent_phys_lo;
+	}
+	return err;
+}
+
+static void assignment_process(struct linux_pbm_info *pbm, int node)
+{
+	struct linux_prom_pci_registers aregs[PROMREG_MAX];
+	char pname[256];
+	int err, iter, numa;
 
-	node = prom_getchild(pbm->prom_node);
+	err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname));
+	if(strncmp(pname, "ebus", 4) == 0) {
+		numa = gimme_ebus_assignments(node, &aregs[0]);
+	} else {
+		err = prom_getproperty(node, "assigned-addresses",
+				       (char *)&aregs[0], sizeof(aregs));
+
+		/* No assignments, nothing to do. */
+		if(err == 0 || err == -1)
+			return;
+
+		numa = (err / sizeof(struct linux_prom_pci_ranges));
+	}
+
+	for(iter = 0; iter < numa; iter++) {
+		struct linux_prom_pci_registers *ap = &aregs[iter];
+		struct pci_vma *vp;
+		int space, breg, io;
+
+		space = (ap->phys_hi >> 24) & 3;
+		if(space != 1 && space != 2)
+			continue;
+		io = (space == 1);
+
+		breg = (ap->phys_hi & 0xff);
+		if(breg == PCI_ROM_ADDRESS)
+			continue;
+
+		vp = pci_vma_alloc();
+
+		/* XXX Means we don't support > 32-bit range of
+		 * XXX PCI MEM space, PSYCHO/PBM does not support it
+		 * XXX either due to it's layout so...
+		 */
+		vp->start = ap->phys_lo;
+		vp->end = vp->start + ap->size_lo;
+		vp->base_reg = breg;
+
+		/* Sanity */
+		if(io && (vp->end & ~(0xffff))) {
+			prom_printf("assignment_process: Out of range PCI I/O "
+				    "[%08lx:%08lx]\n", vp->start, vp->end);
+			prom_halt();
+		}
+
+		pci_add_vma(pbm, vp, io);
+	}
+}
+
+static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
+{
 	while(node) {
-		u32 devfn;
-		int err, nregs;
+		int child = prom_getchild(node);
+		if(child)
+			assignment_walk_siblings(pbm, child);
 
-		err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
-		if(err == 0 || err == -1) {
-			prom_printf("fixup_pci_dev: No PCI device reg property?!?!\n");
+		assignment_process(pbm, node);
+
+		node = prom_getsibling(node);
+	}
+}
+
+static void record_assignments(struct linux_pbm_info *pbm)
+{
+	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,
+		       int nregs,
+		       struct linux_prom_pci_registers *assigned,
+		       int numaa)
+{
+	int preg, rng;
+	int IO_seen = 0;
+	int MEM_seen = 0;
+
+	for(preg = 0; preg < nregs; preg++) {
+		struct linux_prom_pci_registers *ap = NULL;
+		int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
+		int bsreg, brindex;
+		u64 pci_addr;
+
+		if(bustype == 0) {
+			/* Config space cookie, nothing to do. */
+			if(preg != 0)
+				prom_printf("fixup_doit: strange, config space not 0\n");
+			continue;
+		} else if(bustype == 3) {
+			/* XXX add support for this... */
+			prom_printf("fixup_doit: Warning, ignoring 64-bit PCI "
+				    "memory space, tell DaveM.\n");
+			continue;
+		}
+		bsreg = (pregs[preg].phys_hi & 0xff);
+
+		/* We can safely ignore these. */
+		if(bsreg == PCI_ROM_ADDRESS)
+			continue;
+
+		/* Sanity */
+		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);
+			continue;
+		}
+
+		brindex = (bsreg - PCI_BASE_ADDRESS_0) >> 2;
+		if(numaa) {
+			int r;
+
+			for(r = 0; r < numaa; r++) {
+				int abreg;
+
+				abreg = (assigned[r].phys_hi & 0xff);
+				if(abreg == bsreg) {
+					ap = &assigned[r];
+					break;
+				}
+			}
+		}
+
+		/* Now construct UPA physical address. */
+		pci_addr  = (((u64)pregs[preg].phys_mid) << 32UL);
+		pci_addr |= (((u64)pregs[preg].phys_lo));
+
+		if(ap) {
+			pci_addr += ((u64)ap->phys_lo);
+			pci_addr += (((u64)ap->phys_mid) << 32UL);
+		}
+
+		/* Final step, apply PBM range. */
+		for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+			struct linux_prom_pci_ranges *rp = &pbm->pbm_ranges[rng];
+			int space = (rp->child_phys_hi >> 24) & 3;
+
+			if(space == bustype) {
+				pci_addr += ((u64)rp->parent_phys_lo);
+				pci_addr += (((u64)rp->parent_phys_hi) << 32UL);
+				break;
+			}
+		}
+		if(rng == pbm->num_pbm_ranges) {
+			/* AIEEE */
+			prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n");
+		}
+		pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
+
+		/* Preserve I/O space bit. */
+		if(bustype == 0x1) {
+			pdev->base_address[brindex] |= 1;
+			IO_seen = 1;
+		} else {
+			MEM_seen = 1;
+		}
+	}
+
+	/* Now handle assignments PROM did not take care of. */
+	if(nregs) {
+		int breg;
+
+		for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
+			unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
+			unsigned int base = (unsigned int)pdev->base_address[ridx];
+			struct pci_vma *vp;
+			u64 pci_addr;
+			int io;
+
+			if(pdev->base_address[ridx] > PAGE_OFFSET)
+				continue;
+
+			io = (base & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO;
+			base &= ~((io ?
+				   PCI_BASE_ADDRESS_IO_MASK :
+				   PCI_BASE_ADDRESS_MEM_MASK));
+			vp = pci_find_vma(pbm, base, io);
+			if(!vp || vp->start > base) {
+				unsigned int size, new_base;
+
+				pcibios_read_config_dword(pdev->bus->number,
+							  pdev->devfn,
+							  breg, &rtmp);
+				pcibios_write_config_dword(pdev->bus->number,
+							   pdev->devfn,
+							   breg, 0xffffffff);
+				pcibios_read_config_dword(pdev->bus->number,
+							  pdev->devfn,
+							  breg, &size);
+				if(io)
+					size &= ~1;
+				size = (~(size) + 1);
+				if(!size)
+					continue;
+
+				new_base = 0;
+				for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) {
+					if(!vp || new_base + size <= vp->start)
+						break;
+					new_base = (vp->end + (size - 1)) & ~(size-1);
+				}
+				if(vp && (new_base + size > vp->start)) {
+					prom_printf("PCI: Impossible full %s space.\n",
+						    (io ? "IO" : "MEM"));
+					prom_halt();
+				}
+				vp = pci_vma_alloc();
+				vp->start = new_base;
+				vp->end = vp->start + size;
+				vp->base_reg = breg;
+
+				/* Sanity */
+				if(io && vp->end & ~(0xffff)) {
+					prom_printf("PCI: Out of range PCI I/O "
+						    "[%08lx:%08lx] during fixup\n",
+						    vp->start, vp->end);
+					prom_halt();
+				}
+				pci_add_vma(pbm, vp, io);
+
+				rtmp = new_base;
+				if(io)
+					rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK);
+				else
+					rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK);
+				pcibios_write_config_dword(pdev->bus->number,
+							   pdev->devfn,
+							   breg, rtmp);
+
+				/* Apply PBM ranges and update pci_dev. */
+				pci_addr = new_base;
+				for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+					struct linux_prom_pci_ranges *rp;
+					int rspace;
+
+					rp = &pbm->pbm_ranges[rng];
+					rspace = (rp->child_phys_hi >> 24) & 3;
+					if(io && rspace != 1)
+						continue;
+					else if(!io && rspace != 2)
+						continue;
+					pci_addr += ((u64)rp->parent_phys_lo);
+					pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+					break;
+				}
+				if(rng == pbm->num_pbm_ranges) {
+					/* AIEEE */
+					prom_printf("fixup_doit: YIEEE, cannot find "
+						    "PBM ranges\n");
+				}
+				pdev->base_address[ridx] = (unsigned long)__va(pci_addr);
+
+				/* Preserve I/O space bit. */
+				if(io) {
+					pdev->base_address[ridx] |= 1;
+					IO_seen = 1;
+				} else {
+					MEM_seen = 1;
+				}
+			}
+		}
+	}
+	if(IO_seen || MEM_seen) {
+		unsigned int l;
+
+		pcibios_read_config_dword(pdev->bus->number,
+					  pdev->devfn,
+					  PCI_COMMAND, &l);
+#ifdef FIXUP_REGS_DEBUG
+		prom_printf("[");
+#endif
+		if(IO_seen) {
+#ifdef FIXUP_REGS_DEBUG
+			prom_printf("IO ");
+#endif
+			l |= PCI_COMMAND_IO;
+		}
+		if(MEM_seen) {
+#ifdef FIXUP_REGS_DEBUG
+			prom_printf("MEM");
+#endif
+			l |= PCI_COMMAND_MEMORY;
+		}
+#ifdef FIXUP_REGS_DEBUG
+		prom_printf("]");
+#endif
+		pcibios_write_config_dword(pdev->bus->number,
+					   pdev->devfn,
+					   PCI_COMMAND, l);
+	}
+
+#ifdef FIXUP_REGS_DEBUG
+	prom_printf("REG_FIXUP[%s]: ", pci_strdev(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]);
+	}
+	prom_printf("\n");
+#endif
+}
+
+#define imap_offset(__member) \
+	((unsigned long)(&(((struct psycho_regs *)0)->__member)))
+
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+	unsigned int bus, slot;
+
+	bus = (ino & 0x10) >> 4;
+	slot = (ino & 0x0c) >> 2;
+
+	if(bus == 0) {
+		/* Perform a sanity check, we might as well.
+		 * PBM A only has 2 PCI slots.
+		 */
+		if(slot > 1) {
+			prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot);
 			prom_halt();
 		}
-		nregs = (err / sizeof(struct linux_prom_pci_registers));
+		if(slot == 0)
+			return imap_offset(imap_a_slot0);
+		else
+			return imap_offset(imap_a_slot1);
+	} else {
+		switch(slot) {
+		case 0:
+			return imap_offset(imap_b_slot0);
+		case 1:
+			return imap_offset(imap_b_slot1);
+		case 2:
+			return imap_offset(imap_b_slot2);
+		case 3:
+			return imap_offset(imap_b_slot3);
+		default:
+			prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
+				    bus, slot);
+			prom_halt();
+			return 0; /* Make gcc happy */
+		};
+	}
+}
 
-		devfn = (pregs[0].phys_hi >> 8) & 0xff;
-		if(devfn == pdev->devfn) {
+/* Exported for EBUS probing layer. */
+unsigned int psycho_irq_build(unsigned int full_ino)
+{
+	unsigned long imap_off, ign, ino;
 
-			return;
+	ign = (full_ino & PSYCHO_IMAP_IGN) >> 6;
+	ino = (full_ino & PSYCHO_IMAP_INO);
+
+	/* Compute IMAP register offset, generic IRQ layer figures out
+	 * the ICLR register address as this is simple given the 32-bit
+	 * irq number and IMAP register address.
+	 */
+	if((ino & 0x20) == 0)
+		imap_off = psycho_pcislot_imap_offset(ino);
+	else {
+		switch(ino) {
+		case 0x20:
+			/* Onboard SCSI. */
+			imap_off = imap_offset(imap_scsi);
+			break;
+
+		case 0x21:
+			/* Onboard Ethernet (ie. CheerIO/HME) */
+			imap_off = imap_offset(imap_eth);
+			break;
+
+		case 0x22:
+			/* Onboard Parallel Port */
+			imap_off = imap_offset(imap_bpp);
+			break;
+
+		case 0x23:
+			/* Audio Record */
+			imap_off = imap_offset(imap_au_rec);
+			break;
+
+		case 0x24:
+			/* Audio Play */
+			imap_off = imap_offset(imap_au_play);
+			break;
+
+		case 0x25:
+			/* Power Fail */
+			imap_off = imap_offset(imap_pfail);
+			break;
+
+		case 0x26:
+			/* Onboard KBD/MOUSE/SERIAL */
+			imap_off = imap_offset(imap_kms);
+			break;
+
+		case 0x27:
+			/* Floppy (ie. fdthree) */
+			imap_off = imap_offset(imap_flpy);
+			break;
+
+		case 0x28:
+			/* Spare HW INT */
+			imap_off = imap_offset(imap_shw);
+			break;
+
+		case 0x29:
+			/* Onboard Keyboard (only) */
+			imap_off = imap_offset(imap_kbd);
+			break;
+
+		case 0x2a:
+			/* Onboard Mouse (only) */
+			imap_off = imap_offset(imap_ms);
+			break;
+
+		case 0x2b:
+			/* Onboard Serial (only) */
+			imap_off = imap_offset(imap_ser);
+			break;
+
+		case 0x32:
+			/* Power Management */
+			imap_off = imap_offset(imap_pmgmt);
+			break;
+
+		default:
+			/* We don't expect anything else.  The other possible
+			 * values are not found in PCI device nodes, and are
+			 * so hardware specific that they should use DCOOKIE's
+			 * anyways.
+			 */
+			prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
+			prom_halt();
+		};
+	}
+	imap_off -= imap_offset(imap_a_slot0);
+
+	return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino);
+}
+
+/* #define FIXUP_IRQ_DEBUG */
+
+static void fixup_irq(struct pci_dev *pdev,
+		      struct linux_pbm_info *pbm,
+		      int node)
+{
+	unsigned int prom_irq, portid = pbm->parent->upa_portid;
+	unsigned char pci_irq_line = pdev->irq;
+	int err;
+
+#ifdef FIXUP_IRQ_DEBUG
+	printk("fixup_irq[%s:%s]: ",
+	       pci_strvendor(pdev->vendor),
+	       pci_strdev(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_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);
+#ifdef FIXUP_IRQ_DEBUG
+		printk("fully specified prom_irq[%x] pdev->irq[%x]",
+		       prom_irq, pdev->irq);
+#endif
+	} else {
+		unsigned int bus, slot, line;
+
+		bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
+		line = (pci_irq_line) & 3;
+
+		/* Slot determination is only slightly complex.  Handle
+		 * the easy case first.
+		 */
+		if(pdev->bus->number == pbm->pci_first_busno) {
+			if(pbm == &pbm->parent->pbm_A)
+				slot = (pdev->devfn >> 3) - 1;
+			else
+				slot = ((pdev->devfn >> 3) >> 1) - 1;
+		} else {
+			/* Underneath a bridge, use slot number of parent
+			 * bridge.
+			 */
+			slot = (pdev->bus->self->devfn >> 3) - 1;
+
+			/* Use low slot number bits of child as IRQ line. */
+			line = ((pdev->devfn >> 3) & 3);
 		}
+		slot = (slot << 2);
 
-		node = prom_getsibling(node);
+		pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) |
+					     (bus | slot | line)));
+#ifdef FIXUP_IRQ_DEBUG
+		do {
+			unsigned char iline, ipin;
+
+			(void)pcibios_read_config_byte(pdev->bus->number,
+						       pdev->devfn,
+						       PCI_INTERRUPT_PIN,
+						       &ipin);
+			(void)pcibios_read_config_byte(pdev->bus->number,
+						       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);
+		} while(0);
+#endif
 	}
+#ifdef FIXUP_IRQ_DEBUG
+	printk("\n");
+#endif
+}
 
-	prom_printf("fixup_pci_dev: Cannot find prom node for PCI device\n");
-	prom_halt();
+static void fixup_doit(struct pci_dev *pdev,
+		       struct linux_pbm_info *pbm,
+		       struct linux_prom_pci_registers *pregs,
+		       int nregs,
+		       int node)
+{
+	struct linux_prom_pci_registers assigned[PROMREG_MAX];
+	int numaa, err;
+
+	/* Get assigned addresses, if any. */
+	err = prom_getproperty(node, "assigned-addresses",
+			       (char *)&assigned[0], sizeof(assigned));
+	if(err == 0 || err == -1)
+		numaa = 0;
+	else
+		numaa = (err / sizeof(struct linux_prom_pci_registers));
+
+	/* First, scan and fixup base registers. */
+	fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa);
+
+	/* Next, fixup interrupt numbers. */
+	fixup_irq(pdev, pbm, node);
+}
+
+static void fixup_pci_dev(struct pci_dev *pdev,
+			  struct pci_bus *pbus,
+			  struct linux_pbm_info *pbm)
+{
+	struct linux_prom_pci_registers pregs[PROMREG_MAX];
+	struct pcidev_cookie *pcp = pdev->sysdata;
+	int node, nregs, err;
+
+	/* If this is a PCI bridge, we must program it. */
+	if(pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) {
+		unsigned short cmd;
+
+		/* First, enable bus mastering. */
+		pcibios_read_config_word(pdev->bus->number,
+					 pdev->devfn,
+					 PCI_COMMAND, &cmd);
+		cmd |= PCI_COMMAND_MASTER;
+		pcibios_write_config_word(pdev->bus->number,
+					  pdev->devfn,
+					  PCI_COMMAND, cmd);
+
+		/* Now, set cache line size to 64-bytes. */
+		pcibios_write_config_byte(pdev->bus->number,
+					  pdev->devfn,
+					  PCI_CACHE_LINE_SIZE, 64);
+	}
+
+	/* Ignore if this is one of the PBM's, EBUS, or a
+	 * sub-bridge underneath the PBM.  We only need to fixup
+	 * true devices.
+	 */
+	if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) ||
+	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) ||
+	   (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
+	   (pcp == NULL))
+		return;
+
+	node = pcp->prom_node;
+
+	err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
+	if(err == 0 || err == -1) {
+		prom_printf("Cannot find REG for pci_dev\n");
+		prom_halt();
+	}
+
+	nregs = (err / sizeof(pregs[0]));
+
+	fixup_doit(pdev, pbm, &pregs[0], nregs, node);
 }
 
 static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
@@ -344,16 +1090,13 @@
  */
 static void psycho_final_fixup(struct linux_psycho *psycho)
 {
-	/* First, walk all PCI devices found.  For each device, and
-	 * PCI bridge which is not one of the PSYCHO PBM's, fill in the
-	 * sysdata with a pointer to the PBM.
-	 */
-	fill_in_pbm_cookies(&psycho->pbm_A);
-	fill_in_pbm_cookies(&psycho->pbm_B);
-
 	/* 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)
@@ -380,9 +1123,25 @@
 	/* Probe busses under PBM B. */
 	pbm_probe(&psycho->pbm_B, &memory_start);
 
+	pci_init_alloc_init(&memory_start);
+
+	/* Walk all PCI devices found.  For each device, and
+	 * PCI bridge which is not one of the PSYCHO PBM's, fill in the
+	 * 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);
+
+	/* 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);
 
+	pci_init_alloc_fini();
+
 	return ebus_init(memory_start, memory_end);
 }
 
@@ -464,8 +1223,6 @@
 	if(out_of_range(bus, device_fn))
 		return PCIBIOS_SUCCESSFUL;
 
-	/* XXX Check no-probe-list conflicts here. XXX */
-
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -485,9 +1242,9 @@
 			*value = (word >> 16) & 0xffff;
 			break;
 		default:
-			prom_printf("pcibios_read_config_word: misaligned "
-				    "reg [%x]\n", where);
-			prom_halt();
+			printk("pcibios_read_config_word: misaligned "
+			       "reg [%x]\n", where);
+			break;
 		};
 	}
 	return PCIBIOS_SUCCESSFUL;
@@ -504,8 +1261,6 @@
 	if(out_of_range(bus, device_fn))
 		return PCIBIOS_SUCCESSFUL;
 
-	/* XXX Check no-probe-list conflicts here. XXX */
-
 	pci_poke_in_progress = 1;
 	pci_poke_faulted = 0;
 	__asm__ __volatile__("membar #Sync\n\t"
@@ -529,8 +1284,6 @@
 	if(out_of_range(bus, device_fn))
 		return PCIBIOS_SUCCESSFUL;
 
-	/* XXX Check no-probe-list conflicts here. XXX */
-
 	pci_poke_in_progress = 1;
 
 	/* Endianness doesn't matter but we have to get the memory
@@ -554,8 +1307,6 @@
 	if(out_of_range(bus, device_fn))
 		return PCIBIOS_SUCCESSFUL;
 
-	/* XXX Check no-probe-list conflicts here. XXX */
-
 	pci_poke_in_progress = 1;
 	__asm__ __volatile__("membar #Sync\n\t"
 			     "stha %0, [%1] %2\n\t"
@@ -573,8 +1324,6 @@
 
 	if(out_of_range(bus, device_fn))
 		return PCIBIOS_SUCCESSFUL;
-
-	/* XXX Check no-probe-list conflicts here. XXX */
 
 	pci_poke_in_progress = 1;
 	__asm__ __volatile__("membar #Sync\n\t"
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- v2.1.52/linux/arch/sparc64/kernel/rtrap.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/rtrap.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.30 1997/08/10 04:49:33 davem Exp $
+/* $Id: rtrap.S,v 1.33 1997/08/21 09:13:22 davem Exp $
  * rtrap.S: Preparing for return from trap on Sparc V9.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -21,60 +21,76 @@
 		sethi			%hi(bh_mask), %l1
 		ldx			[%l2 + %lo(bh_active)], %l4
 		ldx			[%l1 + %lo(bh_mask)], %l7
+
 		andcc			%l4, %l7, %g0
 		be,pt			%xcc, 2f
-
 		 nop
 		call			do_bottom_half
 		 nop
 2:		ldx			[%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
 		sethi			%hi(0xf << 20), %l4
 		andcc			%l1, TSTATE_PRIV, %l3
+
 		and			%l1, %l4, %l4
 		rdpr			%pstate, %l7
-
 		andn			%l1, %l4, %l1
 		be,pt			%icc, to_user
 		 andn			%l7, PSTATE_IE, %l7
-rt_continue:	ld			[%sp + PTREGS_OFF + PT_V9_FPRS], %l2
-		ld			[%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0
-		ldx			[%sp + PTREGS_OFF + PT_V9_G1], %g1
-		brnz,pn			%l2, rt_fpu_restore
-		 ldx			[%sp + PTREGS_OFF + PT_V9_G2], %g2
 
-rt_after_fpu:	ldx			[%sp + PTREGS_OFF + PT_V9_G3], %g3
+		ld			[%sp + PTREGS_OFF + PT_V9_FPRS], %l2
+		andcc			%l2, FPRS_FEF, %g0
+		be,pt			%icc, rt_continue
+		 and			%l2, FPRS_DL, %l6
+		wr			%g0, FPRS_FEF, %fprs
+		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x108], %g5
+		membar			#StoreLoad | #LoadLoad
+		brz,pn			%l6, 1f
+		 wr			%g0, ASI_BLK_P, %asi
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16
+1:		andcc			%l2, FPRS_DU, %g0
+		be,pn			%icc, 1f
+		 wr			%g5, 0, %gsr
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32
+		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
+		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
 		ldx			[%sp + PTREGS_OFF + PT_V9_G4], %g4
 		ldx			[%sp + PTREGS_OFF + PT_V9_G5], %g5
 		ldx			[%sp + PTREGS_OFF + PT_V9_G6], %g6
 		ldx			[%sp + PTREGS_OFF + PT_V9_G7], %g7
+
 		wrpr			%l7, PSTATE_AG, %pstate
 		ldx			[%sp + PTREGS_OFF + PT_V9_I0], %i0
-
 		ldx			[%sp + PTREGS_OFF + PT_V9_I1], %i1
 		ldx			[%sp + PTREGS_OFF + PT_V9_I2], %i2
 		ldx			[%sp + PTREGS_OFF + PT_V9_I3], %i3
 		ldx			[%sp + PTREGS_OFF + PT_V9_I4], %i4
 		ldx			[%sp + PTREGS_OFF + PT_V9_I5], %i5
 		ldx			[%sp + PTREGS_OFF + PT_V9_I6], %i6
+
 		ldx			[%sp + PTREGS_OFF + PT_V9_I7], %i7
 		ld			[%sp + PTREGS_OFF + PT_V9_Y], %o3
-
 		ldx			[%sp + PTREGS_OFF + PT_V9_TPC], %l2
 		ldx			[%sp + PTREGS_OFF + PT_V9_TNPC], %o2
 		wr			%o3, %g0, %y
 		srl			%l4, 20, %l4
 		wrpr			%l4, 0x0, %pil
 		wrpr			%g0, 0x1, %tl
+
 		wrpr			%l1, %g0, %tstate
 		wrpr			%l2, %g0, %tpc
-
-		wr			%g0, ASI_DMMU, %asi
+		wrpr			%o2, %g0, %tnpc
+		mov			PRIMARY_CONTEXT, %l7
 		brnz,pn			%l3, kern_rtt
-		 wrpr			%o2, %g0, %tnpc
-		stxa			%l0, [%g0 + SECONDARY_CONTEXT] %asi
-		flush			%l6
-		stxa			%l0, [%g0 + PRIMARY_CONTEXT] %asi
+		 mov			SECONDARY_CONTEXT, %l5
+		stxa			%l0, [%l7] ASI_DMMU
+		stxa			%l0, [%l5] ASI_DMMU
 		flush			%l6
 		rdpr			%wstate, %l1
 
@@ -95,17 +111,17 @@
 		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
+
 		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
@@ -113,31 +129,115 @@
 		mov			%l6, %o3
 		call			do_signal
 		 add			%sp, STACK_BIAS + REGWIN_SZ, %o1
-
 		ldx			[%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
 		clr			%l6
-check_user_wins:brz,pt			%o2, rt_continue
-		 nop
+check_user_wins:brz,pt			%o2, 1f
+		 sethi			%hi(TSTATE_PEF), %o3
+
 		call			fault_in_user_windows
 		 add			%sp, STACK_BIAS + REGWIN_SZ, %o0
-		ba,a,pt			%xcc, rt_continue
-rt_fpu_restore:	wr			%g0, FPRS_FEF, %fprs
 
-		add			%sp, PTREGS_OFF + TRACEREG_SZ, %g4
-		wr			%g0, ASI_BLK_P, %asi
+		sethi			%hi(TSTATE_PEF), %o3
+1:		andcc			%l2, FPRS_FEF, %g0
+		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
+		wr			%g0, FPRS_FEF, %fprs
+		wr			%o3, 0, %gsr
+		andcc			%l2, SPARC_FLAG_USEDFPUL, %g0
+		bne,pn			%icc, 2f
+		 andcc			%l2, SPARC_FLAG_USEDFPUU, %g0
+		fzero			%f0
+		bne,pn			%icc, 1f
+		 fzero			%f2
+		faddd			%f0, %f2, %f4
+		fmuld			%f0, %f2, %f6
+		faddd			%f0, %f2, %f8
+		fmuld			%f0, %f2, %f10
+		faddd			%f0, %f2, %f12
+		fmuld			%f0, %f2, %f14
+		faddd			%f0, %f2, %f16
+		fmuld			%f0, %f2, %f18
+		faddd			%f0, %f2, %f20
+		fmuld			%f0, %f2, %f22
+		faddd			%f0, %f2, %f24
+		fmuld			%f0, %f2, %f26
+		faddd			%f0, %f2, %f28
+		fmuld			%f0, %f2, %f30
+		faddd			%f0, %f2, %f32
+		fmuld			%f0, %f2, %f34
+		faddd			%f0, %f2, %f36
+		fmuld			%f0, %f2, %f38
+		faddd			%f0, %f2, %f40
+		fmuld			%f0, %f2, %f42
+		faddd			%f0, %f2, %f44
+		fmuld			%f0, %f2, %f46
+		faddd			%f0, %f2, %f48
+		fmuld			%f0, %f2, %f50
+		faddd			%f0, %f2, %f52
+		fmuld			%f0, %f2, %f54
+		faddd			%f0, %f2, %f56
+		fmuld			%f0, %f2, %f58
+		faddd			%f0, %f2, %f60
+		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr
+		ba,pt			%xcc, rt_continue
+		 wr			%g0, FPRS_FEF, %fprs
+1:		wr			%g0, ASI_BLK_P, %asi
 		membar			#StoreLoad | #LoadLoad
-		ldda			[%g4 + 0x000] %asi, %f0
-		ldda			[%g4 + 0x040] %asi, %f16
-		ldda			[%g4 + 0x080] %asi, %f32
-		ldda			[%g4 + 0x0c0] %asi, %f48
-		ldx			[%g4 + 0x100], %fsr
-
-		ldx			[%g4 + 0x108], %g3
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48
+		faddd			%f0, %f2, %f4
+		fmuld			%f0, %f2, %f6
+		faddd			%f0, %f2, %f8
+		fmuld			%f0, %f2, %f10
+		faddd			%f0, %f2, %f12
+		fmuld			%f0, %f2, %f14
+		faddd			%f0, %f2, %f16
+		fmuld			%f0, %f2, %f18
+		faddd			%f0, %f2, %f20
+		fmuld			%f0, %f2, %f22
+		faddd			%f0, %f2, %f24
+		fmuld			%f0, %f2, %f26
+		faddd			%f0, %f2, %f28
+		fmuld			%f0, %f2, %f30
 		membar			#Sync
-		b,pt			%xcc, rt_after_fpu
-		 wr			%g3, 0, %gsr
-		nop
-		nop
-		nop
-		nop
+		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr
+		ba,pt			%xcc, rt_continue
+		 wr			%g0, FPRS_FEF, %fprs
+2:		membar			#StoreLoad | #LoadLoad
+		andcc			%l2, SPARC_FLAG_USEDFPUU, %g0
+		bne,pt			%icc, 3f
+		 wr			%g0, ASI_BLK_P, %asi
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16
+		fzero			%f32
+		fzero			%f34
+		faddd			%f32, %f34, %f36
+		fmuld			%f32, %f34, %f38
+		faddd			%f32, %f34, %f40
+		fmuld			%f32, %f34, %f42
+		faddd			%f32, %f34, %f44
+		fmuld			%f32, %f34, %f46
+		faddd			%f32, %f34, %f48
+		fmuld			%f32, %f34, %f50
+		faddd			%f32, %f34, %f52
+		fmuld			%f32, %f34, %f54
+		faddd			%f32, %f34, %f56
+		fmuld			%f32, %f34, %f58
+		faddd			%f32, %f34, %f60
+		fmuld			%f32, %f34, %f62
+		membar			#Sync
+		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr
+		ba,pt			%xcc, rt_continue
+		 wr			%g0, FPRS_FEF, %fprs
+3:		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x000] %asi, %f0
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x040] %asi, %f16
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x080] %asi, %f32
+		ldda			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48
+		membar			#Sync
+		ldx			[%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr
+		ba,pt			%xcc, rt_continue
+		 wr			%g0, FPRS_FEF, %fprs
+
 #undef PTREGS_OFF
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.1.52/linux/arch/sparc64/kernel/setup.c	Mon Aug  4 16:25:37 1997
+++ linux/arch/sparc64/kernel/setup.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.11 1997/07/24 12:15:05 davem Exp $
+/*  $Id: setup.c,v 1.12 1997/08/28 02:23:19 ecd Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -232,8 +232,6 @@
 #define RAMDISK_LOAD_FLAG		0x4000
 
 extern int root_mountflags;
-
-extern void register_console(void (*proc)(const char *));
 
 char saved_command_line[256];
 char reboot_command[256];
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
--- v2.1.52/linux/arch/sparc64/kernel/signal.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/signal.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.22 1997/08/05 19:19:36 davem Exp $
+/*  $Id: signal.c,v 1.24 1997/09/02 20:53:03 davem Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -67,7 +67,7 @@
 	regs->tpc = pc;
 	regs->tnpc = npc;
 	__get_user(regs->y, &((*grp)[MC_Y]));
-	__get_user(tstate, &((*grp)[MC_Y]));
+	__get_user(tstate, &((*grp)[MC_TSTATE]));
 	regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
 	regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
 	__get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1]));
@@ -94,11 +94,18 @@
 	__get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab));
 	if(fenab) {
 		unsigned long *fpregs = (unsigned long *)(regs+1);
-		copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs),
-			       (sizeof(unsigned long) * 32));
+		unsigned long fprs;
+		__get_user(fprs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fprs));
+		if (fprs & FPRS_DL)
+			copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs),
+			       	       (sizeof(unsigned int) * 32));
+		if (fprs & FPRS_DU)
+			copy_from_user(fpregs+16, ((unsigned long *)&(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs))+16,
+			       	       (sizeof(unsigned int) * 32));
 		__get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr));
 		__get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr));
-		regs->fprs = FPRS_FEF;
+		regs->fprs = fprs;
+		regs->tstate |= TSTATE_PEF;
 	}
 	return;
 do_sigsegv:
@@ -113,7 +120,7 @@
 	mc_gregset_t *grp;
 	mcontext_t *mcp;
 	unsigned long fp, i7;
-	unsigned char fenab = (current->flags & PF_USEDFPU);
+	unsigned char fenab = (current->tss.flags & SPARC_FLAG_USEDFPU);
 
 	synchronize_user_stack();
 	if(tp->w_saved || clear_user(ucp, sizeof(*ucp)))
@@ -154,11 +161,25 @@
 	__put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab));
 	if(fenab) {
 		unsigned long *fpregs = (unsigned long *)(regs+1);
-		copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs,
-			     (sizeof(unsigned long) * 32));
+		unsigned long fprs;
+		
+		fprs = (regs->fprs & FPRS_FEF) | 
+		       (current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU));
+		if (fprs & FPRS_DL)
+			copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs,
+				     (sizeof(unsigned int) * 32));
+		else
+			clear_user(&(mcp->mc_fpregs.mcfpu_fregs),
+				     (sizeof(unsigned int) * 32));
+		if (fprs & FPRS_DU)
+			copy_to_user(((unsigned long *)&(mcp->mc_fpregs.mcfpu_fregs))+16, fpregs+16,
+				     (sizeof(unsigned int) * 32));
+		else
+			clear_user(((unsigned long *)&(mcp->mc_fpregs.mcfpu_fregs))+16,
+				     (sizeof(unsigned int) * 32));
 		__put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr));
 		__put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr));
-		__put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs));
+		__put_user(fprs, &(mcp->mc_fpregs.mcfpu_fprs));
 	}
 	return;
 do_sigsegv:
@@ -241,11 +262,19 @@
 restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
 	unsigned long *fpregs = (unsigned long *)(regs+1);
-	copy_from_user(fpregs, &fpu->si_float_regs[0],
-		       (sizeof(unsigned int) * 64));
+	unsigned long fprs;
+	
+	__get_user(fprs, &fpu->si_fprs);
+	if (fprs & FPRS_DL)
+		copy_from_user(fpregs, &fpu->si_float_regs[0],
+		       	       (sizeof(unsigned int) * 32));
+	if (fprs & FPRS_DU)
+		copy_from_user(fpregs+16, &fpu->si_float_regs[32],
+		       	       (sizeof(unsigned int) * 32));
 	__get_user(fpregs[32], &fpu->si_fsr);
 	__get_user(fpregs[33], &fpu->si_gsr);
-	regs->fprs = FPRS_FEF;
+	regs->fprs = fprs;
+	regs->tstate |= TSTATE_PEF;
 }
 
 void do_sigreturn(struct pt_regs *regs)
@@ -312,11 +341,26 @@
 save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
 	unsigned long *fpregs = (unsigned long *)(regs+1);
-	copy_to_user(&fpu->si_float_regs[0], fpregs,
-		     (sizeof(unsigned int) * 64));
+	unsigned long fprs;
+	
+	fprs = (regs->fprs & FPRS_FEF) | 
+		(current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU));
+	if (fprs & FPRS_DL)
+		copy_to_user(&fpu->si_float_regs[0], fpregs,
+		     	     (sizeof(unsigned int) * 32));
+	else
+		clear_user(&fpu->si_float_regs[0],
+		     	     (sizeof(unsigned int) * 32));
+	if (fprs & FPRS_DU)
+		copy_to_user(&fpu->si_float_regs[32], fpregs+16,
+		     	     (sizeof(unsigned int) * 32));
+	else
+		clear_user(&fpu->si_float_regs[32],
+		     	     (sizeof(unsigned int) * 32));
 	__put_user(fpregs[32], &fpu->si_fsr);
 	__put_user(fpregs[33], &fpu->si_gsr);
-	regs->fprs = 0;
+	__put_user(fprs, &fpu->si_fprs);
+	regs->tstate &= ~TSTATE_PEF;
 }
 
 static inline void
@@ -329,7 +373,7 @@
 	/* 1. Make sure everything is clean */
 	synchronize_user_stack();
 	sigframe_size = NF_ALIGNEDSZ;
-	if (!(current->flags & PF_USEDFPU))
+	if (!(current->tss.flags & SPARC_FLAG_USEDFPU))
 		sigframe_size -= sizeof(__siginfo_fpu_t);
 
 	sf = (struct new_signal_frame *)
@@ -347,7 +391,7 @@
 	/* 2. Save the current process state */
 	copy_to_user(&sf->info.si_regs, regs, sizeof (*regs));
 
-	if (current->flags & PF_USEDFPU) {
+	if (current->tss.flags & SPARC_FLAG_USEDFPU) {
 		save_fpu_state(regs, &sf->fpu_state);
 		__put_user((u64)&sf->fpu_state, &sf->fpu_save);
 	} else {
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- v2.1.52/linux/arch/sparc64/kernel/signal32.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/signal32.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.28 1997/08/05 19:19:40 davem Exp $
+/*  $Id: signal32.c,v 1.30 1997/08/29 15:51:33 jj Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -117,9 +117,17 @@
 static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
 	unsigned long *fpregs = (unsigned long *)(regs + 1);
-	copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64));
+	unsigned long fprs;
+	
+	__get_user(fprs, &fpu->si_fprs);
+	if (fprs & FPRS_DL)
+		copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 32));
+	if (fprs & FPRS_DU)
+		copy_from_user(fpregs+16, &fpu->si_float_regs[32], (sizeof(unsigned int) * 32));
 	__get_user(fpregs[32], &fpu->si_fsr);
 	__get_user(fpregs[33], &fpu->si_gsr);
+	regs->fprs = fprs;
+	regs->tstate |= TSTATE_PEF;
 }
 
 void do_new_sigreturn32(struct pt_regs *regs)
@@ -169,8 +177,10 @@
 	regs->tstate &= ~(TSTATE_ICC);
 	regs->tstate |= psr_to_tstate_icc(psr);
 
+#if 0
 	if (psr & PSR_EF)
-		regs->fprs = FPRS_FEF;
+		regs->tstate |= TSTATE_PEF;
+#endif		
 
 	__get_user(fpu_save, &sf->fpu_save);
 	if (fpu_save)
@@ -273,7 +283,7 @@
 	__put_user(pc, &sc->sigc_pc);
 	__put_user(npc, &sc->sigc_npc);
 	psr = tstate_to_psr (regs->tstate);
-	if(current->flags & PF_USEDFPU)
+	if(current->tss.flags & SPARC_FLAG_USEDFPU)
 		psr |= PSR_EF;
 	__put_user(psr, &sc->sigc_psr);
 	__put_user(regs->u_regs[UREG_G1], &sc->sigc_g1);
@@ -318,10 +328,22 @@
 static inline void save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu)
 {
 	unsigned long *fpregs = (unsigned long *)(regs+1);
-	copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64));
+	unsigned long fprs;
+	
+	fprs = (regs->fprs & FPRS_FEF) |
+		(current->tss.flags & (SPARC_FLAG_USEDFPUL | SPARC_FLAG_USEDFPUU));
+	if (fprs & FPRS_DL)
+		copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 32));
+	else
+		clear_user(&fpu->si_float_regs[0], (sizeof(unsigned int) * 32));
+	if (fprs & FPRS_DU)
+		copy_to_user(&fpu->si_float_regs[32], fpregs+16, (sizeof(unsigned int) * 32));
+	else
+		clear_user(&fpu->si_float_regs[32], (sizeof(unsigned int) * 32));
 	__put_user(fpregs[32], &fpu->si_fsr);
 	__put_user(fpregs[33], &fpu->si_gsr);
-	regs->fprs = 0;
+	__put_user(fprs, &fpu->si_fprs);
+	regs->tstate &= ~TSTATE_PEF;
 }
 
 static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs,
@@ -335,7 +357,7 @@
 	/* 1. Make sure everything is clean */
 	synchronize_user_stack();
 	sigframe_size = NF_ALIGNEDSZ;
-	if (!(current->flags & PF_USEDFPU))
+	if (!(current->tss.flags & SPARC_FLAG_USEDFPU))
 		sigframe_size -= sizeof(__siginfo_fpu_t);
 
 	regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
@@ -362,7 +384,7 @@
 	__put_user(regs->tnpc, &sf->info.si_regs.npc);
 	__put_user(regs->y, &sf->info.si_regs.y);
 	psr = tstate_to_psr (regs->tstate);
-	if(current->flags & PF_USEDFPU)
+	if(current->tss.flags & SPARC_FLAG_USEDFPU)
 		psr |= PSR_EF;
 	__put_user(psr, &sf->info.si_regs.psr);
 	for (i = 0; i < 16; i++)
@@ -469,7 +491,7 @@
 	__put_user(regs->tpc, &((*gr) [SVR4_PC]));
 	__put_user(regs->tnpc, &((*gr) [SVR4_NPC]));
 	psr = tstate_to_psr (regs->tstate);
-	if(current->flags & PF_USEDFPU)
+	if(current->tss.flags & SPARC_FLAG_USEDFPU)
 		psr |= PSR_EF;
 	__put_user(psr, &((*gr) [SVR4_PSR]));
 	__put_user(regs->y, &((*gr) [SVR4_Y]));
@@ -488,7 +510,7 @@
 	/* Save the currently window file: */
 
 	/* 1. Link sfp->uc->gwins to our windows */
-	__put_user(gw, &mc->gwin);
+	__put_user((u32)(long)gw, &mc->gwin);
 	    
 	/* 2. Number of windows to restore at setcontext (): */
 	__put_user(current->tss.w_saved, &gw->count);
@@ -506,9 +528,9 @@
 	 */
 #if 0	 
 	for(window = 0; window < current->tss.w_saved; window++) {
-		__put_user((int *) &(gw->win [window]), &gw->winptr [window]);
+		__put_user((int *) &(gw->win [window]), (int **)gw->winptr +window );
 		copy_to_user(&gw->win [window], &current->tss.reg_window [window], sizeof (svr4_rwindow_t));
-		__put_user(0, gw->winptr [window]);
+		__put_user(0, (int *)gw->winptr + window);
 	}
 #endif	
 
@@ -546,7 +568,7 @@
 }
 
 asmlinkage int
-svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs)
+svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs)
 {
 	svr4_gregset_t  *gr;
 	svr4_mcontext_t *mc;
@@ -572,7 +594,7 @@
 	__put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]);
 	__put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]);
 	__put_user((tstate_to_psr(regs->tstate) |
-		    ((current->flags & PF_USEDFPU) ? PSR_EF : 0)),
+		    ((current->tss.flags & SPARC_FLAG_USEDFPU) ? PSR_EF : 0)),
 		   &uc->mcontext.greg [SVR4_PSR]);
         __put_user(regs->y, &uc->mcontext.greg [SVR4_Y]);
 	
@@ -595,7 +617,7 @@
 
 
 /* Set the context for a svr4 application, this is Solaris way to sigreturn */
-asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs)
+asmlinkage int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs)
 {
 	struct thread_struct *tp = &current->tss;
 	svr4_gregset_t  *gr;
@@ -640,9 +662,10 @@
 	__get_user(psr, &((*gr) [SVR4_PSR]));
 	regs->tstate &= ~(TSTATE_ICC);
 	regs->tstate |= psr_to_tstate_icc(psr);
+#if 0	
 	if(psr & PSR_EF)
-		regs->fprs = FPRS_FEF;
-
+		regs->tstate |= TSTATE_PEF;
+#endif
 	/* Restore g[1..7] and o[0..7] registers */
 	for (i = 0; i < 7; i++)
 		__get_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i);
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- v2.1.52/linux/arch/sparc64/kernel/smp.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/smp.c	Thu Sep  4 12:54:48 1997
@@ -395,7 +395,9 @@
 		if(mm->cpu_vm_mask == (1UL << smp_processor_id()))
 			goto local_flush_and_out;
 		return smp_cross_call_avoidance(mm);
-	} else if(mm != current->mm && mm->count == 1) {
+	}
+#if 0 /* XXX Disabled until further notice... */
+	else if(mm != current->mm && mm->count == 1) {
 		/* Try to handle two special cases to avoid cross calls
 		 * in common scenerios where we are swapping process
 		 * pages out.
@@ -405,6 +407,7 @@
 		if(mm->cpu_vm_mask == (1UL << smp_processor_id()))
 			goto local_flush_and_out;
 	}
+#endif
 	smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0);
 
 local_flush_and_out:
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.52/linux/arch/sparc64/kernel/sparc64_ksyms.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.17 1997/08/10 01:51:01 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.21 1997/09/03 12:29:07 jj Exp $
  * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/in6.h>
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -28,11 +29,13 @@
 #include <asm/ptrace.h>
 #include <asm/user.h>
 #include <asm/uaccess.h>
+#include <asm/checksum.h>
 #ifdef CONFIG_SBUS
 #include <asm/sbus.h>
 #include <asm/dma.h>
 #endif
 #include <asm/a.out.h>
+#include <asm/svr4.h>
 
 struct poll {
 	int fd;
@@ -45,14 +48,27 @@
 void _sigpause_common (unsigned int set, struct pt_regs *);
 extern void *__bzero_1page(void *);
 extern void *__bzero(void *, size_t);
+extern void *__bzero_noasi(void *, size_t);
 extern void *__memscan_zero(void *, size_t);
 extern void *__memscan_generic(void *, int, size_t);
 extern int __memcmp(const void *, const void *, __kernel_size_t);
 extern int __strncmp(const char *, const char *, __kernel_size_t);
-extern unsigned int csum_partial_copy_sparc64(const char *src, char *dst,
-					      int len, unsigned int sum);
 extern char saved_command_line[];
-
+extern char *getname32(u32 name);
+extern void linux_sparc_syscall(void);
+extern void rtrap(void);
+extern void show_regs(struct pt_regs *);
+extern void solaris_syscall(void);
+extern void syscall_trace(void);
+extern u32 sunos_sys_table[], sys_call_table32[];
+extern void tl0_solaris(void);
+extern void sys_sigsuspend(void);
+extern int sys_getppid(void);
+extern int svr4_getcontext(svr4_ucontext_t *uc, struct pt_regs *regs);
+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 void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
 
@@ -155,6 +171,25 @@
 EXPORT_SYMBOL(strstr);
 EXPORT_SYMBOL(strspn);
 
+#ifdef CONFIG_SOLARIS_EMUL_MODULE
+EXPORT_SYMBOL(getname32);
+EXPORT_SYMBOL(linux_sparc_syscall);
+EXPORT_SYMBOL(rtrap);
+EXPORT_SYMBOL(show_regs);
+EXPORT_SYMBOL(solaris_syscall);
+EXPORT_SYMBOL(syscall_trace);
+EXPORT_SYMBOL(sunos_sys_table);
+EXPORT_SYMBOL(sys_call_table32);
+EXPORT_SYMBOL(tl0_solaris);
+EXPORT_SYMBOL(sys_sigsuspend);
+EXPORT_SYMBOL(sys_getppid);
+EXPORT_SYMBOL(svr4_getcontext);
+EXPORT_SYMBOL(svr4_setcontext);
+EXPORT_SYMBOL(linux_cpus);
+EXPORT_SYMBOL(sys_ioctl);
+EXPORT_SYMBOL(sys32_ioctl);
+#endif
+
 /* Special internal versions of library functions. */
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memset);
@@ -172,6 +207,7 @@
 EXPORT_SYMBOL(__copy_to_user);
 EXPORT_SYMBOL(__copy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(__bzero_noasi);
 
 /* No version information on this, heavily used in inline asm,
  * and will always be 'void __ret_efault(void)'.
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/sys32.S linux/arch/sparc64/kernel/sys32.S
--- v2.1.52/linux/arch/sparc64/kernel/sys32.S	Sun Jul 27 12:11:00 1997
+++ linux/arch/sparc64/kernel/sys32.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.2 1997/07/20 09:18:47 davem Exp $
+/* $Id: sys32.S,v 1.3 1997/08/22 20:11:47 davem Exp $
  * sys32.S: I-cache tricks for 32-bit compatability layer simple
  *          conversions.
  *
@@ -73,7 +73,7 @@
 	.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_utimes, sys32_ustat
+	.globl		sys32_mkdir, sys32_mknod, sys32_ustat
 sys32_read:
 	srl		%o1, 0, %o1
 	mov		%o7, %g1
@@ -202,12 +202,6 @@
 	srl		%o2, 16, %o2
 	call		sys_mknod
 	 mov		%g1, %o7
-sys32_utimes:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_utimes
-	 mov		%g1, %o7
 sys32_ustat:
 	srl		%o1, 0, %o1
 	mov		%o7, %g1
@@ -285,24 +279,9 @@
 	call		sys_setsockopt
 	 mov		%g1, %o7
 
-	.align		32
-	.globl		sys32_gettimeofday, sys32_settimeofday
-sys32_gettimeofday:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_gettimeofday
-	 mov		%g1, %o7
-sys32_settimeofday:
-	srl		%o0, 0, %o0
-	mov		%o7, %g1
-	srl		%o1, 0, %o1
-	call		sys_settimeofday
-	 mov		%g1, %o7
-
 	.globl		sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog
-	.globl		sys32_personality, sys32_waitpid, sys32_getitimer
-	.globl		sys32_setitimer, sys32_sched_setscheduler
+	.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
@@ -337,17 +316,6 @@
 	srl		%o1, 0, %o1
 	mov		%o7, %g1
 	call		sys_waitpid
-	 mov		%g1, %o7
-sys32_getitimer:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	call		sys_getitimer
-	 mov		%g1, %o7
-sys32_setitimer:
-	srl		%o1, 0, %o1
-	mov		%o7, %g1
-	srl		%o2, 0, %o2
-	call		sys_setitimer
 	 mov		%g1, %o7
 sys32_sched_setscheduler:
 	srl		%o2, 0, %o2
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.1.52/linux/arch/sparc64/kernel/sys_sparc.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/sys_sparc.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.3 1997/07/29 09:35:10 davem Exp $
+/* $Id: sys_sparc.c,v 1.5 1997/09/03 12:29:05 jj Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -191,9 +191,12 @@
 asmlinkage unsigned long
 c_sys_nis_syscall (struct pt_regs *regs)
 {
+	static int count=0;
 	lock_kernel();
-	printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
-	show_regs (regs);
+	if (++count <= 20) { /* Don't make the system unusable, if someone goes stuck */
+		printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
+		show_regs (regs);
+	}
 	unlock_kernel();
 	return -ENOSYS;
 }
@@ -256,4 +259,16 @@
 asmlinkage int sys_aplib(void)
 {
 	return -ENOSYS;
+}
+
+asmlinkage int solaris_syscall(struct pt_regs *regs)
+{
+	lock_kernel();
+	regs->tpc = regs->tnpc;
+	regs->tnpc += 4;
+	printk ("For Solaris binary emulation you need solaris module loaded\n");
+	show_regs (regs);
+	send_sig(SIGSEGV, current, 1);
+	unlock_kernel();
+	return 0;
 }
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.1.52/linux/arch/sparc64/kernel/sys_sparc32.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.43 1997/07/17 02:20:45 davem Exp $
+/* $Id: sys_sparc32.c,v 1.55 1997/09/04 01:54:51 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -44,6 +44,7 @@
 #include <asm/ipc.h>
 #include <asm/uaccess.h>
 #include <asm/fpumacro.h>
+#include <asm/semaphore.h>
 
 /* As gcc will warn about casting u32 to some ptr, we have to cast it to
  * unsigned long first, and that's what is A() for.
@@ -53,6 +54,33 @@
  */
 #define A(x) ((unsigned long)x)
  
+extern char * getname_quicklist;
+extern int getname_quickcount;
+extern struct semaphore getname_quicklock;
+extern int kerneld_msqid;
+
+/* Tuning: increase locality by reusing same pages again...
+ * if getname_quicklist becomes too long on low memory machines, either a limit
+ * should be added or after a number of cycles some pages should
+ * be released again ...
+ */
+static inline char * get_page(void)
+{
+	char * res;
+	down(&getname_quicklock);
+	res = getname_quicklist;
+	if (res) {
+		getname_quicklist = *(char**)res;
+		getname_quickcount--;
+	}
+	else
+		res = (char*)__get_free_page(GFP_KERNEL);
+	up(&getname_quicklock);
+	return res;
+}
+
+#define putname32 putname
+
 /* In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
  * kernel data space before using them..
@@ -74,44 +102,67 @@
 	return retval;
 }
 
-/* This is a single page for faster getname.
- *   If the page is available when entering getname, use it.
- *   If the page is not available, call __get_free_page instead.
- * This works even though do_getname can block (think about it).
- * -- Michael Chastain, based on idea of Linus Torvalds, 1 Dec 1996.
- * We don't use the common getname/putname from namei.c, so that
- * this still works well, as every routine which calls getname32
- * will then call getname, then putname and then putname32.
- */
-static unsigned long name_page_cache32 = 0;
+char * getname32(u32 filename)
+{
+	char *tmp, *result;
+
+	result = ERR_PTR(-ENOMEM);
+	tmp = get_page();
+	if (tmp)  {
+		int retval = do_getname32(filename, tmp);
+
+		result = tmp;
+		if (retval < 0) {
+			putname32(tmp);
+			result = ERR_PTR(retval);
+		}
+	}
+	return result;
+}
 
-void putname32(char * name)
+/* 32-bit timeval and related flotsam.  */
+
+struct timeval32
 {
-	unsigned long page;
+    int tv_sec, tv_usec;
+};
 
-	page = xchg(&name_page_cache32, ((unsigned long)name));
-	if(page)
-		free_page(page);
+struct itimerval32
+{
+    struct timeval32 it_interval;
+    struct timeval32 it_value;
+};
+
+static inline long get_tv32(struct timeval *o, struct timeval32 *i)
+{
+	return (!access_ok(VERIFY_READ, tv32, sizeof(*tv32)) ||
+		(__get_user(o->tv_sec, &i->tv_sec) |
+		 __get_user(o->tv_usec, &i->tv_usec)));
 }
 
-int getname32(u32 filename, char **result)
+static inline long put_tv32(struct timeval32 *o, struct timeval *i)
 {
-	unsigned long page;
-	int retval;
+	return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) ||
+		(__put_user(i->tv_sec, &o->tv_sec) |
+		 __put_user(i->tv_usec, &o->tv_usec)));
+}
 
-	page = xchg(&name_page_cache32, NULL);
-	if (!page) {
-		page = __get_free_page(GFP_KERNEL);
-		if (!page)
-			return -ENOMEM;
-	}
+static inline long get_it32(struct itimerval *o, struct itimerval32 *i)
+{
+	return (!access_ok(VERIFY_READ, i32, sizeof(*i32)) ||
+		(__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) |
+		 __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) |
+		 __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) |
+		 __get_user(o->it_value.tv_usec, &i->it_value.tv_usec)));
+}
 
-	retval = do_getname32(filename, (char *) page);
-	if (retval < 0)
-		putname32( (char *) page );
-	else
-		*result = (char *) page;
-	return retval;
+static inline long put_it32(struct itimerval32 *o, struct itimerval *i)
+{
+	return (!access_ok(VERIFY_WRITE, i32, sizeof(*i32)) ||
+		(__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) |
+		 __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) |
+		 __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) |
+		 __put_user(i->it_value.tv_usec, &o->it_value.tv_usec)));
 }
 
 extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
@@ -266,14 +317,25 @@
 		switch (call) {
 		case MSGSND:
 			{
-				struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL);
+				struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL);
 				
 				if (!p) err = -ENOMEM;
 				else {
-					if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-					    __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
-						err = -EFAULT;
-					else {
+					err = 0;
+					if (first == kerneld_msqid) {
+						*(int *)p->mtext = 0;
+						if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+						    __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) ||
+						    __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4))
+							err = -EFAULT;
+						else
+							second += 4;
+					} else {
+						if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+						    __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
+							err = -EFAULT;
+					}
+					if (!err) {
 						unsigned long old_fs = get_fs();
 						set_fs (KERNEL_DS);
 						err = sys_msgsnd (first, p, second, third);
@@ -287,6 +349,7 @@
 			{
 				struct msgbuf *p;
 				unsigned long old_fs;
+				long msgtyp = fifth;
 				
 				if (!version) {
 					struct ipc_kludge tmp;
@@ -297,20 +360,35 @@
 					if(copy_from_user(&tmp,(struct ipc_kludge *)A(ptr), sizeof (tmp)))
 						goto out;
 					ptr = tmp.msgp;
-					fifth = tmp.msgtyp;
+					msgtyp = tmp.msgtyp;
 				}
-				p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL);
+
+				p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL);
 				if (!p) {
 					err = -EFAULT;
 					goto out;
 				}
+
 				old_fs = get_fs();
 				set_fs (KERNEL_DS);
-				err = sys_msgrcv (first, p, second, fifth, third);
+				err = sys_msgrcv (first, p, second + 4, msgtyp, third);
 				set_fs (old_fs);
-				if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
-				    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, second))
-					err = -EFAULT;
+
+				if (err < 0)
+					goto out;
+
+				if (first == kerneld_msqid) {
+					if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) ||
+					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8))
+						err = -EFAULT;
+					else
+						err -= 4;
+				} else {
+					if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+					    __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
+						err = -EFAULT;
+				}
 				kfree (p);
 				goto out;
 			}
@@ -472,8 +550,9 @@
 			err = -EINVAL;
 			goto out;
 		}
-	else
-		err = -EINVAL;
+
+	err = -EINVAL;
+
 out:
 	unlock_kernel();
 	return err;
@@ -565,8 +644,9 @@
 		return sys_quotactl(cmd, (const char *)A(special),
 				    id, (caddr_t)A(addr));
 	}
-	err = getname32 (special, &spec);
-	if (err) return err;
+	spec = getname32 (special);
+	err = PTR_ERR(spec);
+	if (IS_ERR(spec)) return err;
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)A(addr));
@@ -608,8 +688,9 @@
 	unsigned long old_fs = get_fs();
 	char *pth;
 	
-	ret = getname32 (path, &pth);
-	if (!ret) {
+	pth = getname32 (path);
+	ret = PTR_ERR(pth);
+	if (!IS_ERR(pth)) {
 		set_fs (KERNEL_DS);
 		ret = sys_statfs((const char *)pth, &s);
 		set_fs (old_fs);
@@ -651,8 +732,9 @@
 	if (get_user (t.actime, &(((struct utimbuf32 *)A(times))->actime)) ||
 	    __get_user (t.modtime, &(((struct utimbuf32 *)A(times))->modtime)))
 		return -EFAULT;
-	ret = getname32 (filename, &filenam);
-	if (!ret) {
+	filenam = getname32 (filename);
+	ret = PTR_ERR(filenam);
+	if (!IS_ERR(filenam)) {
 		old_fs = get_fs();
 		set_fs (KERNEL_DS); 
 		ret = sys_utime(filenam, &t);
@@ -1019,7 +1101,13 @@
 
 	ret = -EFAULT;
 
-	nn = (n + (8 * sizeof(unsigned long)) - 1) / (8 * sizeof (unsigned long));
+	nn = (n + (8 * sizeof(long)) - 1) / (8 * sizeof(long));
+	if (inp && verify_area(VERIFY_WRITE, Inp, nn*sizeof(long)))
+		goto out;
+	if (outp && verify_area(VERIFY_WRITE, Outp, nn*sizeof(long)))
+		goto out;
+	if (exp && verify_area(VERIFY_WRITE, Exp, nn*sizeof(long)))
+		goto out;
 	for (i = 0; i < nn; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) {
 		if(inp && (__get_user (q[1], Inp) || __get_user (q[0], Inp+1)))
 			goto out;
@@ -1033,7 +1121,7 @@
 
 	ktvp = NULL;
 	if(tvp) {
-		if(copy_from_user(&kern_tv, (struct timeval *)A(tvp), sizeof(*ktvp)))
+		if (get_tv32(&kern_tv, (struct timeval32 *)A(tvp)))
 			goto out;
 		ktvp = &kern_tv;
 	}
@@ -1048,8 +1136,12 @@
 			 ktvp);
 	set_fs (old_fs);
 
-	if(tvp && !(current->personality & STICKY_TIMEOUTS))
-		copy_to_user((struct timeval *)A(tvp), &kern_tv, sizeof(*ktvp));
+	if(tvp && !(current->personality & STICKY_TIMEOUTS)) {
+		if (put_tv32((struct timeval32 *)A(tvp), &kern_tv)) {
+			ret = -EFAULT;
+			goto out;
+		}
+	}
 
 	q = (u32 *)p;
 	Inp = (u32 *)A(inp);
@@ -1112,8 +1204,9 @@
 	char *filenam;
 	unsigned long old_fs = get_fs();
 	
-	ret = getname32 (filename, &filenam);
-	if (!ret) {
+	filenam = getname32 (filename);
+	ret = PTR_ERR(filenam);
+	if (!IS_ERR(filenam)) {
 		set_fs (KERNEL_DS);
 		ret = sys_newstat(filenam, &s);
 		set_fs (old_fs);
@@ -1133,8 +1226,9 @@
 	char *filenam;
 	unsigned long old_fs = get_fs();
 	
-	ret = getname32 (filename, &filenam);
-	if (!ret) {
+	filenam = getname32 (filename);
+	ret = PTR_ERR(filenam);
+	if (!IS_ERR(filenam)) {
 		set_fs (KERNEL_DS);
 		ret = sys_newlstat(filenam, &s);
 		set_fs (old_fs);
@@ -1240,12 +1334,12 @@
 	struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
 	struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
 
-	s->dir_mode = s32->dir_mode;
-	s->file_mode = s32->file_mode;
-	s->gid = s32->gid;
-	s->uid = s32->uid;
-	memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr)));
+	s->version = s32->version;
 	s->mounted_uid = s32->mounted_uid;
+	s->uid = s32->uid;
+	s->gid = s32->gid;
+	s->file_mode = s32->file_mode;
+	s->dir_mode = s32->dir_mode;
 	return raw_data;
 }
 
@@ -1345,8 +1439,8 @@
 }
 
 struct rusage32 {
-        struct timeval ru_utime;
-        struct timeval ru_stime;
+        struct timeval32 ru_utime;
+        struct timeval32 ru_stime;
         s32    ru_maxrss;
         s32    ru_ixrss;
         s32    ru_idrss;
@@ -1704,7 +1798,7 @@
 	s32 constant;
 	s32 precision;
 	s32 tolerance;
-	struct timeval time;
+	struct timeval32 time;
 	s32 tick;
 	s32 ppsfreq;
 	s32 jitter;
@@ -2302,6 +2396,7 @@
 	if(!error) {
 		fprs_write(0);
 		regs->fprs = 0;
+		regs->tstate &= ~TSTATE_PEF;
 	}
 out:
 	unlock_kernel();
@@ -2361,8 +2456,12 @@
 	case QM_MODULES:
 	case QM_REFS:
 	case QM_DEPS:
-		if (name_user && (ret = getname32 (name_user, &usernam)))
-			return ret;
+		if (name_user) {
+			usernam = getname32 (name_user);
+			ret = PTR_ERR(usernam);
+			if (IS_ERR(usernam))
+				return ret;
+		}
 		buff = kmalloc (bufsiz, GFP_KERNEL);
 		if (!buff) {
 			if (name_user) putname32 (usernam);
@@ -2423,8 +2522,12 @@
 		if (name_user) putname32 (usernam);
 		return ret;
 	case QM_INFO:
-		if (name_user && (ret = getname32 (name_user, &usernam)))
-			return ret;
+		if (name_user) {
+			usernam = getname32 (name_user);
+			ret = PTR_ERR(usernam);
+			if (IS_ERR(usernam))
+				return ret;
+		}
 		set_fs (KERNEL_DS);
 		ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val);
 		set_fs (old_fs);
@@ -2782,4 +2885,107 @@
 	if(kres)
 		kfree(kres);
 	return err;
+}
+
+/* Translations due to time_t size differences.  Which affects all
+   sorts of things, like timeval and itimerval.  */
+
+extern struct timezone sys_tz;
+extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz);
+
+asmlinkage int sys32_gettimeofday(u32 tv, u32 tz)
+{
+	if (tv) {
+		struct timeval ktv;
+		do_gettimeofday(&ktv);
+		if (put_tv32((struct timeval32 *)A(tv), &ktv))
+			return -EFAULT;
+	}
+	if (tz) {
+		if (copy_to_user((void*)A(tz), &sys_tz, sizeof(sys_tz)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+asmlinkage int sys32_settimeofday(u32 tv, u32 tz)
+{
+	struct timeval ktv;
+	struct timezone ktz;
+
+ 	if (tv) {
+		if (get_tv32(&ktv, (struct timeval32 *)A(tv)))
+			return -EFAULT;
+	}
+	if (tz) {
+		if (copy_from_user(&ktz, (void*)A(tz), sizeof(ktz)))
+			return -EFAULT;
+	}
+
+	return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL);
+}
+
+extern int do_getitimer(int which, struct itimerval *value);
+
+asmlinkage int sys32_getitimer(int which, u32 it)
+{
+	struct itimerval kit;
+	int error;
+
+	error = do_getitimer(which, &kit);
+	if (!error && put_it32((struct itimerval32 *)A(it), &kit))
+		error = -EFAULT;
+
+	return error;
+}
+
+extern int do_setitimer(int which, struct itimerval *, struct itimerval *);
+
+asmlinkage int sys32_setitimer(int which, u32 in, u32 out)
+{
+	struct itimerval kin, kout;
+	int error;
+
+	if (in) {
+		if (get_it32(&kin, (struct itimerval32 *)A(in)))
+			return -EFAULT;
+	} else
+		memset(&kin, 0, sizeof(kin));
+
+	error = do_setitimer(which, &kin, out ? &kout : NULL);
+	if (error || !out)
+		return error;
+	if (put_it32((struct itimerval32 *)A(out), &kout))
+		return -EFAULT;
+
+	return 0;
+
+}
+
+asmlinkage int sys_utimes(char *, struct timeval *);
+
+asmlinkage int sys32_utimes(u32 filename, u32 tvs)
+{
+	char *kfilename;
+	struct timeval ktvs[2];
+	unsigned long old_fs;
+	int ret;
+
+	kfilename = getname32(filename);
+	ret = PTR_ERR(kfilename);
+	if (!IS_ERR(kfilename)) {
+		if (tvs) {
+			if (get_tv32(&ktvs[0], (struct timeval32 *)A(tvs)) ||
+			    get_tv32(&ktvs[1], 1+(struct timeval32 *)A(tvs)))
+				return -EFAULT;
+		}
+
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = sys_utimes(kfilename, &ktvs[0]);
+		set_fs(old_fs);
+
+		putname32(kfilename);
+	}
+	return ret;
 }
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.1.52/linux/arch/sparc64/kernel/systbls.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/systbls.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.23 1997/08/09 18:33:17 jj Exp $
+/* $Id: systbls.S,v 1.24 1997/08/22 20:12:06 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -173,9 +173,9 @@
 	.word sys_listen, sunos_nosys, sunos_sigaction
 	.word sunos_sigblock, sunos_sigsetmask, sys_sigpause
 	.word sys32_sigstack, sys32_recvmsg, sys32_sendmsg
-	.word sunos_nosys, sys_gettimeofday, sys32_getrusage
+	.word sunos_nosys, sys32_gettimeofday, sys32_getrusage
 	.word sunos_getsockopt, sunos_nosys, sunos_readv
-	.word sunos_writev, sys_settimeofday, sys_fchown
+	.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
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c
--- v2.1.52/linux/arch/sparc64/kernel/time.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/time.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.9 1997/08/12 04:12:40 ecd Exp $
+/* $Id: time.c,v 1.12 1997/08/22 20:12:13 davem Exp $
  * time.c: UltraSparc timer and TOD clock support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -18,6 +18,7 @@
 #include <linux/interrupt.h>
 #include <linux/timex.h>
 #include <linux/init.h>
+#include <linux/ioport.h>
 
 #include <asm/oplib.h>
 #include <asm/mostek.h>
@@ -242,11 +243,25 @@
 				if (edev->prom_node == node)
 					break;
 			if (!edev) {
-				prom_printf("%s: Mostek not probed by EBUS\n");
+				prom_printf("%s: Mostek not probed by EBUS\n",
+					    __FUNCTION__);
 				prom_halt();
 			}
 
-			clk_reg[0] = edev->regs[0];
+			if (check_region(edev->base_address[0],
+					 sizeof(struct mostek48t59))) {
+				prom_printf("%s: Can't get region %lx, %d\n",
+					    __FUNCTION__, edev->base_address[0],
+					    sizeof(struct mostek48t59));
+				prom_halt();
+			}
+			request_region(edev->base_address[0],
+				       sizeof(struct mostek48t59), "clock");
+
+			mstk48t59_regs = (struct mostek48t59 *)
+						edev->base_address[0];
+			mstk48t02_regs = &mstk48t59_regs->regs;
+			break;
 		}
 #endif
 		else {
@@ -333,25 +348,29 @@
 	/* Load doubles must be used on xtime so that what we get
 	 * is guarenteed to be atomic, this is why we can run this
 	 * with interrupts on full blast.  Don't touch this... -DaveM
+	 *
+	 * Note with time_t changes to the timeval type, I must now use
+	 * nucleus atomic quad 128-bit loads.
 	 */
 	__asm__ __volatile__("
 	sethi	%hi(linux_timers), %o1
 	sethi	%hi(xtime), %g2
 	ldx	[%o1 + %lo(linux_timers)], %g3
-1:	ldd	[%g2 + %lo(xtime)], %o4
+	or	%g2, %lo(xtime), %g2
+1:	ldda	[%g2] 0x24, %o4
 	membar	#LoadLoad | #MemIssue
 	ldx	[%g3], %o1
 	membar	#LoadLoad | #MemIssue
-	ldd	[%g2 + %lo(xtime)], %o2
+	ldda	[%g2] 0x24, %o2
 	membar	#LoadLoad
 	xor	%o4, %o2, %o2
 	xor	%o5, %o3, %o3
 	orcc	%o2, %o3, %g0
-	bne,pn	%icc, 1b
+	bne,pn	%xcc, 1b
 	 cmp	%o1, 0
 	bge,pt	%icc, 1f
 	 sethi	%hi(tick), %o3
-	ld	[%o3 + %lo(tick)], %o3
+	ldx	[%o3 + %lo(tick)], %o3
 	sethi	%hi(0x1fffff), %o2
 	or	%o2, %lo(0x1fffff), %o2
 	add	%o5, %o3, %o5
@@ -360,12 +379,12 @@
 	sethi	%hi(1000000), %o2
 	or	%o2, %lo(1000000), %o2
 	cmp	%o5, %o2
-	bl,a,pn	%icc, 1f
-	 st	%o4, [%o0 + 0x0]
+	bl,a,pn	%xcc, 1f
+	 stx	%o4, [%o0 + 0x0]
 	add	%o4, 0x1, %o4
 	sub	%o5, %o2, %o5
-	st	%o4, [%o0 + 0x0]
-1:	st	%o5, [%o0 + 0x4]");
+	stx	%o4, [%o0 + 0x0]
+1:	stx	%o5, [%o0 + 0x8]");
 }
 
 void do_settimeofday(struct timeval *tv)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S
--- v2.1.52/linux/arch/sparc64/kernel/ttable.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/kernel/ttable.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: ttable.S,v 1.19 1997/07/26 18:38:56 davem Exp $
+/* $Id: ttable.S,v 1.20 1997/08/29 15:51:39 jj Exp $
  * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -100,6 +100,7 @@
 tl0_resv102:	BTRAP(0x102)
 tl0_flushw:	FLUSH_WINDOW_TRAP
 tl0_resv104:	BTRAP(0x104) BTRAP(0x105) BTRAP(0x106) BTRAP(0x107)
+		.globl tl0_solaris
 tl0_solaris:	SOLARIS_SYSCALL_TRAP
 tl0_netbsd:	NETBSD_SYSCALL_TRAP
 tl0_resv10a:	BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d) BTRAP(0x10e)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
--- v2.1.52/linux/arch/sparc64/kernel/unaligned.c	Mon Jul  7 08:18:54 1997
+++ linux/arch/sparc64/kernel/unaligned.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.3 1997/06/25 10:12:15 jj Exp $
+/* $Id: unaligned.c,v 1.4 1997/08/19 15:25:11 jj Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -205,19 +205,19 @@
 	"stx	%%g7, [%0 + 8]\n"						\
 "0:\n\n\t"									\
 	".section __ex_table\n\t"						\
-	".xword	4b, " #errh "\n\t"						\
-	".xword	5b, " #errh "\n\t"						\
-	".xword	6b, " #errh "\n\t"						\
-	".xword	7b, " #errh "\n\t"						\
-	".xword	8b, " #errh "\n\t"						\
-	".xword	9b, " #errh "\n\t"						\
-	".xword	10b, " #errh "\n\t"						\
-	".xword	11b, " #errh "\n\t"						\
-	".xword	12b, " #errh "\n\t"						\
-	".xword	13b, " #errh "\n\t"						\
-	".xword	14b, " #errh "\n\t"						\
-	".xword	15b, " #errh "\n\t"						\
-	".xword	16b, " #errh "\n\n\t"						\
+	".word	4b, " #errh "\n\t"						\
+	".word	5b, " #errh "\n\t"						\
+	".word	6b, " #errh "\n\t"						\
+	".word	7b, " #errh "\n\t"						\
+	".word	8b, " #errh "\n\t"						\
+	".word	9b, " #errh "\n\t"						\
+	".word	10b, " #errh "\n\t"						\
+	".word	11b, " #errh "\n\t"						\
+	".word	12b, " #errh "\n\t"						\
+	".word	13b, " #errh "\n\t"						\
+	".word	14b, " #errh "\n\t"						\
+	".word	15b, " #errh "\n\t"						\
+	".word	16b, " #errh "\n\n\t"						\
 	".previous\n\t"								\
 	: : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi)	\
 	: "l1", "l2", "g7", "g1", "cc");					\
@@ -259,20 +259,20 @@
 "17:\t"	"stba	%%l1, [%0 + 1] %%asi\n"						\
 "0:\n\n\t"									\
 	".section __ex_table\n\t"						\
-	".xword	4b, " #errh "\n\t"						\
-	".xword	5b, " #errh "\n\t"						\
-	".xword	6b, " #errh "\n\t"						\
-	".xword	7b, " #errh "\n\t"						\
-	".xword	8b, " #errh "\n\t"						\
-	".xword	9b, " #errh "\n\t"						\
-	".xword	10b, " #errh "\n\t"						\
-	".xword	11b, " #errh "\n\t"						\
-	".xword	12b, " #errh "\n\t"						\
-	".xword	13b, " #errh "\n\t"						\
-	".xword	14b, " #errh "\n\t"						\
-	".xword	15b, " #errh "\n\t"						\
-	".xword	16b, " #errh "\n\t"						\
-	".xword	17b, " #errh "\n\n\t"						\
+	".word	4b, " #errh "\n\t"						\
+	".word	5b, " #errh "\n\t"						\
+	".word	6b, " #errh "\n\t"						\
+	".word	7b, " #errh "\n\t"						\
+	".word	8b, " #errh "\n\t"						\
+	".word	9b, " #errh "\n\t"						\
+	".word	10b, " #errh "\n\t"						\
+	".word	11b, " #errh "\n\t"						\
+	".word	12b, " #errh "\n\t"						\
+	".word	13b, " #errh "\n\t"						\
+	".word	14b, " #errh "\n\t"						\
+	".word	15b, " #errh "\n\t"						\
+	".word	16b, " #errh "\n\t"						\
+	".word	17b, " #errh "\n\n\t"						\
 	".previous\n\t"								\
 	: : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi)		\
 	: "l1", "l2", "g7", "g1", "cc");					\
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/Makefile linux/arch/sparc64/lib/Makefile
--- v2.1.52/linux/arch/sparc64/lib/Makefile	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/Makefile	Thu Sep  4 12:54:48 1997
@@ -1,10 +1,10 @@
-# $Id: Makefile,v 1.13 1997/07/16 10:12:03 jj Exp $
+# $Id: Makefile,v 1.15 1997/08/19 03:11:50 davem Exp $
 # Makefile for Sparc library files..
 #
 
-CFLAGS := $(CFLAGS) -ansi
+CFLAGS := $(CFLAGS)
 
-OBJS  = blockops.o locks.o strlen.o strncmp.o \
+OBJS  = PeeCeeI.o blockops.o locks.o strlen.o strncmp.o \
 	memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \
 	VIScopy.o VISbzero.o VISmemset.o VIScsum.o VIScsumcopy.o
 
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/PeeCeeI.c linux/arch/sparc64/lib/PeeCeeI.c
--- v2.1.52/linux/arch/sparc64/lib/PeeCeeI.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/lib/PeeCeeI.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,225 @@
+/* $Id: PeeCeeI.c,v 1.3 1997/08/28 23:59:52 davem Exp $
+ * PeeCeeI.c: The emerging standard...
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PCI
+
+#include <asm/io.h>
+
+void outsb(unsigned long addr, const void *src, unsigned long count)
+{
+	const u8 *p = src;
+
+	while(count--)
+		outb(*p++, addr);
+}
+
+void outsw(unsigned long addr, const void *src, unsigned long count)
+{
+	if(count) {
+		const u16 *ps = src;
+		const u32 *pi;
+
+		if(((u64)src) & 0x2) {
+			outw(*ps++, addr);
+			count--;
+		}
+		pi = (const u32 *)ps;
+		while(count >= 2) {
+			u32 w;
+
+			w = *pi++;
+			outw(w >> 16, addr);
+			outw(w, addr);
+			count -= 2;
+		}
+		ps = (const u16 *)pi;
+		if(count)
+			outw(*ps, addr);
+	}
+}
+
+void outsl(unsigned long addr, const void *src, unsigned long count)
+{
+	if(count) {
+		if((((u64)src) & 0x3) == 0) {
+			const u32 *p = src;
+			while(count--)
+				outl(*p++, addr);
+		} else {
+			const u8 *pb;
+			const u16 *ps = src;
+			u32 l = 0, l2;
+			const u32 *pi;
+
+			switch(((u64)src) & 0x3) {
+			case 0x2:
+				count -= 1;
+				l = *ps++;
+				pi = (const u32 *)ps;
+				while(count--) {
+					l2 = *pi++;
+					outl(((l <<16) | (l2 >> 16)), addr);
+					l = l2;
+				}
+				ps = (const u16 *)pi;
+				outl(((l << 16) | (*ps >> 16)), addr);
+				break;
+
+			case 0x1:
+				count -= 1;
+				pb = src;
+				l = (*pb++ << 16);
+				ps = (const u16 *)pb;
+				l |= *ps++;
+				pi = (const u32 *)ps;
+				while(count--) {
+					l2 = *pi++;
+					outl(((l << 8) | (l2 >> 24)), addr);
+					l = l2;
+				}
+				pb = (const u8 *)pi;
+				outl(((l << 8) | (*pb >> 24)), addr);
+				break;
+
+			case 0x3:
+				count -= 1;
+				pb = src;
+				l = (*pb++ >> 24);
+				pi = (const u32 *)pb;
+				while(count--) {
+					l2 = *pi++;
+					outl(((l << 24) | (l2 >> 8)), addr);
+					l = l2;
+				}
+				ps = (const u16 *)pi;
+				l2 = (*ps++ << 16);
+				pb = (const u8 *)ps;
+				l2 |= (*pb << 8);
+				outl(((l << 24) | (l2 >> 8)), addr);
+				break;
+			}
+		}
+	}
+}
+
+void insb(unsigned long addr, void *dst, unsigned long count)
+{
+	if(count) {
+		u32 *pi;
+		u8 *pb = dst;
+
+		while((((unsigned long)pb) & 0x3) && count--)
+			*pb++ = inb(addr);
+		pi = (u32 *)pb;
+		while(count >= 4) {
+			u32 w;
+
+			w  = (inb(addr) << 24);
+			w |= (inb(addr) << 16);
+			w |= (inb(addr) << 8);
+			w |= inb(addr);
+			*pi++ = w;
+			count -= 4;
+		}
+		pb = (u8 *)pi;
+		while(count--)
+			*pb++ = inb(addr);
+	}
+}
+
+void insw(unsigned long addr, void *dst, unsigned long count)
+{
+	if(count) {
+		u16 *ps = dst;
+		u32 *pi;
+
+		if(((unsigned long)ps) & 0x2) {
+			*ps++ = inw(addr);
+			count--;
+		}
+		pi = (u32 *)ps;
+		while(count >= 2) {
+			u32 w;
+
+			w  = (inw(addr) << 16);
+			w |= inw(addr);
+			*pi++ = w;
+			count -= 2;
+		}
+		ps = (u16 *)pi;
+		if(count)
+			*ps = inw(addr);
+	}
+}
+
+void insl(unsigned long addr, void *dst, unsigned long count)
+{
+	if(count) {
+		if((((unsigned long)dst) & 0x3) == 0) {
+			u32 *pi = dst;
+			while(count--)
+				*pi++ = inl(addr);
+		} else {
+			u32 l = 0, l2, *pi;
+			u16 *ps;
+			u8 *pb;
+
+			switch(((unsigned long)dst) & 3) {
+			case 0x2:
+				ps = dst;
+				count -= 1;
+				l = inl(addr);
+				*ps++ = (l >> 16);
+				pi = (u32 *)ps;
+				while(count--) {
+					l2 = inl(addr);
+					*pi++ = (l << 16) | (l2 >> 16);
+					l = l2;
+				}
+				ps = (u16 *)pi;
+				*ps = (l << 16);
+				break;
+
+			case 0x1:
+				pb = dst;
+				count -= 1;
+				*pb++ = (l >> 24);
+				ps = (u16 *)pb;
+				*ps++ = (l >> 8);
+				pi = (u32 *)ps;
+				while(count--) {
+					l2 = inl(addr);
+					*pi++ = ((l << 24) | (l2 >> 8));
+					l = l2;
+				}
+				pb = (u8 *)pi;
+				*pb = (l >> 8);
+				break;
+
+			case 0x3:
+				pb = (u8 *)dst;
+				count -= 1;
+				l = inl(addr);
+				*pb++ = l >> 24;
+				pi = (u32 *)pb;
+				while(count--) {
+					l2 = inl(addr);
+					*pi++ = ((l >> 24) | (l2 << 8));
+					l = l2;
+				}
+				ps = (u16 *)pi;
+				*ps++ = l >> 8;
+				pb = (u8 *)ps;
+				*pb = l;
+				break;
+			}
+		}
+	}
+}
+
+#endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/VISbzero.S linux/arch/sparc64/lib/VISbzero.S
--- v2.1.52/linux/arch/sparc64/lib/VISbzero.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/VISbzero.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: VISbzero.S,v 1.6 1997/08/08 08:34:00 jj Exp $
+/* $Id: VISbzero.S,v 1.8 1997/08/22 15:54:50 jj Exp $
  * VISbzero.S: High speed clear operations utilizing the UltraSparc
  *        Visual Instruction Set.
  *
@@ -16,8 +16,8 @@
 99:	ba	VISbzerofixup_ret##z;		\
 	 a, b, %o0;				\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, 99b;			\
+	.align	4;				\
+	.word	98b, 99b;			\
 	.text;					\
 	.align	4;
 #define EXC(x,y,a,b,c...) 			\
@@ -28,15 +28,15 @@
 	ba	VISbzerofixup_ret0;		\
 	 a, b, %o0;				\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, 99b;			\
+	.align	4;				\
+	.word	98b, 99b;			\
 	.text;					\
 	.align	4;
 #define EXO1(x,y) 				\
 98: 	x,y;					\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, VISbzerofixup_reto1;	\
+	.align	4;				\
+	.word	98b, VISbzerofixup_reto1;	\
 	.text;					\
 	.align	4;
 #define EX(x,y,a,b) EXN(x,y,a,b,0)
@@ -44,8 +44,8 @@
 #define EX2(x,y,a,b) EXN(x,y,a,b,2)
 #define EXT(start,end,handler) 			\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	start, 0, end, handler;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
 	.text;					\
 	.align	4
 #else
@@ -180,6 +180,10 @@
 #ifdef __KERNEL__
 	wr		%g0, 0, %fprs
 	wr		%g7, 0x0, %asi
+#else
+#ifndef REGS_64BIT
+	wr		%g0, FPRS_FEF, %fprs
+#endif
 #endif
 	membar		#StoreLoad | #StoreStore
 9:	andcc		%o1, 0xf8, %o2
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/VIScopy.S linux/arch/sparc64/lib/VIScopy.S
--- v2.1.52/linux/arch/sparc64/lib/VIScopy.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/VIScopy.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: VIScopy.S,v 1.11 1997/08/08 08:34:02 jj Exp $
+/* $Id: VIScopy.S,v 1.14 1997/08/22 15:54:53 jj Exp $
  * VIScopy.S: High speed copy operations utilizing the UltraSparc
  *            Visual Instruction Set.
  *
@@ -42,8 +42,8 @@
 99:	ba	VIScopyfixup_ret;		\
 	 a, b, %o0;				\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, 99b;			\
+	.align	4;				\
+	.word	98b, 99b;			\
 	.text;					\
 	.align	4;
 #define EX2(x,y,c,d,e,a,b) 			\
@@ -54,37 +54,48 @@
 	ba	VIScopyfixup_ret;		\
 	 a, b, %o0;				\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, 99b;			\
+	.align	4;				\
+	.word	98b, 99b;			\
 	.text;					\
 	.align	4;
 #define EXO2(x,y) 				\
 98: 	x,y;					\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, VIScopyfixup_reto2;	\
+	.align	4;				\
+	.word	98b, VIScopyfixup_reto2;	\
 	.text;					\
 	.align	4;
 #define EXVISN(x,y,n) 				\
 98: 	x,y;					\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	98b, VIScopyfixup_vis##n;	\
+	.align	4;				\
+	.word	98b, VIScopyfixup_vis##n;	\
 	.text;					\
 	.align	4;
 #define EXT(start,end,handler) 			\
 	.section __ex_table;			\
-	.align	8;				\
-	.xword	start, 0, end, handler;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
 	.text;					\
 	.align	4;
 #else
-#define FPU_CLEAN_RETL	\
-	retl;		\
+#ifdef REGS_64BIT
+#define FPU_CLEAN_RETL				\
+	retl;					\
 	 mov	%g6, %o0;
-#define FPU_RETL	\
-	retl;		\
+#define FPU_RETL				\
+	retl;					\
 	 mov	%g6, %o0;
+#else
+#define FPU_CLEAN_RETL				\
+	wr	%g0, FPRS_FEF, %fprs;		\
+	retl;					\
+	 mov	%g6, %o0;
+#define FPU_RETL				\
+	wr	%g0, FPRS_FEF, %fprs;		\
+	retl;					\
+	 mov	%g6, %o0;
+#endif
 #define NORMAL_RETL	\
 	retl;		\
 	 mov	%g6, %o0;
@@ -113,8 +124,8 @@
 
 #define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt)	\
 	EXVIS(LDBLK		[%src] ASIBLK, %fdest);		\
-	add			%src, 0x40, %src;		\
 	ASI_SETDST_BLK						\
+	add			%src, 0x40, %src;		\
 	add			%dest, 0x40, %dest;		\
 	subcc			%len, 0x40, %len;		\
 	be,pn			%xcc, jmptgt;			\
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/VIScsum.S linux/arch/sparc64/lib/VIScsum.S
--- v2.1.52/linux/arch/sparc64/lib/VIScsum.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/VIScsum.S	Thu Sep  4 12:54:48 1997
@@ -1,439 +1,3 @@
-/* $Id: VIScsum.S,v 1.1 1997/07/16 10:12:02 jj Exp $
- * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc
- *            Visual Instruction Set.
- *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- *
- * Based on older sparc32/sparc64 checksum.S, which is:
- *
- *      Copyright(C) 1995 Linus Torvalds
- *      Copyright(C) 1995 Miguel de Icaza
- *      Copyright(C) 1996,1997 David S. Miller
- *    derived from:
- *	  Linux/Alpha checksum c-code
- *        Linux/ix86 inline checksum assembly
- *        RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
- *	  David Mosberger-Tang for optimized reference c-code
- *	  BSD4.4 portable checksum routine
- */
-
-#ifdef __sparc_v9__
-#define STACKOFF	2175
-#else
-#define STACKOFF	64
-#endif
-
-#ifdef __KERNEL__
-#include <asm/head.h>
-#include <asm/asi.h>
-#else
-#define ASI_BLK_P	0xf0
-#define FRPS_FEF	0x04
-#endif
-
-/* Dobrou noc, SunSoft engineers. Spete sladce.
- * This has a couple of tricks in and those
- * tricks are UltraLinux trade secrets :))
- */
-
-#define START_THE_TRICK(fz,f0,f2,f4,f6,f8,f10)							\
-	fcmpgt32	%fz, %f0, %g1		/*  FPM		Group			*/;	\
-	fcmpgt32	%fz, %f2, %g2		/*  FPM		Group			*/;	\
-	fcmpgt32	%fz, %f4, %g3		/*  FPM		Group			*/;	\
-	fcmpgt32	%fz, %f6, %g5		/*  FPM		Group			*/;	\
-	inc		%g1			/*  IEU0				*/;	\
-	fcmpgt32	%fz, %f8, %g7		/*  FPM		Group			*/;	\
-	srl		%g1, 1, %g1		/*  IEU0				*/;	\
-	inc		%g2			/*  IEU1				*/;	\
-	fcmpgt32	%fz, %f10, %o3		/*  FPM		Group			*/;	\
-	srl		%g2, 1, %g2		/*  IEU0				*/;	\
-	add		%o2, %g1, %o2		/*  IEU1				*/;	\
-	add		%g3, 1, %g3		/*  IEU0	Group			*/;	\
-	srl		%g3, 1, %g3		/*  IEU0	Group			*/;	\
-	add		%o2, %g2, %o2		/*  IEU1				*/;	\
-	inc		%g5			/*  IEU0	Group			*/;	\
-	add		%o2, %g3, %o2		/*  IEU1				*/;
-
-#define DO_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,F0,F2,F4,F6,F8,F10,F12,F14)		\
-	fcmpgt32	%O12, %f12, %o4		/*  FPM		Group			*/;	\
-	srl		%g5, 1, %g5		/*  IEU0				*/;	\
-	inc		%g7			/*  IEU1				*/;	\
-	fpadd32		%F0, %f0, %F0		/*  FPA					*/;	\
-	fcmpgt32	%O14, %f14, %o5		/*  FPM		Group			*/;	\
-	srl		%g7, 1, %g7		/*  IEU0				*/;	\
-	add		%o2, %g5, %o2		/*  IEU1				*/;	\
-	fpadd32		%F2, %f2, %F2		/*  FPA					*/;	\
-	inc		%o3			/*  IEU0	Group			*/;	\
-	add		%o2, %g7, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%f0, %F0, %g1		/*  FPM		Group			*/;	\
-	srl		%o3, 1, %o3		/*  IEU0				*/;	\
-	inc		%o4			/*  IEU1				*/;	\
-	fpadd32		%F4, %f4, %F4		/*  FPA					*/;	\
-	fcmpgt32	%f2, %F2, %g2		/*  FPM		Group			*/;	\
-	srl		%o4, 1, %o4		/*  IEU0				*/;	\
-	add		%o2, %o3, %o2		/*  IEU1				*/;	\
-	fpadd32		%F6, %f6, %F6		/*  FPA					*/;	\
-	inc		%o5			/*  IEU0	Group			*/;	\
-	add		%o2, %o4, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%f4, %F4, %g3		/*  FPM		Group			*/;	\
-	srl		%o5, 1, %o5		/*  IEU0				*/;	\
-	inc		%g1			/*  IEU1				*/;	\
-	fpadd32		%F8, %f8, %F8		/*  FPA					*/;	\
-	fcmpgt32	%f6, %F6, %g5		/*  FPM		Group			*/;	\
-	srl		%g1, 1, %g1		/*  IEU0				*/;	\
-	add		%o2, %o5, %o2		/*  IEU1				*/;	\
-	fpadd32		%F10, %f10, %F10	/*  FPA					*/;	\
-	inc		%g2			/*  IEU0	Group			*/;	\
-	add		%o2, %g1, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%f8, %F8, %g7		/*  FPM		Group			*/;	\
-	srl		%g2, 1, %g2		/*  IEU0				*/;	\
-	inc		%g3			/*  IEU1				*/;	\
-	fpadd32		%F12, %f12, %F12	/*  FPA					*/;	\
-	fcmpgt32	%f10, %F10, %o3		/*  FPM		Group			*/;	\
-	srl		%g3, 1, %g3		/*  IEU0				*/;	\
-	add		%o2, %g2, %o2		/*  IEU1				*/;	\
-	fpadd32		%F14, %f14, %F14	/*  FPA					*/;	\
-	inc		%g5			/*  IEU0	Group			*/;	\
-	add		%o2, %g3, %o2		/*  IEU1				*/;
-
-#define END_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,S0,S1,S2,S3,T0,T1,U0,fz)		\
-	fcmpgt32	%O12, %f12, %o4		/*  FPM		Group			*/;	\
-	srl		%g5, 1, %g5		/*  IEU0				*/;	\
-	inc		%g7			/*  IEU1				*/;	\
-	fpadd32		%f2, %f0, %S0		/*  FPA					*/;	\
-	fcmpgt32	%O14, %f14, %o5		/*  FPM		Group			*/;	\
-	srl		%g7, 1, %g7		/*  IEU0				*/;	\
-	add		%o2, %g5, %o2		/*  IEU1				*/;	\
-	fpadd32		%f6, %f4, %S1		/*  FPA					*/;	\
-	inc		%o3			/*  IEU0	Group			*/;	\
-	add		%o2, %g7, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%f0, %S0, %g1		/*  FPM		Group			*/;	\
-	srl		%o3, 1, %o3		/*  IEU0				*/;	\
-	inc		%o4			/*  IEU1				*/;	\
-	fpadd32		%f10, %f8, %S2		/*  FPA					*/;	\
-	fcmpgt32	%f4, %S1, %g2		/*  FPM		Group			*/;	\
-	srl		%o4, 1, %o4		/*  IEU0				*/;	\
-	add		%o2, %o3, %o2		/*  IEU1				*/;	\
-	fpadd32		%f14, %f12, %S3		/*  FPA					*/;	\
-	inc		%o5			/*  IEU0	Group			*/;	\
-	add		%o2, %o4, %o2		/*  IEU1				*/;	\
-	fzero		%fz			/*  FPA					*/;	\
-	fcmpgt32	%f8, %S2, %g3		/*  FPM		Group			*/;	\
-	srl		%o5, 1, %o5		/*  IEU0				*/;	\
-	inc		%g1			/*  IEU1				*/;	\
-	fpadd32		%S0, %S1, %T0		/*  FPA					*/;	\
-	fcmpgt32	%f12, %S3, %g5		/*  FPM		Group			*/;	\
-	srl		%g1, 1, %g1		/*  IEU0				*/;	\
-	add		%o2, %o5, %o2		/*  IEU1				*/;	\
-	fpadd32		%S2, %S3, %T1		/*  FPA					*/;	\
-	inc		%g2			/*  IEU0	Group			*/;	\
-	add		%o2, %g1, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%S0, %T0, %g7		/*  FPM		Group			*/;	\
-	srl		%g2, 1, %g2		/*  IEU0				*/;	\
-	inc		%g3			/*  IEU1				*/;	\
-	fcmpgt32	%S2, %T1, %o3		/*  FPM		Group			*/;	\
-	srl		%g3, 1, %g3		/*  IEU0				*/;	\
-	add		%o2, %g2, %o2		/*  IEU1				*/;	\
-	inc		%g5			/*  IEU0	Group			*/;	\
-	add		%o2, %g3, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%fz, %f2, %o4		/*  FPM		Group			*/;	\
-	srl		%g5, 1, %g5		/*  IEU0				*/;	\
-	inc		%g7			/*  IEU1				*/;	\
-	fpadd32		%T0, %T1, %U0		/*  FPA					*/;	\
-	fcmpgt32	%fz, %f6, %o5		/*  FPM		Group			*/;	\
-	srl		%g7, 1, %g7		/*  IEU0				*/;	\
-	add		%o2, %g5, %o2		/*  IEU1				*/;	\
-	inc		%o3			/*  IEU0	Group			*/;	\
-	add		%o2, %g7, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%fz, %f10, %g1		/*  FPM		Group			*/;	\
-	srl		%o3, 1, %o3		/*  IEU0				*/;	\
-	inc		%o4			/*  IEU1				*/;	\
-	fcmpgt32	%fz, %f14, %g2		/*  FPM		Group			*/;	\
-	srl		%o4, 1, %o4		/*  IEU0				*/;	\
-	add		%o2, %o3, %o2		/*  IEU1				*/;	\
-	std		%U0, [%sp + STACKOFF]	/*  Store	Group			*/;	\
-	inc		%o5			/*  IEU0				*/;	\
-	sub		%o2, %o4, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%fz, %S1, %g3		/*  FPM		Group			*/;	\
-	srl		%o5, 1, %o5		/*  IEU0				*/;	\
-	inc		%g1			/*  IEU1				*/;	\
-	fcmpgt32	%fz, %S3, %g5		/*  FPM		Group			*/;	\
-	srl		%g1, 1, %g1		/*  IEU0				*/;	\
-	sub		%o2, %o5, %o2		/*  IEU1				*/;	\
-	ldx		[%sp + STACKOFF], %o5	/*  Load	Group			*/;	\
-	inc		%g2			/*  IEU0				*/;	\
-	sub		%o2, %g1, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%fz, %T1, %g7		/*  FPM		Group			*/;	\
-	srl		%g2, 1, %g2		/*  IEU0				*/;	\
-	inc		%g3			/*  IEU1				*/;	\
-	fcmpgt32	%T0, %U0, %o3		/*  FPM		Group			*/;	\
-	srl		%g3, 1, %g3		/*  IEU0				*/;	\
-	sub		%o2, %g2, %o2		/*  IEU1				*/;	\
-	inc		%g5			/*  IEU0	Group			*/;	\
-	sub		%o2, %g3, %o2		/*  IEU1				*/;	\
-	fcmpgt32	%fz, %U0, %o4		/*  FPM		Group			*/;	\
-	srl		%g5, 1, %g5		/*  IEU0				*/;	\
-	inc		%g7			/*  IEU1				*/;	\
-	srl		%g7, 1, %g7		/*  IEU0	Group			*/;	\
-	sub		%o2, %g5, %o2		/*  IEU1				*/;	\
-	inc		%o3			/*  IEU0	Group			*/;	\
-	sub		%o2, %g7, %o2		/*  IEU1				*/;	\
-	srl		%o3, 1, %o3		/*  IEU0	Group			*/;	\
-	inc		%o4			/*  IEU1				*/;	\
-	srl		%o4, 1, %o4		/*  IEU0	Group			*/;	\
-	add		%o2, %o3, %o2		/*  IEU1				*/;	\
-	sub		%o2, %o4, %o2		/*  IEU0	Group			*/;	\
-	addcc		%o2, %o5, %o2		/*  IEU1	Group			*/;	\
-	bcs,a,pn	%xcc, 33f		/*  CTI					*/;	\
-	 add		%o2, 1, %o2		/*  IEU0				*/;	\
-33:						/*  That's it				*/;
-
-#define CSUM_LASTCHUNK(offset)									\
-        ldx             [%o0 - offset - 0x10], %g2;						\
-        ldx             [%o0 - offset - 0x08], %g3;						\
-        addcc           %g2, %o2, %o2;								\
-        bcs,a,pn        %xcc, 31f;								\
-         add            %o2, 1, %o2;								\
-31:     addcc           %g3, %o2, %o2;								\
-        bcs,a,pn        %xcc, 32f;								\
-         add            %o2, 1, %o2;								\
-32:
-
-	.text
-	.globl		csum_partial
-	.align		32
-csum_partial:
-	andcc		%o0, 7, %g0		/*  IEU1	Group			*/
-	be,pt		%icc, 4f		/*  CTI					*/
-	 andcc		%o0, 0x38, %g3		/*  IEU1				*/
-	mov		1, %g5			/*  IEU0	Group			*/
-	cmp		%o1, 6			/*  IEU1				*/
-	bl,pn		%icc, 21f		/*  CTI					*/
-	 andcc		%o0, 2, %g0		/*  IEU1	Group			*/
-	be,pt		%icc, 1f		/*  CTI					*/
-	 and		%o0, 4, %g7		/*  IEU0				*/
-	lduh		[%o0], %g2		/*  Load				*/
-	sub		%o1, 2, %o1		/*  IEU0	Group			*/
-	add		%o0, 2, %o0		/*  IEU1				*/
-	andcc		%o0, 4, %g7		/*  IEU1	Group			*/
-	sll		%g5, 16, %g5		/*  IEU0				*/
-	sll		%g2, 16, %g2		/*  IEU0	Group 			*/
-	addcc		%g2, %o2, %o2		/*  IEU1	Group (regdep)		*/
-	bcs,a,pn	%icc, 1f		/*  CTI					*/
-	 add		%o2, %g5, %o2		/*  IEU0				*/
-1:	ld		[%o0], %g2		/*  Load				*/
-	brz,a,pn	%g7, 4f			/*  CTI+IEU1	Group			*/
-	 and		%o0, 0x38, %g3		/*  IEU0				*/
-	add		%o0, 4, %o0		/*  IEU0	Group			*/
-	sub		%o1, 4, %o1		/*  IEU1				*/
-	addcc		%g2, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%icc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	and		%o0, 0x38, %g3		/*  IEU1	Group			*/
-4:	srl		%o2, 0, %o2		/*  IEU0	Group			*/
-	mov		0x40, %g1		/*  IEU1				*/
-	brz,pn		%g3, 3f			/*  CTI+IEU1	Group			*/
-	 sub		%g1, %g3, %g1		/*  IEU0				*/
-	cmp		%o1, 56			/*  IEU1	Group			*/
-	blu,pn		%icc, 20f		/*  CTI					*/
-	 andcc		%o0, 8, %g0		/*  IEU1	Group			*/
-	be,pn		%icc, 1f		/*  CTI					*/
-	 ldx		[%o0], %g2		/*  Load				*/
-	add		%o0, 8, %o0		/*  IEU0	Group			*/
-	sub		%o1, 8, %o1		/*  IEU1				*/
-	addcc		%g2, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	andcc		%g1, 0x10, %g0		/*  IEU1	Group			*/
-	be,pn		%icc, 2f		/*  CTI					*/
-	 and		%g1, 0x20, %g1		/*  IEU0				*/
-	ldx		[%o0], %g2		/*  Load				*/
-	ldx		[%o0+8], %g3		/*  Load	Group			*/
-	add		%o0, 16, %o0		/*  IEU0				*/
-	sub		%o1, 16, %o1		/*  IEU1				*/
-	addcc		%g2, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	addcc		%g3, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 2f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-2:	brz,pn		%g1, 3f			/*  CTI+IEU1	Group			*/
-	 ldx		[%o0], %g2		/*  Load				*/
-	ldx		[%o0+8], %g3		/*  Load	Group			*/
-	ldx		[%o0+16], %g5		/*  Load	Group			*/
-	ldx		[%o0+24], %g7		/*  Load	Group			*/
-	add		%o0, 32, %o0		/*  IEU0				*/
-	sub		%o1, 32, %o1		/*  IEU1				*/
-	addcc		%g2, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	addcc		%g3, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	addcc		%g5, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	addcc		%g7, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 3f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-3:	cmp		%o1, 0xc0		/*  IEU1	Group			*/
-	blu,pn		%icc, 20f		/*  CTI					*/
-	 sllx		%o2, 32, %g1		/*  IEU0				*/
-	addcc		%o2, %g1, %o2		/*  IEU1	Group			*/
-	sub		%o1, 0xc0, %o1		/*  IEU0				*/
-	wr		%g0, ASI_BLK_P, %asi	/*  LSU		Group			*/
-#ifdef __KERNEL__
-	wr		%g0, FPRS_FEF, %fprs	/*  LSU		Group			*/
-#endif
-	membar		#StoreLoad		/*  LSU		Group			*/
-	srlx		%o2, 32, %o2		/*  IEU0	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU1				*/
-1:	andcc		%o1, 0x80, %g0		/*  IEU1	Group			*/
-	bne,pn		%icc, 7f		/*  CTI					*/
-	 andcc		%o1, 0x40, %g0		/*  IEU1	Group			*/
-	be,pn		%icc, 6f		/*  CTI					*/
-	 fzero		%f12			/*  FPA					*/
-	fzero		%f14			/*  FPA		Group			*/
-	ldda		[%o0 + 0x000] %asi, %f16
-	ldda		[%o0 + 0x040] %asi, %f32
-	ldda		[%o0 + 0x080] %asi, %f48
-	START_THE_TRICK(f12,f16,f18,f20,f22,f24,f26)
-	ba,a,pt		%xcc, 3f
-6:	sub		%o0, 0x40, %o0		/*  IEU0	Group			*/
-	fzero		%f28			/*  FPA					*/
-	fzero		%f30			/*  FPA		Group			*/
-	ldda		[%o0 + 0x040] %asi, %f32
-	ldda		[%o0 + 0x080] %asi, %f48
-	ldda		[%o0 + 0x0c0] %asi, %f0
-	START_THE_TRICK(f28,f32,f34,f36,f38,f40,f42)
-	ba,a,pt		%xcc, 4f
-7:	bne,pt		%icc, 8f		/*  CTI					*/
-	 fzero		%f44			/*  FPA					*/
-	add		%o0, 0x40, %o0		/*  IEU0	Group			*/
-	fzero		%f60			/*  FPA					*/
-	fzero		%f62			/*  FPA		Group			*/
-	ldda		[%o0 - 0x040] %asi, %f0
-	ldda		[%o0 + 0x000] %asi, %f16
-	ldda		[%o0 + 0x040] %asi, %f32
-	START_THE_TRICK(f60,f0,f2,f4,f6,f8,f10)
-	ba,a,pt		%xcc, 2f
-8:	add		%o0, 0x80, %o0		/*  IEU0	Group			*/
-	fzero		%f46			/*  FPA					*/
-	ldda		[%o0 - 0x080] %asi, %f48
-	ldda		[%o0 - 0x040] %asi, %f0
-	ldda		[%o0 + 0x000] %asi, %f16
-	START_THE_TRICK(f44,f48,f50,f52,f54,f56,f58)
-1:	DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14)
-	ldda		[%o0 + 0x040] %asi, %f32
-2:	DO_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30)
-	ldda		[%o0 + 0x080] %asi, %f48
-3:	DO_THE_TRICK(f12,f14,f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46)
-	ldda		[%o0 + 0x0c0] %asi, %f0
-4:	DO_THE_TRICK(f28,f30,f32,f34,f36,f38,f40,f42,f44,f46,f48,f50,f52,f54,f56,f58,f60,f62)
-	add		%o0, 0x100, %o0		/*  IEU0	Group			*/
-	subcc		%o1, 0x100, %o1		/*  IEU1				*/
-	bgeu,a,pt	%icc, 1b		/*  CTI					*/
-	 ldda		[%o0 + 0x000] %asi, %f16
-	membar		#Sync			/*  LSU		Group			*/
-	DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14)
-	END_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30)
-	and		%o1, 0x3f, %o1		/*  IEU0	Group			*/
-#ifdef __KERNEL__
-	wr		%g0, 0, %fprs		/*  LSU		Group			*/
-#endif
-20:	andcc		%o1, 0xf0, %g1		/*  IEU1	Group			*/
-	be,pn		%icc, 23f		/*  CTI					*/
-	 and		%o1, 0xf, %o3		/*  IEU0				*/
-22:	rd		%pc, %g7		/*  LSU		Group			*/
-	sll		%g1, 1, %o4		/*  IEU0	Group			*/
-	sub		%g7, %o4, %g7		/*  IEU0	Group (regdep)		*/
-	jmpl		%g7 + (23f - 22b), %g0	/*  CTI		Group brk forced	*/
-	add		%o0, %g1, %o0		/*  IEU0				*/
-	CSUM_LASTCHUNK(0xe0)
-	CSUM_LASTCHUNK(0xd0)
-	CSUM_LASTCHUNK(0xc0)
-	CSUM_LASTCHUNK(0xb0)
-	CSUM_LASTCHUNK(0xa0)
-	CSUM_LASTCHUNK(0x90)
-	CSUM_LASTCHUNK(0x80)
-	CSUM_LASTCHUNK(0x70)
-	CSUM_LASTCHUNK(0x60)
-	CSUM_LASTCHUNK(0x50)
-	CSUM_LASTCHUNK(0x40)
-	CSUM_LASTCHUNK(0x30)
-	CSUM_LASTCHUNK(0x20)
-	CSUM_LASTCHUNK(0x10)
-	CSUM_LASTCHUNK(0x00)
-23:	brnz,pn		%o3, 26f		/*  CTI+IEU1	Group			*/
-24:	 sllx		%o2, 32, %g1		/*  IEU0				*/
-25:	addcc		%o2, %g1, %o0		/*  IEU1	Group			*/
-	srlx		%o0, 32, %o0		/*  IEU0	Group (regdep)		*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o0, 1, %o0		/*  IEU1				*/
-1:	retl					/*  CTI		Group brk forced	*/
-	 srl		%o0, 0, %o0		/*  IEU0				*/
-26:	andcc		%o1, 8, %g0		/*  IEU1	Group			*/
-	be,pn		%icc, 1f		/*  CTI					*/
-	 ldx		[%o0], %g3		/*  Load				*/
-	add		%o0, 8, %o0		/*  IEU0	Group			*/
-	addcc		%g3, %o2, %o2		/*  IEU1	Group			*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	andcc		%o1, 4, %g0		/*  IEU1	Group			*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%g2			/*  IEU0				*/
-	ld		[%o0], %g2		/*  Load				*/
-	add		%o0, 4, %o0		/*  IEU0	Group			*/
-	sllx		%g2, 32, %g2		/*  IEU0	Group			*/
-1:	andcc		%o1, 2, %g0		/*  IEU1				*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%o4			/*  IEU0	Group			*/
-	lduh		[%o0], %o4		/*  Load				*/
-	add		%o0, 2, %o0		/*  IEU1				*/
-	sll		%o4, 16, %o4		/*  IEU0	Group			*/
-1:	andcc		%o1, 1, %g0		/*  IEU1				*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%o5			/*  IEU0	Group			*/
-	ldub		[%o0], %o5		/*  Load				*/
-	sll		%o5, 8, %o5		/*  IEU0	Group			*/
-1:	or		%g2, %o4, %o4		/*  IEU1				*/
-	or		%o5, %o4, %o4		/*  IEU0	Group (regdep)		*/
-	addcc		%o4, %o2, %o2		/*  IEU1	Group (regdep)		*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	ba,pt		%xcc, 25b		/*  CTI		Group			*/
-	 sllx		%o2, 32, %g1		/*  IEU0				*/
-21:	srl		%o2, 0, %o2		/*  IEU0	Group			*/
-	cmp		%o1, 0			/*  IEU1				*/
-	be,pn		%icc, 24b		/*  CTI					*/
-	 andcc		%o1, 4, %g0		/*  IEU1	Group			*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%g2			/*  IEU0				*/
-	lduh		[%o0], %g3		/*  Load				*/
-	lduh		[%o0+2], %g2		/*  Load	Group			*/
-	add		%o0, 4, %o0		/*  IEU0	Group			*/
-	sllx		%g3, 48, %g3		/*  IEU0	Group			*/
-	sllx		%g2, 32, %g2		/*  IEU0	Group			*/
-	or		%g3, %g2, %g2		/*  IEU0	Group			*/
-1:	andcc		%o1, 2, %g0		/*  IEU1				*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%o4			/*  IEU0	Group			*/
-	lduh		[%o0], %o4		/*  Load				*/
-	add		%o0, 2, %o0		/*  IEU1				*/
-	sll		%o4, 16, %o4		/*  IEU0	Group			*/
-1:	andcc		%o1, 1, %g0		/*  IEU1				*/
-	be,a,pn		%icc, 1f		/*  CTI					*/
-	 clr		%o5			/*  IEU0	Group			*/
-	ldub		[%o0], %o5		/*  Load				*/
-	sll		%o5, 8, %o5		/*  IEU0	Group			*/
-1:	or		%g2, %o4, %o4		/*  IEU1				*/
-	or		%o5, %o4, %o4		/*  IEU0	Group (regdep)		*/
-	addcc		%o4, %o2, %o2		/*  IEU1	Group (regdep)		*/
-	bcs,a,pn	%xcc, 1f		/*  CTI					*/
-	 add		%o2, 1, %o2		/*  IEU0				*/
-1:	ba,pt		%xcc, 25b		/*  CTI		Group			*/
-	 sllx		%o2, 32, %g1		/*  IEU0				*/
 /* $Id: VIScsum.S,v 1.2 1997/08/08 08:34:05 jj Exp $
  * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc
  *            Visual Instruction Set.
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/VIScsumcopy.S linux/arch/sparc64/lib/VIScsumcopy.S
--- v2.1.52/linux/arch/sparc64/lib/VIScsumcopy.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/VIScsumcopy.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: VIScsumcopy.S,v 1.1 1997/08/09 18:14:29 jj Exp $
+/* $Id: VIScsumcopy.S,v 1.2 1997/08/19 15:25:22 jj Exp $
  * VIScsumcopy.S: High bandwidth IP checksumming with simultaneous
  *            copying utilizing the UltraSparc Visual Instruction Set.
  *
@@ -875,6 +875,6 @@
 end:
 
 	.section	__ex_table
-	.align		8
-	.xword		csum_partial_copy_vis, 0, end, cpc_handler
+	.align		4
+	.word		csum_partial_copy_vis, 0, end, cpc_handler
 #endif
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/VISmemset.S linux/arch/sparc64/lib/VISmemset.S
--- v2.1.52/linux/arch/sparc64/lib/VISmemset.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/VISmemset.S	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: VISmemset.S,v 1.6 1997/08/08 08:34:13 jj Exp $
+/* $Id: VISmemset.S,v 1.7 1997/08/22 15:54:56 jj Exp $
  * VISmemset.S: High speed memset operations utilizing the UltraSparc
  *        Visual Instruction Set.
  *
@@ -171,6 +171,10 @@
 12:
 #ifdef __KERNEL__
 	wr		%g0, 0, %fprs
+#else
+#ifndef REGS_64BIT
+	wr		%g0, FPRS_FEF, %fprs
+#endif
 #endif
 	membar		#StoreLoad | #StoreStore
 9:	andcc		%o2, 0x78, %g5
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/checksum.S linux/arch/sparc64/lib/checksum.S
--- v2.1.52/linux/arch/sparc64/lib/checksum.S	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/lib/checksum.S	Thu Sep  4 12:54:48 1997
@@ -500,6 +500,6 @@
 	 nop
 
 	.section __ex_table
-	.align  8
-	.xword  cpc_start, 0, cpc_end, cpc_handler
+	.align  4
+	.word  cpc_start, 0, cpc_end, cpc_handler
 
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/strlen_user.S linux/arch/sparc64/lib/strlen_user.S
--- v2.1.52/linux/arch/sparc64/lib/strlen_user.S	Thu Jul 17 10:06:04 1997
+++ linux/arch/sparc64/lib/strlen_user.S	Thu Sep  4 12:54:48 1997
@@ -78,11 +78,11 @@
          clr    %o0
 
 	.section __ex_table,#alloc
-	.align	8
+	.align	4
 
-	.xword	10b, 30b
-	.xword	11b, 30b
-	.xword	12b, 30b
-	.xword	15b, 30b
-	.xword	13b, 30b
-	.xword	14b, 30b
+	.word	10b, 30b
+	.word	11b, 30b
+	.word	12b, 30b
+	.word	15b, 30b
+	.word	13b, 30b
+	.word	14b, 30b
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/lib/strncpy_from_user.S linux/arch/sparc64/lib/strncpy_from_user.S
--- v2.1.52/linux/arch/sparc64/lib/strncpy_from_user.S	Mon Jun 16 16:35:54 1997
+++ linux/arch/sparc64/lib/strncpy_from_user.S	Thu Sep  4 12:54:48 1997
@@ -49,6 +49,6 @@
 	 mov	-EFAULT, %o0
 
 	.section __ex_table,#alloc
-	.align	8
-	.xword	10b, 4b
-	.xword	11b, 4b
+	.align	4
+	.word	10b, 4b
+	.word	11b, 4b
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c
--- v2.1.52/linux/arch/sparc64/mm/fault.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/mm/fault.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.18 1997/07/17 02:20:56 davem Exp $
+/* $Id: fault.c,v 1.20 1997/08/04 16:16:51 davem Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.1.52/linux/arch/sparc64/mm/init.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/mm/init.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/*  $Id: init.c,v 1.54 1997/08/15 06:44:23 davem Exp $
+/*  $Id: init.c,v 1.55 1997/08/24 01:22:29 davem Exp $
  *  arch/sparc64/mm/init.c
  *
  *  Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -180,7 +180,7 @@
 	prom_printf("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
 		    (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs);
 #endif
-	printk("IOMMU: IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
+	printk("IOMMU(SBUS): IMPL[%x] VERS[%x] SYSIO mapped at %016lx\n",
 	       (unsigned int) impl, (unsigned int)vers, (unsigned long) sregs);
 	
 	control &= ~(IOMMU_CTRL_TSBSZ);
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/prom/console.c linux/arch/sparc64/prom/console.c
--- v2.1.52/linux/arch/sparc64/prom/console.c	Mon Aug  4 16:25:37 1997
+++ linux/arch/sparc64/prom/console.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: console.c,v 1.7 1997/07/19 08:28:29 ecd Exp $
+/* $Id: console.c,v 1.8 1997/08/16 08:00:16 davem Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
@@ -85,13 +85,13 @@
 	if(prom_node_has_property(st_p, "keyboard"))
 		return PROMDEV_IKBD;
 	prom_getproperty(st_p, "device_type", propb, sizeof(propb));
-	if(strncmp(propb, "serial", sizeof("serial")))
+	if(strncmp(propb, "serial", 6))
 		return PROMDEV_I_UNK;
 	/* FIXME: Is there any better way how to find out? */	
 	memset(propb, 0, sizeof(propb));
 	st_p = prom_finddevice ("/options");
 	prom_getproperty(st_p, "input-device", propb, sizeof(propb));
-	if (strncmp (propb, "tty", 3) || !propb[3] || propb[4])
+	if (strncmp (propb, "tty", 3) || !propb[3])
 		return PROMDEV_I_UNK;
 	switch (propb[3]) {
 		case 'a': return PROMDEV_ITTYA;
@@ -114,13 +114,13 @@
 	if (propl >= 0 && propl == sizeof("display") &&
 	    strncmp("display", propb, sizeof("display")) == 0)
 		return PROMDEV_OSCREEN;
-	if(strncmp("serial", propb, sizeof("serial")))
+	if(strncmp("serial", propb, 6))
 		return PROMDEV_O_UNK;
 	/* FIXME: Is there any better way how to find out? */	
 	memset(propb, 0, sizeof(propb));
 	st_p = prom_finddevice ("/options");
 	prom_getproperty(st_p, "output-device", propb, sizeof(propb));
-	if (strncmp (propb, "tty", 3) || !propb[3] || propb[4])
+	if (strncmp (propb, "tty", 3) || !propb[3])
 		return PROMDEV_O_UNK;
 	switch (propb[3]) {
 		case 'a': return PROMDEV_OTTYA;
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/prom/ranges.c linux/arch/sparc64/prom/ranges.c
--- v2.1.52/linux/arch/sparc64/prom/ranges.c	Mon Aug 18 18:19:45 1997
+++ linux/arch/sparc64/prom/ranges.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.7 1997/08/15 06:44:29 davem Exp $
+/* $Id: ranges.c,v 1.8 1997/08/17 22:39:45 ecd Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -87,16 +87,6 @@
 				 central->num_central_ranges);
 }
 
-#ifdef CONFIG_PCI
-void prom_apply_ebus_ranges(struct linux_ebus *ebus,
-			    struct linux_prom_registers *regs, int nregs)
-{
-	if (ebus->num_ebus_ranges)
-		prom_adjust_regs(regs, nregs, ebus->ebus_ranges,
-				 ebus->num_ebus_ranges);
-}
-#endif
-
 __initfunc(void prom_ranges_init(void))
 {
 }
@@ -140,32 +130,14 @@
 #ifdef CONFIG_PCI
 __initfunc(void prom_ebus_ranges_init(struct linux_ebus *ebus))
 {
-	struct ebus_range {
-		unsigned int	cld_space;
-		unsigned int	cld_base;
-		unsigned int	prn_space;
-		unsigned int	prn_base_hi;
-		unsigned int	prn_base_lo;
-		unsigned int	size;
-	} ranges[PROMREG_MAX];
-	int i, success;
+	int success;
 
 	ebus->num_ebus_ranges = 0;
 	success = prom_getproperty(ebus->prom_node, "ranges",
-				   (char *)ranges, sizeof (ranges));
+				   (char *)ebus->ebus_ranges,
+				   sizeof(ebus->ebus_ranges));
 	if (success != -1)
-		ebus->num_ebus_ranges = (success/sizeof(struct ebus_range));
-
-	for (i = 0; i < ebus->num_ebus_ranges; i++) {
-		ebus->ebus_ranges[i].ot_child_space = ranges[i].cld_space;
-		ebus->ebus_ranges[i].ot_child_base = ranges[i].cld_base;
-		ebus->ebus_ranges[i].ot_parent_space =
-				ranges[i].prn_space & 0x0f000000;
-		if (ranges[i].prn_base_hi)
-			prom_printf("WARNING: %s: ot_parent_base high lost\n");
-		ebus->ebus_ranges[i].ot_parent_base = ranges[i].prn_base_lo;
-		ebus->ebus_ranges[i].or_size = ranges[i].size;
-	}
+		ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
 }
 
 __initfunc(void prom_pbm_ranges_init(int pnode, struct linux_pbm_info *pbm))
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/Makefile linux/arch/sparc64/solaris/Makefile
--- v2.1.52/linux/arch/sparc64/solaris/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/Makefile	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,26 @@
+#
+# Makefile for the Solaris binary 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 := solaris.o
+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)
+endif
+
+.S.s:
+	$(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
+ifneq ($(CONFIG_SOLARIS_EMUL),y)
+do_it_all:
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/conv.h linux/arch/sparc64/solaris/conv.h
--- v2.1.52/linux/arch/sparc64/solaris/conv.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/conv.h	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,28 @@
+/* $Id: conv.h,v 1.2 1997/09/03 12:29:13 jj Exp $
+ * conv.h: Utility macros for Solaris emulation
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+ 
+/* #define DEBUG_SOLARIS */
+
+#ifndef __ASSEMBLY__
+
+#include <asm/unistd.h>
+
+/* As gcc will warn about casting u32 to some ptr, we have to cast it to
+ * unsigned long first, and that's what is A() for.
+ * You just do (void *)A(x), instead of having to
+ * type (void *)((unsigned long)x) or instead of just (void *)x, which will
+ * produce warnings.
+ */
+#define A(x) ((unsigned long)x)
+
+extern unsigned sys_call_table[];
+extern unsigned sys_call_table32[];
+extern unsigned sunos_sys_table[];
+
+#define SYS(name) ((long)sys_call_table[__NR_##name])
+#define SUNOS(x) ((long)sunos_sys_table[x])
+
+#endif /* __ASSEMBLY__ */
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/entry64.S linux/arch/sparc64/solaris/entry64.S
--- v2.1.52/linux/arch/sparc64/solaris/entry64.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/entry64.S	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,202 @@
+/* $Id: entry64.S,v 1.3 1997/09/03 12:29:12 jj Exp $
+ * entry64.S:  Solaris syscall emulation entry point.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Miguel de Icaza      (miguel@nuclecu.unam.mx)
+ */
+
+#include <linux/errno.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/smp.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/signal.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+
+#include "conv.h"
+
+#define NR_SYSCALLS	256
+
+	.text
+solaris_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
+
+solaris_sucks:
+/* Solaris is a big system which needs to be able to do all the things
+ * in Inf+1 different ways */
+	add		%i6, 0x5c, %o0
+	mov		%i0, %g1
+	mov		%i1, %i0
+	mov		%i2, %i1
+	srl		%o0, 0, %o0
+	mov		%i3, %i2
+	mov		%i4, %i3
+	mov		%i5, %i4
+	ba,pt		%xcc, solaris_sparc_syscall
+exen:	 lduwa		[%o0] ASI_S, %i5
+
+exenf:	ba,pt		%xcc, solaris_sparc_syscall
+	 clr		%i5
+
+/* For shared binaries, binfmt_elf32 already sets up personality
+   and exec_domain. This is to handle static binaries as well */
+solaris_reg:
+	call		solaris_register
+	 nop
+	ba,pt		%xcc, 1f
+	 mov		%i0, %o0
+
+linux_syscall_for_solaris:
+	sll		%l7, 2, %l4
+	ba,pt		%xcc, 10f
+	 lduw		[%l6 + %l4], %l7
+
+	/* Solaris system calls enter here... */
+	.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
+	sll		%g1, 2, %l4
+	cmp		%l0, 1
+	bne,pn		%icc, solaris_reg
+1:	 mov		%i1, %o1				
+	lduw		[%l7 + %l4], %l7
+	mov		%i2, %o2				
+	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
+	andn		%l7, 3, %l7
+	andcc		%l5, 0x20, %g0				
+	bne,pn		%icc, solaris_syscall_trace		
+	 mov		%i0, %l5
+2:	call		%l7
+	 mov		%i5, %o5
+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
+	mov		%ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2
+	ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3
+	cmp		%o0, -ENOIOCTLCMD
+	sllx		%g2, 32, %g2
+	bgeu,pn		%xcc, 1f
+	 andcc		%l6, 0x20, %l6	
+
+	/* System call success, clear Carry condition code. */
+	andn		%g3, %g2, %g3
+	stx		%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]	
+	bne,pn		%icc, solaris_syscall_trace2
+	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
+	add		%l1, 0x4, %l2				         !npc = npc+4
+	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
+	clr		%l6
+	call		rtrap
+	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+1:
+	/* System call failure, set Carry condition code.
+	 * Also, get abs(errno) to return to the process.
+	 */
+	sub		%g0, %o0, %o0
+	or		%g3, %g2, %g3
+	cmp		%o0, ERANGE	/* 0-ERANGE are identity mapped */
+	bleu,pt		%icc, 1f
+	 cmp		%o0, EMEDIUMTYPE
+	bgu,pn		%icc, 1f
+	 sethi		%hi(solaris_err_table), %l6
+	sll		%o0, 2, %o0
+	or		%l6, %lo(solaris_err_table), %l6
+	ldsw		[%l6 + %o0], %o0
+1:	stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
+	mov		1, %l6
+	stx		%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]
+	bne,pn		%icc, solaris_syscall_trace2
+	 ldx		[%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
+	add		%l1, 0x4, %l2				         !npc = npc+4
+
+	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
+	call		rtrap
+	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+solaris_syscall_trace2:
+	call		syscall_trace
+	 add		%l1, 0x4, %l2			/* npc = npc+4 */
+	stx		%l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
+	call		rtrap
+	 stx		%l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
+
+	/* This one is tricky, so that's why we do it in assembly */
+	.globl		solaris_sigsuspend
+solaris_sigsuspend:
+	call		do_sol_sigsuspend
+	 nop
+	brlz,pn		%o0, ret_from_solaris
+	 nop
+	call		sys_sigsuspend
+	 stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
+
+	.globl		solaris_getpid
+solaris_getpid:
+	call		sys_getppid	/* This is tricky, so don't do it in assembly */
+	 nop
+	stx		%o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+	b,pt		%xcc, ret_from_solaris
+	 lduw		[%g6 + AOFF_task_pid], %o0
+
+	.globl	solaris_getuid
+solaris_getuid:
+	lduh		[%g6 + AOFF_task_euid], %o1
+	lduh		[%g6 + AOFF_task_uid], %o0
+	b,pt		%xcc, ret_from_solaris
+	 stx		%o1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+
+	.globl	solaris_getgid
+solaris_getgid:
+	lduh		[%g6 + AOFF_task_egid], %o1
+	lduh		[%g6 + AOFF_task_gid], %o0
+	b,pt		%xcc, ret_from_solaris
+	 stx		%o1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+
+	.globl		solaris_unimplemented
+solaris_unimplemented:
+	call		do_sol_unimplemented
+	 add		%sp, STACK_BIAS + REGWIN_SZ, %o0
+	ba,pt		%xcc, ret_from_solaris
+	 nop
+
+	.section	__ex_table,#alloc
+	.align		4
+	.word		exen, exenf
+
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/fs.c linux/arch/sparc64/solaris/fs.c
--- v2.1.52/linux/arch/sparc64/solaris/fs.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/fs.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,711 @@
+/* $Id: fs.c,v 1.4 1997/09/04 15:46:26 jj Exp $
+ * fs.c: fs related syscall emulation for Solaris
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/smp_lock.h>
+#include <linux/limits.h>
+#include <linux/resource.h>
+
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/ptrace.h>
+
+#include "conv.h"
+
+extern char * getname32(u32 filename);
+#define putname32 putname
+ 
+#define R4_DEV(DEV) ((DEV & 0xff) | ((DEV & 0xff00) << 10))
+#define R4_MAJOR(DEV) (((DEV) >> 18) & 0x3fff)
+#define R4_MINOR(DEV) ((DEV) & 0x3ffff)
+#define R3_VERSION	1
+#define R4_VERSION	2
+
+typedef struct {
+	s32	tv_sec;
+	s32	tv_nsec;
+} timestruct_t;
+
+struct sol_stat {
+	u32		st_dev;
+	s32		st_pad1[3];     /* network id */
+	u32		st_ino;
+	u32		st_mode;
+	u32		st_nlink;
+	u32		st_uid;
+	u32		st_gid;
+	u32		st_rdev;
+	s32		st_pad2[2];
+	s32		st_size;
+	s32		st_pad3;	/* st_size, off_t expansion */
+	timestruct_t	st_atime;
+	timestruct_t	st_mtime;
+	timestruct_t	st_ctime;
+	s32		st_blksize;
+	s32		st_blocks;
+	char		st_fstype[16];
+	s32		st_pad4[8];     /* expansion area */
+};
+
+#define UFSMAGIC (((unsigned)'u'<<24)||((unsigned)'f'<<16)||((unsigned)'s'<<8))
+
+static inline int putstat(struct sol_stat *ubuf, struct stat *kbuf)
+{
+	if (put_user (R4_DEV(kbuf->st_dev), &ubuf->st_dev)	||
+	    __put_user (kbuf->st_ino, &ubuf->st_ino)		||
+	    __put_user (kbuf->st_mode, &ubuf->st_mode)		||
+	    __put_user (kbuf->st_nlink, &ubuf->st_nlink)	||
+	    __put_user (kbuf->st_uid, &ubuf->st_uid)		||
+	    __put_user (kbuf->st_gid, &ubuf->st_gid)		||
+	    __put_user (R4_DEV(kbuf->st_rdev), &ubuf->st_rdev)	||
+	    __put_user (kbuf->st_size, &ubuf->st_size)		||
+	    __put_user (kbuf->st_atime, &ubuf->st_atime.tv_sec)	||
+	    __put_user (0, &ubuf->st_atime.tv_nsec)		||
+	    __put_user (kbuf->st_mtime, &ubuf->st_mtime.tv_sec)	||
+	    __put_user (0, &ubuf->st_mtime.tv_nsec)		||
+	    __put_user (kbuf->st_ctime, &ubuf->st_ctime.tv_sec)	||
+	    __put_user (0, &ubuf->st_ctime.tv_nsec)		||
+	    __put_user (kbuf->st_blksize, &ubuf->st_blksize)	||
+	    __put_user (kbuf->st_blocks, &ubuf->st_blocks)	||
+	    __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype))
+		return -EFAULT;
+	return 0;
+}
+
+asmlinkage int solaris_stat(u32 filename, u32 statbuf)
+{
+	int ret;
+	struct stat s;
+	char *filenam;
+	unsigned long old_fs = get_fs();
+	int (*sys_newstat)(char *,struct stat *) = 
+		(int (*)(char *,struct stat *))SYS(stat);
+	
+	filenam = getname32 (filename);
+	ret = PTR_ERR(filenam);
+	if (!IS_ERR(filenam)) {
+		set_fs (KERNEL_DS);
+		ret = sys_newstat(filenam, &s);
+		set_fs (old_fs);
+		putname32 (filenam);
+		if (putstat ((struct sol_stat *)A(statbuf), &s))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+asmlinkage int solaris_xstat(int vers, u32 filename, u32 statbuf)
+{
+	/* Solaris doesn't bother with looking at vers, so we do neither */
+	return solaris_stat(filename, statbuf);
+}
+
+asmlinkage int solaris_lstat(u32 filename, u32 statbuf)
+{
+	int ret;
+	struct stat s;
+	char *filenam;
+	unsigned long old_fs = get_fs();
+	int (*sys_newlstat)(char *,struct stat *) = 
+		(int (*)(char *,struct stat *))SYS(lstat);
+	
+	filenam = getname32 (filename);
+	ret = PTR_ERR(filenam);
+	if (!IS_ERR(filenam)) {
+		set_fs (KERNEL_DS);
+		ret = sys_newlstat(filenam, &s);
+		set_fs (old_fs);
+		putname32 (filenam);
+		if (putstat ((struct sol_stat *)A(statbuf), &s))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+asmlinkage int solaris_lxstat(int vers, u32 filename, u32 statbuf)
+{
+	return solaris_lstat(filename, statbuf);
+}
+
+asmlinkage int solaris_fstat(unsigned int fd, u32 statbuf)
+{
+	int ret;
+	struct stat s;
+	unsigned long old_fs = get_fs();
+	int (*sys_newfstat)(unsigned,struct stat *) = 
+		(int (*)(unsigned,struct stat *))SYS(fstat);
+	
+	set_fs (KERNEL_DS);
+	ret = sys_newfstat(fd, &s);
+	set_fs (old_fs);
+	if (putstat ((struct sol_stat *)A(statbuf), &s))
+		return -EFAULT;
+	return ret;
+}
+
+asmlinkage int solaris_fxstat(int vers, u32 fd, u32 statbuf)
+{
+	return solaris_fstat(fd, statbuf);
+}
+
+asmlinkage int solaris_mknod(u32 path, u32 mode, s32 dev)
+{
+	int (*sys_mknod)(const char *,int,dev_t) = 
+		(int (*)(const char *,int,dev_t))SYS(mknod);
+	int major, minor;
+
+	if ((major = R4_MAJOR(dev)) > 255 || 
+	    (minor = R4_MINOR(dev)) > 255) return -EINVAL;
+	return sys_mknod((const char *)A(path), mode, MKDEV(major,minor));
+}
+
+asmlinkage int solaris_xmknod(int vers, u32 path, u32 mode, s32 dev)
+{
+	return solaris_mknod(path, mode, dev);
+}
+
+/* This statfs thingie probably will go in the near future, but... */
+
+struct sol_statfs {
+	short	f_type;
+	s32	f_bsize;
+	s32	f_frsize;
+	s32	f_blocks;
+	s32	f_bfree;
+	u32	f_files;
+	u32	f_ffree;
+	char	f_fname[6];
+	char	f_fpack[6];
+};
+
+asmlinkage int solaris_statfs(u32 path, u32 buf, int len, int fstype)
+{
+	int ret;
+	struct statfs s;
+	unsigned long 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);
+	
+	if (len != sizeof(struct sol_statfs)) return -EINVAL;
+	if (!fstype) {
+		set_fs (KERNEL_DS);
+		ret = sys_statfs((const char *)A(path), &s);
+		set_fs (old_fs);
+		if (!ret) {
+			if (put_user (s.f_type, &ss->f_type)		||
+			    __put_user (s.f_bsize, &ss->f_bsize)	||
+			    __put_user (0, &ss->f_frsize)		||
+			    __put_user (s.f_blocks, &ss->f_blocks)	||
+			    __put_user (s.f_bfree, &ss->f_bfree)	||
+			    __put_user (s.f_files, &ss->f_files)	||
+			    __put_user (s.f_ffree, &ss->f_ffree)	||
+			    __clear_user (&ss->f_fname, 12))
+				return -EFAULT;
+		}
+		return ret;
+	}
+/* Linux can't stat unmounted filesystems so we
+ * simply lie and claim 100MB of 1GB is free. Sorry.
+ */
+	if (put_user (fstype, &ss->f_type)		||
+	    __put_user (1024, &ss->f_bsize)		||
+	    __put_user (0, &ss->f_frsize)		||
+	    __put_user (1024*1024, &ss->f_blocks)	||
+	    __put_user (100*1024, &ss->f_bfree)		||
+	    __put_user (60000, &ss->f_files)		||
+	    __put_user (50000, &ss->f_ffree)		||
+	    __clear_user (&ss->f_fname, 12))
+		return -EFAULT;
+	return 0;
+}
+
+asmlinkage int solaris_fstatfs(u32 fd, u32 buf, int len, int fstype)
+{
+	int ret;
+	struct statfs s;
+	unsigned long 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);
+	
+	if (len != sizeof(struct sol_statfs)) return -EINVAL;
+	if (!fstype) {
+		set_fs (KERNEL_DS);
+		ret = sys_fstatfs(fd, &s);
+		set_fs (old_fs);
+		if (!ret) {
+			if (put_user (s.f_type, &ss->f_type)		||
+			    __put_user (s.f_bsize, &ss->f_bsize)	||
+			    __put_user (0, &ss->f_frsize)		||
+			    __put_user (s.f_blocks, &ss->f_blocks)	||
+			    __put_user (s.f_bfree, &ss->f_bfree)	||
+			    __put_user (s.f_files, &ss->f_files)	||
+			    __put_user (s.f_ffree, &ss->f_ffree)	||
+			    __clear_user (&ss->f_fname, 12))
+				return -EFAULT;
+		}
+		return ret;
+	}
+	/* Otherwise fstatfs is the same as statfs */
+	return solaris_statfs(0, buf, len, fstype);
+}
+
+struct sol_statvfs {
+	u32	f_bsize;
+	u32	f_frsize;
+	u32	f_blocks;
+	u32	f_bfree;
+	u32	f_bavail;
+	u32	f_files;
+	u32	f_ffree;
+	u32	f_favail;
+	u32	f_fsid;
+	char	f_basetype[16];
+	u32	f_flag;
+	u32	f_namemax;
+	char	f_fstr[32];
+	u32	f_filler[16];
+};
+
+static int report_statvfs(struct inode *inode, u32 buf)
+{
+	struct statfs s;
+	unsigned long old_fs = get_fs();
+	int error;
+	struct sol_statvfs *ss = (struct sol_statvfs *)A(buf);
+			
+	set_fs (KERNEL_DS);
+	error = inode->i_sb->s_op->statfs(inode->i_sb, &s, sizeof(struct statfs));
+	set_fs (old_fs);
+	if (!error) {
+		const char *p = inode->i_sb->s_type->name;
+		int i = 0;
+		int j = strlen (p);
+		
+		if (j > 15) j = 15;
+		if (IS_RDONLY(inode)) i = 1;
+		if (IS_NOSUID(inode)) i |= 2;
+		if (put_user (s.f_bsize, &ss->f_bsize)		||
+		    __put_user (0, &ss->f_frsize)		||
+		    __put_user (s.f_blocks, &ss->f_blocks)	||
+		    __put_user (s.f_bfree, &ss->f_bfree)	||
+		    __put_user (s.f_bavail, &ss->f_bavail)	||
+		    __put_user (s.f_files, &ss->f_files)	||
+		    __put_user (s.f_ffree, &ss->f_ffree)	||
+		    __put_user (s.f_ffree, &ss->f_favail)	||
+		    __put_user (R4_DEV(inode->i_sb->s_dev), &ss->f_fsid) ||
+		    __copy_to_user (ss->f_basetype,p,j)		||
+		    __put_user (0, (char *)&ss->f_basetype[j])	||
+		    __put_user (s.f_namelen, &ss->f_namemax)	||
+		    __put_user (i, &ss->f_flag)			||		    
+		    __clear_user (&ss->f_fstr, 32))
+			return -EFAULT;
+	}
+	return error;
+}
+
+asmlinkage int solaris_statvfs(u32 path, u32 buf)
+{
+	struct dentry * dentry;
+	int error;
+
+	lock_kernel();
+	dentry = namei((const char *)A(path));
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		struct inode * inode = dentry->d_inode;
+
+		error = -ENOSYS;
+		if (inode->i_sb->s_op->statfs)
+			error = report_statvfs(inode, buf);
+		dput(dentry);
+	}
+	unlock_kernel();
+	return error;
+}
+
+asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf)
+{
+	struct inode * inode;
+	struct dentry * dentry;
+	struct file * file;
+	int error;
+
+	lock_kernel();
+	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+		error = -EBADF;
+	else if (!(dentry = file->f_dentry))
+		error = -ENOENT;
+	else if (!(inode = dentry->d_inode))
+		error = -ENOENT;
+	else if (!inode->i_sb)
+		error = -ENODEV;
+	else if (!inode->i_sb->s_op->statfs)
+		error = -ENOSYS;
+	else
+		error = report_statvfs(inode, buf);
+	unlock_kernel();
+	return error;
+}
+
+asmlinkage int solaris_open(u32 filename, int flags, u32 mode)
+{
+	int (*sys_open)(const char *,int,int) = 
+		(int (*)(const char *,int,int))SYS(open);
+	int fl = flags & 0xf;
+	
+	if (flags & 0x8050) fl |= O_SYNC;
+	if (flags & 0x80) fl |= O_NONBLOCK;
+	if (flags & 0x100) fl |= O_CREAT;
+	if (flags & 0x200) fl |= O_TRUNC;
+	if (flags & 0x400) fl |= O_EXCL;
+	if (flags & 0x800) fl |= O_NOCTTY;
+	return sys_open((const char *)A(filename), fl, mode);
+}
+
+#define SOL_F_SETLK	6
+#define SOL_F_SETLKW	7
+#define SOL_F_FREESP    11
+#define SOL_F_ISSTREAM  13
+#define SOL_F_GETLK     14
+#define SOL_F_PRIV      15
+#define SOL_F_NPRIV     16
+#define SOL_F_QUOTACTL  17
+#define SOL_F_BLOCKS    18
+#define SOL_F_BLKSIZE   19
+#define SOL_F_GETOWN    23
+#define SOL_F_SETOWN    24
+
+struct sol_flock {
+	short	l_type;
+	short	l_whence;
+	u32	l_start;
+	u32	l_len;
+	s32	l_sysid;
+	s32	l_pid;
+	s32	l_pad[4];
+};
+
+asmlinkage int solaris_fcntl(unsigned fd, unsigned cmd, u32 arg)
+{
+	int (*sys_fcntl)(unsigned,unsigned,unsigned long) = 
+		(int (*)(unsigned,unsigned,unsigned long))SYS(fcntl);
+	int ret, flags;
+
+	switch (cmd) {
+	case F_DUPFD:
+	case F_GETFD:
+	case F_SETFD: return sys_fcntl(fd, cmd, (unsigned long)arg);
+	case F_GETFL:
+		flags = sys_fcntl(fd, cmd, 0);
+		ret = flags & 0xf;
+		if (flags & O_SYNC) ret |= 0x8050;
+		if (flags & O_NONBLOCK) ret |= 0x80;
+		return ret;
+	case F_SETFL:
+		flags = arg & 0xf;
+		if (arg & 0x8050) flags |= O_SYNC;
+		if (arg & 0x80) flags |= O_NONBLOCK;
+		return sys_fcntl(fd, cmd, (long)flags);
+	case SOL_F_GETLK:
+	case SOL_F_SETLK:
+	case SOL_F_SETLKW:
+		{
+			struct flock f;
+			unsigned long old_fs = get_fs();
+
+			switch (cmd) {
+			case SOL_F_GETLK: cmd = F_GETLK; break;
+			case SOL_F_SETLK: cmd = F_SETLK; break;
+			case SOL_F_SETLKW: cmd = F_SETLKW; break;
+			}
+
+			get_user_ret (f.l_type, &((struct sol_flock *)A(arg))->l_type, -EFAULT);
+			__get_user_ret (f.l_whence, &((struct sol_flock *)A(arg))->l_whence, -EFAULT);
+			__get_user_ret (f.l_start, &((struct sol_flock *)A(arg))->l_start, -EFAULT);
+			__get_user_ret (f.l_len, &((struct sol_flock *)A(arg))->l_len, -EFAULT);
+			__get_user_ret (f.l_pid, &((struct sol_flock *)A(arg))->l_sysid, -EFAULT);
+			set_fs(KERNEL_DS);
+			ret = sys_fcntl(fd, cmd, (unsigned long)&f);
+			set_fs(old_fs);
+			__put_user_ret (f.l_type, &((struct sol_flock *)A(arg))->l_type, -EFAULT);
+			__put_user_ret (f.l_whence, &((struct sol_flock *)A(arg))->l_whence, -EFAULT);
+			__put_user_ret (f.l_start, &((struct sol_flock *)A(arg))->l_start, -EFAULT);
+			__put_user_ret (f.l_len, &((struct sol_flock *)A(arg))->l_len, -EFAULT);
+			__put_user_ret (f.l_pid, &((struct sol_flock *)A(arg))->l_pid, -EFAULT);
+			__put_user_ret (0, &((struct sol_flock *)A(arg))->l_sysid, -EFAULT);
+			return ret;
+		}
+	}
+	return -EINVAL;
+}
+
+asmlinkage int solaris_ulimit(int cmd, int val)
+{
+	switch (cmd) {
+	case 1: /* UL_GETFSIZE - in 512B chunks */
+		return current->rlim[RLIMIT_FSIZE].rlim_cur >> 9;
+	case 2: /* UL_SETFSIZE */
+		if ((unsigned long)val > (LONG_MAX>>9)) return -ERANGE;
+		val <<= 9;
+		lock_kernel();
+		if (val > current->rlim[RLIMIT_FSIZE].rlim_max) {
+			if (!suser()) {
+				unlock_kernel();
+				return -EPERM;
+			}
+			current->rlim[RLIMIT_FSIZE].rlim_max = val;
+		}
+		current->rlim[RLIMIT_FSIZE].rlim_cur = val;
+		unlock_kernel();
+		return 0;
+	case 3: /* UL_GMEMLIM */
+		return current->rlim[RLIMIT_DATA].rlim_cur;
+	case 4: /* UL_GDESLIM */
+		return NR_OPEN;
+	}
+	return -EINVAL;
+}
+
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+{
+	struct inode * inode;
+	struct iattr newattrs;
+	int error;
+
+	error = -ENOENT;
+	if (!(inode = dentry->d_inode)) {
+		printk("chown_common: NULL inode\n");
+		goto out;
+	}
+	error = -EROFS;
+	if (IS_RDONLY(inode))
+		goto out;
+	error = -EPERM;
+	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+		goto out;
+	if (user == (uid_t) -1)
+		user = inode->i_uid;
+	if (group == (gid_t) -1)
+		group = inode->i_gid;
+	newattrs.ia_mode = inode->i_mode;
+	newattrs.ia_uid = user;
+	newattrs.ia_gid = group;
+	newattrs.ia_valid =  ATTR_UID | ATTR_GID | ATTR_CTIME;
+	/*
+	 * If the owner has been changed, remove the setuid bit
+	 */
+	if (inode->i_mode & S_ISUID) {
+		newattrs.ia_mode &= ~S_ISUID;
+		newattrs.ia_valid |= ATTR_MODE;
+	}
+	/*
+	 * If the group has been changed, remove the setgid bit
+	 *
+	 * Don't remove the setgid bit if no group execute bit.
+	 * This is a file marked for mandatory locking.
+	 */
+	if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
+		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);
+out:
+	return error;
+}
+
+/* Linux chown works like Solaris lchown. Solaris chown does follow symlink */
+asmlinkage int solaris_chown(u32 filename, s32 user, s32 group)
+{
+	struct dentry * dentry;
+	int error;
+
+	lock_kernel();
+	dentry = namei((const char *)A(filename));
+
+	error = PTR_ERR(dentry);
+	if (!IS_ERR(dentry)) {
+		error = chown_common(dentry, user, group);
+		dput(dentry);
+	}
+	unlock_kernel();
+	return error;
+}
+
+/* At least at the time I'm writing this, Linux doesn't have ACLs, so we
+   just fake this */
+asmlinkage int solaris_acl(u32 filename, int cmd, int nentries, u32 aclbufp)
+{
+	return -ENOSYS;
+}
+
+asmlinkage int solaris_facl(unsigned int fd, int cmd, int nentries, u32 aclbufp)
+{
+	return -ENOSYS;
+}
+
+asmlinkage int solaris_pread(int fd, u32 buf, u32 nbyte, s32 offset)
+{
+	off_t temp;
+	int retval;
+	struct file * file;
+	long (*sys_read)(unsigned int, char *, unsigned long) =
+		(long (*)(unsigned int, char *, unsigned long))SYS(read);
+	long (*sys_lseek)(unsigned int, off_t, unsigned int) =
+		(long (*)(unsigned int, off_t, unsigned int))SYS(lseek);
+	
+	lock_kernel();
+	retval = -EBADF;
+	if (fd >= NR_OPEN ||
+            !(file = current->files->fd[fd]))
+		goto bad;
+	temp = file->f_pos;
+	if (temp != offset) {
+		retval = sys_lseek(fd, offset, 0);
+		if (retval < 0) goto bad;
+	}
+	retval = sys_read(fd, (char *)A(buf), nbyte);
+	if (file->f_pos != temp) {
+		if (!retval)
+			retval = sys_lseek(fd, temp, 0);
+		else
+			sys_lseek(fd, temp, 0);
+	}
+bad:
+	unlock_kernel();
+	return retval;
+}
+
+asmlinkage int solaris_pwrite(int fd, u32 buf, u32 nbyte, s32 offset)
+{
+	off_t temp;
+	int retval;
+	struct file * file;
+	long (*sys_write)(unsigned int, char *, unsigned long) =
+		(long (*)(unsigned int, char *, unsigned long))SYS(read);
+	long (*sys_lseek)(unsigned int, off_t, unsigned int) =
+		(long (*)(unsigned int, off_t, unsigned int))SYS(lseek);
+	
+	lock_kernel();
+	retval = -EBADF;
+	if (fd >= NR_OPEN ||
+            !(file = current->files->fd[fd]))
+		goto bad;
+	temp = file->f_pos;
+	if (temp != offset) {
+		retval = sys_lseek(fd, offset, 0);
+		if (retval < 0) goto bad;
+	}
+	retval = sys_write(fd, (char *)A(buf), nbyte);
+	if (file->f_pos != temp) {
+		if (!retval)
+			retval = sys_lseek(fd, temp, 0);
+		else
+			sys_lseek(fd, temp, 0);
+	}
+bad:
+	unlock_kernel();
+	return retval;
+}
+
+/* POSIX.1 names */
+#define _PC_LINK_MAX    1
+#define _PC_MAX_CANON   2
+#define _PC_MAX_INPUT   3
+#define _PC_NAME_MAX    4
+#define _PC_PATH_MAX    5
+#define _PC_PIPE_BUF    6
+#define _PC_NO_TRUNC    7
+#define _PC_VDISABLE    8
+#define _PC_CHOWN_RESTRICTED    9
+/* POSIX.4 names */
+#define _PC_ASYNC_IO    10
+#define _PC_PRIO_IO     11
+#define _PC_SYNC_IO     12
+#define _PC_LAST        12
+
+/* This is not a real and complete implementation yet, just to keep
+ * the easy Solaris binaries happy.
+ */
+asmlinkage int solaris_fpathconf(int fd, int name)
+{
+	int ret;
+
+	switch(name) {
+	case _PC_LINK_MAX:
+		ret = LINK_MAX;
+		break;
+	case _PC_MAX_CANON:
+		ret = MAX_CANON;
+		break;
+	case _PC_MAX_INPUT:
+		ret = MAX_INPUT;
+		break;
+	case _PC_NAME_MAX:
+		ret = NAME_MAX;
+		break;
+	case _PC_PATH_MAX:
+		ret = PATH_MAX;
+		break;
+	case _PC_PIPE_BUF:
+		ret = PIPE_BUF;
+		break;
+	case _PC_CHOWN_RESTRICTED:
+		ret = 1;
+		break;
+	case _PC_NO_TRUNC:
+	case _PC_VDISABLE:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+asmlinkage int solaris_pathconf(u32 path, int name)
+{
+	return solaris_fpathconf(0, name);
+}
+
+/* solaris_llseek returns long long - quite difficult */
+asmlinkage long solaris_llseek(struct pt_regs *regs, u32 off_hi, u32 off_lo, int whence)
+{
+	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();
+	loff_t retval;
+	
+	set_fs(KERNEL_DS);
+	ret = sys_llseek((unsigned int)regs->u_regs[UREG_I0], off_hi, off_lo, &retval, whence);
+	set_fs(old_fs);
+	if (ret < 0) return ret;
+	regs->u_regs[UREG_I1] = (u32)retval;
+	return (retval >> 32);
+}
+
+/* Have to mask out all but lower 3 bits */
+asmlinkage int solaris_access(u32 filename, long mode)
+{
+	int (*sys_access)(const char *, int) = 
+		(int (*)(const char *, int))SYS(access);
+		
+	return sys_access((const char *)A(filename), mode & 7);
+}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/ioctl.c linux/arch/sparc64/solaris/ioctl.c
--- v2.1.52/linux/arch/sparc64/solaris/ioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/ioctl.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,316 @@
+/* $Id: ioctl.c,v 1.2 1997/09/04 00:59:22 davem Exp $
+ * ioctl.c: Solaris ioctl emulation.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Streams & timod emulation based on code
+ * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/termios.h>
+
+#include "conv.h"
+
+extern char * getname32(u32 filename);
+#define putname32 putname
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, 
+	unsigned long arg);
+extern asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd,
+	u32 arg);
+asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+
+/* termio* stuff {{{ */
+
+struct solaris_termios {
+	u32	c_iflag;
+	u32	c_oflag;
+	u32	c_cflag;
+	u32	c_lflag;
+	u8	c_cc[19];
+};
+
+struct solaris_termio {
+	u16	c_iflag;
+	u16	c_oflag;
+	u16	c_cflag;
+	u16	c_lflag;
+	s8	c_line;
+	u8	c_cc[8];
+};
+
+struct solaris_termiox {
+	u16	x_hflag;
+	u16	x_cflag;
+	u16	x_rflag[5];
+	u16	x_sflag;
+};
+
+static u32 solaris_to_linux_cflag(u32 cflag)
+{
+	cflag &= 0x7fdff000;
+	if (cflag & 0x200000) {
+		int baud = cflag & 0xf;
+		cflag &= ~0x20000f;
+		switch (baud) {
+		case 0: baud = B57600; break;
+		case 1: baud = B76800; break;
+		case 2: baud = B115200; break;
+		case 3: baud = B153600; break;
+		case 4: baud = B230400; break;
+		case 5: baud = B307200; break;
+		case 6: baud = B460800; break;
+		}
+		cflag |= CBAUDEX | baud;
+	}
+	return cflag;
+}
+
+static u32 linux_to_solaris_cflag(u32 cflag)
+{
+	cflag &= ~(CMSPAR | CIBAUD);
+	if (cflag & CBAUDEX) {
+		int baud = cflag & CBAUD;
+		cflag &= ~CBAUD;
+		switch (baud) {
+		case B57600: baud = 0; break;
+		case B76800: baud = 1; break;
+		case B115200: baud = 2; break;
+		case B153600: baud = 3; break;
+		case B230400: baud = 4; break;
+		case B307200: baud = 5; break;
+		case B460800: baud = 6; break;
+		case B614400: baud = 7; break;
+		case B921600: baud = 8; break;
+		/* case B1843200: baud = 9; break; */
+		}
+		cflag |= 0x200000 | baud;
+	}
+	return cflag;
+}
+
+static inline int linux_to_solaris_termio(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	int ret;
+	
+	ret = sys_ioctl(fd, cmd, A(arg));
+	if (!ret) {
+		u32 cflag;
+		
+		if (__get_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag))
+			return -EFAULT;
+		cflag = linux_to_solaris_cflag(cflag);
+		if (__put_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+static int solaris_to_linux_termio(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	int ret;
+	struct solaris_termio s;
+	unsigned long old_fs = get_fs();
+	
+	if (copy_from_user (&s, (struct solaris_termio *)A(arg), sizeof(struct solaris_termio)))
+		return -EFAULT;
+	s.c_cflag = solaris_to_linux_cflag(s.c_cflag);
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long)&s);
+	set_fs(old_fs);
+	return ret;
+}
+
+static inline int linux_to_solaris_termios(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	int ret;
+	struct solaris_termios s;
+	unsigned long old_fs = get_fs();
+
+	set_fs(KERNEL_DS);	
+	ret = sys_ioctl(fd, cmd, (unsigned long)&s);
+	set_fs(old_fs);
+	if (!ret) {
+		if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) ||
+		    __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) ||
+		    __put_user (linux_to_solaris_cflag(s.c_cflag), &((struct solaris_termios *)A(arg))->c_cflag) ||
+		    __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) ||
+		    __copy_to_user (((struct solaris_termios *)A(arg))->c_cc, s.c_cc, 16) ||
+		    __clear_user (((struct solaris_termios *)A(arg))->c_cc + 16, 2))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+static int solaris_to_linux_termios(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	int ret;
+	struct solaris_termios s;
+	unsigned long old_fs = get_fs();
+
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, TCGETS, (unsigned long)&s);
+	set_fs(old_fs);
+	if (ret) return ret;
+	if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) ||
+	    __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) ||
+	    __put_user (s.c_cflag, &((struct solaris_termios *)A(arg))->c_cflag) ||
+	    __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) ||
+	    __copy_from_user (s.c_cc, ((struct solaris_termios *)A(arg))->c_cc, 16))
+		return -EFAULT;
+	s.c_cflag = solaris_to_linux_cflag(s.c_cflag);
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long)&s);
+	set_fs(old_fs);
+	return ret;
+}
+
+static inline int solaris_T(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	switch (cmd & 0xff) {
+	case 1: /* TCGETA */
+		return linux_to_solaris_termio(fd, TCGETA, arg);
+	case 2: /* TCSETA */
+		return solaris_to_linux_termio(fd, TCSETA, arg);
+	case 3: /* TCSETAW */
+		return solaris_to_linux_termio(fd, TCSETAW, arg);
+	case 4: /* TCSETAF */
+		return solaris_to_linux_termio(fd, TCSETAF, arg);
+	case 5: /* TCSBRK */
+		return sys_ioctl(fd, TCSBRK, arg);
+	case 6: /* TCXONC */
+		return sys_ioctl(fd, TCXONC, arg);
+	case 7: /* TCFLSH */
+		return sys_ioctl(fd, TCFLSH, arg);
+	case 13: /* TCGETS */
+		return linux_to_solaris_termios(fd, TCGETS, arg);
+	case 14: /* TCSETS */
+		return solaris_to_linux_termios(fd, TCSETS, arg);
+	case 15: /* TCSETSW */
+		return solaris_to_linux_termios(fd, TCSETSW, arg);
+	case 16: /* TCSETSF */
+		return solaris_to_linux_termios(fd, TCSETSF, arg);
+	case 103: /* TIOCSWINSZ */
+		return sys_ioctl(fd, TIOCSWINSZ, arg);
+	case 104: /* TIOCGWINSZ */
+		return sys_ioctl(fd, TIOCGWINSZ, arg);
+	}
+	return -ENOSYS;
+}
+
+static inline int solaris_t(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	switch (cmd & 0xff) {
+	case 20: /* TIOCGPGRP */
+		return sys_ioctl(fd, TIOCGPGRP, arg);
+	case 21: /* TIOCSPGRP */
+		return sys_ioctl(fd, TIOCSPGRP, arg);
+	}
+	return -ENOSYS;
+}
+
+/* }}} */
+
+/* A pseudo STREAMS support {{{ */
+
+struct strioctl {
+	int cmd, timeout, len;
+	u32 data;
+};
+
+static inline int solaris_S(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	char *p;
+	int ret;
+	unsigned long old_fs;
+	struct strioctl si;
+	
+	switch (cmd & 0xff) {
+	case 1: /* I_NREAD */
+		return -ENOSYS;
+	case 2: /* I_PUSH */
+		p = getname32 (arg);
+		if (IS_ERR (p))
+			return PTR_ERR(p);
+		putname32 (p);
+		return 0;
+	case 3: /* I_POP */
+		return 0;
+	case 5: /* I_FLUSH */
+		return 0;
+	case 8: /* I_STR */
+		if (copy_from_user (&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
+			return -EFAULT;
+		switch ((si.cmd >> 8) & 0xff) {
+		case 'T':
+		default:
+			return solaris_ioctl(fd, si.cmd, si.data);
+		}
+	case 9: /* I_SETSIG */
+		return sys_ioctl(fd, FIOSETOWN, current->pid);
+	case 10: /* I_GETSIG */
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		sys_ioctl(fd, FIOGETOWN, (unsigned long)&ret);
+		set_fs(old_fs);
+		if (ret == current->pid) return 0x3ff;
+		else return -EINVAL;
+	case 11: /* I_FIND */
+		p = getname32 (arg);
+		if (IS_ERR (p))
+			return PTR_ERR(p);
+		ret = !strcmp(p, "timod");
+		putname32 (p);
+		return ret;
+	}
+	return -ENOSYS;
+}
+/* }}} */
+
+asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg)
+{
+	struct file * filp;
+	int error = -EBADF;
+
+	lock_kernel();
+	if(fd >= NR_OPEN) goto out;
+
+	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 't': error = solaris_t(fd, cmd, arg); break;
+	default:
+		error = -ENOSYS;
+		break;
+	}
+out:
+	if (error == -ENOSYS) {
+		unsigned char c = cmd>>8;
+		
+		if (c < ' ' || c > 126) c = '.';
+		printk("solaris_ioctl: Unknown cmd fd(%d) cmd(%08x '%c') arg(%08x)\n",
+		       (int)fd, (unsigned int)cmd, c, (unsigned int)arg);
+		error = -EINVAL;
+	}
+	unlock_kernel();
+	return error;
+}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/ipc.c linux/arch/sparc64/solaris/ipc.c
--- v2.1.52/linux/arch/sparc64/solaris/ipc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/ipc.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,122 @@
+/* $Id: ipc.c,v 1.1 1997/09/03 12:29:29 jj Exp $
+ * ipc.c: Solaris IPC emulation
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/types.h>
+#include <linux/smp_lock.h>
+#include <linux/shm.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/ipc.h>
+
+#include "conv.h"
+
+struct solaris_ipc_perm {
+	s32	uid;
+	s32	gid;
+	s32	cuid;
+	s32	cgid;
+	u32	mode;
+	u32	seq;
+	int	key;
+	s32	pad[4];
+};
+
+struct solaris_shmid_ds {
+	struct solaris_ipc_perm	shm_perm;
+	int			shm_segsz;
+	u32			shm_amp;
+	unsigned short		shm_lkcnt;
+	char			__padxx[2];
+	s32			shm_lpid;
+	s32			shm_cpid;
+	u32			shm_nattch;
+	u32			shm_cnattch;
+	s32			shm_atime;
+	s32			shm_pad1;
+	s32			shm_dtime;
+	s32			shm_pad2;
+	s32			shm_ctime;
+	s32			shm_pad3;
+	unsigned short		shm_cv;
+	char			shm_pad4[2];
+	u32			shm_sptas;
+	s32			shm_pad5[2];
+};
+
+asmlinkage long solaris_shmsys(int cmd, u32 arg1, u32 arg2, u32 arg3)
+{
+	int (*sys_ipc)(unsigned,int,int,unsigned long,void *,long) = 
+		(int (*)(unsigned,int,int,unsigned long,void *,long))SYS(ipc);
+	unsigned long old_fs;
+	unsigned long raddr;
+	int ret;
+		
+	switch (cmd) {
+	case 0: /* shmat */
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+		ret = sys_ipc(SHMAT, arg1, arg3 & ~0x4000, (unsigned long)&raddr, (void *)A(arg2), 0);
+		set_fs(old_fs);
+		if (ret >= 0) return (u32)raddr;
+		else return ret;
+	case 1: /* shmctl */
+		switch (arg2) {
+		case 3: /* SHM_LOCK */
+		case 4: /* SHM_UNLOCK */
+			return sys_ipc(SHMCTL, arg1, (arg2 == 3) ? SHM_LOCK : SHM_UNLOCK, 0, NULL, 0);
+		case 10: /* IPC_RMID */
+			return sys_ipc(SHMCTL, arg1, IPC_RMID, 0, NULL, 0);
+		case 11: /* IPC_SET */
+			{
+				struct shmid_ds s;
+				
+				if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) ||
+				    __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) || 
+				    __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode)))
+					return -EFAULT;
+				old_fs = get_fs();
+				set_fs(KERNEL_DS);
+				ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0);
+				set_fs(old_fs);
+				return ret;
+			}
+		case 12: /* IPC_STAT */
+			{
+				struct shmid_ds s;
+				
+				old_fs = get_fs();
+				set_fs(KERNEL_DS);
+				ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0);
+				set_fs(old_fs);
+				if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) ||
+				    __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) || 
+				    __get_user (s.shm_perm.cuid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cuid)) ||
+				    __get_user (s.shm_perm.cgid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cgid)) || 
+				    __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode)) ||
+				    __get_user (s.shm_perm.seq, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.seq)) ||
+				    __get_user (s.shm_perm.key, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.key)) ||
+				    __get_user (s.shm_segsz, &(((struct solaris_shmid_ds *)A(arg3))->shm_segsz)) ||
+				    __get_user (s.shm_lpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_lpid)) ||
+				    __get_user (s.shm_cpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_cpid)) ||
+				    __get_user (s.shm_nattch, &(((struct solaris_shmid_ds *)A(arg3))->shm_nattch)) ||
+				    __get_user (s.shm_atime, &(((struct solaris_shmid_ds *)A(arg3))->shm_atime)) ||
+				    __get_user (s.shm_dtime, &(((struct solaris_shmid_ds *)A(arg3))->shm_dtime)) ||
+				    __get_user (s.shm_ctime, &(((struct solaris_shmid_ds *)A(arg3))->shm_ctime)))
+					return -EFAULT;
+				return ret;
+			}
+		default: return -EINVAL;
+		}
+	case 2: /* shmdt */
+		return sys_ipc(SHMDT, 0, 0, 0, (void *)A(arg1), 0);
+	case 3: /* shmget */
+		return sys_ipc(SHMGET, arg1, arg2, arg3, NULL, 0);
+	}
+	return -EINVAL;
+}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/misc.c linux/arch/sparc64/solaris/misc.c
--- v2.1.52/linux/arch/sparc64/solaris/misc.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/misc.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,463 @@
+/* $Id: misc.c,v 1.4 1997/09/04 14:57:31 jj Exp $
+ * misc.c: Miscelaneous syscall emulation for Solaris
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/module.h> 
+#include <linux/types.h>
+#include <linux/smp_lock.h>
+#include <linux/utsname.h>
+#include <linux/limits.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+
+#include <asm/uaccess.h>
+#include <asm/string.h>
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
+#include "conv.h"
+
+/* Conversion from Linux to Solaris errnos. 0-34 are identity mapped.
+   Some Linux errnos (EPROCLIM, EDOTDOT, ERREMOTE, EUCLEAN, ENOTNAM, 
+   ENAVAIL, EISNAM, EREMOTEIO, ENOMEDIUM, EMEDIUMTYPE) have no Solaris
+   equivalents. I return EINVAL in that case, which is very wrong. If
+   someone suggest a better value for them, you're welcomed.
+   On the other side, Solaris ECANCELED and ENOTSUP have no Linux equivalents,
+   but that doesn't matter here. --jj */
+int solaris_err_table[] = {
+/* 0 */  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+/* 10 */  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+/* 20 */  20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+/* 30 */  30, 31, 32, 33, 34, 22, 150, 149, 95, 96,
+/* 40 */  97, 98, 99, 120, 121, 122, 123, 124, 125, 126, 
+/* 50 */ 127, 128, 129, 130, 131, 132, 133, 134, 143, 144,
+/* 60 */ 145, 146, 90, 78, 147, 148, 93, 22, 94, 49,
+/* 70 */ 151, 66, 60, 62, 63, 35, 77, 36, 45, 46, 
+/* 80 */ 64, 22, 67, 68, 69, 70, 71, 74, 22, 82, 
+/* 90 */ 89, 92, 79, 81, 37, 38, 39, 40, 41, 42,
+/* 100 */ 43, 44, 50, 51, 52, 53, 54, 55, 56, 57,
+/* 110 */ 87, 61, 84, 65, 83, 80, 91, 22, 22, 22,
+/* 120 */ 22, 22, 88, 86, 85, 22, 22,
+};
+
+asmlinkage u32 solaris_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off)
+{
+	u32 (*sunos_mmap)(u32,u32,u32,u32,u32,u32) = 
+		(u32 (*)(u32,u32,u32,u32,u32,u32))SUNOS(71);
+	u32 ret;
+	
+	ret = sunos_mmap(addr,len,prot,flags,fd,off);
+	/* sunos_mmap sets personality to PER_BSD */
+	current->personality = PER_SVR4;
+	return ret;
+}
+
+asmlinkage int solaris_brk(u32 brk)
+{
+	int (*sunos_brk)(u32) = (int (*)(u32))SUNOS(17);
+	
+	return sunos_brk(brk);
+}
+
+#define set_utsfield(to, from, dotchop, countfrom) {			\
+	char *p; 							\
+	int i, len = (countfrom) ? 					\
+		((sizeof(to) > sizeof(from) ? 				\
+			sizeof(from) : sizeof(to))) : sizeof(to); 	\
+	copy_to_user_ret(to, from, len, -EFAULT); 			\
+	if (dotchop) 							\
+		for (p=from,i=0; *p && *p != '.' && --len; p++,i++); 	\
+	else 								\
+		i = len - 1; 						\
+	__put_user_ret('\0', (char *)(to+i), -EFAULT); 			\
+}
+
+struct sol_uname {
+	char sysname[9];
+	char nodename[9];
+	char release[9];
+	char version[9];
+	char machine[9];
+};
+
+struct sol_utsname {
+	char sysname[257];
+	char nodename[257];
+	char release[257];
+	char version[257];
+	char machine[257];
+};
+
+static char *machine(void)
+{
+	switch (sparc_cpu_model) {
+	case sun4: return "sun4";
+	case sun4c: return "sun4c";
+	case sun4e: return "sun4e";
+	case sun4m: return "sun4m";
+	case sun4d: return "sun4d";
+	case sun4u: return "sun4u";
+	default: return "sparc";
+	}
+}
+
+static char *platform(char *buffer)
+{
+	int i;
+	struct {
+		char *platform;
+		int id_machtype;
+	} platforms [] = {
+		{ "sun4", (SM_SUN4 | SM_4_110) },
+		{ "sun4", (SM_SUN4 | SM_4_260) },
+		{ "sun4", (SM_SUN4 | SM_4_330) },
+		{ "sun4", (SM_SUN4 | SM_4_470) },
+		{ "SUNW,Sun_4_60", (SM_SUN4C | SM_4C_SS1) },
+		{ "SUNW,Sun_4_40", (SM_SUN4C | SM_4C_IPC) },
+		{ "SUNW,Sun_4_65", (SM_SUN4C | SM_4C_SS1PLUS) },
+		{ "SUNW,Sun_4_20", (SM_SUN4C | SM_4C_SLC) },
+		{ "SUNW,Sun_4_75", (SM_SUN4C | SM_4C_SS2) },
+		{ "SUNW,Sun_4_25", (SM_SUN4C | SM_4C_ELC) },
+		{ "SUNW,Sun_4_50", (SM_SUN4C | SM_4C_IPX) },
+		{ "SUNW,Sun_4_600", (SM_SUN4M | SM_4M_SS60) },
+		{ "SUNW,SPARCstation-5", (SM_SUN4M | SM_4M_SS50) },
+		{ "SUNW,SPARCstation-20", (SM_SUN4M | SM_4M_SS40) }
+	};
+
+	*buffer = 0;
+	prom_getproperty(prom_root_node, "name", buffer, 256);
+	if (*buffer) {
+		char *p;
+
+		for (p = buffer; *p; p++)
+			if (*p == '/' || *p == ' ') *p = '_';
+		return buffer;
+	}
+	for (i = 0; i < sizeof (platforms)/sizeof (platforms[0]); i++)
+		if (platforms[i].id_machtype == idprom->id_machtype)
+			return platforms[i].platform;
+	return "sun4c";
+}
+
+static char *serial(char *buffer)
+{
+	int node = prom_getchild(prom_root_node);
+
+	node = prom_searchsiblings(node, "options");
+	*buffer = 0;
+	prom_getproperty(node, "system-board-serial#", buffer, 256);
+	if (!*buffer)
+		return "4512348717234";
+	else
+		return buffer;
+}
+
+asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2)
+{
+	switch (which) {
+	case 0:	/* old uname */
+		/* Lets cheat */
+		set_utsfield(((struct sol_uname *)A(buf))->sysname, 
+			"SunOS", 1, 0);
+		set_utsfield(((struct sol_uname *)A(buf))->nodename, 
+			system_utsname.nodename, 1, 1);
+		set_utsfield(((struct sol_uname *)A(buf))->release, 
+			"2.6", 0, 0);
+		set_utsfield(((struct sol_uname *)A(buf))->version, 
+			"Generic", 0, 0);
+		set_utsfield(((struct sol_uname *)A(buf))->machine, 
+			machine(), 0, 0);
+		return 0;
+	case 2: /* ustat */
+		return -ENOSYS;
+	case 3: /* fusers */
+		return -ENOSYS;
+	default:
+		return -ENOSYS;
+	}
+}
+
+asmlinkage int solaris_utsname(u32 buf)
+{
+	/* Why should we not lie a bit? */
+	set_utsfield(((struct sol_utsname *)A(buf))->sysname, 
+			"SunOS", 0, 0);
+	set_utsfield(((struct sol_utsname *)A(buf))->nodename, 
+			system_utsname.nodename, 1, 1);
+	set_utsfield(((struct sol_utsname *)A(buf))->release, 
+			"5.6", 0, 0);
+	set_utsfield(((struct sol_utsname *)A(buf))->version, 
+			"Generic", 0, 0);
+	set_utsfield(((struct sol_utsname *)A(buf))->machine, 
+			machine(), 0, 0);
+	return 0;
+}
+
+#define SI_SYSNAME		1       /* return name of operating system */
+#define SI_HOSTNAME		2       /* return name of node */
+#define SI_RELEASE		3       /* return release of operating system */
+#define SI_VERSION		4       /* return version field of utsname */
+#define SI_MACHINE		5       /* return kind of machine */
+#define SI_ARCHITECTURE		6       /* return instruction set arch */
+#define SI_HW_SERIAL		7       /* return hardware serial number */
+#define SI_HW_PROVIDER		8       /* return hardware manufacturer */
+#define SI_SRPC_DOMAIN		9       /* return secure RPC domain */
+#define SI_PLATFORM		513     /* return platform identifier */
+
+asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count)
+{
+	char *p, *q, *r;
+	char buffer[256];
+	int len;
+	
+	/* Again, we cheat :)) */
+	switch (cmd) {
+	case SI_SYSNAME: r = "SunOS"; break;
+	case SI_HOSTNAME:
+		r = buffer + 256;
+		for (p = system_utsname.nodename, q = buffer; 
+		     q < r && *p && *p != '.'; *q++ = *p++);
+		*q = 0;
+		r = buffer;
+		break;
+	case SI_RELEASE: r = "5.6"; break;
+	case SI_MACHINE: r = machine(); break;
+	case SI_ARCHITECTURE: r = "sparc"; break;
+	case SI_HW_PROVIDER: r = "Sun_Microsystems"; break;
+	case SI_HW_SERIAL: r = serial(buffer); break;
+	case SI_PLATFORM: r = platform(buffer); break;
+	case SI_SRPC_DOMAIN: r = ""; break;
+	case SI_VERSION: r = "Generic"; break;
+	default: return -EINVAL;
+	}
+	len = strlen(r) + 1;
+	if (count < len) {
+		copy_to_user_ret((char *)A(buf), r, count - 1, -EFAULT);
+		__put_user_ret(0, (char *)A(buf) + count - 1, -EFAULT);
+	} else
+		copy_to_user_ret((char *)A(buf), r, len, -EFAULT);
+	return len;
+}
+
+#define	SOLARIS_CONFIG_NGROUPS			2
+#define	SOLARIS_CONFIG_CHILD_MAX		3
+#define	SOLARIS_CONFIG_OPEN_FILES		4
+#define	SOLARIS_CONFIG_POSIX_VER		5
+#define	SOLARIS_CONFIG_PAGESIZE			6
+#define	SOLARIS_CONFIG_CLK_TCK			7
+#define	SOLARIS_CONFIG_XOPEN_VER		8
+#define	SOLARIS_CONFIG_PROF_TCK			10
+#define	SOLARIS_CONFIG_NPROC_CONF		11
+#define	SOLARIS_CONFIG_NPROC_ONLN		12
+#define	SOLARIS_CONFIG_AIO_LISTIO_MAX		13
+#define	SOLARIS_CONFIG_AIO_MAX			14
+#define	SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX	15
+#define	SOLARIS_CONFIG_DELAYTIMER_MAX		16
+#define	SOLARIS_CONFIG_MQ_OPEN_MAX		17
+#define	SOLARIS_CONFIG_MQ_PRIO_MAX		18
+#define	SOLARIS_CONFIG_RTSIG_MAX		19
+#define	SOLARIS_CONFIG_SEM_NSEMS_MAX		20
+#define	SOLARIS_CONFIG_SEM_VALUE_MAX		21
+#define	SOLARIS_CONFIG_SIGQUEUE_MAX		22
+#define	SOLARIS_CONFIG_SIGRT_MIN		23
+#define	SOLARIS_CONFIG_SIGRT_MAX		24
+#define	SOLARIS_CONFIG_TIMER_MAX		25
+#define	SOLARIS_CONFIG_PHYS_PAGES		26
+#define	SOLARIS_CONFIG_AVPHYS_PAGES		27
+
+asmlinkage int solaris_sysconf(int id)
+{
+	switch (id) {
+	case SOLARIS_CONFIG_NGROUPS:	return NGROUPS_MAX;
+	case SOLARIS_CONFIG_CHILD_MAX:	return CHILD_MAX;
+	case SOLARIS_CONFIG_OPEN_FILES:	return OPEN_MAX;
+	case SOLARIS_CONFIG_POSIX_VER:	return 199309;
+	case SOLARIS_CONFIG_PAGESIZE:	return PAGE_SIZE;
+	case SOLARIS_CONFIG_XOPEN_VER:	return 3;
+	case SOLARIS_CONFIG_CLK_TCK:
+	case SOLARIS_CONFIG_PROF_TCK:
+		return prom_getintdefault(
+			linux_cpus[smp_processor_id()].prom_node,
+			"clock-frequency", 167000000);
+#ifdef __SMP__	
+	case SOLARIS_CONFIG_NPROC_CONF:	return NCPUS;
+	case SOLARIS_CONFIG_NPROC_ONLN:	return smp_num_cpus;
+#else
+	case SOLARIS_CONFIG_NPROC_CONF:	return 1;
+	case SOLARIS_CONFIG_NPROC_ONLN:	return 1;
+#endif
+	case SOLARIS_CONFIG_SIGRT_MIN:		return 37;
+	case SOLARIS_CONFIG_SIGRT_MAX:		return 44;
+	case SOLARIS_CONFIG_PHYS_PAGES:
+	case SOLARIS_CONFIG_AVPHYS_PAGES:
+		{
+			struct sysinfo s;
+			
+			si_meminfo(&s);
+			if (id == SOLARIS_CONFIG_PHYS_PAGES)
+				return s.totalram >>= PAGE_SHIFT;
+			else
+				return s.freeram >>= PAGE_SHIFT;
+		}
+	/* XXX support these as well -jj */
+	case SOLARIS_CONFIG_AIO_LISTIO_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_AIO_MAX:		return -EINVAL;
+	case SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_DELAYTIMER_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_MQ_OPEN_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_MQ_PRIO_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_RTSIG_MAX:		return -EINVAL;
+	case SOLARIS_CONFIG_SEM_NSEMS_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_SEM_VALUE_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_SIGQUEUE_MAX:	return -EINVAL;
+	case SOLARIS_CONFIG_TIMER_MAX:		return -EINVAL;
+	default: return -EINVAL;
+	}
+}
+
+asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid)
+{
+	int ret;
+	
+	switch (cmd) {
+	case 0: /* getpgrp */
+		return current->pgrp;
+	case 1: /* setpgrp */
+		{
+			int (*sys_setpgid)(pid_t,pid_t) =
+				(int (*)(pid_t,pid_t))SYS(setpgid);
+				
+			/* can anyone explain me the difference between
+			   Solaris setpgrp and setsid? */
+			ret = sys_setpgid(0, 0);
+			if (ret) return ret;
+			current->tty = NULL;
+			return current->pgrp;
+		}
+	case 2: /* getsid */
+		{
+			int (*sys_getsid)(pid_t) = (int (*)(pid_t))SYS(getsid);
+			return sys_getsid(pid);
+		}
+	case 3: /* setsid */
+		{
+			int (*sys_setsid)(void) = (int (*)(void))SYS(setsid);
+			return sys_setsid();
+		}
+	case 4: /* getpgid */
+		{
+			int (*sys_getpgid)(pid_t) = (int (*)(pid_t))SYS(getpgid);
+			return sys_getpgid(pid);
+		}
+	case 5: /* setpgid */
+		{
+			int (*sys_setpgid)(pid_t,pid_t) = 
+				(int (*)(pid_t,pid_t))SYS(setpgid);
+			return sys_setpgid(pid,pgid);
+		}
+	}
+	return -EINVAL;
+}
+
+asmlinkage int do_sol_unimplemented(struct pt_regs *regs)
+{
+	printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n", 
+			(int)regs->u_regs[UREG_G1], 
+			(int)regs->u_regs[UREG_I0],
+			(int)regs->u_regs[UREG_I1],
+			(int)regs->u_regs[UREG_I2],
+			(int)regs->u_regs[UREG_I3]);
+	return -ENOSYS;
+}
+
+asmlinkage void solaris_register(void)
+{
+	lock_kernel();
+	current->personality = PER_SVR4;
+	if (current->exec_domain && current->exec_domain->module)
+		__MOD_DEC_USE_COUNT(current->exec_domain->module);
+	current->exec_domain = lookup_exec_domain(current->personality);
+	if (current->exec_domain && current->exec_domain->module)
+		__MOD_INC_USE_COUNT(current->exec_domain->module);
+	unlock_kernel();
+}
+
+extern long solaris_to_linux_signals[], linux_to_solaris_signals[];
+
+struct exec_domain solaris_exec_domain = {
+	"Solaris",
+	(lcall7_func)NULL,
+	1, 1,	/* PER_SVR4 personality */
+	solaris_to_linux_signals,
+	linux_to_solaris_signals,
+#ifdef MODULE
+	&__this_module,
+#else
+	NULL,
+#endif
+	NULL
+};
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz)");
+MODULE_DESCRIPTION("Solaris binary emulation module");
+
+#ifdef __sparc_v9__
+extern u32 tl0_solaris[8];
+#define update_ttable(x) 										\
+	tl0_solaris[3] = (((long)(x) - (long)tl0_solaris - 3) >> 2) | 0x40000000;			\
+	__asm__ __volatile__ ("membar #StoreStore; flush %0" : : "r" (&tl0_solaris[3]))
+#else
+#endif	
+
+extern u32 solaris_sparc_syscall[];
+extern u32 solaris_syscall[];
+extern int init_socksys(void);
+extern void cleanup_socksys(void);
+
+int init_module(void)
+{
+	int ret;
+	register_exec_domain(&solaris_exec_domain);
+	if ((ret = init_socksys())) {
+		unregister_exec_domain(&solaris_exec_domain);
+		return ret;
+	}
+	update_ttable(solaris_sparc_syscall);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	update_ttable(solaris_syscall);
+	cleanup_socksys();
+	unregister_exec_domain(&solaris_exec_domain);
+}
+
+#else
+int init_solaris_emul(void)
+{
+	register_exec_domain(&solaris_exec_domain);
+	init_socksys();
+}
+#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.52/linux/arch/sparc64/solaris/signal.c linux/arch/sparc64/solaris/signal.c
--- v2.1.52/linux/arch/sparc64/solaris/signal.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/signal.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,419 @@
+/* $Id: signal.c,v 1.2 1997/09/03 12:29:19 jj Exp $
+ * signal.c: Signal emulation for Solaris
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/types.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/svr4.h>
+#include <asm/string.h>
+
+#include "conv.h"
+#include "signal.h"
+
+#define _S(nr) (1L<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+long linux_to_solaris_signals[] = {
+        0,
+	SOLARIS_SIGHUP,		SOLARIS_SIGINT,	
+	SOLARIS_SIGQUIT,	SOLARIS_SIGILL,
+	SOLARIS_SIGTRAP,	SOLARIS_SIGIOT,
+	SOLARIS_SIGEMT,		SOLARIS_SIGFPE,
+	SOLARIS_SIGKILL,	SOLARIS_SIGBUS,
+	SOLARIS_SIGSEGV,	SOLARIS_SIGSYS,
+	SOLARIS_SIGPIPE,	SOLARIS_SIGALRM,
+	SOLARIS_SIGTERM,	SOLARIS_SIGURG,
+	SOLARIS_SIGSTOP,	SOLARIS_SIGTSTP,
+	SOLARIS_SIGCONT,	SOLARIS_SIGCLD,
+	SOLARIS_SIGTTIN,	SOLARIS_SIGTTOU,
+	SOLARIS_SIGPOLL,	SOLARIS_SIGXCPU,
+	SOLARIS_SIGXFSZ,	SOLARIS_SIGVTALRM,
+	SOLARIS_SIGPROF,	SOLARIS_SIGWINCH,
+	SOLARIS_SIGUSR1,	SOLARIS_SIGUSR1,
+	SOLARIS_SIGUSR2,	-1,
+};
+
+long solaris_to_linux_signals[] = {
+        0,
+        SIGHUP,		SIGINT,		SIGQUIT,	SIGILL,
+        SIGTRAP,	SIGIOT,		SIGEMT,		SIGFPE,
+        SIGKILL,	SIGBUS,		SIGSEGV,	SIGSYS,
+        SIGPIPE,	SIGALRM,	SIGTERM,	SIGUSR1,
+        SIGUSR2,	SIGCHLD,	-1,		SIGWINCH,
+        SIGURG,		SIGPOLL,	SIGSTOP,	SIGTSTP,
+        SIGCONT,	SIGTTIN,	SIGTTOU,	SIGVTALRM,
+        SIGPROF,	SIGXCPU,	SIGXFSZ,        -1,
+	-1,		-1,		-1,		-1,
+	-1,		-1,		-1,		-1,
+	-1,		-1,		-1,		-1,
+};
+
+static inline long mapsig(long sig)
+{
+	if ((unsigned long)sig > SOLARIS_NSIGNALS)
+		return -EINVAL;
+	return solaris_to_linux_signals[sig];
+}
+
+asmlinkage int solaris_kill(int pid, int sig)
+{
+	int (*sys_kill)(int,int) = 
+		(int (*)(int,int))SYS(kill);
+	int s = mapsig(sig);
+	
+	if (s < 0) return s;
+	return sys_kill(pid, s);
+}
+
+static long sig_handler(int sig, u32 arg, int one_shot)
+{
+	struct sigaction sa, old;
+	int ret;
+	unsigned long old_fs = get_fs();
+	int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = 
+		(int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
+	
+	sa.sa_mask = 0L;
+	sa.sa_restorer = NULL;
+	sa.sa_handler = (__sighandler_t)A(arg);
+	sa.sa_flags = 0;
+	if (one_shot) sa.sa_flags = SA_ONESHOT | SA_NOMASK;
+	set_fs (KERNEL_DS);
+	ret = sys_sigaction(sig, &sa, &old);
+	set_fs (old_fs);
+	if (ret < 0) return ret;
+	return (u32)(long)old.sa_handler;
+}
+
+static inline long solaris_signal(int sig, u32 arg)
+{
+	return sig_handler (sig, arg, 1);
+}
+
+static long solaris_sigset(int sig, u32 arg)
+{
+	if (arg != 2) /* HOLD */ {
+		spin_lock_irq(&current->sigmask_lock);
+		current->blocked &= ~_S(sig);
+		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;
+		spin_unlock_irq(&current->sigmask_lock);
+		return 0;
+	}
+}
+
+static inline long solaris_sighold(int sig)
+{
+	return solaris_sigset(sig, 2);
+}
+
+static inline long solaris_sigrelse(int sig)
+{
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked &= ~_S(sig);
+	spin_unlock_irq(&current->sigmask_lock);
+	return 0;
+}
+
+static inline long solaris_sigignore(int sig)
+{
+	return sig_handler (sig, (u32)SIG_IGN, 0);
+}
+
+static inline long solaris_sigpause(int sig)
+{
+	printk ("Need to support solaris sigpause\n");
+	return -ENOSYS;
+}
+
+asmlinkage long solaris_sigfunc(int sig, u32 arg)
+{
+	int func = sig & ~0xff;
+	
+	sig = mapsig(sig & 0xff); 
+	if (sig < 0) return sig; 
+	switch (func) {
+	case 0: return solaris_signal(sig, arg); 
+	case 0x100: return solaris_sigset(sig, arg); 
+	case 0x200: return solaris_sighold(sig);
+	case 0x400: return solaris_sigrelse(sig); 
+	case 0x800: return solaris_sigignore(sig); 
+	case 0x1000: return solaris_sigpause(sig);
+	}
+	return -EINVAL;
+}
+
+typedef struct {
+	u32 __sigbits[4];
+} sol_sigset_t;
+
+static inline int mapin(u32 *p, sigset_t *q)
+{
+	int i;
+	u32 x;
+	int sig;
+	
+	*q = 0L;
+	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);
+		}
+		x >>= 1;
+		if (i == 32)
+			x = p[1];
+	}
+	return 0;
+}
+
+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) {
+			sig = linux_to_solaris_signals[i];
+			if (sig == -1)
+				return -EINVAL;
+			if (sig > 32)
+				p[1] |= 1L << (sig - 33);
+			else
+				p[0] |= 1L << (sig - 1);
+		}
+	}
+	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();
+	int ret;
+	int (*sys_sigprocmask)(int,sigset_t *,sigset_t *) = 
+		(int (*)(int,sigset_t *,sigset_t *))SYS(sigprocmask);
+	
+	ins = NULL; outs = NULL;
+	if (in) {
+		u32 tmp[2];
+		
+		if (copy_from_user (tmp, (sol_sigset_t *)A(in), 2*sizeof(u32)))
+			return -EFAULT;
+		ins = &in_s;
+		if (mapin (tmp, ins)) return -EINVAL;
+	}
+	if (out) outs = &out_s;
+	set_fs (KERNEL_DS);
+	ret = sys_sigprocmask((how == 3) ? SIG_SETMASK : how, ins, outs);
+	set_fs (old_fs);
+	if (ret) return ret;
+	if (out) {
+		u32 tmp[4];
+		
+		tmp[2] = 0; tmp[3] = 0;
+		if (mapout (outs, tmp)) return -EINVAL;
+		if (copy_to_user((sol_sigset_t *)A(out), tmp, 4*sizeof(u32)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+asmlinkage long do_sol_sigsuspend(u32 mask)
+{
+	sigset_t s;
+	u32 tmp[2];
+		
+	if (copy_from_user (tmp, (sol_sigset_t *)A(mask), 2*sizeof(u32)))
+		return -EFAULT;
+	if (mapin (tmp, &s)) return -EINVAL;
+	return (long)s;
+}
+
+struct sol_sigaction {
+	int	sa_flags;
+	u32	sa_handler;
+	u32	sa_mask[4];
+	int	sa_resv[2];
+};
+
+asmlinkage int solaris_sigaction(int sig, u32 act, u32 old)
+{
+	u32 tmp, tmp2[4];
+	struct sigaction s, s2;
+	int ret;
+	unsigned long old_fs = get_fs();
+	int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = 
+		(int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
+	
+	sig = mapsig(sig); 
+	if (sig < 0) {
+		/* We cheat a little bit for Solaris only signals */
+		if (old && clear_user((struct sol_sigaction *)A(old), sizeof(struct sol_sigaction)))
+			return -EFAULT;
+		return 0;
+	}
+	if (act) {
+		if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_flags))
+			return -EFAULT;
+		s.sa_flags = 0;
+		if (tmp & SOLARIS_SA_ONSTACK) s.sa_flags |= SA_STACK;
+		if (tmp & SOLARIS_SA_RESTART) s.sa_flags |= SA_RESTART;
+		if (tmp & SOLARIS_SA_NODEFER) s.sa_flags |= SA_NOMASK;
+		if (tmp & SOLARIS_SA_RESETHAND) s.sa_flags |= SA_ONESHOT;
+		if (tmp & SOLARIS_SA_NOCLDSTOP) s.sa_flags |= SA_NOCLDSTOP;
+		if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_handler) ||
+		    copy_from_user (tmp2, &((struct sol_sigaction *)A(act))->sa_mask, 2*sizeof(u32)))
+			return -EFAULT;
+		s.sa_handler = (__sighandler_t)A(tmp);
+		if (mapin (tmp2, &s.sa_mask)) return -EINVAL;
+		s.sa_restorer = 0;
+	}
+	set_fs(KERNEL_DS);
+	ret = sys_sigaction(sig, act ? &s : NULL, old ? &s2 : NULL);
+	set_fs(old_fs);
+	if (ret) return ret;
+	if (old) {
+		if (mapout (&s2.sa_mask, tmp2)) return -EINVAL;
+		tmp = 0; tmp2[2] = 0; tmp2[3] = 0;
+		if (s2.sa_flags & SA_STACK) tmp |= SOLARIS_SA_ONSTACK;
+		if (s2.sa_flags & SA_RESTART) tmp |= SOLARIS_SA_RESTART;
+		if (s2.sa_flags & SA_NOMASK) tmp |= SOLARIS_SA_NODEFER;
+		if (s2.sa_flags & SA_ONESHOT) tmp |= SOLARIS_SA_RESETHAND;
+		if (s2.sa_flags & SA_NOCLDSTOP) tmp |= SOLARIS_SA_NOCLDSTOP;
+		if (put_user (tmp, &((struct sol_sigaction *)A(old))->sa_flags) ||
+		    __put_user ((u32)(long)s2.sa_handler, &((struct sol_sigaction *)A(old))->sa_handler) ||
+		    copy_to_user (&((struct sol_sigaction *)A(old))->sa_mask, tmp2, 4*sizeof(u32)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+asmlinkage int solaris_sigpending(int which, u32 set)
+{
+	sigset_t s;
+	u32 tmp[4];
+	switch (which) {
+	case 1: /* sigpending */
+		lock_kernel();
+		s = current->blocked & current->signal;
+		unlock_kernel();
+		break;
+	case 2: /* sigfillset - I just set signals which have linux equivalents */
+		s = 0x7fffffff;
+		break;
+	default: return -EINVAL;
+	}
+	if (mapout (&s, tmp)) return -EINVAL;
+	tmp[2] = 0; tmp[3] = 0;
+	if (copy_to_user ((u32 *)A(set), tmp, sizeof(tmp)))
+		return -EFAULT;
+	return 0;
+}
+
+asmlinkage int solaris_wait(u32 stat_loc)
+{
+	int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) =
+		(int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4);
+	int ret, status;
+	
+	ret = sys_wait4(-1, (unsigned int *)A(stat_loc), WUNTRACED, NULL);
+	if (ret >= 0 && stat_loc) {
+		if (get_user (status, (unsigned int *)A(stat_loc)))
+			return -EFAULT;
+		if (((status - 1) & 0xffff) < 0xff)
+			status = linux_to_solaris_signals[status & 0x7f] & 0x7f;
+		else if ((status & 0xff) == 0x7f)
+			status = (linux_to_solaris_signals[(status >> 8) & 0xff] << 8) | 0x7f;
+		if (__put_user (status, (unsigned int *)A(stat_loc)))
+			return -EFAULT;
+	}
+	return ret;
+}
+
+asmlinkage int solaris_waitid(int idtype, s32 pid, u32 info, int options)
+{
+	int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) =
+		(int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4);
+	int opts, status, ret;
+	
+	switch (idtype) {
+	case 0: /* P_PID */ break;
+	case 1: /* P_PGID */ pid = -pid; break;
+	case 7: /* P_ALL */ pid = -1; break;
+	default: return -EINVAL;
+	}
+	opts = 0;
+	if (options & SOLARIS_WUNTRACED) opts |= WUNTRACED;
+	if (options & SOLARIS_WNOHANG) opts |= WNOHANG;
+	current->state = TASK_RUNNING;
+	ret = sys_wait4(pid, (unsigned int *)A(info), opts, NULL);
+	if (ret < 0) return ret;
+	if (info) {
+		struct sol_siginfo *s = (struct sol_siginfo *)A(info);
+	
+		if (get_user (status, (unsigned int *)A(info))) return -EFAULT;
+		__put_user_ret (SOLARIS_SIGCLD, &s->si_signo, -EFAULT);
+		__put_user_ret (ret, &s->_data._proc._pid, -EFAULT);
+		switch (status & 0xff) {
+		case 0: ret = SOLARIS_CLD_EXITED;
+			status = (status >> 8) & 0xff;
+			break;
+		case 0x7f:
+			status = (status >> 8) & 0xff;
+			switch (status) {
+			case SIGSTOP:
+			case SIGTSTP: ret = SOLARIS_CLD_STOPPED;
+			default: ret = SOLARIS_CLD_EXITED;
+			}
+			status = linux_to_solaris_signals[status];
+			break;
+		default:
+			if (status & 0x80) ret = SOLARIS_CLD_DUMPED;
+			else ret = SOLARIS_CLD_KILLED;
+			status = linux_to_solaris_signals[status & 0x7f];
+			break;
+		}
+		__put_user_ret (ret, &s->si_code, -EFAULT);
+		__put_user_ret (status, &s->_data._proc._pdata._cld._status, -EFAULT);
+	}
+	return 0;
+}
+
+extern int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs);
+extern int svr4_getcontext(svr4_ucontext_t *c, struct pt_regs *regs);
+
+asmlinkage int solaris_context(struct pt_regs *regs)
+{
+	switch ((unsigned)regs->u_regs[UREG_I0]) {
+	case 0: /* getcontext */
+		return svr4_getcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs);
+	case 1: /* setcontext */
+		return svr4_setcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs);
+	default:
+		return -EINVAL;
+
+	}
+}
+
+asmlinkage int solaris_sigaltstack(u32 ss, u32 oss)
+{
+/* XXX Implement this soon */
+	return 0;
+}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/signal.h linux/arch/sparc64/solaris/signal.h
--- v2.1.52/linux/arch/sparc64/solaris/signal.h	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/signal.h	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,109 @@
+/* $Id: signal.h,v 1.2 1997/09/03 12:29:21 jj Exp $
+ * signal.h: Signal emulation for Solaris
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+    
+#define SOLARIS_SIGHUP		1
+#define SOLARIS_SIGINT		2
+#define SOLARIS_SIGQUIT		3
+#define SOLARIS_SIGILL		4
+#define SOLARIS_SIGTRAP		5
+#define SOLARIS_SIGIOT		6
+#define SOLARIS_SIGEMT		7
+#define SOLARIS_SIGFPE		8
+#define SOLARIS_SIGKILL		9
+#define SOLARIS_SIGBUS		10
+#define SOLARIS_SIGSEGV		11
+#define SOLARIS_SIGSYS		12
+#define SOLARIS_SIGPIPE		13
+#define SOLARIS_SIGALRM		14
+#define SOLARIS_SIGTERM		15
+#define SOLARIS_SIGUSR1		16
+#define SOLARIS_SIGUSR2		17
+#define SOLARIS_SIGCLD		18
+#define SOLARIS_SIGPWR		19
+#define SOLARIS_SIGWINCH	20
+#define SOLARIS_SIGURG		21
+#define SOLARIS_SIGPOLL		22
+#define SOLARIS_SIGSTOP		23
+#define SOLARIS_SIGTSTP		24
+#define SOLARIS_SIGCONT		25
+#define SOLARIS_SIGTTIN		26
+#define SOLARIS_SIGTTOU		27
+#define SOLARIS_SIGVTALRM	28
+#define SOLARIS_SIGPROF		29
+#define SOLARIS_SIGXCPU		30
+#define SOLARIS_SIGXFSZ		31
+#define SOLARIS_SIGWAITING	32
+#define SOLARIS_SIGLWP		33
+#define SOLARIS_SIGFREEZE	34
+#define SOLARIS_SIGTHAW		35
+#define SOLARIS_SIGCANCEL	36
+#define SOLARIS_SIGRTMIN	37
+#define SOLARIS_SIGRTMAX	44
+#define SOLARIS_NSIGNALS	44
+
+
+#define SOLARIS_SA_ONSTACK	1
+#define SOLARIS_SA_RESETHAND	2
+#define SOLARIS_SA_RESTART	4
+#define SOLARIS_SA_SIGINFO	8
+#define SOLARIS_SA_NODEFER	16
+#define SOLARIS_SA_NOCLDWAIT	0x10000
+#define SOLARIS_SA_NOCLDSTOP	0x20000
+
+struct sol_siginfo {
+	int	si_signo;
+	int	si_code;
+	int	si_errno;
+	union	{
+		char	pad[128-3*sizeof(int)];
+		struct { 
+			s32	_pid;
+			union {
+				struct {
+					s32	_uid;
+					s32	_value;
+				} _kill;
+				struct {
+					s32	_utime;
+					int	_status;
+					s32	_stime;
+				} _cld;
+			} _pdata;
+		} _proc;
+		struct { /* SIGSEGV, SIGBUS, SIGILL and SIGFPE */
+			u32	_addr;
+			int	_trapno;
+		} _fault;
+		struct { /* SIGPOLL, SIGXFSZ */
+			int	_fd;
+			s32	_band;
+		} _file;
+	} _data;
+};
+
+#define SOLARIS_WUNTRACED	0x04
+#define SOLARIS_WNOHANG		0x40
+#define SOLARIS_WEXITED         0x01
+#define SOLARIS_WTRAPPED        0x02
+#define SOLARIS_WSTOPPED        WUNTRACED
+#define SOLARIS_WCONTINUED      0x08
+#define SOLARIS_WNOWAIT         0x80
+
+#define SOLARIS_TRAP_BRKPT      1
+#define SOLARIS_TRAP_TRACE      2
+#define SOLARIS_CLD_EXITED      1
+#define SOLARIS_CLD_KILLED      2
+#define SOLARIS_CLD_DUMPED      3
+#define SOLARIS_CLD_TRAPPED     4
+#define SOLARIS_CLD_STOPPED     5
+#define SOLARIS_CLD_CONTINUED   6
+#define SOLARIS_POLL_IN         1
+#define SOLARIS_POLL_OUT        2
+#define SOLARIS_POLL_MSG        3
+#define SOLARIS_POLL_ERR        4
+#define SOLARIS_POLL_PRI        5
+#define SOLARIS_POLL_HUP        6
+    
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/socksys.c linux/arch/sparc64/solaris/socksys.c
--- v2.1.52/linux/arch/sparc64/solaris/socksys.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/socksys.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,127 @@
+/* $Id: socksys.c,v 1.1 1997/09/03 12:29:27 jj Exp $
+ * socksys.c: /dev/inet/ stuff for Solaris emulation.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+#include <asm/termios.h>
+
+#include "conv.h"
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, 
+	unsigned long arg);
+	
+static int af_inet_protocols[] = {
+IPPROTO_ICMP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_TCP,
+IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_RAW,
+0, 0, 0, 0, 0, 0,
+};
+
+static struct file_operations socksys_file_ops = {
+	NULL,		/* lseek */
+	NULL,		/* read */
+	NULL,		/* write */
+	NULL,		/* readdir */
+	NULL,		/* poll */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	NULL,		/* open */
+	NULL,		/* release */
+};
+
+static int socksys_open(struct inode * inode, struct file * filp)
+{
+	int family, type, protocol, fd;
+	int (*sys_socket)(int,int,int) =
+		(int (*)(int,int,int))SUNOS(97);
+	
+	family = ((MINOR(inode->i_rdev) >> 4) & 0xf);
+	switch (family) {
+	case AF_UNIX:
+		type = SOCK_STREAM;
+		protocol = 0;
+		break;
+	case AF_INET:
+		protocol = af_inet_protocols[MINOR(inode->i_rdev) & 0xf];
+		switch (protocol) {
+		case IPPROTO_TCP: type = SOCK_STREAM; break;
+		case IPPROTO_UDP: type = SOCK_DGRAM; break;
+		default: type = SOCK_RAW; break;
+		}
+		break;
+	default:
+		type = SOCK_RAW;
+		protocol = 0;
+		break;
+	}
+	fd = sys_socket(family, type, protocol);
+	if (fd < 0) return fd;
+	return 0;
+}
+
+static int socksys_release(struct inode * inode, struct file * filp)
+{
+	return 0;
+}
+
+static unsigned int socksys_poll(struct file * filp, poll_table * wait)
+{
+	return 0;
+}
+	
+static struct file_operations socksys_fops = {
+	NULL,		/* lseek */
+	NULL,		/* read */
+	NULL,		/* write */
+	NULL,		/* readdir */
+	NULL,		/* poll */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	socksys_open,	/* open */
+	socksys_release,/* release */
+};
+
+__initfunc(int
+init_socksys(void))
+{
+	int ret;
+	int (*sys_socket)(int,int,int) =
+		(int (*)(int,int,int))SUNOS(97);
+	int (*sys_close)(unsigned int) = 
+		(int (*)(unsigned int))SYS(close);
+	
+	ret = register_chrdev (30, "socksys", &socksys_fops);
+	if (ret < 0) {
+		printk ("Couldn't register socksys character device\n");
+		return ret;
+	}
+	ret = sys_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (ret < 0) {
+		printk ("Couldn't create socket\n");
+		return ret;
+	}
+	socksys_file_ops = *current->files->fd[ret]->f_op;
+	sys_close(ret);
+	socksys_file_ops.poll = socksys_poll;
+	socksys_file_ops.release = socksys_release;
+	return 0;
+}
+
+void
+cleanup_socksys(void)
+{
+	if (unregister_chrdev (30, "socksys"))
+		printk ("Couldn't unregister socksys character device\n");
+}
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/solaris/systbl.S linux/arch/sparc64/solaris/systbl.S
--- v2.1.52/linux/arch/sparc64/solaris/systbl.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/solaris/systbl.S	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,289 @@
+/* $Id: systbl.S,v 1.5 1997/09/04 15:46:24 jj Exp $
+ * systbl.S: System call entry point table for Solaris compatibility.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ */
+
+#include <asm/unistd.h>
+
+/* Fall back to sys_call_table32 entry */
+#define CHAIN(name)	__NR_##name
+
+/* Pass pt_regs pointer as first argument */
+#define REGS(name)	name+1
+
+/* Hack till all be implemented */
+#define solaris_getmsg		solaris_unimplemented
+#define solaris_getpmsg		solaris_unimplemented
+#define solaris_hrtsys		solaris_unimplemented
+#define solaris_msgsys		solaris_unimplemented
+#define solaris_putmsg		solaris_unimplemented
+#define solaris_putpmsg		solaris_unimplemented
+#define solaris_semsys		solaris_unimplemented
+
+        .data
+        .align  1024
+	.globl		solaris_sys_table
+solaris_sys_table:
+	.word solaris_unimplemented	/* nosys		0	*/
+	.word CHAIN(exit)		/* exit		d	1	*/
+	.word CHAIN(fork)		/* fork			2	*/
+	.word CHAIN(read)		/* read		dpd	3	*/
+	.word CHAIN(write)		/* write	dpd	4	*/
+	.word solaris_open		/* open		soo	5	*/
+	.word CHAIN(close)		/* close	d	6	*/
+	.word solaris_wait		/* wait		xxx	7	*/
+	.word CHAIN(creat)		/* creat	so	8	*/
+	.word CHAIN(link)		/* link		ss	9	*/
+	.word CHAIN(unlink)		/* unlink	s	10	*/
+	.word solaris_unimplemented	/* exec		sxx	11	*/
+	.word CHAIN(chdir)		/* chdir	s	12	*/
+	.word CHAIN(time)		/* time			13	*/
+	.word solaris_mknod		/* mknod	sox	14	*/
+	.word CHAIN(chmod)		/* chmod	so	15	*/ 
+	.word solaris_chown		/* chown	sdd	16	*/
+	.word solaris_brk		/* brk/break	x	17	*/
+	.word solaris_stat		/* stat		sp	18	*/
+	.word CHAIN(lseek)		/* seek/lseek	ddd	19	*/
+	.word solaris_getpid		/* getpid		20	*/
+	.word solaris_unimplemented	/* mount		21	*/
+	.word CHAIN(umount)		/* umount	s	22	*/
+	.word CHAIN(setuid)		/* setuid	d	23	*/
+	.word solaris_getuid		/* getuid		24	*/
+	.word CHAIN(stime)		/* stime	d	25	*/
+#if 0
+	.word solaris_ptrace		/* ptrace	xdxx	26	*/
+#else
+	.word CHAIN(ptrace)		/* ptrace	xdxx	26	*/
+#endif
+	.word CHAIN(alarm)		/* alarm	d	27	*/
+	.word solaris_fstat		/* fstat	dp	28	*/
+	.word CHAIN(pause)		/* pause		29	*/
+	.word CHAIN(utime)		/* utime	xx	30	*/
+	.word solaris_unimplemented	/* stty			31	*/
+	.word solaris_unimplemented	/* gtty			32	*/
+	.word solaris_access		/* access	so	33	*/
+	.word CHAIN(nice)		/* nice		d	34	*/
+	.word solaris_statfs		/* statfs	spdd	35	*/
+	.word CHAIN(sync)		/* sync			36	*/
+	.word solaris_kill		/* kill		dd	37	*/
+	.word solaris_fstatfs		/* fstatfs	dpdd	38	*/
+	.word solaris_procids		/* pgrpsys	ddd	39	*/
+	.word solaris_unimplemented	/* xenix		40	*/
+	.word CHAIN(dup)		/* dup		d	41	*/
+	.word CHAIN(pipe)		/* pipe			42	*/
+	.word CHAIN(times)		/* times	p	43	*/
+	.word CHAIN(profil)		/* prof		xxxx	44	*/
+	.word solaris_unimplemented	/* lock/plock		45	*/
+	.word CHAIN(setgid)		/* setgid	d	46	*/
+	.word solaris_getgid		/* getgid		47	*/
+	.word solaris_sigfunc		/* sigfunc	xx	48	*/
+	.word REGS(solaris_msgsys)	/* msgsys	dxddd	49	*/
+	.word solaris_unimplemented	/* syssun/3b		50	*/
+	.word CHAIN(acct)		/* acct/sysacct	x	51	*/
+	.word solaris_shmsys		/* shmsys	ddxo	52	*/
+	.word REGS(solaris_semsys)	/* semsys	dddx	53	*/
+	.word solaris_ioctl		/* ioctl	dxx	54	*/
+	.word solaris_unimplemented	/* uadmin	xxx	55	*/
+	.word solaris_unimplemented	/* reserved:exch	56	*/
+	.word solaris_utssys		/* utssys	x	57	*/
+	.word CHAIN(fsync)		/* fsync	d	58	*/
+	.word CHAIN(execve)		/* execv	spp	59	*/
+	.word CHAIN(umask)		/* umask	o	60	*/
+	.word CHAIN(chroot)		/* chroot	s	61	*/
+	.word solaris_fcntl		/* fcntl	dxx	62	*/
+	.word solaris_ulimit		/* ulimit	xx	63	*/
+	.word solaris_unimplemented	/* ?			64	*/
+	.word solaris_unimplemented	/* ?			65	*/
+	.word solaris_unimplemented	/* ?			66	*/
+	.word solaris_unimplemented	/* ?			67	*/
+	.word solaris_unimplemented	/* ?			68	*/
+	.word solaris_unimplemented	/* ?			69	*/
+	.word solaris_unimplemented	/* advfs		70	*/
+	.word solaris_unimplemented	/* unadvfs		71	*/
+	.word solaris_unimplemented	/* rmount		72	*/
+	.word solaris_unimplemented	/* rumount		73	*/
+	.word solaris_unimplemented	/* rfstart		74	*/
+	.word solaris_unimplemented	/* ?			75	*/
+	.word solaris_unimplemented	/* rdebug		76	*/
+	.word solaris_unimplemented	/* rfstop		77	*/
+	.word solaris_unimplemented	/* rfsys		78	*/
+	.word CHAIN(rmdir)		/* rmdir	s	79	*/
+	.word CHAIN(mkdir)		/* mkdir	so	80	*/
+	.word CHAIN(getdents)		/* getdents	dxd	81	*/
+	.word solaris_unimplemented	/* libattach		82	*/
+	.word solaris_unimplemented	/* libdetach		83	*/
+	.word CHAIN(sysfs)		/* sysfs	dxx	84	*/
+	.word REGS(solaris_getmsg)	/* getmsg	dxxx	85	*/
+	.word REGS(solaris_putmsg)	/* putmsg	dxxd	86	*/
+	.word CHAIN(poll)		/* poll		xdd	87	*/
+	.word solaris_lstat		/* lstat	sp	88	*/
+	.word CHAIN(symlink)		/* symlink	ss	89	*/
+	.word CHAIN(readlink)		/* readlink	spd	90	*/
+	.word CHAIN(setgroups)		/* setgroups	dp	91	*/
+	.word CHAIN(getgroups)		/* getgroups	dp	92	*/
+	.word CHAIN(fchmod)		/* fchmod	do	93	*/
+	.word CHAIN(fchown)		/* fchown	ddd	94	*/
+	.word solaris_sigprocmask	/* sigprocmask	dxx	95	*/
+	.word solaris_sigsuspend	/* sigsuspend	x	96	*/
+	.word solaris_sigaltstack	/* sigaltstack	xx	97	*/
+	.word solaris_sigaction		/* sigaction	dxx	98	*/
+	.word solaris_sigpending	/* sigpending	dd	99	*/
+	.word REGS(solaris_context)	/* context		100	*/
+	.word solaris_unimplemented	/* evsys		101	*/
+	.word solaris_unimplemented	/* evtrapret		102	*/
+	.word solaris_statvfs		/* statvfs	sp	103	*/
+	.word solaris_fstatvfs		/* fstatvfs	dp	104	*/
+	.word solaris_unimplemented	/* unknown		105	*/
+	.word solaris_unimplemented	/* nfssys		106	*/
+	.word solaris_waitid		/* waitid	ddxd	107	*/
+	.word solaris_unimplemented	/* sigsendsys	ddd	108	*/
+	.word REGS(solaris_hrtsys)	/* hrtsys	xxx	109	*/
+	.word solaris_unimplemented	/* acancel	dxd	110	*/
+	.word solaris_unimplemented	/* async		111	*/
+	.word solaris_unimplemented	/* priocntlsys		112	*/
+	.word solaris_pathconf		/* pathconf	sd	113	*/
+	.word solaris_unimplemented	/* mincore	xdx	114	*/
+	.word solaris_mmap		/* mmap		xxxxdx	115	*/
+	.word CHAIN(mprotect)		/* mprotect	xdx	116	*/
+	.word CHAIN(munmap)		/* munmap	xd	117	*/
+	.word solaris_fpathconf		/* fpathconf	dd	118	*/
+	.word CHAIN(fork)		/* fork			119	*/
+	.word solaris_unimplemented	/* fchdir	d	120	*/
+	.word CHAIN(readv)		/* readv	dxd	121	*/
+	.word CHAIN(writev)		/* writev	dxd	122	*/
+	.word solaris_xstat		/* xstat	dsx	123	*/
+	.word solaris_lxstat		/* lxstat	dsx	124	*/
+	.word solaris_fxstat		/* fxstat	ddx	125	*/
+	.word solaris_xmknod		/* xmknod	dsox	126	*/
+	.word solaris_unimplemented	/* syslocal	d	127	*/
+	.word solaris_unimplemented	/* setrlimit		128	*/
+	.word solaris_unimplemented	/* getrlimit		129	*/
+	.word CHAIN(chown)		/* lchown	sdd	130	*/
+	.word solaris_unimplemented	/* memcntl		131	*/
+	.word solaris_getpmsg		/* getpmsg	dxxxx	132	*/
+	.word solaris_putpmsg		/* putpmsg	dxxdd	133	*/
+	.word CHAIN(rename)		/* rename	ss	134	*/
+	.word solaris_utsname		/* uname	x	135	*/
+	.word solaris_unimplemented	/* setegid		136	*/
+	.word solaris_sysconf		/* sysconfig	d	137	*/
+	.word solaris_unimplemented	/* adjtime		138	*/
+	.word solaris_sysinfo		/* systeminfo	dsd	139	*/
+	.word solaris_unimplemented	/* ?			140	*/
+	.word solaris_unimplemented	/* seteuid		141	*/
+	.word solaris_unimplemented	/* ?			142	*/
+	.word solaris_unimplemented	/* ?			143	*/
+	.word solaris_unimplemented	/* secsys	dx	144	*/
+	.word solaris_unimplemented	/* filepriv	sdxd	145	*/
+	.word solaris_unimplemented	/* procpriv	dxd	146	*/
+	.word solaris_unimplemented	/* devstat	sdx	147	*/
+	.word solaris_unimplemented	/* aclipc	ddddx	148	*/
+	.word solaris_unimplemented	/* fdevstat	ddx	149	*/
+	.word solaris_unimplemented	/* flvlfile	ddx	150	*/
+	.word solaris_unimplemented	/* lvlfile	sdx	151	*/
+	.word solaris_unimplemented	/* ?			152	*/
+	.word solaris_unimplemented	/* fchroot	d	153	*/
+	.word solaris_unimplemented	/* lvlproc	dx	154	*/
+	.word solaris_unimplemented	/* ?			155	*/
+	.word CHAIN(gettimeofday)	/* gettimeofday	xx	156	*/
+	.word CHAIN(getitimer)		/* getitimer	dx	157	*/
+	.word CHAIN(setitimer)		/* setitimer	dxx	158	*/
+	.word solaris_unimplemented	/* lwp-xxx		159	*/
+	.word solaris_unimplemented	/* lwp-xxx		160	*/
+	.word solaris_unimplemented	/* lwp-xxx		161	*/
+	.word solaris_unimplemented	/* lwp-xxx		162	*/
+	.word solaris_unimplemented	/* lwp-xxx		163	*/
+	.word solaris_unimplemented	/* lwp-xxx		164	*/
+	.word solaris_unimplemented	/* lwp-xxx		165	*/
+	.word solaris_unimplemented	/* lwp-xxx		166	*/
+	.word solaris_unimplemented	/* lwp-xxx		167	*/
+	.word solaris_unimplemented	/* lwp-xxx		168	*/
+	.word solaris_unimplemented	/* lwp-xxx		169	*/
+	.word solaris_unimplemented	/* lwp-xxx		170	*/
+	.word solaris_unimplemented	/* lwp-xxx		171	*/
+	.word solaris_unimplemented	/* lwp-xxx		172	*/
+	.word solaris_pread		/* pread	dpdd	173	*/
+	.word solaris_pwrite		/* pwrite	dpdd	174	*/
+	.word REGS(solaris_llseek)	/* llseek	dLd	175	*/
+	.word solaris_unimplemented	/* lwpself		176	*/
+	.word solaris_unimplemented	/* lwpinfo		177	*/
+	.word solaris_unimplemented	/* lwpprivate		178	*/
+	.word solaris_unimplemented	/* processorbind	179	*/
+	.word solaris_unimplemented	/* processorexbind	180	*/
+	.word solaris_unimplemented	/* 			181	*/
+	.word solaris_unimplemented	/* sync_mailbox		182	*/
+	.word solaris_unimplemented	/* prepblock		183	*/
+	.word solaris_unimplemented	/* block		184	*/
+	.word solaris_acl		/* acl		sddp	185	*/
+	.word solaris_unimplemented	/* unblock		186	*/
+	.word solaris_unimplemented	/* cancelblock		187	*/
+	.word solaris_unimplemented	/* ?			188	*/
+	.word solaris_unimplemented	/* xxxxx		189	*/
+	.word solaris_unimplemented	/* xxxxxe		190	*/
+	.word solaris_unimplemented	/*			191	*/
+	.word solaris_unimplemented	/*			192	*/
+	.word solaris_unimplemented	/*			193	*/
+	.word solaris_unimplemented	/*			194	*/
+	.word solaris_unimplemented	/* 			195	*/
+	.word solaris_unimplemented	/* 			196	*/
+	.word solaris_unimplemented	/* 			197	*/
+	.word solaris_unimplemented	/* 			198	*/
+	.word CHAIN(nanosleep)		/* nanosleep	dd	199	*/
+	.word solaris_facl		/* facl		dddp	200	*/
+	.word solaris_unimplemented	/* 			201	*/
+	.word solaris_unimplemented	/* 			202	*/
+	.word solaris_unimplemented	/* 			203	*/
+	.word solaris_unimplemented	/* 			204	*/
+	.word solaris_unimplemented	/* 			205	*/
+	.word solaris_unimplemented	/* 			206	*/
+	.word solaris_unimplemented	/* 			207	*/
+	.word solaris_unimplemented	/* 			208	*/
+	.word solaris_unimplemented	/* 			209	*/
+	.word solaris_unimplemented	/* 			210	*/
+	.word solaris_unimplemented	/* 			211	*/
+	.word solaris_unimplemented	/* 			212	*/
+	.word solaris_unimplemented	/* 			213	*/
+	.word solaris_unimplemented	/* 			214	*/
+	.word solaris_unimplemented	/* 			215	*/
+	.word solaris_unimplemented	/* 			216	*/
+	.word solaris_unimplemented	/* 			217	*/
+	.word solaris_unimplemented	/* 			218	*/
+	.word solaris_unimplemented	/* 			219	*/
+	.word solaris_unimplemented	/* 			220	*/
+	.word solaris_unimplemented	/* 			221	*/
+	.word solaris_unimplemented	/* 			222	*/
+	.word solaris_unimplemented	/* 			223	*/
+	.word solaris_unimplemented	/* 			224	*/
+	.word solaris_unimplemented	/* 			225	*/
+	.word solaris_unimplemented	/* 			226	*/
+	.word solaris_unimplemented	/* 			227	*/
+	.word solaris_unimplemented	/* 			228	*/
+	.word solaris_unimplemented	/* 			229	*/
+	.word solaris_unimplemented	/* 			230	*/
+	.word solaris_unimplemented	/* 			231	*/
+	.word solaris_unimplemented	/* 			232	*/
+	.word solaris_unimplemented	/* 			233	*/
+	.word solaris_unimplemented	/* 			234	*/
+	.word solaris_unimplemented	/* 			235	*/
+	.word solaris_unimplemented	/* 			236	*/
+	.word solaris_unimplemented	/* 			237	*/
+	.word solaris_unimplemented	/* 			238	*/
+	.word solaris_unimplemented	/* 			239	*/
+	.word solaris_unimplemented	/* 			240	*/
+	.word solaris_unimplemented	/* 			241	*/
+	.word solaris_unimplemented	/* 			242	*/
+	.word solaris_unimplemented	/* 			243	*/
+	.word solaris_unimplemented	/* 			244	*/
+	.word solaris_unimplemented	/* 			245	*/
+	.word solaris_unimplemented	/* 			246	*/
+	.word solaris_unimplemented	/* 			247	*/
+	.word solaris_unimplemented	/* 			248	*/
+	.word solaris_unimplemented	/* 			249	*/
+	.word solaris_unimplemented	/* 			250	*/
+	.word solaris_unimplemented	/* 			251	*/
+	.word solaris_unimplemented	/* 			252	*/
+	.word solaris_unimplemented	/* 			253	*/
+	.word solaris_unimplemented	/* 			254	*/
+	.word solaris_unimplemented	/* 			255	*/
+
diff -u --recursive --new-file v2.1.52/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.1.52/linux/drivers/net/3c509.c	Tue May 13 22:41:08 1997
+++ linux/drivers/net/3c509.c	Thu Sep  4 13:25:28 1997
@@ -27,6 +27,7 @@
 	FIXES:
 		Alan Cox:       Removed the 'Unexpected interrupt' bug.
 		Michael Meskes:	Upgraded to Donald Becker's version 1.07.
+		Phil Blundell:  Media selection support.
 */
 
 static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
@@ -127,6 +128,7 @@
 static struct net_device_stats *el3_get_stats(struct device *dev);
 static int el3_rx(struct device *dev);
 static int el3_close(struct device *dev);
+static int el3_set_config(struct device *dev, struct ifmap *map);
 #ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev);
 #endif
@@ -136,9 +138,15 @@
 __initfunc(int el3_probe(struct device *dev))
 {
 	short lrs_state = 0xff, i;
-	ushort ioaddr, irq, if_port;
+	ushort ioaddr, irq, port;
 	short *phys_addr = (short *)dev->dev_addr;
 	static int current_tag = 0;
+	static int el3_portmap[] = { 
+	  IF_PORT_10BASET, 
+	  IF_PORT_AUI, 
+	  IF_PORT_UNKNOWN, 
+	  IF_PORT_10BASE2 
+	};
 
 	/* First check all slots of the EISA bus.  The next slot address to
 	   probe is kept in 'eisa_addr' to support multiple probe() calls. */
@@ -156,7 +164,7 @@
 			outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
 
 			irq = inw(ioaddr + WN0_IRQ) >> 12;
-			if_port = inw(ioaddr + 6)>>14;
+			port = inw(ioaddr + 6)>>14;
 			for (i = 0; i < 3; i++)
 				phys_addr[i] = htons(read_eeprom(ioaddr, i));
 
@@ -179,7 +187,7 @@
 			if ((mca_adaptor_id(i) | 1) == 0x627c) {
 				ioaddr = mca_pos_base_addr(i);
 				irq = inw(ioaddr + WN0_IRQ) >> 12;
-				if_port = inw(ioaddr + 6)>>14;
+				port = inw(ioaddr + 6)>>14;
 				for (i = 0; i < 3; i++)
 					phys_addr[i] = htons(read_eeprom(ioaddr, i));
 
@@ -245,7 +253,7 @@
 
 	{
 		unsigned short iobase = id_read_eeprom(8);
-		if_port = iobase >> 14;
+		port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
 	if (dev->irq > 1  &&  dev->irq < 16)
@@ -273,13 +281,13 @@
  found:
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
-	dev->if_port = if_port;
+	dev->if_port = el3_portmap[port];
 	request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
 
 	{
-		const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+		static const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
 		printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
-			   dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
+			   dev->name, dev->base_addr, current_tag, if_names[port]);
 	}
 
 	/* Read in the station address. */
@@ -301,12 +309,14 @@
 	dev->hard_start_xmit = &el3_start_xmit;
 	dev->stop = &el3_close;
 	dev->get_stats = &el3_get_stats;
+	dev->set_config = &el3_set_config;
 #ifdef HAVE_MULTICAST
 		dev->set_multicast_list = &set_multicast_list;
 #endif
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
+	dev->flags |= IFF_PORTSEL;
 	return 0;
 }
 
@@ -345,6 +355,59 @@
 
 
 static int
+el3_set_config(struct device *dev, struct ifmap *map)
+{
+	int ioaddr = dev->base_addr;
+    if (map->port != dev->if_port) {
+	  switch (map->port) {
+	  case IF_PORT_10BASE2:
+	  case IF_PORT_10BASET:
+	  case IF_PORT_AUI:
+		if (dev->start) {
+		  if (dev->if_port == IF_PORT_10BASE2)
+			/* Turn off thinnet power. */
+			outw(StopCoax, ioaddr + EL3_CMD);
+		  else if (dev->if_port == IF_PORT_10BASET) {
+			/* Disable link beat and jabber */
+			EL3WINDOW(4);
+			outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+			EL3WINDOW(1);
+		  }
+		}
+		printk(KERN_INFO "%s: %s port selected.\n", dev->name, 
+			   if_port_text[map->port]);
+		dev->if_port = map->port;
+		if (dev->start) {
+		  if (dev->if_port == IF_PORT_10BASE2)
+			/* Start the thinnet transceiver. We should really wait 50ms...*/
+			outw(StartCoax, ioaddr + EL3_CMD);
+		  else if (dev->if_port == IF_PORT_10BASET) {
+			/* 10baseT interface, enabled link beat and jabber check. */
+			EL3WINDOW(4);
+			outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+			EL3WINDOW(1);
+		  }
+		}
+		break;
+	  default:
+		printk(KERN_ERR "%s: %s port not supported.\n", dev->name, 
+			   if_port_text[map->port]);
+		return -EINVAL;
+	  }
+	}
+	if (map->irq != dev->irq) {
+	  printk(KERN_ERR "%s: cannot change interrupt.\n", dev->name);
+	  return -EINVAL;
+	}
+	if (map->base_addr != dev->base_addr) {
+	  printk(KERN_ERR "%s: cannot change base address.\n", dev->name);
+	  return -EINVAL;
+	}
+	return 0;
+}
+
+
+static int
 el3_open(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
@@ -376,10 +439,10 @@
 	for (i = 0; i < 6; i++)
 		outb(dev->dev_addr[i], ioaddr + i);
 
-	if (dev->if_port == 3)
+	if (dev->if_port == IF_PORT_10BASE2)
 		/* Start the thinnet transceiver. We should really wait 50ms...*/
 		outw(StartCoax, ioaddr + EL3_CMD);
-	else if (dev->if_port == 0) {
+	else if (dev->if_port == IF_PORT_10BASET) {
 		/* 10baseT interface, enabled link beat and jabber check. */
 		EL3WINDOW(4);
 		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
@@ -731,10 +794,10 @@
 	outw(RxDisable, ioaddr + EL3_CMD);
 	outw(TxDisable, ioaddr + EL3_CMD);
 
-	if (dev->if_port == 3)
+	if (dev->if_port == IF_PORT_10BASE2)
 		/* Turn off thinnet power.  Green! */
 		outw(StopCoax, ioaddr + EL3_CMD);
-	else if (dev->if_port == 0) {
+	else if (dev->if_port == IF_PORT_10BASET) {
 		/* Disable link beat and jabber, if_port may change ere next open(). */
 		EL3WINDOW(4);
 		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
diff -u --recursive --new-file v2.1.52/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.52/linux/drivers/net/Config.in	Wed Sep  3 20:52:42 1997
+++ linux/drivers/net/Config.in	Thu Sep  4 13:25:28 1997
@@ -6,15 +6,25 @@
 if [ "$CONFIG_ARCNET" != "n" ]; then
   bool '  Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH
   bool '  Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051
+  dep_tristate '  ARCnet COM90xx (normal) chipset driver' CONFIG_ARCNET_COM90xx $CONFIG_ARCNET
+  dep_tristate '  ARCnet COM90xx (IO mapped) chipset driver' CONFIG_ARCNET_COM90xxIO $CONFIG_ARCNET
+  dep_tristate '  ARCnet COM90xx (RIM I) chipset driver' CONFIG_ARCNET_RIM_I $CONFIG_ARCNET
+  dep_tristate '  ARCnet COM20020 chipset driver' CONFIG_ARCNET_COM20020 $CONFIG_ARCNET
 fi
+
 tristate 'Dummy net driver support' CONFIG_DUMMY
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
-
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Ethertap network tap' CONFIG_ETHERTAP
+fi
 #
 #	Ethernet
 #
 bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
 if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
+  if [ "$CONFIG_PMAC" = "y" ]; then
+    bool 'MACE (Power Mac ethernet) support' CONFIG_MACE
+  fi
   if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then
     bool 'MIPS JAZZ onboard SONIC ethernet support' CONFIG_MIPS_JAZZ_SONIC
   fi
@@ -43,6 +53,14 @@
     fi
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
+  bool 'Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL
+  if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'NI5010 support' CONFIG_NI5010
+    fi
+    tristate 'NI5210 support' CONFIG_NI52
+    tristate 'NI6510 support' CONFIG_NI65
+  fi
   bool 'Other ISA cards' CONFIG_NET_ISA
   if [ "$CONFIG_NET_ISA" = "y" ]; then
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -63,8 +81,6 @@
       tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
     fi
     tristate 'NE2000/NE1000 support' CONFIG_NE2000
-    tristate 'NI5210 support' CONFIG_NI52
-    tristate 'NI6510 support' CONFIG_NI65
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       bool 'SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005
     fi
@@ -152,14 +168,11 @@
       bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
       bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
       bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
-# if [ -f doesn't work with xconfig. Enable it again when the drivers are really
-# included.
-#      if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then
-#        bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
-#      fi
-#      if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then
-#        bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
-#      fi
+    fi
+    tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+    if [ "$CONFIG_HFMODEM" != "n" ]; then
+      bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+      bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
     fi
     tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
     if [ "$CONFIG_HFMODEM" != "n" ]; then
@@ -210,4 +223,3 @@
     dep_tristate 'X.25 async driver' CONFIG_X25_ASY $CONFIG_LAPB
 fi
 fi
-
diff -u --recursive --new-file v2.1.52/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.52/linux/drivers/net/Makefile	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/Makefile	Thu Sep  4 13:25:28 1997
@@ -53,6 +53,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ETHERTAP),y)
+L_OBJS += ethertap.o
+else
+  ifeq ($(CONFIG_ETHERTAP),m)
+  M_OBJS += ethertap.o
+  endif
+endif
+
 ifeq ($(CONFIG_SHAPER),y)
 L_OBJS += shaper.o
 else
@@ -431,6 +439,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_NI5010),y)
+L_OBJS += ni5010.o
+else
+  ifeq ($(CONFIG_NI5010),m)
+  M_OBJS += ni5010.o
+  endif
+endif
+
 ifeq ($(CONFIG_NI52),y)
 L_OBJS += ni52.o
 else
@@ -482,10 +498,42 @@
 endif
 
 ifeq ($(CONFIG_ARCNET),y)
-L_OBJS += arcnet.o
+LX_OBJS += arcnet.o
 else
   ifeq ($(CONFIG_ARCNET),m)
-  M_OBJS += arcnet.o
+  MX_OBJS += arcnet.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM90xx),y)
+L_OBJS += com90xx.o
+else
+  ifeq ($(CONFIG_ARCNET_COM90xx),m)
+  M_OBJS += com90xx.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM90xxIO),y)
+L_OBJS += com90io.o
+else
+  ifeq ($(CONFIG_ARCNET_COM90xxIO),m)
+  M_OBJS += com90io.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARCNET_RIM_I),y)
+L_OBJS += arc-rimi.o
+else
+  ifeq ($(CONFIG_ARCNET_RIM_I),m)
+  M_OBJS += arc-rimi.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARCNET_COM20020),y)
+L_OBJS += com20020.o
+else
+  ifeq ($(CONFIG_ARCNET_COM20020),m)
+  M_OBJS += com20020.o
   endif
 endif
 
@@ -700,6 +748,10 @@
   ALL_SUB_DIRS += soundmodem
   MOD_SUB_DIRS += soundmodem
   endif
+endif
+
+ifeq ($(CONFIG_MACE),y)
+L_OBJS += mace.o
 endif
 
 # If anything built-in uses the hdlcdrv, then build it into the kernel also.
diff -u --recursive --new-file v2.1.52/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.52/linux/drivers/net/Space.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/Space.c	Thu Sep  4 13:25:28 1997
@@ -32,6 +32,8 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 
+#include <net/netlink.h>
+
 #define	NEXT_DEV	NULL
 
 
@@ -63,15 +65,14 @@
 extern int ewrk3_probe(struct device *);
 extern int de4x5_probe(struct device *);
 extern int el1_probe(struct device *);
-#if	defined(CONFIG_WAVELAN)
 extern int wavelan_probe(struct device *);
-#endif	/* defined(CONFIG_WAVELAN) */
 extern int el16_probe(struct device *);
 extern int elmc_probe(struct device *);
 extern int elplus_probe(struct device *);
 extern int ac3200_probe(struct device *);
 extern int es_probe(struct device *);
 extern int e2100_probe(struct device *);
+extern int ni5010_probe(struct device *);
 extern int ni52_probe(struct device *);
 extern int ni65_probe(struct device *);
 extern int sonic_probe(struct device *);
@@ -90,7 +91,9 @@
 extern int ariadne_probe(struct device *);
 extern int hydra_probe(struct device *);
 extern int tlan_probe(struct device *);
+extern int mace_probe(struct device *);
 extern int cs89x0_probe(struct device *dev);
+extern int ethertap_probe(struct device *dev);
 
 /* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
@@ -105,6 +108,12 @@
 	return 1;		/* ENXIO */
 
     if (1
+#ifdef CONFIG_HAPPYMEAL
+	/* Please keep this one first, we'd like the on-board ethernet
+	 * to be probed first before other PCI cards on Ultra/PCI.  -DaveM
+	 */
+	&& happy_meal_probe(dev)
+#endif
 #ifdef CONFIG_DGRS
 	&& dgrs_probe(dev)
 #endif
@@ -222,6 +231,9 @@
 #if defined(CONFIG_SK_G16)
 	&& SK_init(dev)
 #endif
+#ifdef CONFIG_NI5010
+        && ni5010_probe(dev)
+#endif
 #ifdef CONFIG_NI52
 	&& ni52_probe(dev)
 #endif
@@ -246,15 +258,15 @@
 #ifdef CONFIG_TLAN
 	&& tlan_probe(dev)
 #endif
-#ifdef CONFIG_HAPPYMEAL
-	&& happy_meal_probe(dev)
-#endif
 #ifdef CONFIG_SUNQE
 	&& qec_probe(dev)
 #endif
 #ifdef CONFIG_MYRI_SBUS
 	&& myri_sbus_probe(dev)
 #endif
+#ifdef CONFIG_MACE
+	&& mace_probe(dev)
+#endif
 #ifdef CONFIG_SGISEEQ
 	&& sgiseeq_probe(dev)
 #endif
@@ -270,6 +282,12 @@
 
 
 
+#ifdef CONFIG_ETHERTAP
+    static struct device tap0_dev = { "tap0", 0, 0, 0, 0, NETLINK_TAPBASE, 0, 0, 0, 0, NEXT_DEV, ethertap_probe, };
+#   undef NEXT_DEV
+#   define NEXT_DEV	(&tap0_dev)
+#endif
+
 #ifdef CONFIG_SDLA
     extern int sdla_init(struct device *);
     static struct device sdla0_dev = { "sdla0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sdla_init, };
@@ -284,14 +302,6 @@
     "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ };
 #   undef NEXT_DEV
 #   define NEXT_DEV	(&atp_dev)
-#endif
-
-#ifdef CONFIG_ARCNET
-    extern int arcnet_probe(struct device *dev);
-    static struct device arcnet_dev = {
-	"arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, };
-#   undef	NEXT_DEV
-#   define	NEXT_DEV	(&arcnet_dev)
 #endif
 
 #if defined(CONFIG_LTPC)
diff -u --recursive --new-file v2.1.52/linux/drivers/net/arc-rimi.c linux/drivers/net/arc-rimi.c
--- v2.1.52/linux/drivers/net/arc-rimi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/arc-rimi.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,907 @@
+/* arc-rimi.c:
+        Derived from the original arcnet.c,
+        Written 1994-1996 by Avery Pennarun,
+	which was in turn derived from skeleton.c by Donald Becker.
+
+	Contact Avery at: apenwarr@bond.net or
+	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+	**********************
+
+	The original copyright of skeleton.c was as follows:
+
+	skeleton.c Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the
+        Director, National Security Agency.  This software may only be used
+        and distributed according to the terms of the GNU Public License as
+        modified by SRC, incorporated herein by reference.
+
+	**********************
+
+	For more details, see drivers/net/arcnet.c
+
+	**********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.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/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+/**************************************************************************/
+
+/* On a fast computer, the buffer copy from memory to the ARCnet card during
+ * a transmit can hog the bus just a little too long.  SLOW_XMIT_COPY
+ * replaces the fast memcpy() with a slower for() loop that seems to solve
+ * my problems with ftape.
+ *
+ * Probably a better solution would be to use memcpy_toio (more portable
+ * anyway) and modify that routine to support REALLY_SLOW_IO-style
+ * defines; ARCnet probably is not the only driver that can screw up an
+ * ftape DMA transfer.
+ *
+ * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
+ * would like to sacrifice a little bit of network speed to reduce tape
+ * write retries or some related problem.
+ */
+#undef SLOW_XMIT_COPY
+
+
+
+
+/* External functions from arcnet.c */
+
+
+
+#if ARCNET_DEBUG_MAX & D_SKB
+extern void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+			    char *desc);
+#else
+#define arcnet_dump_skb(dev,skb,desc) ;
+#endif
+
+#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
+extern void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+			       char *desc);
+#else
+#define arcnet_dump_packet(dev,buffer,ext,desc) ;
+#endif
+
+extern void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+extern void arcnet_makename(char *device);
+extern void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+extern void arcnet_setup(struct device *dev);
+extern int arcnet_go_tx(struct device *dev,int enable_irq);
+extern void arcnetA_continue_tx(struct device *dev);
+extern void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+extern void arcnet_use_count(int open);
+
+
+
+/* Internal function declarations */
+static int arcrimi_probe(struct device *dev);
+static void arcrimi_rx(struct device *dev,int recbuf);
+static int arcrimi_found(struct device *dev,int ioaddr,int airq,u_long shmem); 
+static void arcrimi_inthandler (struct device *dev);
+static int arcrimi_reset (struct device *dev, int reset_delay);
+static void arcrimi_setmask (struct device *dev, u_char mask);
+static void arcrimi_command (struct device *dev, u_char command);
+static u_char arcrimi_status (struct device *dev);
+static void arcrimi_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+       	       char *data,int length,int daddr,int exceptA, int offset);
+static  void arcrimi_openclose(int open);
+
+
+/* Module parameters */
+
+
+#ifdef MODULE
+static int shmem=0x0;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0;	/* or use the insmod io= irq= shmem= options */
+static char *device;	/* use eg. device="arc1" to change name */
+static int node=0;	/* you must specify the node ID for RIM I cards */
+
+MODULE_PARM(shmem, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+MODULE_PARM (node, "i");
+#else
+__initfunc(void arcrimi_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+/* Handy defines for ARCnet specific stuff */
+
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0)	/* writable */
+#define _STATUS  (ioaddr+0)	/* readable */
+#define _COMMAND (ioaddr+1)	/* writable, returns random vals on read (?) */
+#define _RESET  (ioaddr+8)	/* software reset (on read) */
+#define _MEMDATA  (ioaddr+12)            /* Data port for IO-mapped memory */
+#define _ADDR_HI  (ioaddr+15)            /* Control registers for said */
+#define _ADDR_LO  (ioaddr+14)
+#define _CONFIG  (ioaddr+2)      /* Configuration register */
+
+#define RDDATAflag      0x00     /* Next access is a read/~write */
+
+
+
+
+#define ARCSTATUS	readb(_STATUS)
+#define ACOMMAND(cmd) writeb((cmd),_COMMAND)
+#define ARCRESET	writeb(TESTvalue,ioaddr-0x800)	/* fake reset */
+#define AINTMASK(msk)	writeb((msk),_INTMASK)
+#define SETCONF	writeb(lp->config,_CONFIG)
+
+
+static const char *version =
+"arc-rimi.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+
+
+/****************************************************************************
+ *                                                                          *
+ * Probe and initialization                                                 *
+ *                                                                          *
+ ****************************************************************************/
+
+
+/* We cannot probe for a RIM I card; one reason is I don't know how to reset
+ * them.  In fact, we can't even get their node ID automatically.  So, we
+ * need to be passed a specific shmem address, IRQ, and node ID.
+ */
+__initfunc(int arcrimi_probe(struct device *dev))
+{
+  BUGLVL(D_NORMAL) printk(version);
+  BUGMSG(D_NORMAL,"Given: node %02Xh, shmem %lXh, irq %d\n",
+	 dev->dev_addr[0],dev->mem_start,dev->irq);
+  
+  if (dev->mem_start<=0 || dev->irq<=0)
+    {
+      BUGMSG(D_NORMAL,"No autoprobe for RIM I; you "
+	     "must specify the shmem and irq!\n");
+      return -ENODEV;
+    }
+  
+  if (dev->dev_addr[0]==0)
+    {
+      BUGMSG(D_NORMAL,"You need to specify your card's station "
+	     "ID!\n");
+      return -ENODEV;
+    }
+  
+  return arcrimi_found(dev,dev->dev_addr[0],dev->irq,dev->mem_start);
+}
+
+
+
+/* Set up the struct device associated with this card.  Called after
+ * probing succeeds.
+ */
+__initfunc(int arcrimi_found(struct device *dev,int node,int airq, u_long shmem))
+{
+  struct arcnet_local *lp;
+  u_long first_mirror,last_mirror;
+  int mirror_size;
+  
+  /* reserve the irq */
+  if (request_irq(airq,&arcnet_interrupt,0,"arcnet (RIM I)",NULL))
+    {
+      BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+      return -ENODEV;
+    }
+  irq2dev_map[airq]=dev;
+  dev->irq=airq;
+  
+  dev->base_addr=0;
+  writeb(TESTvalue,shmem);
+  writeb(node,shmem+1);	/* actually the node ID */
+  
+  /* find the real shared memory start/end points, including mirrors */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+  
+  /* guess the actual size of one "memory mirror" - the number of
+   * bytes between copies of the shared memory.  On most cards, it's
+   * 2k (or there are no mirrors at all) but on some, it's 4k.
+   */
+  mirror_size=MIRROR_SIZE;
+  if (readb(shmem)==TESTvalue
+      && readb(shmem-mirror_size)!=TESTvalue
+      && readb(shmem-2*mirror_size)==TESTvalue)
+    mirror_size*=2;
+  
+  first_mirror=last_mirror=shmem;
+  while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
+  first_mirror+=mirror_size;
+  
+  while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
+  last_mirror-=mirror_size;
+  
+  dev->mem_start=first_mirror;
+  dev->mem_end=last_mirror+MIRROR_SIZE-1;
+  dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
+  dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
+  
+  /* Initialize the rest of the device structure. */
+  
+  dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+  if (dev->priv == NULL)
+    {
+      irq2dev_map[airq] = NULL;
+      free_irq(airq,NULL);
+      return -ENOMEM;
+    }
+  memset(dev->priv,0,sizeof(struct arcnet_local));
+  lp=(struct arcnet_local *)(dev->priv);
+  lp->card_type = ARC_RIM_I;
+  lp->card_type_str = "RIM I";
+  lp->arcnet_reset=arcrimi_reset;
+  lp->asetmask=arcrimi_setmask;
+  lp->astatus=arcrimi_status;
+  lp->acommand=arcrimi_command;
+  lp->openclose_device=arcrimi_openclose;
+  lp->prepare_tx=arcrimi_prepare_tx;
+  lp->inthandler=arcrimi_inthandler;
+  
+  /* Fill in the fields of the device structure with generic
+   * values.
+   */
+  arcnet_setup(dev);
+  
+  /* And now fill particular fields with arcnet values */
+  dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+  dev->hard_header_len=sizeof(struct ClientData);
+  lp->sequence=1;
+  lp->recbuf=0;
+  
+  BUGMSG(D_DURING,"ClientData header size is %d.\n",
+	 sizeof(struct ClientData));
+  BUGMSG(D_DURING,"HardHeader size is %d.\n",
+	 sizeof(struct archdr));
+  
+  /* get and check the station ID from offset 1 in shmem */
+  lp->stationid = readb(first_mirror+1);
+  
+  if (lp->stationid==0)
+    BUGMSG(D_NORMAL,"WARNING!  Station address 00 is reserved "
+	   "for broadcasts!\n");
+  else if (lp->stationid==255)
+    BUGMSG(D_NORMAL,"WARNING!  Station address FF may confuse "
+	   "DOS networking programs!\n");
+  dev->dev_addr[0]=lp->stationid;
+  
+  BUGMSG(D_NORMAL,"ARCnet RIM I: station %02Xh found at IRQ %d, "
+	 "ShMem %lXh (%ld*%d bytes).\n",
+	 lp->stationid,
+	 dev->irq, dev->mem_start,
+	 (dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
+  
+  return 0;
+}
+
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arcrimi_reset(struct device *dev,int reset_delay)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  short ioaddr=dev->mem_start + 0x800;
+  int recbuf=lp->recbuf;
+  
+  if (reset_delay==3)
+    {
+      ARCRESET;
+      return 0;
+    }
+  
+  /* no IRQ's, please! */
+  lp->intmask=0;
+  SETMASK;
+  
+  BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+	 dev->name,ARCSTATUS);
+  
+  ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+  ACOMMAND(CFLAGScmd|CONFIGclear);
+  
+  /* clear out status variables */
+  recbuf=lp->recbuf=0;
+  lp->txbuf=2;
+  
+  /* enable extended (512-byte) packets */
+  ACOMMAND(CONFIGcmd|EXTconf);
+  
+#ifndef SLOW_XMIT_COPY
+  /* clean out all the memory to make debugging make more sense :) */
+  BUGLVL(D_DURING)
+    memset_io(dev->mem_start,0x42,2048);
+#endif
+  
+  /* and enable receive of our first packet to the first buffer */
+  EnableReceiver();
+  
+  /* re-enable interrupts */
+  lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+  lp->intmask|=RECONflag;
+#endif
+  SETMASK;
+  
+  /* done!  return success. */
+  return 0;
+}
+
+
+static void arcrimi_openclose(int open)
+{
+  if (open)
+    MOD_INC_USE_COUNT;
+  else
+    MOD_DEC_USE_COUNT;
+}
+
+static void arcrimi_setmask(struct device *dev, u_char mask)
+{
+  int ioaddr=dev->mem_start+0x800;
+  
+  AINTMASK(mask);
+}
+
+static u_char arcrimi_status(struct device *dev)
+{
+  int ioaddr=dev->mem_start+0x800;
+  
+  return ARCSTATUS;
+}
+
+static void arcrimi_command(struct device *dev, u_char cmd)
+{
+  int ioaddr=dev->mem_start+0x800;
+  
+  ACOMMAND(cmd);
+}
+
+
+
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arcrimi_inthandler(struct device *dev)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  int ioaddr=dev->mem_start+0x800, status, boguscount = 3, didsomething;
+  
+  AINTMASK(0);
+  
+  BUGMSG(D_DURING,"in arcrimi_inthandler (status=%Xh, intmask=%Xh)\n",
+	 ARCSTATUS,lp->intmask);
+  
+  do
+    {
+      status = ARCSTATUS;
+      didsomething=0;
+      
+      
+      /* RESET flag was enabled - card is resetting and if RX
+       * is disabled, it's NOT because we just got a packet.
+       */
+      if (status & RESETflag)
+	{
+	  BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+		 status);
+	  arcrimi_reset(dev,0);
+	  
+	  /* all other flag values are just garbage */
+	  break;
+	}
+      
+      
+      /* RX is inhibited - we must have received something. */
+      if (status & lp->intmask & NORXflag)
+	{
+	  int recbuf=lp->recbuf=!lp->recbuf;
+	  
+	  BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+		 status);
+	  
+	  /* enable receive of our next packet */
+	  EnableReceiver();
+	  
+	  /* Got a packet. */
+	  arcrimi_rx(dev,!recbuf);
+	  
+	  didsomething++;
+	}
+      
+      /* it can only be an xmit-done irq if we're xmitting :) */
+      /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+      if (status & lp->intmask & TXFREEflag)
+	{
+	  struct Outgoing *out=&(lp->outgoing);
+	  int was_sending=lp->sending;
+	  
+	  lp->intmask &= ~TXFREEflag;
+	  
+	  lp->in_txhandler++;
+	  if (was_sending) lp->sending--;
+	  
+	  BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+		 status,out->numsegs,out->segnum,out->skb);
+	  
+	  if (was_sending && !(status&TXACKflag))
+	    {
+	      if (lp->lasttrans_dest != 0)
+		{
+		  BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+			 status,lp->lasttrans_dest);
+		  lp->stats.tx_errors++;
+		  lp->stats.tx_carrier_errors++;
+		}
+	      else
+		{
+		  BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+			 status,
+			 lp->lasttrans_dest);
+		}
+	    }
+	  
+	  /* send packet if there is one */
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	  
+	  if (lp->intx)
+	    {
+	      BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+		     ARCSTATUS,lp->intx);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  if (!lp->outgoing.skb)
+	    {
+	      BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  /* if more than one segment, and not all segments
+	   * are done, then continue xmit.
+	   */
+	  if (out->segnum<out->numsegs)
+	    arcnetA_continue_tx(dev);
+	  arcnet_go_tx(dev,0);
+	  
+	  /* if segnum==numsegs, the transmission is finished;
+	   * free the skb.
+	   */
+	  if (out->segnum>=out->numsegs)
+	    {
+	      /* transmit completed */
+	      out->segnum++;
+	      if (out->skb)
+		{
+		  lp->stats.tx_bytes += out->skb->len;
+		  dev_kfree_skb(out->skb,FREE_WRITE);
+		}
+	      out->skb=NULL;
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	    }
+	  didsomething++;
+	  
+	  lp->in_txhandler--;
+	}
+      else if (lp->txready && !lp->sending && !lp->intx)
+	{
+	  BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+		 status);
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	}
+      
+#ifdef DETECT_RECONFIGS
+      if (status & (lp->intmask) & RECONflag)
+	{
+	  ACOMMAND(CFLAGScmd|CONFIGclear);
+	  lp->stats.tx_carrier_errors++;
+	  
+#ifdef SHOW_RECONFIGS
+	  BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
+		 status);
+	  
+	  
+#endif /* SHOW_RECONFIGS */
+	  
+#ifdef RECON_THRESHOLD
+	  /* is the RECON info empty or old? */
+	  if (!lp->first_recon || !lp->last_recon ||
+	      jiffies-lp->last_recon > HZ*10)
+	    {
+	      if (lp->network_down)
+		BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+	      lp->first_recon=lp->last_recon=jiffies;
+	      lp->num_recons=lp->network_down=0;
+	      
+	      BUGMSG(D_DURING,"recon: clearing counters.\n");
+	    }
+	  else /* add to current RECON counter */
+	    {
+	      lp->last_recon=jiffies;
+	      lp->num_recons++;
+	      
+	      BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+		     lp->num_recons,
+		     (lp->last_recon-lp->first_recon)/HZ,
+		     lp->network_down);
+	      
+	      /* if network is marked up;
+	       * and first_recon and last_recon are 60+ sec
+	       *   apart;
+	       * and the average no. of recons counted is
+	       *   > RECON_THRESHOLD/min;
+	       * then print a warning message.
+	       */
+	      if (!lp->network_down
+		  && (lp->last_recon-lp->first_recon)<=HZ*60
+		  && lp->num_recons >= RECON_THRESHOLD)
+		{
+		  lp->network_down=1;
+		  BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+		}
+	      else if (!lp->network_down
+		       && lp->last_recon-lp->first_recon > HZ*60)
+		{
+		  /* reset counters if we've gone for
+		   * over a minute.
+		   */
+		  lp->first_recon=lp->last_recon;
+		  lp->num_recons=1;
+		}
+	    }
+	}
+      else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+	{
+	  if (lp->network_down)
+	    BUGMSG(D_NORMAL,"cabling restored?\n");
+	  lp->first_recon=lp->last_recon=0;
+	  lp->num_recons=lp->network_down=0;
+	  
+	  BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+	}
+#endif /* DETECT_RECONFIGS */
+    } while (--boguscount && didsomething);
+  
+  BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+	 ARCSTATUS,boguscount);
+  BUGMSG(D_DURING,"\n");
+  
+  SETMASK;	/* put back interrupt mask */
+  
+}
+
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */ 
+
+static void
+arcrimi_rx(struct device *dev,int recbuf)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int ioaddr=dev->mem_start+0x800;
+  union ArcPacket *arcpacket=
+    (union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
+  u_char *arcsoft;
+  short length,offset;
+  u_char daddr,saddr;
+  
+  lp->stats.rx_packets++;
+  
+  saddr=arcpacket->hardheader.source;
+  
+  /* if source is 0, it's a "used" packet! */
+  if (saddr==0)
+    {
+      BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+	     ARCSTATUS);
+      lp->stats.rx_errors++;
+      return;
+    }
+  /* Set source address to zero to mark it as old */
+  
+  arcpacket->hardheader.source=0;
+  
+  daddr=arcpacket->hardheader.destination;
+  
+  if (arcpacket->hardheader.offset1) /* Normal Packet */
+    {
+      offset=arcpacket->hardheader.offset1;
+      arcsoft=&arcpacket->raw[offset];
+      length=256-offset;
+    }
+  else		/* ExtendedPacket or ExceptionPacket */
+    {
+      offset=arcpacket->hardheader.offset2;
+      arcsoft=&arcpacket->raw[offset];
+      
+      length=512-offset;
+    }
+  
+  
+  arcnet_rx(lp, arcsoft, length, saddr, daddr);
+  
+  
+  BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+  
+#ifndef SLOW_XMIT_COPY
+  /* clean out the page to make debugging make more sense :) */
+  BUGLVL(D_DURING)
+    memset((void *)arcpacket->raw,0x42,512);
+#endif
+  
+}
+
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arcrimi_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+		   char *data,int length,int daddr,int exceptA, int offset)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  union ArcPacket *arcpacket =
+    (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
+  
+#ifdef SLOW_XMIT_COPY
+  char *iptr,*iend,*optr;
+#endif
+  
+  lp->txbuf=lp->txbuf^1;	/* XOR with 1 to alternate between 2 and 3 */
+  
+  length+=hdrlen;
+  
+  BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+	 hdr,length,data);
+  
+#ifndef SLOW_XMIT_COPY
+  /* clean out the page to make debugging make more sense :) */
+  BUGLVL(D_DURING)
+    memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
+  
+  arcpacket->hardheader.destination=daddr;
+  
+  /* load packet into shared memory */
+  if (length<=MTU)	/* Normal (256-byte) Packet */
+    arcpacket->hardheader.offset1=offset=offset?offset:256-length;
+  
+  else if (length>=MinTU || offset)	/* Extended (512-byte) Packet */
+    {
+      arcpacket->hardheader.offset1=0;
+      arcpacket->hardheader.offset2=offset=offset?offset:512-length;   
+    }
+  else if (exceptA)		/* RFC1201 Exception Packet */
+    {
+      arcpacket->hardheader.offset1=0;
+      arcpacket->hardheader.offset2=offset=512-length-4;
+      
+      /* exception-specific stuff - these four bytes
+       * make the packet long enough to fit in a 512-byte
+       * frame.
+       */
+      
+      arcpacket->raw[offset+0]=hdr[0];
+      arcpacket->raw[offset+1]=0xFF; /* FF flag */
+      arcpacket->raw[offset+2]=0xFF; /* FF padding */
+      arcpacket->raw[offset+3]=0xFF; /* FF padding */
+      offset+=4;
+    }
+  else				/* "other" Exception packet */
+    {
+      /* RFC1051 - set 4 trailing bytes to 0 */
+      memset(&arcpacket->raw[508],0,4);
+      
+      /* now round up to MinTU */
+      arcpacket->hardheader.offset1=0;
+      arcpacket->hardheader.offset2=offset=512-MinTU;
+    }
+  
+  
+  /* copy the packet into ARCnet shmem
+   *  - the first bytes of ClientData header are skipped
+   */
+  
+  memcpy((u_char*)arcpacket+offset, (u_char*)hdr,hdrlen);
+#ifdef SLOW_XMIT_COPY
+  for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcpacket+offset+hdrlen;
+       iptr<iend; iptr++,optr++)
+    {
+      *optr=*iptr;
+      /*udelay(5);*/
+    }
+#else
+  memcpy((u_char*)arcpacket+offset+hdrlen, data,length-hdrlen);
+#endif
+  
+  BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+	 daddr,length);
+  
+  BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
+  
+  lp->lastload_dest=daddr;
+  lp->txready=lp->txbuf;	/* packet is ready for sending */
+}
+
+
+
+/****************************************************************************
+ *                                                                          *
+ * Kernel Loadable Module Support                                           *
+ *                                                                          *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static char devicename[9] = "";
+static struct device thiscard = {
+  devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+  0, 0, 0, 0,
+  0, 0,  /* I/O address, IRQ */
+  0, 0, 0, NULL, arcrimi_probe
+};
+
+
+int init_module(void)
+{
+  struct device *dev=&thiscard;
+  if (device)
+    strcpy(dev->name,device);
+  else arcnet_makename(dev->name);
+  
+  if (node && node != 0xff)
+    dev->dev_addr[0]=node;
+  
+  dev->irq=irq;
+  if (dev->irq==2) dev->irq=9;
+  
+  if (shmem)
+    {
+      dev->mem_start=shmem;
+      dev->mem_end=thiscard.mem_start+512*4-1;
+      dev->rmem_start=thiscard.mem_start+512*0;
+      dev->rmem_end=thiscard.mem_start+512*2-1;
+    }
+  
+  if (register_netdev(dev) != 0)
+    return -EIO;
+  arcnet_use_count(1);
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  struct device *dev=&thiscard;
+  int ioaddr=dev->mem_start;
+  
+  if (dev->start) (*dev->stop)(dev);
+  
+  /* Flush TX and disable RX */
+  if (ioaddr)
+    {
+      AINTMASK(0);		/* disable IRQ's */
+      ACOMMAND(NOTXcmd);	/* stop transmit */
+      ACOMMAND(NORXcmd);	/* disable receive */
+    }
+  
+  if (dev->irq)
+    {
+      irq2dev_map[dev->irq] = NULL;
+      free_irq(dev->irq,NULL);
+    }
+  
+  unregister_netdev(dev);
+  kfree(dev->priv);
+  dev->priv = NULL;
+  arcnet_use_count(0);
+}
+
+#else
+
+
+__initfunc(void arcrimi_setup (char *str, int *ints))
+{
+  struct device *dev;
+
+  if (arcnet_num_devs == MAX_ARCNET_DEVS)
+    {
+      printk("ARCnet RIM I: Too many ARCnet devices registered (max %d).\n",
+	     MAX_ARCNET_DEVS);
+      return;
+    }
+  
+  dev=&arcnet_devs[arcnet_num_devs];
+  
+  if (ints[0] < 3)
+    {
+      printk("ARCnet RIM I: You must give address, IRQ and node ID.\n");
+      return;
+    }
+
+  dev->init=arcrimi_probe;
+
+  switch(ints[0])
+    {
+    case 4: /* ERROR */
+      printk("ARCnet RIM I: Too many arguments.\n");
+
+    case 3: /* Node ID */
+      dev->dev_addr[0]=(u_char)ints[3];
+
+    case 2: /* IRQ */
+      dev->irq=ints[2];
+      
+    case 1: /* Mem address */
+      dev->mem_start=ints[1];
+    }
+
+  dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+  
+  if (str)
+    strncpy(dev->name, str, 9);
+  
+  arcnet_num_devs++;
+}
+
+#endif /* MODULE */
+
+
+
diff -u --recursive --new-file v2.1.52/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.52/linux/drivers/net/arcnet.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/net/arcnet.c	Thu Sep  4 13:25:28 1997
@@ -17,6 +17,26 @@
 
 	**********************
 
+	v2.91 ALPHA (97/19/08)
+	  - Add counting of octets in/out.
+
+	v2.90 ALPHA (97/08/08)
+	  - Add support for kernel command line parsing so that chipset
+	    drivers are usable when compiled in.
+
+	v2.80 ALPHA (97/08/01)
+	  - Split source into multiple files; generic arcnet support and
+	    individual chipset drivers. <dwmw2@cam.ac.uk>
+
+	v2.61 ALPHA (97/07/30)    by David Woodhouse (dwmw2@cam.ac.uk) for
+	                            Nortel (Northern Telecom).
+          - Added support for IO-mapped modes and for SMC COM20020 chipset.
+	  - Fixed (avoided) race condition in send_packet routines which was
+            discovered when the buffer copy routines got slow (?).
+          - Fixed support for device naming at load time.
+	  - Added backplane, clock and timeout options for COM20020.
+	  - Added support for promiscuous mode.
+
 	v2.60 ALPHA (96/11/23)
 	  - Added patch from Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>
 	    and Martin Mares <mj@k332.feld.cvut.cz> to make the driver work
@@ -95,30 +115,26 @@
            This is half-done in ARCnet 2.60, but still uses some
            undocumented i386 stuff.  (We shouldn't call phys_to_virt,
            for example.)
+	 - Allow use of RFC1051 or Ether devices without RFC1201.
+	 - Keep separate stats for each device.
          - Support "arpless" mode like NetBSD does, and as recommended
            by the (obsoleted) RFC1051.
-         - Some way to make RIM_I_MODE runtime switchable?  Yuck...
          - Smarter recovery from RECON-during-transmit conditions. (ie.
            retransmit immediately)
-         - Make arcnetE_send_packet use arcnet_prepare_tx for loading the
-           packet into ARCnet memory.
-         - Probe for multiple devices in one shot (trying to decide whether
-           to do it the "ugly" way or not).
          - Add support for the new 1.3.x IP header cache, and other features.
          - Debug level should be changed with a system call, not a hack to
            the "metric" flag.
+
          - What about cards with shared memory that can be "turned off?"
            (or that have none at all, like the SMC PC500longboard)
+     Does this work now, with IO_MAPPED_BUFFERS?
+
          - Autoconfigure PDI5xxPlus cards. (I now have a PDI508Plus to play
            with temporarily.)  Update: yes, the Pure Data config program
            for DOS works fine, but the PDI508Plus I have doesn't! :)
-         - Try to implement promiscuous (receive-all-packets) mode available
-           on some newer cards with COM20020 and similar chips.  I don't have
-           one, but SMC sent me the specs.
          - ATA protocol support??
          - VINES TCP/IP encapsulation?? (info needed)
 
-
 	Sources:
 	 - Crynwr arcnet.com/arcether.com packet drivers.
 	 - arcnet.c v0.00 dated 1/1/94 and apparently by
@@ -128,6 +144,7 @@
 	 - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
 	 - The official ARCnet COM9026 data sheets (!) thanks to Ken
 		Cornetet <kcornete@nyx10.cs.du.edu>
+	 - The official ARCnet COM20020 data sheets.
 	 - Information on some more obscure ARCnet controller chips, thanks
 	   to the nice people at SMC.
 	 - net/inet/eth.c (from kernel 1.1.50) for header-building info.
@@ -137,7 +154,7 @@
 */
 
 static const char *version =
- "arcnet.c: v2.60 96/11/23 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
 
 
 
@@ -164,6 +181,9 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -171,95 +191,6 @@
 
 #include <net/arp.h>
 
-/**************************************************************************/
-
-/* Define this if you have a really ancient "RIM I" ARCnet card with no I/O
- * port at all and _only_ shared memory; this option MAY work for you.  It's
- * untested, though, so good luck and write to me with any results!
- */
-#undef RIM_I_MODE
-
-/* Normally, the ARCnet device needs to be assigned a name (default arc0).
- * Ethernet devices have a function to automatically try eth0, eth1, etc
- * until a free name is found.  To name the ARCnet device using an "eth?"
- * device name, define this option.
- */
-#undef CONFIG_ARCNET_ETHNAME
-
-/* On a fast computer, the buffer copy from memory to the ARCnet card during
- * a transmit can hog the bus just a little too long.  SLOW_XMIT_COPY
- * replaces the fast memcpy() with a slower for() loop that seems to solve
- * my problems with ftape.
- *
- * Probably a better solution would be to use memcpy_toio (more portable
- * anyway) and modify that routine to support REALLY_SLOW_IO-style
- * defines; ARCnet probably is not the only driver that can screw up an
- * ftape DMA transfer.
- *
- * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
- * would like to sacrifice a little bit of network speed to reduce tape
- * write retries or some related problem.
- */
-#undef SLOW_XMIT_COPY
-
-/* The card sends the reconfiguration signal when it loses the connection to
- * the rest of its network. It is a 'Hello, is anybody there?' cry.  This
- * usually happens when a new computer on the network is powered on or when
- * the cable is broken.
- *
- * Define DETECT_RECONFIGS if you want to detect network reconfigurations.
- * Recons may be a real nuisance on a larger ARCnet network; if you are a
- * network administrator you probably would like to count them.
- * Reconfigurations will be recorded in stats.tx_carrier_errors (the last
- * field of the /proc/net/dev file).
- *
- * Define SHOW_RECONFIGS if you really want to see a log message whenever
- * a RECON occurs.
- */
-#define DETECT_RECONFIGS
-#undef SHOW_RECONFIGS
-
-/* RECON_THRESHOLD is the maximum number of RECON messages to receive within
- * one minute before printing a "cabling problem" warning.  You must have
- * DETECT_RECONFIGS enabled if you want to use this.  The default value
- * should be fine.
- *
- * After that, a "cabling restored" message will be printed on the next IRQ
- * if no RECON messages have been received for 10 seconds.
- *
- * Do not define RECON_THRESHOLD at all if you want to disable this feature.
- */
-#define RECON_THRESHOLD 30
-
-/* Define this to the minimum "timeout" value.  If a transmit takes longer
- * than TX_TIMEOUT jiffies, Linux will abort the TX and retry.  On a large
- * network, or one with heavy network traffic, this timeout may need to be
- * increased.  The larger it is, though, the longer it will be between
- * necessary transmits - don't set this too large.
- */
-#define TX_TIMEOUT 20
-
-/* Define this to speed up the autoprobe by assuming if only one io port and
- * shmem are left in the list at Stage 5, they must correspond to each
- * other.
- *
- * This is undefined by default because it might not always be true, and the
- * extra check makes the autoprobe even more careful.  Speed demons can turn
- * it on - I think it should be fine if you only have one ARCnet card
- * installed.
- *
- * If no ARCnet cards are installed, this delay never happens anyway and thus
- * the option has no effect.
- */
-#undef FAST_PROBE
-
-/* Define this to speed up "ifconfig up" by moving the card reset command
- * around.  This is a new option in 2.41 ALPHA.  If it causes problems,
- * undefine this to get the old behaviour; then send me email, because if
- * there are no problems, this option will go away very soon.
- */
-#define FAST_IFCONFIG
-
 /* Define this if you want to make it easier to use the "call trace" when
  * a kernel NULL pointer assignment occurs.  Hopefully unnecessary, most of
  * the time.  It will make all the function names (and other things) show
@@ -269,353 +200,83 @@
 
 /**************************************************************************/
 
-/* New debugging bitflags: each option can be enabled individually.
- *
- * These can be set while the driver is running by typing:
- *	ifconfig arc0 down metric 1xxx HOSTNAME
- *		where 1xxx is 1000 + the debug level you want
- *		and HOSTNAME is your hostname/ip address
- * and then resetting your routes.
- *
- * An ioctl() should be used for this instead, someday.
- *
- * Note: only debug flags included in the ARCNET_DEBUG_MAX define will
- *   actually be available.  GCC will (at least, GCC 2.7.0 will) notice
- *   lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
- *   them out.
- */
-#define D_NORMAL	1	/* important operational info		*/
-#define D_EXTRA		2	/* useful, but non-vital information	*/
-#define	D_INIT		4	/* show init/probe messages		*/
-#define D_INIT_REASONS	8	/* show reasons for discarding probes	*/
-/* debug levels below give LOTS of output during normal operation! */
-#define D_DURING	16	/* trace operations (including irq's)	*/
-#define D_TX		32	/* show tx packets			*/
-#define D_RX		64	/* show rx packets			*/
-#define D_SKB		128	/* show skb's				*/
-
-#ifndef ARCNET_DEBUG_MAX
-#define ARCNET_DEBUG_MAX (~0)		/* enable ALL debug messages	 */
-/*#define ARCNET_DEBUG_MAX (D_NORMAL|D_EXTRA|D_INIT|D_INIT_REASONS) 	 */
-/*#define ARCNET_DEBUG_MAX 0	*/	/* enable NO messages (bad idea) */
-#endif
-
-#ifndef ARCNET_DEBUG
-#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
-#endif
-int arcnet_debug = ARCNET_DEBUG;
-
-/* macros to simplify debug checking */
-#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
-#define BUGMSG2(x,msg,args...) BUGLVL(x) printk(msg, ## args)
-#define BUGMSG(x,msg,args...) BUGMSG2(x,"%s%6s: " msg, \
-            x==D_NORMAL	? KERN_WARNING : \
-      x<=D_INIT_REASONS	? KERN_INFO    : KERN_DEBUG , \
-	dev->name , ## args)
-
-/* Some useful multiprotocol macros.  The idea here is that GCC will
- * optimize away multiple tests or assignments to lp->adev.  Relying on this
- * results in the cleanest mess possible.
+/* These are now provided by the chipset driver. There's a performance 
+ * overhead in using them.
  */
-#define ADEV lp->adev
-
-#ifdef CONFIG_ARCNET_ETH
- #define EDEV lp->edev
-#else
- #define EDEV lp->adev
-#endif
-
-#ifdef CONFIG_ARCNET_1051
- #define SDEV lp->sdev
-#else
- #define SDEV lp->adev
-#endif
-
-#define TBUSY ADEV->tbusy=EDEV->tbusy=SDEV->tbusy
-#define IF_TBUSY (ADEV->tbusy||EDEV->tbusy||SDEV->tbusy)
 
-#define INTERRUPT ADEV->interrupt=EDEV->interrupt=SDEV->interrupt
-#define IF_INTERRUPT (ADEV->interrupt||EDEV->interrupt||SDEV->interrupt)
 
-#define START ADEV->start=EDEV->start=SDEV->start
+#define AINTMASK(x) ((*lp->asetmask)(dev, x))
+#define ARCSTATUS ((*lp->astatus)(dev))
+#define ACOMMAND(x) ((*lp->acommand)(dev, x))
 
 
-/* The number of low I/O ports used by the ethercard. */
-#define ARCNET_TOTAL_SIZE	16
+int arcnet_debug=ARCNET_DEBUG;
 
-/* Handy defines for ARCnet specific stuff */
-	/* COM 9026 controller chip --> ARCnet register addresses */
-#define _INTMASK (ioaddr+0)	/* writable */
-#define _STATUS  (ioaddr+0)	/* readable */
-#define _COMMAND (ioaddr+1)	/* writable, returns random vals on read (?) */
-#define _RESET  (ioaddr+8)	/* software reset (on read) */
+/* Exported function prototypes */
 
-/* RIM I (command/status is memory mapped) versus RIM III (standard I/O
- * mapped) macros.  These make things a bit cleaner.
- */
-#ifdef RIM_I_MODE
-  #define IOADDR	(dev->mem_start+0x800)
-  #define ARCSTATUS	readb(_STATUS)
-  #define ACOMMAND(cmd) writeb((cmd),_COMMAND)
-  #define ARCRESET	writeb(TESTvalue,ioaddr-0x800)	/* fake reset */
-  #define AINTMASK(msk)	writeb((msk),_INTMASK)
-  #define RELEASE_REGION(x,y)	/* nothing */
+#ifdef MODULE
+int  init_module(void);
+void cleanup_module(void);
 #else
-  #define IOADDR	(dev->base_addr)
-  #define ARCSTATUS	inb(_STATUS)
-  #define ACOMMAND(cmd) outb((cmd),_COMMAND)
-  #define AINTMASK(msk)	outb((msk),_INTMASK)
-  #define ARCRESET	inb(_RESET)
-  #define RELEASE_REGION(x,y)	release_region((x),(y))
-#endif
-
-#define SETMASK AINTMASK(lp->intmask)
-
-	/* Time needed to reset the card - in jiffies.  This works on my SMC
-	 * PC100.  I can't find a reference that tells me just how long I
-	 * should wait.
-	 */
-#define RESETtime (HZ * 3 / 10)		/* reset */
-
-	/* these are the max/min lengths of packet data. (including
-	 * ClientData header)
-	 * note: packet sizes 250, 251, 252 are impossible (God knows why)
-	 *  so exception packets become necessary.
-	 *
-	 * These numbers are compared with the length of the full packet,
-	 * including ClientData header.
-	 */
-#define MTU	253	/* normal packet max size */
-#define MinTU	257	/* extended packet min size */
-#define XMTU	508	/* extended packet max size */
-
-	/* status/interrupt mask bit fields */
-#define TXFREEflag	0x01            /* transmitter available */
-#define TXACKflag       0x02            /* transmitted msg. ackd */
-#define RECONflag       0x04            /* system reconfigured */
-#define TESTflag        0x08            /* test flag */
-#define RESETflag       0x10            /* power-on-reset */
-#define RES1flag        0x20            /* reserved - usually set by jumper */
-#define RES2flag        0x40            /* reserved - usually set by jumper */
-#define NORXflag        0x80            /* receiver inhibited */
-
-       /* in the command register, the following bits have these meanings:
-        *                0-2     command
-        *                3-4     page number (for enable rcv/xmt command)
-        *                 7      receive broadcasts
-        */
-#define NOTXcmd         0x01            /* disable transmitter */
-#define NORXcmd         0x02            /* disable receiver */
-#define TXcmd           0x03            /* enable transmitter */
-#define RXcmd           0x04            /* enable receiver */
-#define CONFIGcmd       0x05            /* define configuration */
-#define CFLAGScmd       0x06            /* clear flags */
-#define TESTcmd         0x07            /* load test flags */
-
-       /* flags for "clear flags" command */
-#define RESETclear      0x08            /* power-on-reset */
-#define CONFIGclear     0x10            /* system reconfigured */
-
-	/* flags for "load test flags" command */
-#define TESTload        0x08            /* test flag (diagnostic) */
-
-	/* byte deposited into first address of buffers on reset */
-#define TESTvalue       0321		 /* that's octal for 0xD1 :) */
-
-	/* for "enable receiver" command */
-#define RXbcasts        0x80            /* receive broadcasts */
-
-	/* flags for "define configuration" command */
-#define NORMALconf      0x00            /* 1-249 byte packets */
-#define EXTconf         0x08            /* 250-504 byte packets */
-
-	/* Starts receiving packets into recbuf.
-	 */
-#define EnableReceiver()	ACOMMAND(RXcmd|(recbuf<<3)|RXbcasts)
-
-	/* RFC1201 Protocol ID's */
-#define ARC_P_IP	212		/* 0xD4 */
-#define ARC_P_ARP	213		/* 0xD5 */
-#define ARC_P_RARP	214		/* 0xD6 */
-#define ARC_P_IPX	250		/* 0xFA */
-#define ARC_P_NOVELL_EC	236		/* 0xEC */
-
-	/* Old RFC1051 Protocol ID's */
-#define ARC_P_IP_RFC1051 240		/* 0xF0 */
-#define ARC_P_ARP_RFC1051 241		/* 0xF1 */
-
-	/* MS LanMan/WfWg protocol */
-#define ARC_P_ETHER	0xE8
-
-	/* Unsupported/indirectly supported protocols */
-#define ARC_P_DATAPOINT_BOOT	0	/* very old Datapoint equipment */
-#define ARC_P_DATAPOINT_MOUNT	1
-#define ARC_P_POWERLAN_BEACON	8	/* Probably ATA-Netbios related */
-#define ARC_P_POWERLAN_BEACON2	243
-#define ARC_P_LANSOFT	251		/* 0xFB - what is this? */
-#define ARC_P_ATALK	0xDD
-
-	/* the header required by the card itself */
-struct HardHeader
-{
-	u_char	source,		/* source ARCnet - filled in automagically */
-		destination,	/* destination ARCnet - 0 for broadcast */
-		offset1,	/* offset of ClientData (256-byte packets) */
-		offset2;	/* offset of ClientData (512-byte packets) */
-};
-
-	/* a complete ARCnet packet */
-union ArcPacket
-{
-	struct HardHeader hardheader;	/* the hardware header */
-	u_char raw[512];		/* raw packet info, incl ClientData */
-};
-
-	/* the "client data" header - RFC1201 information
-	 * notice that this screws up if it's not an even number of bytes
-	 * <sigh>
-	 */
-struct ClientData
-{
-	/* data that's NOT part of real packet - we MUST get rid of it before
-	 * actually sending!!
-	 */
-	u_char  saddr,		/* Source address - needed for IPX */
-		daddr;		/* Destination address */
-
-	/* data that IS part of real packet */
-	u_char	protocol_id,	/* ARC_P_IP, ARC_P_ARP, etc */
-		split_flag;	/* for use with split packets */
-	u_short	sequence;	/* sequence number */
-};
-#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4)
-
-
-	/* the "client data" header - RFC1051 information
-	 * this also screws up if it's not an even number of bytes
-	 * <sigh again>
-	 */
-struct S_ClientData
-{
-	/* data that's NOT part of real packet - we MUST get rid of it before
-	 * actually sending!!
-	 */
-	u_char  saddr,		/* Source address - needed for IPX */
-		daddr,		/* Destination address */
-		junk;		/* padding to make an even length */
-
-	/* data that IS part of real packet */
-	u_char	protocol_id;	/* ARC_P_IP, ARC_P_ARP, etc */
-};
-#define S_EXTRA_CLIENTDATA (sizeof(struct S_ClientData)-1)
-
-
-/* "Incoming" is information needed for each address that could be sending
- * to us.  Mostly for partially-received split packets.
- */
-struct Incoming
-{
-	struct sk_buff *skb;		/* packet data buffer             */
-	unsigned char lastpacket,	/* number of last packet (from 1) */
-		      numpackets;	/* number of packets in split     */
-	u_short sequence;		/* sequence number of assembly	  */
-};
-
-struct Outgoing
-{
-	struct sk_buff *skb;		/* buffer from upper levels */
-	struct ClientData *hdr;		/* clientdata of last packet */
-	u_char *data;			/* pointer to data in packet */
-	short length,			/* bytes total */
-	      dataleft,			/* bytes left */
-	      segnum,			/* segment being sent */
-	      numsegs,			/* number of segments */
-	      seglen;			/* length of segment */
-};
-
-
-/* Information that needs to be kept for each board. */
-struct arcnet_local {
-	struct net_device_stats stats;
-	u_short sequence;	/* sequence number (incs with each packet) */
-	u_char stationid,	/* our 8-bit station address */
-		recbuf,		/* receive buffer # (0 or 1) */
-		txbuf,		/* transmit buffer # (2 or 3) */
-		txready,	/* buffer where a packet is ready to send */
-		intmask;	/* current value of INTMASK register */
-	short intx,		/* in TX routine? */
-	      in_txhandler,	/* in TX_IRQ handler? */
-	      sending,		/* transmit in progress? */
-	      lastload_dest,	/* can last loaded packet be acked? */
-	      lasttrans_dest;	/* can last TX'd packet be acked? */
-
-#if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
-	time_t first_recon,	/* time of "first" RECON message to count */
-		last_recon;	/* time of most recent RECON */
-	int num_recons,		/* number of RECONs between first and last. */
-	    network_down;	/* do we think the network is down? */
-#endif
-
-	struct timer_list timer; /* the timer interrupt struct */
-	struct Incoming incoming[256];	/* one from each address */
-	struct Outgoing outgoing; /* packet currently being sent */
-
-	struct device *adev;	/* RFC1201 protocol device */
-
-#ifdef CONFIG_ARCNET_ETH
-	struct device *edev;	/* Ethernet-Encap device */
+void arcnet_init(void);
+static int init_module(void);
+#ifdef CONFIG_ARCNET_COM90xx
+extern char com90xx_explicit;
+extern int arc90xx_probe(struct device *dev);
 #endif
-
-#ifdef CONFIG_ARCNET_1051
-	struct device *sdev;	/* RFC1051 protocol device */
 #endif
-};
 
-
-/* Index to functions, as function prototypes. */
+void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+void arcnet_use_count (int open);
+void arcnet_setup(struct device *dev);
+void arcnet_makename(char *device);
+void arcnetA_continue_tx(struct device *dev);
+int arcnet_go_tx(struct device *dev,int enable_irq);
+void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+
+
+EXPORT_SYMBOL(arcnet_debug);
+EXPORT_SYMBOL(arcnet_tx_done);
+EXPORT_SYMBOL(arcnet_use_count);
+EXPORT_SYMBOL(arcnet_setup);
+EXPORT_SYMBOL(arcnet_makename);
+EXPORT_SYMBOL(arcnetA_continue_tx);
+EXPORT_SYMBOL(arcnet_go_tx);
+EXPORT_SYMBOL(arcnet_interrupt);
+EXPORT_SYMBOL(arcnet_rx);
 
 #if ARCNET_DEBUG_MAX & D_SKB
-static void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
 	char *desc);
+EXPORT_SYMBOL(arcnet_dump_skb);
 #else
 #	define arcnet_dump_skb(dev,skb,desc) ;
 #endif
 
 #if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
-static void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
 	char *desc);
+EXPORT_SYMBOL(arcnet_dump_packet);
 #else
 #	define arcnet_dump_packet(dev,buffer,ext,desc) ;
 #endif
 
-extern int arcnet_probe(struct device *dev);
-static int arcnet_found(struct device *dev,int port,int airq,u_long shmem);
+/* Internal function prototypes */
 
-static void arcnet_setup(struct device *dev);
 static int arcnet_open(struct device *dev);
 static int arcnet_close(struct device *dev);
-static int arcnet_reset(struct device *dev,int reset_delay);
-
+static int arcnetA_header(struct sk_buff *skb,struct device *dev,
+		unsigned short type,void *daddr,void *saddr,unsigned len);
+static int arcnetA_rebuild_header(struct sk_buff *skb);
 static int arcnet_send_packet_bad(struct sk_buff *skb,struct device *dev);
 static int arcnetA_send_packet(struct sk_buff *skb, struct device *dev);
-static void arcnetA_continue_tx(struct device *dev);
-static void arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
-		char *data,int length,int daddr,int exceptA);
-static int arcnet_go_tx(struct device *dev,int enable_irq);
-
-static void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
-static void arcnet_inthandler(struct device *dev);
-
-static void arcnet_rx(struct device *dev,int recbuf);
 static void arcnetA_rx(struct device *dev,u_char *buf,
 	int length,u_char saddr, u_char daddr);
-
 static struct net_device_stats *arcnet_get_stats(struct device *dev);
+static unsigned short arcnetA_type_trans(struct sk_buff *skb,
+					 struct device *dev);
 
-int arcnetA_header(struct sk_buff *skb,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetA_rebuild_header(struct sk_buff *skb);
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev);
 
 #ifdef CONFIG_ARCNET_ETH
 	/* functions specific to Ethernet-Encap */
@@ -626,6 +287,7 @@
 	int length,u_char saddr, u_char daddr);
 #endif
 
+
 #ifdef CONFIG_ARCNET_1051
 	/* functions specific to RFC1051 */
 static int arcnetS_init(struct device *dev);
@@ -633,20 +295,15 @@
 static int arcnetS_send_packet(struct sk_buff *skb, struct device *dev);
 static void arcnetS_rx(struct device *dev,u_char *buf,
 	int length,u_char saddr, u_char daddr);
-int arcnetS_header(struct sk_buff *skb,struct device *dev,
+static int arcnetS_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetS_rebuild_header(struct sk_buff *skb);
-unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
+static int arcnetS_rebuild_header(struct sk_buff *skb);
+static unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
 #endif
 
-#ifdef MODULE
-int  init_module(void);
-void cleanup_module(void);
-#endif
 
-#define tx_done(dev) 1
 
-#define JIFFER(time) for (delayval=jiffies+time; jiffies<delayval;) ;
+
 
 
 /****************************************************************************
@@ -699,1209 +356,568 @@
 }
 #endif
 
-/****************************************************************************
- *                                                                          *
- * Probe and initialization                                                 *
- *                                                                          *
- ****************************************************************************/
 
-#ifdef RIM_I_MODE
 
-/* We cannot probe for a RIM I card; one reason is I don't know how to reset
- * them.  In fact, we can't even get their node ID automatically.  So, we
- * need to be passed a specific shmem address, IRQ, and node ID (stored in
- * dev->base_addr)
+
+/* Setup a struct device for ARCnet.  This should really be in net_init.c
+ * but since there are three different ARCnet devices ANYWAY... <gargle>
+ *
+ * Actually, the whole idea of having all this kernel-dependent stuff (ie.
+ * "new-style flags") setup per-net-device is kind of weird anyway.
+ *
+ * Intelligent defaults?!  Nah.
  */
-__initfunc(int arcnet_probe(struct device *dev))
+
+void arcnet_setup(struct device *dev)
 {
-	BUGLVL(D_NORMAL) printk(version);
-	BUGMSG(D_NORMAL,"Compiled for ARCnet RIM I (autoprobe disabled)\n");
-	BUGMSG(D_NORMAL,"Given: node %02lXh, shmem %lXh, irq %d\n",
-		dev->base_addr,dev->mem_start,dev->irq);
+	dev_init_buffers(dev);
 
-	if (dev->mem_start<=0 || dev->irq<=0)
-	{
-		BUGMSG(D_NORMAL,"No autoprobe for RIM I; you "
-			"must specify the shmem and irq!\n");
-		return -ENODEV;
-	}
+	dev->broadcast[0]	= 0x00;	/* for us, broadcasts are address 0 */
+	dev->addr_len		= 1;
+	dev->type		= ARPHRD_ARCNET;
+	dev->tx_queue_len	= 30;
 
-	if (dev->base_addr<=0 || dev->base_addr>255)
-	{
-		BUGMSG(D_NORMAL,"You need to specify your card's station "
-			"ID!\n");
-		return -ENODEV;
-	}
+	/* New-style flags. */
+	dev->flags		= IFF_BROADCAST;
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr 	= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= 4;
+
+
+	/* Put in this stuff here, so we don't have to export the symbols 
+	 * to the chipset drivers.
+	 */
 
-	return arcnet_found(dev,dev->base_addr,dev->irq,dev->mem_start);
+  	dev->open=arcnet_open;
+	dev->stop=arcnet_close;
+	dev->hard_start_xmit=arcnetA_send_packet;
+	dev->get_stats=arcnet_get_stats;
+	dev->hard_header=arcnetA_header;
+	dev->rebuild_header=arcnetA_rebuild_header;
 }
 
-#else /* not RIM_I_MODE, so use a real autoprobe */
 
-/* Check for an ARCnet network adaptor, and return '0' if one exists.
- *  If dev->base_addr == 0, probe all likely locations.
- *  If dev->base_addr == 1, always return failure.
- *  If dev->base_addr == 2, allocate space for the device and return success
- *  			    (detachable devices only).
- *
- * NOTE: the list of possible ports/shmems is static, so it is retained
- * across calls to arcnet_probe.  So, if more than one ARCnet probe is made,
- * values that were discarded once will not even be tried again.
+/****************************************************************************
+ *                                                                          *
+ * Open and close the driver                                                *
+ *                                                                          *
+ ****************************************************************************/
+
+
+/* Open/initialize the board.  This is called sometime after booting when
+ * the 'ifconfig' program is run.
  *
- * FIXME: grab all devices in one shot and eliminate the big static array.
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
  */
+static int
+arcnet_open(struct device *dev)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ 
+  if (dev->metric>=1000)
+    {
+      arcnet_debug=dev->metric-1000;
+      printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
+      dev->metric=1;
+    }
+  
+  BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
+  
+  /* try to put the card in a defined state - if it fails the first
+   * time, actually reset it.
+   */
+  if ((*lp->arcnet_reset)(dev,0) && (*lp->arcnet_reset)(dev,1))
+    return -ENODEV;
+#if 0
+  /* reset the card twice in case something goes wrong the first time.
+   */
+  if ((*(lp->arcnet_reset))(dev,1) && (*(lp->arcnet_reset))(dev,1))
+    return -ENODEV;
+#endif
+  
+  dev->tbusy=0;
+  dev->interrupt=0;
+  lp->intx=0;
+  lp->in_txhandler=0;
+  
+  /* The RFC1201 driver is the default - just store */
+  lp->adev=dev;
+
+  /* we're started */
+  dev->start=1;
+  
+#ifdef CONFIG_ARCNET_ETH
+  /* Initialize the ethernet-encap protocol driver */
+  lp->edev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
+  if (lp->edev == NULL)
+    return -ENOMEM;
+  memcpy(lp->edev,dev,sizeof(struct device));
+  lp->edev->type=ARPHRD_ETHER;
+  lp->edev->name=(char *)kmalloc(10,GFP_KERNEL);
+  if (lp->edev->name == NULL) {
+    kfree(lp->edev);
+    lp->edev = NULL;
+    return -ENOMEM;
+  }
+  sprintf(lp->edev->name,"%se",dev->name);
+  lp->edev->init=arcnetE_init;
+  register_netdev(lp->edev);
+#endif
+  
+#ifdef CONFIG_ARCNET_1051
+  /* Initialize the RFC1051-encap protocol driver */
+  lp->sdev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
+  memcpy(lp->sdev,dev,sizeof(struct device));
+  lp->sdev->name=(char *)kmalloc(10,GFP_KERNEL);
+  sprintf(lp->sdev->name,"%ss",dev->name);
+  lp->sdev->init=arcnetS_init;
+  register_netdev(lp->sdev);
+#endif
+    
+  /* Enable TX if we need to */
+  if (lp->en_dis_able_TX)
+    (*lp->en_dis_able_TX)(dev, 1);
+  
+  /* make sure we're ready to receive IRQ's.
+   * arcnet_reset sets this for us, but if we receive one before
+   * START is set to 1, it could be ignored.  So, we turn IRQ's
+   * off, then on again to clean out the IRQ controller.
+   */
+  
+  AINTMASK(0);
+  udelay(1);	/* give it time to set the mask before
+		 * we reset it again. (may not even be
+		 * necessary)
+		 */
+  SETMASK;
+  
+  /* Let it increase its use count */
+  (*lp->openclose_device)(1);
+  
+  return 0;
+}
 
-static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata = { 0 };
-static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata = { 0 };
 
-__initfunc(int arcnet_probe(struct device *dev))
+/* The inverse routine to arcnet_open - shuts down the card.
+ */
+static int
+arcnet_close(struct device *dev)
 {
-	static int init_once = 0;
-	static int numports=sizeof(ports)/sizeof(ports[0]),
-	    	   numshmems=sizeof(shmems)/sizeof(shmems[0]);
-
-	int count,status,delayval,ioaddr,numprint,airq,retval=-ENODEV,
-		openparen=0;
-	unsigned long airqmask;
-	int *port;
-	u_long *shmem;
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  if (test_and_set_bit(0, (int *)&dev->tbusy))
+    BUGMSG(D_NORMAL, "arcnet_close: tbusy already set!\n");
 
-	if (!init_once)
-	{
-		for (count=0x200; count<=0x3f0; count+=16)
-			ports[(count-0x200)/16] = count;
-		for (count=0xA0000; count<=0xFF800; count+=2048)
-			shmems[(count-0xA0000)/2048] = count;
-		init_once=1;
-	}
+  dev->start=0;
+#ifdef CONFIG_ARCNET_1051
+  lp->sdev->tbusy=1;
+  lp->sdev->start=0;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+  lp->edev->tbusy=1;
+  lp->edev->start=0;
+#endif
 
-	BUGLVL(D_NORMAL) printk(version);
+  
+  /* Shut down the card */
+  
+  /* Disable TX if we need to */
+  if (lp->en_dis_able_TX)
+    (*lp->en_dis_able_TX)(dev, 0);
+  
+  (*lp->arcnet_reset)(dev, 3);	/* reset IRQ won't run if START=0 */
+#if 0
+  lp->intmask=0;
+  SETMASK;	/* no IRQ's (except RESET, of course) */
+  ACOMMAND(NOTXcmd);	/* stop transmit */
+  ACOMMAND(NORXcmd);	/* disable receive */
+#endif
+  
+  /* reset more flags */
+	dev->interrupt=0;
+#ifdef CONFIG_ARCNET_ETH
+	lp->edev->interrupt=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+	lp->sdev->interrupt=0;
+#endif
 
-	BUGMSG(D_DURING,"space used for probe buffers: %d+%d=%d bytes\n",
-		sizeof(ports),sizeof(shmems),
-		sizeof(ports)+sizeof(shmems));
 
 
-#if 1
-	BUGLVL(D_EXTRA)
-	{
-		printk("arcnet: ***\n");
-		printk("arcnet: * Read arcnet.txt for important release notes!\n");
-		printk("arcnet: *\n");
-		printk("arcnet: * This is an ALPHA version!  (Last stable release: v2.56)  E-mail me if\n");
-		printk("arcnet: * you have any questions, comments, or bug reports.\n");
-		printk("arcnet: ***\n");
-	}
+  
+  /* do NOT free lp->adev!!  It's static! */
+  lp->adev=NULL;
+  
+#ifdef CONFIG_ARCNET_ETH
+  /* free the ethernet-encap protocol device */
+  lp->edev->priv=NULL;
+  dev_close(lp->edev);
+  unregister_netdev(lp->edev);
+  kfree(lp->edev->name);
+  kfree(lp->edev);
+  lp->edev=NULL;
 #endif
+  
+#ifdef CONFIG_ARCNET_1051
+  /* free the RFC1051-encap protocol device */
+  lp->sdev->priv=NULL;
+  dev_close(lp->sdev);
+  unregister_netdev(lp->sdev);
+  kfree(lp->sdev->name);
+  kfree(lp->sdev);
+  lp->sdev=NULL;
+#endif
+  
+  /* Update the statistics here. (not necessary in ARCnet) */
+  
+  /* Decrease the use count */
+  (*lp->openclose_device)(0);
+  
+  return 0;
+}
 
-	BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
-			dev->base_addr,dev->irq,dev->mem_start);
 
-	if (dev->base_addr > 0x1ff)	/* Check a single specified port */
-	{
-		ports[0]=dev->base_addr;
-		numports=1;
-	}
-	else if (dev->base_addr > 0)	/* Don't probe at all. */
-		return -ENXIO;
 
-	if (dev->mem_start)
+/****************************************************************************
+ *                                                                          *
+ * Transmitter routines                                                     *
+ *                                                                          *
+ ****************************************************************************/
+
+/* Generic error checking routine for arcnet??_send_packet
+ */
+static int
+arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
+	 ARCSTATUS,lp->intx);
+  
+  if (lp->in_txhandler)
+    {
+      BUGMSG(D_NORMAL,"send_packet called while in txhandler!\n");
+      lp->stats.tx_dropped++;
+      return 1;
+    }
+  
+  if (lp->intx>1)
+    {
+      BUGMSG(D_NORMAL,"send_packet called while intx!\n");
+      lp->stats.tx_dropped++;
+      return 1;
+    }
+  
+  if (test_bit(0, (int *)&dev->tbusy))
+    {
+      /* If we get here, some higher level has decided we are broken.
+	 There should really be a "kick me" function call instead. */
+      int tickssofar = jiffies - dev->trans_start;
+
+      int status=ARCSTATUS;
+      
+      if (tickssofar < TX_TIMEOUT)
+	{
+	  BUGMSG(D_DURING,"premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n",
+		 status,tickssofar,lp->outgoing.skb,
+		 lp->outgoing.numsegs,
+		 lp->outgoing.segnum);
+	  return 1;
+	}
+      
+      lp->intmask &= ~TXFREEflag;
+      SETMASK;
+      
+      if (status&TXFREEflag)	/* transmit _DID_ finish */
+	{
+	  BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, ticks=%d, mask=%Xh, dest=%02Xh)\n",
+		 status,tickssofar,lp->intmask,lp->lasttrans_dest);
+	  lp->stats.tx_errors++;
+	}
+      else
+	{
+	  BUGMSG(D_EXTRA,"tx timed out (status=%Xh, tickssofar=%d, intmask=%Xh, dest=%02Xh)\n",
+		 status,tickssofar,lp->intmask,lp->lasttrans_dest);
+	  lp->stats.tx_errors++;
+	  lp->stats.tx_aborted_errors++;
+	  
+	  ACOMMAND(NOTXcmd);
+	}
+      
+      if (lp->outgoing.skb)
 	{
-		shmems[0]=dev->mem_start;
-		numshmems=1;
+	  dev_kfree_skb(lp->outgoing.skb,FREE_WRITE);
+	  lp->stats.tx_dropped++;
 	}
+      lp->outgoing.skb=NULL;
+      
+#ifdef CONFIG_ARCNET_ETH
+      lp->edev->tbusy=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+      lp->sdev->tbusy=0;
+#endif
+      if (!test_and_clear_bit(0,(int *)&dev->tbusy))
+	BUGMSG(D_EXTRA, "after timing out, tbusy was clear!\n");
 
+      lp->txready=0;
+      lp->sending=0;
+      
+      return 1;
+    }
+  
+  if (lp->txready)	/* transmit already in progress! */
+    {
+      BUGMSG(D_NORMAL,"trying to start new packet while busy! (status=%Xh)\n",
+	     ARCSTATUS);
+      lp->intmask &= ~TXFREEflag;
+      SETMASK;
+      ACOMMAND(NOTXcmd); /* abort current send */
+      (*lp->inthandler)(dev); /* fake an interrupt */
+      lp->stats.tx_errors++;
+      lp->stats.tx_fifo_errors++;
+      lp->txready=0;	/* we definitely need this line! */
+      
+      return 1;
+    }
+  
+  /* 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 (test_and_set_bit(0, (int *)&lp->adev->tbusy))
+    {
+      BUGMSG(D_NORMAL,"transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+	     ARCSTATUS,lp->intx,jiffies-dev->trans_start);
+      lp->stats.tx_errors++;
+      lp->stats.tx_fifo_errors++;
+      return -EBUSY;
+    }
+#ifdef CONFIG_ARCNET_1051
+  lp->sdev->tbusy=1;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+  lp->edev->tbusy=1;
+#endif
+  
+  return 0;
+}
 
-	/* Stage 1: abandon any reserved ports, or ones with status==0xFF
-	 * (empty), and reset any others by reading the reset port.
-	 */
-	BUGMSG(D_INIT,"Stage 1: ");
-	numprint=0;
-	for (port = &ports[0]; port-ports<numports; port++)
-	{
-		numprint++;
-		if (numprint>8)
-		{
-			BUGMSG2(D_INIT,"\n");
-			BUGMSG(D_INIT,"Stage 1: ");
-			numprint=1;
-		}
-		BUGMSG2(D_INIT,"%Xh ",*port);
-
-		ioaddr=*port;
 
-		if (check_region(*port, ARCNET_TOTAL_SIZE))
-		{
-			BUGMSG2(D_INIT_REASONS,"(check_region)\n");
-			BUGMSG(D_INIT_REASONS,"Stage 1: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-			*port=ports[numports-1];
-			numports--;
-			port--;
-			continue;
-		}
+/* Called by the kernel in order to transmit a packet.
+ */
+static int
+arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int bad,oldmask=0;
+  struct Outgoing *out=&(lp->outgoing);
+  
+  lp->intx++;
+  
+  oldmask |= lp->intmask;
+  lp->intmask=0;
+  SETMASK;
+  
+  bad=arcnet_send_packet_bad(skb,dev);
+  if (bad)
+    {
+      lp->intx--;
+      lp->intmask=oldmask;
+      SETMASK;
+      return bad;
+    }
+  
+  /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
+  
+  lp->intmask = oldmask & ~TXFREEflag;
+  SETMASK;
+  
+  out->length = 1 < skb->len ? skb->len : 1;
+  out->hdr=(struct ClientData*)skb->data;
+  out->skb=skb;
+  
+  BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
+  
+  out->hdr->sequence=(lp->sequence++);
+  
+  /* fits in one packet? */
+  if (out->length-EXTRA_CLIENTDATA<=XMTU)
+    {
+      BUGMSG(D_DURING,"not splitting %d-byte packet. (split_flag=%d)\n",
+	     out->length,out->hdr->split_flag);
+      if (out->hdr->split_flag)
+	BUGMSG(D_NORMAL,"short packet has split_flag set?! (split_flag=%d)\n",
+	       out->hdr->split_flag);
+      out->numsegs=1;
+      out->segnum=1;
+      (*lp->prepare_tx)(dev,
+			((char *)out->hdr)+EXTRA_CLIENTDATA,
+			sizeof(struct ClientData)-EXTRA_CLIENTDATA,
+			((char *)skb->data)+sizeof(struct ClientData),
+			out->length-sizeof(struct ClientData),
+			out->hdr->daddr,1,0);
+      
+      /* done right away */
+      lp->stats.tx_bytes += out->skb->len;
+      dev_kfree_skb(out->skb,FREE_WRITE);
+      out->skb=NULL;
+      
+      if (arcnet_go_tx(dev,1))
+	{
+	  /* inform upper layers */
+	  arcnet_tx_done(dev, lp);
+	}
+    }
+  else			/* too big for one - split it */
+    {
+      int maxsegsize=XMTU-4;
+      
+      out->data=(u_char *)skb->data
+	+ sizeof(struct ClientData);
+      out->dataleft=out->length-sizeof(struct ClientData);
+      out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
+      out->segnum=0;
+      
+      BUGMSG(D_TX,"packet (%d bytes) split into %d fragments:\n",
+	     out->length,out->numsegs);
+      
+      /* if a packet waiting, launch it */
+      arcnet_go_tx(dev,1);
+      
+      if (!lp->txready)
+	{
+	  /* prepare a packet, launch it and prepare
+	   * another.
+	   */
+	  arcnetA_continue_tx(dev);
+	  if (arcnet_go_tx(dev,1))
+	    {
+	      arcnetA_continue_tx(dev);
+	      arcnet_go_tx(dev,1);
+	    }
+	}
+      
+      /* if segnum==numsegs, the transmission is finished;
+       * free the skb right away.
+       */
+
+      if (out->segnum==out->numsegs)
+	{
+	  /* transmit completed */
+	  out->segnum++;
+	  if (out->skb)
+	    {
+	      lp->stats.tx_bytes += skb->len;
+	      dev_kfree_skb(out->skb,FREE_WRITE);
+	    }
+	  out->skb=NULL;
+	}
+    }
+  
+  dev->trans_start=jiffies;
+  lp->intx--;
+ 
+  /* make sure we didn't ignore a TX IRQ while we were in here */
+  lp->intmask |= TXFREEflag;
+  SETMASK;
+  
+  return 0;
+}
 
-		if (ARCSTATUS == 0xFF)
-		{
-			BUGMSG2(D_INIT_REASONS,"(empty)\n");
-			BUGMSG(D_INIT_REASONS,"Stage 1: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-			*port=ports[numports-1];
-			numports--;
-			port--;
-			continue;
-		}
 
-		ARCRESET;	/* begin resetting card */
+/* After an RFC1201 split packet has been set up, this function calls
+ * arcnetAS_prepare_tx to load the next segment into the card.  This function
+ * does NOT automatically call arcnet_go_tx.
+ */
+void arcnetA_continue_tx(struct device *dev)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int maxsegsize=XMTU-4;
+  struct Outgoing *out=&(lp->outgoing);
+  
+  BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
+	 ARCSTATUS,lp->intx,lp->in_txhandler,lp->intmask);
+  
+  if (lp->txready)
+    {
+      BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
+      return;
+    }
+  
+  if (out->segnum>=out->numsegs)
+    {
+      BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
+	     out->segnum+1,out->numsegs);
+    }
+  
+  if (!out->segnum)	/* first packet */
+    out->hdr->split_flag=((out->numsegs-2)<<1)+1;
+  else
+    out->hdr->split_flag=out->segnum<<1;
+  
+  out->seglen=maxsegsize;
+  if (out->seglen>out->dataleft) out->seglen=out->dataleft;
+  
+  BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
+	 out->segnum+1,out->seglen,out->numsegs,
+	 out->length,out->hdr->split_flag);
+  
+  (*lp->prepare_tx)(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
+		    sizeof(struct ClientData)-EXTRA_CLIENTDATA,
+		    out->data,out->seglen,out->hdr->daddr,1,0);
+  
+  out->dataleft-=out->seglen;
+  out->data+=out->seglen;
+  out->segnum++;
+}
 
-		BUGMSG2(D_INIT_REASONS,"\n");
-		BUGMSG(D_INIT_REASONS,"Stage 1: ");
-		BUGLVL(D_INIT_REASONS) numprint=0;
-	}
-	BUGMSG2(D_INIT,"\n");
 
-	if (!numports)
-	{
-		BUGMSG(D_NORMAL,"Stage 1: No ARCnet cards found.\n");
-		return -ENODEV;
-	}
+/* Actually start transmitting a packet that was placed in the card's
+ * buffer by arcnetAS_prepare_tx.  Returns 1 if a Tx is really started.
+ *
+ * This should probably always be called with the INTMASK register set to 0,
+ * so go_tx is not called recursively.
+ *
+ * The enable_irq flag determines whether to actually write INTMASK value
+ * to the card; TXFREEflag is always OR'ed into the memory variable either
+ * way.
+ */
+int arcnet_go_tx(struct device *dev,int enable_irq)
+{
+	struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
 
+	BUGMSG(D_DURING,"go_tx: status=%Xh, intmask=%Xh, txready=%d, sending=%d\n",
+		ARCSTATUS,lp->intmask,lp->txready,lp->sending);
 
-	/* Stage 2: we have now reset any possible ARCnet cards, so we can't
-	 * do anything until they finish.  If D_INIT, print the list of
-	 * cards that are left.
-	 */
-	BUGMSG(D_INIT,"Stage 2: ");
-	numprint=0;
-	for (port = &ports[0]; port-ports<numports; port++)
+	if (lp->sending || !lp->txready)
 	{
-		numprint++;
-		if (numprint>8)
+		if (enable_irq && lp->sending)
 		{
-			BUGMSG2(D_INIT,"\n");
-			BUGMSG(D_INIT,"Stage 2: ");
-			numprint=1;
+			lp->intmask |= TXFREEflag;
+			SETMASK;
 		}
-		BUGMSG2(D_INIT,"%Xh ",*port);
+		return 0;
 	}
-	BUGMSG2(D_INIT,"\n");
-	JIFFER(RESETtime);
-
 
-	/* Stage 3: abandon any shmem addresses that don't have the signature
-	 * 0xD1 byte in the right place, or are read-only.
-	 */
-	BUGMSG(D_INIT,"Stage 3: ");
-	numprint=0;
-	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
-	{
-		u_long ptr;
+	/* start sending */
+	ACOMMAND(TXcmd|(lp->txready<<3));
 
-		numprint++;
-		if (numprint>8)
-		{
-			BUGMSG2(D_INIT,"\n");
-			BUGMSG(D_INIT,"Stage 3: ");
-			numprint=1;
-		}
-		BUGMSG2(D_INIT,"%lXh ",*shmem);
+	lp->stats.tx_packets++;
+	lp->txready=0;
+	lp->sending++;
 
-		ptr=(u_long)(*shmem);
+	lp->lasttrans_dest=lp->lastload_dest;
+	lp->lastload_dest=0;
 
-		if (readb(ptr) != TESTvalue)
-		{
-			BUGMSG2(D_INIT_REASONS,"(mem=%02Xh, not %02Xh)\n",
-				readb(ptr),TESTvalue);
-			BUGMSG(D_INIT_REASONS,"Stage 3: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-			*shmem=shmems[numshmems-1];
-			numshmems--;
-			shmem--;
-			continue;
-		}
+	lp->intmask |= TXFREEflag;
 
-		/* By writing 0x42 to the TESTvalue location, we also make
-		 * sure no "mirror" shmem areas show up - if they occur
-		 * in another pass through this loop, they will be discarded
-		 * because *cptr != TESTvalue.
-		 */
-		writeb(0x42,ptr);
-		if (readb(ptr) != 0x42)
-		{
-			BUGMSG2(D_INIT_REASONS,"(read only)\n");
-			BUGMSG(D_INIT_REASONS,"Stage 3: ");
-			*shmem=shmems[numshmems-1];
-			numshmems--;
-			shmem--;
-			continue;
-		}
-
-		BUGMSG2(D_INIT_REASONS,"\n");
-		BUGMSG(D_INIT_REASONS,"Stage 3: ");
-		BUGLVL(D_INIT_REASONS) numprint=0;
-	}
-	BUGMSG2(D_INIT,"\n");
-
-	if (!numshmems)
-	{
-		BUGMSG(D_NORMAL,"Stage 3: No ARCnet cards found.\n");
-		return -ENODEV;
-	}
-
-	/* Stage 4: something of a dummy, to report the shmems that are
-	 * still possible after stage 3.
-	 */
-	BUGMSG(D_INIT,"Stage 4: ");
-	numprint=0;
-	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
-	{
-		numprint++;
-		if (numprint>8)
-		{
-			BUGMSG2(D_INIT,"\n");
-			BUGMSG(D_INIT,"Stage 4: ");
-			numprint=1;
-		}
-		BUGMSG2(D_INIT,"%lXh ",*shmem);
-	}
-	BUGMSG2(D_INIT,"\n");
-
-
-	/* Stage 5: for any ports that have the correct status, can disable
-	 * the RESET flag, and (if no irq is given) generate an autoirq,
-	 * register an ARCnet device.
-	 *
-	 * Currently, we can only register one device per probe, so quit
-	 * after the first one is found.
-	 */
-	BUGMSG(D_INIT,"Stage 5: ");
-	numprint=0;
-	for (port = &ports[0]; port-ports<numports; port++)
-	{
-		numprint++;
-		if (numprint>8)
-		{
-			BUGMSG2(D_INIT,"\n");
-			BUGMSG(D_INIT,"Stage 5: ");
-			numprint=1;
-		}
-		BUGMSG2(D_INIT,"%Xh ",*port);
-
-		ioaddr=*port;
-		status=ARCSTATUS;
-
-		if ((status & 0x9D)
-			!= (NORXflag|RECONflag|TXFREEflag|RESETflag))
-		{
-			BUGMSG2(D_INIT_REASONS,"(status=%Xh)\n",status);
-			BUGMSG(D_INIT_REASONS,"Stage 5: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-			*port=ports[numports-1];
-			numports--;
-			port--;
-			continue;
-		}
-
-		ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
-		status=ARCSTATUS;
-		if (status & RESETflag)
-		{
-			BUGMSG2(D_INIT_REASONS," (eternal reset, status=%Xh)\n",
-					status);
-			BUGMSG(D_INIT_REASONS,"Stage 5: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-			*port=ports[numports-1];
-			numports--;
-			port--;
-			continue;
-		}
-
-		/* skip this completely if an IRQ was given, because maybe
-		 * we're on a machine that locks during autoirq!
-		 */
-		if (!dev->irq)
-		{
-			/* if we do this, we're sure to get an IRQ since the
-			 * card has just reset and the NORXflag is on until
-			 * we tell it to start receiving.
-			 */
-			airqmask = probe_irq_on();
-			AINTMASK(NORXflag);
-			udelay(1);
-			AINTMASK(0);
-			airq = probe_irq_off(airqmask);
-
-			if (airq<=0)
-			{
-				BUGMSG2(D_INIT_REASONS,"(airq=%d)\n",airq);
-				BUGMSG(D_INIT_REASONS,"Stage 5: ");
-				BUGLVL(D_INIT_REASONS) numprint=0;
-				*port=ports[numports-1];
-				numports--;
-				port--;
-				continue;
-			}
-		}
-		else
-		{
-			airq=dev->irq;
-		}
-
-		BUGMSG2(D_INIT,"(%d,", airq);
-		openparen=1;
-
-		/* Everything seems okay.  But which shmem, if any, puts
-		 * back its signature byte when the card is reset?
-		 *
-		 * If there are multiple cards installed, there might be
-		 * multiple shmems still in the list.
-		 */
-#ifdef FAST_PROBE
-		if (numports>1 || numshmems>1)
-		{
-			ARCRESET;
-			JIFFER(RESETtime);
-		}
-		else
-		{
-			/* just one shmem and port, assume they match */
-			writeb(TESTvalue,shmems[0]);
-		}
-#else
-		ARCRESET;
-		JIFFER(RESETtime);
-#endif
-
-
-		for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
-		{
-			u_long ptr;
-			ptr=(u_long)(*shmem);
-
-			if (readb(ptr) == TESTvalue)	/* found one */
-			{
-				BUGMSG2(D_INIT,"%lXh)\n", *shmem);
-				openparen=0;
-
-				/* register the card */
-				retval=arcnet_found(dev,*port,airq,*shmem);
-				if (retval) openparen=0;
-
-				/* remove shmem from the list */
-				*shmem=shmems[numshmems-1];
-				numshmems--;
-
-				break;
-			}
-			else
-			{
-				BUGMSG2(D_INIT_REASONS,"%Xh-", readb(ptr));
-			}
-		}
-
-		if (openparen)
-		{
-			BUGMSG2(D_INIT,"no matching shmem)\n");
-			BUGMSG(D_INIT_REASONS,"Stage 5: ");
-			BUGLVL(D_INIT_REASONS) numprint=0;
-		}
-
-		*port=ports[numports-1];
-		numports--;
-		port--;
-
-		if (!retval) break;
-	}
-	BUGMSG(D_INIT_REASONS,"\n");
-
-	/* Now put back TESTvalue on all leftover shmems.
-	 */
-	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
-		writeb(TESTvalue,*shmem);
-
-	if (retval) BUGMSG(D_NORMAL,"Stage 5: No ARCnet cards found.\n");
-	return retval;
-}
-
-#endif /* !RIM_I_MODE */
-
-
-/* Set up the struct device associated with this card.  Called after
- * probing succeeds.
- */
-__initfunc(int arcnet_found(struct device *dev,int port,int airq, u_long shmem))
-{
-	u_long first_mirror,last_mirror;
-	struct arcnet_local *lp;
-	int mirror_size;
-
-	/* reserve the irq */
-	if (request_irq(airq,&arcnet_interrupt,0,"arcnet",NULL))
-	{
-		BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
-		return -ENODEV;
-	}
-	irq2dev_map[airq]=dev;
-	dev->irq=airq;
-
-#ifdef RIM_I_MODE
-	dev->base_addr=0;
-	writeb(TESTvalue,shmem);
-	writeb(port,shmem+1);	/* actually the node ID */
-#else
-	/* reserve the I/O region - guaranteed to work by check_region */
-	request_region(port,ARCNET_TOTAL_SIZE,"arcnet");
-	dev->base_addr=port;
-#endif
-
-	/* find the real shared memory start/end points, including mirrors */
-
-	#define BUFFER_SIZE (512)
-	#define MIRROR_SIZE (BUFFER_SIZE*4)
-
-	/* guess the actual size of one "memory mirror" - the number of
-	 * bytes between copies of the shared memory.  On most cards, it's
-	 * 2k (or there are no mirrors at all) but on some, it's 4k.
-	 */
-	mirror_size=MIRROR_SIZE;
-	if (readb(shmem)==TESTvalue
-	    && readb(shmem-mirror_size)!=TESTvalue
-	    && readb(shmem-2*mirror_size)==TESTvalue)
-		mirror_size*=2;
-
-	first_mirror=last_mirror=shmem;
-	while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
-	first_mirror+=mirror_size;
-
-	while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
-	last_mirror-=mirror_size;
-
-	dev->mem_start=first_mirror;
-	dev->mem_end=last_mirror+MIRROR_SIZE-1;
-	dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
-	dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
-
-	/* Initialize the rest of the device structure. */
-
-	dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
-	if (dev->priv == NULL)
-	{
-		irq2dev_map[airq] = NULL;
-		free_irq(airq,NULL);
-		RELEASE_REGION(port,ARCNET_TOTAL_SIZE);
-		return -ENOMEM;
-	}
-	memset(dev->priv,0,sizeof(struct arcnet_local));
-	lp=(struct arcnet_local *)(dev->priv);
-
-	dev->open=arcnet_open;
-	dev->stop=arcnet_close;
-	dev->hard_start_xmit=arcnetA_send_packet;
-	dev->get_stats=arcnet_get_stats;
-	/*dev->set_multicast_list = &set_multicast_list;*/
-
-	/* Fill in the fields of the device structure with generic
-	 * values.
-	 */
-	arcnet_setup(dev);
-
-	/* And now fill particular fields with arcnet values */
-	dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
-	dev->hard_header_len=sizeof(struct ClientData);
-	dev->hard_header=arcnetA_header;
-	dev->rebuild_header=arcnetA_rebuild_header;
-	lp->sequence=1;
-	lp->recbuf=0;
-
-	BUGMSG(D_DURING,"ClientData header size is %d.\n",
-		sizeof(struct ClientData));
-	BUGMSG(D_DURING,"HardHeader size is %d.\n",
-		sizeof(struct HardHeader));
-
-	/* get and check the station ID from offset 1 in shmem */
-	lp->stationid = readb(first_mirror+1);
-	if (lp->stationid==0)
-		BUGMSG(D_NORMAL,"WARNING!  Station address 00 is reserved "
-			"for broadcasts!\n");
-	else if (lp->stationid==255)
-		BUGMSG(D_NORMAL,"WARNING!  Station address FF may confuse "
-			"DOS networking programs!\n");
-	dev->dev_addr[0]=lp->stationid;
-
-	BUGMSG(D_NORMAL,"ARCnet station %02Xh found at %03lXh, IRQ %d, "
-		"ShMem %lXh (%ld*%d bytes).\n",
-		lp->stationid,
-		dev->base_addr,dev->irq,dev->mem_start,
-		(dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
-
-	return 0;
-}
-
-
-/* Do a hardware reset on the card, and set up necessary registers.
- *
- * This should be called as little as possible, because it disrupts the
- * token on the network (causes a RECON) and requires a significant delay.
- *
- * However, it does make sure the card is in a defined state.
- */
-int arcnet_reset(struct device *dev,int reset_delay)
-{
-	struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
-	short ioaddr=IOADDR;
-	int delayval,recbuf=lp->recbuf;
-
-	/* no IRQ's, please! */
-	lp->intmask=0;
-	SETMASK;
-
-	BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
-			dev->name,ARCSTATUS);
-
-	if (reset_delay)
-	{
-		/* reset the card */
-		ARCRESET;
-		JIFFER(RESETtime);
-	}
-
-	ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
-	ACOMMAND(CFLAGScmd|CONFIGclear);
-
-	/* verify that the ARCnet signature byte is present */
-	if (readb(dev->mem_start) != TESTvalue)
-	{
-		BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
-		return 1;
-	}
-
-	/* clear out status variables */
-	recbuf=lp->recbuf=0;
-	lp->txbuf=2;
-
-	/* enable extended (512-byte) packets */
-	ACOMMAND(CONFIGcmd|EXTconf);
-
-#ifndef SLOW_XMIT_COPY
-	/* clean out all the memory to make debugging make more sense :) */
-	BUGLVL(D_DURING)
-		memset_io(dev->mem_start,0x42,2048);
-#endif
-
-	/* and enable receive of our first packet to the first buffer */
-	EnableReceiver();
-
-	/* re-enable interrupts */
-	lp->intmask|=NORXflag;
-#ifdef DETECT_RECONFIGS
-	lp->intmask|=RECONflag;
-#endif
-	SETMASK;
-
-	/* done!  return success. */
-	return 0;
-}
-
-
-/* Setup a struct device for ARCnet.  This should really be in net_init.c
- * but since there are three different ARCnet devices ANYWAY... <gargle>
- *
- * Actually, the whole idea of having all this kernel-dependent stuff (ie.
- * "new-style flags") setup per-net-device is kind of weird anyway.
- *
- * Intelligent defaults?!  Nah.
- */
-
-void arcnet_setup(struct device *dev)
-{
-	dev_init_buffers(dev);
-
-	dev->broadcast[0]	= 0x00;	/* for us, broadcasts are address 0 */
-	dev->addr_len		= 1;
-	dev->type		= ARPHRD_ARCNET;
-	dev->tx_queue_len	= 30;	/* fairly long queue - arcnet is
-					 * quite speedy.
-					 */
-
-	/* New-style flags. */
-	dev->flags		= IFF_BROADCAST;
-	dev->family		= AF_INET;
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr 	= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= 4;
-}
-
-
-/****************************************************************************
- *                                                                          *
- * Open and close the driver                                                *
- *                                                                          *
- ****************************************************************************/
-
-
-/* Open/initialize the board.  This is called sometime after booting when
- * the 'ifconfig' program is run.
- *
- * This routine should set everything up anew at each open, even
- * registers that "should" only need to be set once at boot, so that
- * there is non-reboot way to recover if something goes wrong.
- */
-static int
-arcnet_open(struct device *dev)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR;
-
-	if (dev->metric>=1000)
-	{
-		arcnet_debug=dev->metric-1000;
-		printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
-		dev->metric=1;
-	}
-
-	BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
-
-#ifdef FAST_IFCONFIG
-	/* try to put the card in a defined state - if it fails the first
-	 * time, actually reset it.
-	 */
-	if (arcnet_reset(dev,0) && arcnet_reset(dev,1))
-		return -ENODEV;
-#else
-	/* reset the card twice in case something goes wrong the first time.
-	 */
-	if (arcnet_reset(dev,1) && arcnet_reset(dev,1))
-		return -ENODEV;
-#endif
-
-	dev->tbusy=0;
-	dev->interrupt=0;
-	lp->intx=0;
-	lp->in_txhandler=0;
-
-	/* The RFC1201 driver is the default - just store */
-	lp->adev=dev;
-	BUGMSG(D_NORMAL,"ARCnet RFC1201 protocol initialized.\n");
-
-#ifdef CONFIG_ARCNET_ETH
-	/* Initialize the ethernet-encap protocol driver */
-	lp->edev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
-	if (lp->edev == NULL)
-		return -ENOMEM;
-	memcpy(lp->edev,dev,sizeof(struct device));
-	lp->edev->name=(char *)kmalloc(10,GFP_KERNEL);
-	if (lp->edev->name == NULL) {
-		kfree(lp->edev);
-		lp->edev = NULL;
-		return -ENOMEM;
-	}
-	sprintf(lp->edev->name,"%se",dev->name);
-	lp->edev->init=arcnetE_init;
-	register_netdev(lp->edev);
-#else
-	BUGMSG(D_NORMAL,"Ethernet-Encap protocol not available (disabled).\n");
-#endif
-
-#ifdef CONFIG_ARCNET_1051
-	/* Initialize the RFC1051-encap protocol driver */
-	lp->sdev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
-	memcpy(lp->sdev,dev,sizeof(struct device));
-	lp->sdev->name=(char *)kmalloc(10,GFP_KERNEL);
-	sprintf(lp->sdev->name,"%ss",dev->name);
-	lp->sdev->init=arcnetS_init;
-	register_netdev(lp->sdev);
-#else
-	BUGMSG(D_NORMAL,"RFC1051 protocol not available (disabled).\n");
-#endif
-
-	/* we're started */
-	START=1;
-
-	/* make sure we're ready to receive IRQ's.
-	 * arcnet_reset sets this for us, but if we receive one before
-	 * START is set to 1, it could be ignored.  So, we turn IRQ's
-	 * off, then on again to clean out the IRQ controller.
-	 */
-	AINTMASK(0);
-	udelay(1);	/* give it time to set the mask before
-			 * we reset it again. (may not even be
-			 * necessary)
-			 */
-	SETMASK;
-
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-
-/* The inverse routine to arcnet_open - shuts down the card.
- */
-static int
-arcnet_close(struct device *dev)
-{
-	int ioaddr=IOADDR;
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
-	TBUSY=1;
-	START=0;
-
-	/* Shut down the card */
-#ifdef FAST_IFCONFIG
-	ARCRESET;	/* reset IRQ won't run if START=0 */
-#else
-	lp->intmask=0;
-	SETMASK;	/* no IRQ's (except RESET, of course) */
-	ACOMMAND(NOTXcmd);	/* stop transmit */
-	ACOMMAND(NORXcmd);	/* disable receive */
-#endif
-
-	/* reset more flags */
-	INTERRUPT=0;
-
-	/* do NOT free lp->adev!!  It's static! */
-	lp->adev=NULL;
-
-#ifdef CONFIG_ARCNET_ETH
-	/* free the ethernet-encap protocol device */
-	lp->edev->priv=NULL;
-	dev_close(lp->edev);
-	unregister_netdev(lp->edev);
-	kfree(lp->edev->name);
-	kfree(lp->edev);
-	lp->edev=NULL;
-#endif
-
-#ifdef CONFIG_ARCNET_1051
-	/* free the RFC1051-encap protocol device */
-	lp->sdev->priv=NULL;
-	dev_close(lp->sdev);
-	unregister_netdev(lp->sdev);
-	kfree(lp->sdev->name);
-	kfree(lp->sdev);
-	lp->sdev=NULL;
-#endif
-
-	/* Update the statistics here. (not necessary in ARCnet) */
-
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
+	if (enable_irq)	SETMASK;
 
-
-
-/****************************************************************************
- *                                                                          *
- * Transmitter routines                                                     *
- *                                                                          *
- ****************************************************************************/
-
-/* Generic error checking routine for arcnet??_send_packet
- */
-static int
-arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR;
-
-	BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
-			ARCSTATUS,lp->intx);
-
-	if (lp->in_txhandler)
-	{
-		BUGMSG(D_NORMAL,"send_packet called while in txhandler!\n");
-		lp->stats.tx_dropped++;
-		return 1;
-	}
-
-	if (lp->intx>1)
-	{
-		BUGMSG(D_NORMAL,"send_packet called while intx!\n");
-		lp->stats.tx_dropped++;
-		return 1;
-	}
-
-	if (IF_TBUSY)
-	{
-		/* If we get here, some higher level has decided we are broken.
-		   There should really be a "kick me" function call instead. */
-		int tickssofar = jiffies - dev->trans_start;
-		/*int recbuf=lp->recbuf;*/
-		int status=ARCSTATUS;
-
-		if (tickssofar < TX_TIMEOUT)
-		{
-			BUGMSG(D_DURING,"premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n",
-					status,tickssofar,lp->outgoing.skb,
-					lp->outgoing.numsegs,
-					lp->outgoing.segnum);
-			return 1;
-		}
-
-		lp->intmask &= ~TXFREEflag;
-		SETMASK;
-
-		if (status&TXFREEflag)	/* transmit _DID_ finish */
-		{
-			BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, ticks=%d, mask=%Xh, dest=%02Xh)\n",
-					status,tickssofar,lp->intmask,lp->lasttrans_dest);
-			lp->stats.tx_errors++;
-		}
-		else
-		{
-			BUGMSG(D_EXTRA,"tx timed out (status=%Xh, tickssofar=%d, intmask=%Xh, dest=%02Xh)\n",
-					status,tickssofar,lp->intmask,lp->lasttrans_dest);
-			lp->stats.tx_errors++;
-			lp->stats.tx_aborted_errors++;
-
-			ACOMMAND(NOTXcmd);
-		}
-
-		if (lp->outgoing.skb)
-		{
-			dev_kfree_skb(lp->outgoing.skb,FREE_WRITE);
-			lp->stats.tx_dropped++;
-		}
-		lp->outgoing.skb=NULL;
-
-		TBUSY=0;
-		lp->txready=0;
-		lp->sending=0;
-
-		return 1;
-	}
-
-	if (lp->txready)	/* transmit already in progress! */
-	{
-		BUGMSG(D_NORMAL,"trying to start new packet while busy! (status=%Xh)\n",
-			ARCSTATUS);
-		lp->intmask &= ~TXFREEflag;
-		SETMASK;
-		ACOMMAND(NOTXcmd); /* abort current send */
-		arcnet_inthandler(dev); /* fake an interrupt */
-		lp->stats.tx_errors++;
-		lp->stats.tx_fifo_errors++;
-		lp->txready=0;	/* we definitely need this line! */
-
-		return 1;
-	}
-
-	/* 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 (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
-        {
-            BUGMSG(D_NORMAL,"transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
-            		ARCSTATUS,lp->intx,jiffies-dev->trans_start);
-            lp->stats.tx_errors++;
-            lp->stats.tx_fifo_errors++;
-            return -EBUSY;
-        }
-
-	return 0;
-}
-
-
-/* Called by the kernel in order to transmit a packet.
- */
-static int
-arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR,bad;
-	struct Outgoing *out=&(lp->outgoing);
-
-	lp->intx++;
-
-	bad=arcnet_send_packet_bad(skb,dev);
-	if (bad)
-	{
-		lp->intx--;
-		return bad;
-	}
-
-	TBUSY=1;
-
-	out->length = 1 < skb->len ? skb->len : 1;
-	out->hdr=(struct ClientData*)skb->data;
-	out->skb=skb;
-
-	BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
-
-	out->hdr->sequence=(lp->sequence++);
-
-	/* fits in one packet? */
-	if (out->length-EXTRA_CLIENTDATA<=XMTU)
-	{
-		BUGMSG(D_DURING,"not splitting %d-byte packet. (split_flag=%d)\n",
-				out->length,out->hdr->split_flag);
-		if (out->hdr->split_flag)
-			BUGMSG(D_NORMAL,"short packet has split_flag set?! (split_flag=%d)\n",
-				out->hdr->split_flag);
-		out->numsegs=1;
-		out->segnum=1;
-		arcnetAS_prepare_tx(dev,
-			((char *)out->hdr)+EXTRA_CLIENTDATA,
-			sizeof(struct ClientData)-EXTRA_CLIENTDATA,
-			((char *)skb->data)+sizeof(struct ClientData),
-			out->length-sizeof(struct ClientData),
-			out->hdr->daddr,1);
-
-		/* done right away */
-		dev_kfree_skb(out->skb,FREE_WRITE);
-		out->skb=NULL;
-
-		if (arcnet_go_tx(dev,1))
-		{
-			/* inform upper layers */
-			TBUSY=0;
-			mark_bh(NET_BH);
-		}
-	}
-	else			/* too big for one - split it */
-	{
-		int maxsegsize=XMTU-4;
-
-		out->data=(u_char *)skb->data
-				+ sizeof(struct ClientData);
-		out->dataleft=out->length-sizeof(struct ClientData);
-		out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
-		out->segnum=0;
-
-		BUGMSG(D_TX,"packet (%d bytes) split into %d fragments:\n",
-			out->length,out->numsegs);
-
-		/* if a packet waiting, launch it */
-		arcnet_go_tx(dev,1);
-
-		if (!lp->txready)
-		{
-			/* prepare a packet, launch it and prepare
-			 * another.
-			 */
-			arcnetA_continue_tx(dev);
-			if (arcnet_go_tx(dev,1))
-			{
-				arcnetA_continue_tx(dev);
-				arcnet_go_tx(dev,1);
-			}
-		}
-
-		/* if segnum==numsegs, the transmission is finished;
-		 * free the skb right away.
-		 */
-		if (out->segnum==out->numsegs)
-		{
-			/* transmit completed */
-			out->segnum++;
-			if (out->skb)
-				dev_kfree_skb(out->skb,FREE_WRITE);
-			out->skb=NULL;
-		}
-	}
-
-	dev->trans_start=jiffies;
-	lp->intx--;
-
-	/* make sure we didn't ignore a TX IRQ while we were in here */
-	lp->intmask |= TXFREEflag;
-	SETMASK;
-
-	return 0;
-}
-
-
-/* After an RFC1201 split packet has been set up, this function calls
- * arcnetAS_prepare_tx to load the next segment into the card.  This function
- * does NOT automatically call arcnet_go_tx.
- */
-static void arcnetA_continue_tx(struct device *dev)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR,maxsegsize=XMTU-4;
-	struct Outgoing *out=&(lp->outgoing);
-
-	BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
-		ARCSTATUS,lp->intx,lp->in_txhandler,lp->intmask);
-
-	if (lp->txready)
-	{
-		BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
-		return;
-	}
-
-	if (out->segnum>=out->numsegs)
-	{
-		BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
-			out->segnum+1,out->numsegs);
-	}
-
-	if (!out->segnum)	/* first packet */
-		out->hdr->split_flag=((out->numsegs-2)<<1)+1;
-	else
-		out->hdr->split_flag=out->segnum<<1;
-
-	out->seglen=maxsegsize;
-	if (out->seglen>out->dataleft) out->seglen=out->dataleft;
-
-	BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
-		out->segnum+1,out->seglen,out->numsegs,
-		out->length,out->hdr->split_flag);
-
-	arcnetAS_prepare_tx(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
-		sizeof(struct ClientData)-EXTRA_CLIENTDATA,
-		out->data,out->seglen,out->hdr->daddr,1);
-
-	out->dataleft-=out->seglen;
-	out->data+=out->seglen;
-	out->segnum++;
-}
-
-
-/* Given an skb, copy a packet into the ARCnet buffers for later transmission
- * by arcnet_go_tx.
- */
-static void
-arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
-		char *data,int length,int daddr,int exceptA)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	struct ClientData *arcsoft;
-	union ArcPacket *arcpacket =
-		(union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
-	int offset;
-
-#ifdef SLOW_XMIT_COPY
-	char *iptr,*iend,*optr;
-#endif
-
-	lp->txbuf=lp->txbuf^1;	/* XOR with 1 to alternate between 2 and 3 */
-
-	length+=hdrlen;
-
-	BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
-			hdr,length,data);
-
-#ifndef SLOW_XMIT_COPY
-	/* clean out the page to make debugging make more sense :) */
-	BUGLVL(D_DURING)
-		memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
-#endif
-
-	arcpacket->hardheader.destination=daddr;
-
-	/* load packet into shared memory */
-	if (length<=MTU)	/* Normal (256-byte) Packet */
-	{
-		arcpacket->hardheader.offset1=offset=256-length;
-		arcsoft=(struct ClientData *)
-			(&arcpacket->raw[offset]);
-	}
-	else if (length>=MinTU)	/* Extended (512-byte) Packet */
-	{
-		arcpacket->hardheader.offset1=0;
-		arcpacket->hardheader.offset2=offset=512-length;
-
-		arcsoft=(struct ClientData *)
-			(&arcpacket->raw[offset]);
-	}
-	else if (exceptA)		/* RFC1201 Exception Packet */
-	{
-		arcpacket->hardheader.offset1=0;
-		arcpacket->hardheader.offset2=offset=512-length-4;
-		arcsoft=(struct ClientData *)
-			(&arcpacket->raw[offset+4]);
-
-		/* exception-specific stuff - these four bytes
-		 * make the packet long enough to fit in a 512-byte
-		 * frame.
-		 */
-		arcpacket->raw[offset+0]=hdr[0];
-		arcpacket->raw[offset+1]=0xFF; /* FF flag */
-			arcpacket->raw[offset+2]=0xFF; /* FF padding */
-			arcpacket->raw[offset+3]=0xFF; /* FF padding */
-	}
-	else				/* "other" Exception packet */
-	{
-		/* RFC1051 - set 4 trailing bytes to 0 */
-		memset(&arcpacket->raw[508],0,4);
-
-		/* now round up to MinTU */
-		arcpacket->hardheader.offset1=0;
-		arcpacket->hardheader.offset2=offset=512-MinTU;
-		arcsoft=(struct ClientData *)(&arcpacket->raw[offset]);
-	}
-
-
-	/* copy the packet into ARCnet shmem
-	 *  - the first bytes of ClientData header are skipped
-	 */
-	memcpy((u_char*)arcsoft,
-		(u_char*)hdr,hdrlen);
-#ifdef SLOW_XMIT_COPY
-	for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcsoft+hdrlen;
-		iptr<iend; iptr++,optr++)
-	{
-		*optr=*iptr;
-		/*udelay(5);*/
-	}
-#else
-	memcpy((u_char*)arcsoft+hdrlen,
-		data,length-hdrlen);
-#endif
-
-	BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
-			daddr,length);
-
-	BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
-	lp->lastload_dest=daddr;
-	lp->txready=lp->txbuf;	/* packet is ready for sending */
-}
-
-/* Actually start transmitting a packet that was placed in the card's
- * buffer by arcnetAS_prepare_tx.  Returns 1 if a Tx is really started.
- *
- * This should probably always be called with the INTMASK register set to 0,
- * so go_tx is not called recursively.
- *
- * The enable_irq flag determines whether to actually write INTMASK value
- * to the card; TXFREEflag is always OR'ed into the memory variable either
- * way.
- */
-static int
-arcnet_go_tx(struct device *dev,int enable_irq)
-{
-	struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR;
-
-	BUGMSG(D_DURING,"go_tx: status=%Xh, intmask=%Xh, txready=%d, sending=%d\n",
-		ARCSTATUS,lp->intmask,lp->txready,lp->sending);
-
-	if (lp->sending || !lp->txready)
-	{
-		if (enable_irq && lp->sending)
-		{
-			lp->intmask |= TXFREEflag;
-			SETMASK;
-		}
-		return 0;
-	}
-
-	/* start sending */
-	ACOMMAND(TXcmd|(lp->txready<<3));
-
-	lp->stats.tx_packets++;
-	lp->txready=0;
-	lp->sending++;
-
-	lp->lasttrans_dest=lp->lastload_dest;
-	lp->lastload_dest=0;
-
-	lp->intmask |= TXFREEflag;
-
-	if (enable_irq)	SETMASK;
-
-	return 1;
-}
+	return 1;
+}
 
 
 /****************************************************************************
@@ -1911,274 +927,82 @@
  ****************************************************************************/
 
 
-/* The typical workload of the driver:  Handle the network interface
- * interrupts.  This doesn't do much right now except call arcnet_inthandler,
- * which takes different parameters but may be called from other places
- * as well.
- */
-static void
-arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)(irq2dev_map[irq]);
-	int ioaddr;
-
-	if (dev==NULL)
-	{
-		BUGLVL(D_DURING)
-			printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq);
-		return;
-	}
-
-	BUGMSG(D_DURING,"in arcnet_interrupt\n");
-
-	/* RESET flag was enabled - if !dev->start, we must clear it right
-	 * away (but nothing else) since inthandler() is never called.
-	 */
-	ioaddr=IOADDR;	/* can't do this before checking dev!=NULL */
-	if (!dev->start)
-	{
-		if (ARCSTATUS & RESETflag)
-			ACOMMAND(CFLAGScmd|RESETclear);
-		return;
-	}
-
-	/* Call the "real" interrupt handler. */
-	arcnet_inthandler(dev);
-}
-
-
-/* The actual interrupt handler routine - handle various IRQ's generated
- * by the card.
- */
-static void
-arcnet_inthandler(struct device *dev)
-{
-	struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR, status, boguscount = 3, didsomething;
-
-	if (IF_INTERRUPT)
-	{
-		BUGMSG(D_NORMAL,"DRIVER PROBLEM!  Nested arcnet interrupts!\n");
-		return;	/* don't even try. */
-	}
-
-	AINTMASK(0);
-	INTERRUPT = 1;
-
-	BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
-		ARCSTATUS,lp->intmask);
-
-	do
-	{
-		status = ARCSTATUS;
-		didsomething=0;
-
-
-		/* RESET flag was enabled - card is resetting and if RX
-		 * is disabled, it's NOT because we just got a packet.
-		 */
-		if (status & RESETflag)
-		{
-			BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
-					status);
-			arcnet_reset(dev,0);
-
-			/* all other flag values are just garbage */
-			break;
-		}
-
-
-		/* RX is inhibited - we must have received something. */
-		if (status & lp->intmask & NORXflag)
-		{
-			int recbuf=lp->recbuf=!lp->recbuf;
-
-			BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
-					status);
-
-			/* enable receive of our next packet */
-			EnableReceiver();
-
-			/* Got a packet. */
-			arcnet_rx(dev,!recbuf);
-			didsomething++;
-		}
-
-		/* it can only be an xmit-done irq if we're xmitting :) */
-		/*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
-		if (status & lp->intmask & TXFREEflag)
-		{
-			struct Outgoing *out=&(lp->outgoing);
-			int was_sending=lp->sending;
-
-			lp->intmask &= ~TXFREEflag;
-
-			lp->in_txhandler++;
-			if (was_sending) lp->sending--;
-
-			BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
-					status,out->numsegs,out->segnum,out->skb);
-
-			if (was_sending && !(status&TXACKflag))
-			{
-				if (lp->lasttrans_dest != 0)
-				{
-					BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
-						status,lp->lasttrans_dest);
-					lp->stats.tx_errors++;
-					lp->stats.tx_carrier_errors++;
-				}
-				else
-				{
-					BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
-							status,
-							lp->lasttrans_dest);
-				}
-			}
-
-			/* send packet if there is one */
-			arcnet_go_tx(dev,0);
-			didsomething++;
-
-			if (lp->intx)
-			{
-				BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
-					ARCSTATUS,lp->intx);
-				lp->in_txhandler--;
-				continue;
-			}
-
-			if (!lp->outgoing.skb)
-			{
-				BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
-
-				/* inform upper layers */
-				if (!lp->txready && IF_TBUSY)
-				{
-					TBUSY=0;
-					mark_bh(NET_BH);
-				}
-				lp->in_txhandler--;
-				continue;
-			}
-
-			/* if more than one segment, and not all segments
-			 * are done, then continue xmit.
-			 */
-			if (out->segnum<out->numsegs)
-				arcnetA_continue_tx(dev);
-			arcnet_go_tx(dev,0);
-
-			/* if segnum==numsegs, the transmission is finished;
-			 * free the skb.
-			 */
-			if (out->segnum>=out->numsegs)
-			{
-				/* transmit completed */
-				out->segnum++;
-				if (out->skb)
-					dev_kfree_skb(out->skb,FREE_WRITE);
-				out->skb=NULL;
-
-				/* inform upper layers */
-				if (!lp->txready && IF_TBUSY)
-				{
-					TBUSY=0;
-					mark_bh(NET_BH);
-				}
-			}
-			didsomething++;
-
-			lp->in_txhandler--;
-		}
-		else if (lp->txready && !lp->sending && !lp->intx)
-		{
-			BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
-						status);
-			arcnet_go_tx(dev,0);
-			didsomething++;
-		}
-
-#ifdef DETECT_RECONFIGS
-		if (status & (lp->intmask) & RECONflag)
-		{
-			ACOMMAND(CFLAGScmd|CONFIGclear);
-			lp->stats.tx_carrier_errors++;
-
-			#ifdef SHOW_RECONFIGS
-			BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
-					status);
-			#endif /* SHOW_RECONFIGS */
-
-			#ifdef RECON_THRESHOLD
-			/* is the RECON info empty or old? */
-			if (!lp->first_recon || !lp->last_recon ||
-				jiffies-lp->last_recon > HZ*10)
-			{
-				if (lp->network_down)
-					BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
-				lp->first_recon=lp->last_recon=jiffies;
-				lp->num_recons=lp->network_down=0;
-
-				BUGMSG(D_DURING,"recon: clearing counters.\n");
-			}
-			else /* add to current RECON counter */
-			{
-				lp->last_recon=jiffies;
-				lp->num_recons++;
-
-				BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
-					lp->num_recons,
-					(lp->last_recon-lp->first_recon)/HZ,
-					lp->network_down);
-
-				/* if network is marked up;
-				 * and first_recon and last_recon are 60+ sec
-				 *   apart;
-				 * and the average no. of recons counted is
-				 *   > RECON_THRESHOLD/min;
-				 * then print a warning message.
-				 */
-				if (!lp->network_down
-				    && (lp->last_recon-lp->first_recon)<=HZ*60
-				    && lp->num_recons >= RECON_THRESHOLD)
-				{
-					lp->network_down=1;
-					BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
-				}
-				else if (!lp->network_down
-				    && lp->last_recon-lp->first_recon > HZ*60)
-				{
-					/* reset counters if we've gone for
-					 * over a minute.
-					 */
-					lp->first_recon=lp->last_recon;
-					lp->num_recons=1;
-				}
-			}
-			#endif
-		}
-		#ifdef RECON_THRESHOLD
-		else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
-		{
-			if (lp->network_down)
-				BUGMSG(D_NORMAL,"cabling restored?\n");
-			lp->first_recon=lp->last_recon=0;
-			lp->num_recons=lp->network_down=0;
+/* The typical workload of the driver:  Handle the network interface
+ * interrupts. Establish which device needs attention, and call the correct
+ * chipset interrupt handler.
+ */
+void
+arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct arcnet_local *lp;
+	
+	if (dev==NULL)
+	  {
+	    BUGLVL(D_DURING)
+	      printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq);
+	    return;
+	  }
+	
+	BUGMSG(D_DURING,"in arcnet_interrupt\n");
+	
+	
+	lp=(struct arcnet_local *)dev->priv;
+	
+	/* RESET flag was enabled - if !dev->start, we must clear it right
+	 * away (but nothing else) since inthandler() is never called.
+	 */
+	
+	if (!dev->start)
+	  {	    
+	    if (ARCSTATUS & RESETflag)
+	      ACOMMAND(CFLAGScmd|RESETclear);
+	    return;
+	  }
+	
+
+	if (test_and_set_bit(0, (int *)&dev->interrupt))
+	  {
+	    BUGMSG(D_NORMAL,"DRIVER PROBLEM!  Nested arcnet interrupts!\n");
+	    return;	/* don't even try. */
+	  }
+#ifdef CONFIG_ARCNET_1051
+	lp->sdev->interrupt=1;
+#endif
+#ifdef CONFIG_ARCNET_ETH
+	lp->edev->interrupt=1;
+#endif
 
-			BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
-		}
-		#endif
-#endif /* DETECT_RECONFIGS */
-	} while (--boguscount && didsomething);
+	/* Call the "real" interrupt handler. */
+	(*lp->inthandler)(dev);
 
-	BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
-			ARCSTATUS,boguscount);
-	BUGMSG(D_DURING,"\n");
+#ifdef CONFIG_ARCNET_ETH
+	lp->edev->interrupt=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+	lp->sdev->interrupt=0;
+#endif
+	if (!test_and_clear_bit(0, (int *)&dev->interrupt))
+	  BUGMSG(D_NORMAL, "Someone cleared our dev->interrupt flag!\n");
 
-	SETMASK;	/* put back interrupt mask */
+}
 
-	INTERRUPT=0;
+void arcnet_tx_done(struct device *dev, struct arcnet_local *lp)
+{
+  if (dev->tbusy)
+    {
+#ifdef CONFIG_ARCNET_ETH
+      lp->edev->tbusy=0;
+#endif
+#ifdef CONFIG_ARCNET_1051
+      lp->sdev->tbusy=0;
+#endif
+      if (!test_and_clear_bit(0, (int *)&dev->tbusy))
+	BUGMSG(D_NORMAL, "In arcnet_tx_done: Someone cleared our dev->tbusy"
+	       " flag!\n");
+      
+      mark_bh(NET_BH);
+    }
 }
 
 
@@ -2188,374 +1012,335 @@
  *                                                                          *
  ****************************************************************************/
 
-
-/* A packet has arrived; grab it from the buffers and possibly unsplit it.
+/* 
  * This is a generic packet receiver that calls arcnet??_rx depending on the
  * protocol ID found.
  */
-static void
-arcnet_rx(struct device *dev,int recbuf)
-{
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR;
-	union ArcPacket *arcpacket=
-		(union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
-	u_char *arcsoft;
-	short length,offset;
-	u_char daddr,saddr;
 
-	lp->stats.rx_packets++;
-
-	saddr=arcpacket->hardheader.source;
-	daddr=arcpacket->hardheader.destination;
-
-	/* if source is 0, it's a "used" packet! */
-	if (saddr==0)
-	{
-		BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
-			ARCSTATUS);
-		lp->stats.rx_errors++;
-		return;
-	}
-	arcpacket->hardheader.source=0;
-
-	if (arcpacket->hardheader.offset1) /* Normal Packet */
-	{
-		offset=arcpacket->hardheader.offset1;
-		arcsoft=&arcpacket->raw[offset];
-		length=256-offset;
-	}
-	else		/* ExtendedPacket or ExceptionPacket */
-	{
-		offset=arcpacket->hardheader.offset2;
-		arcsoft=&arcpacket->raw[offset];
-
-		length=512-offset;
-	}
-
-       	BUGMSG(D_DURING,"received packet from %02Xh to %02Xh (%d bytes)\n",
-       			saddr,daddr,length);
-
-	/* call the right receiver for the protocol */
-	switch (arcsoft[0])
-	{
-	case ARC_P_IP:
-	case ARC_P_ARP:
-	case ARC_P_RARP:
-	case ARC_P_IPX:
-	case ARC_P_NOVELL_EC:
-		arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
-		break;
+void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr)
+{
+  struct device *dev=lp->adev;
+  
+  BUGMSG(D_DURING,"received packet from %02Xh to %02Xh (%d bytes)\n",
+	 saddr,daddr,length);
+  
+  /* call the right receiver for the protocol */
+  switch (arcsoft[0])
+    {
+    case ARC_P_IP:
+    case ARC_P_ARP:
+    case ARC_P_RARP:
+    case ARC_P_IPX:
+    case ARC_P_NOVELL_EC:
+      arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
+      break;
 #ifdef CONFIG_ARCNET_ETH
-	case ARC_P_ETHER:
-		arcnetE_rx(lp->edev,arcsoft,length,saddr,daddr);
-		break;
+    case ARC_P_ETHER:
+      arcnetE_rx(lp->edev,arcsoft,length,saddr,daddr);
+      break;
 #endif
 #ifdef CONFIG_ARCNET_1051
-	case ARC_P_IP_RFC1051:
-	case ARC_P_ARP_RFC1051:
-		arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
-		break;
-#endif
-	case ARC_P_DATAPOINT_BOOT:
-	case ARC_P_DATAPOINT_MOUNT:
-		break;
-	case ARC_P_POWERLAN_BEACON:
-	case ARC_P_POWERLAN_BEACON2:
-		break;
-	case ARC_P_LANSOFT: /* don't understand.  fall through. */
-	default:
-		BUGMSG(D_EXTRA,"received unknown protocol %d (%Xh) from station %d.\n",
-			arcsoft[0],arcsoft[0],saddr);
-		lp->stats.rx_errors++;
-		lp->stats.rx_crc_errors++;
-		break;
-	}
-
-        BUGLVL(D_RX) arcnet_dump_packet(dev,arcpacket->raw,length>240,"rx");
-
-
-#ifndef SLOW_XMIT_COPY
-	/* clean out the page to make debugging make more sense :) */
-	BUGLVL(D_DURING)
-		memset((void *)arcpacket->raw,0x42,512);
-#endif
-
-
-	/* If any worth-while packets have been received, a mark_bh(NET_BH)
-	 * has been done by netif_rx and Linux will handle them after we
-	 * return.
-	 */
+    case ARC_P_IP_RFC1051:
+    case ARC_P_ARP_RFC1051:
+      arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
+      break;
+#endif
+    case ARC_P_DATAPOINT_BOOT:
+    case ARC_P_DATAPOINT_MOUNT:
+      break;
+    case ARC_P_POWERLAN_BEACON:
+    case ARC_P_POWERLAN_BEACON2:
+      break;
+    case ARC_P_LANSOFT: /* don't understand.  fall through. */
+    default:
+      BUGMSG(D_EXTRA,"received unknown protocol %d (%Xh) from station %d.\n",
+	     arcsoft[0],arcsoft[0],saddr);
+      lp->stats.rx_errors++;
+      lp->stats.rx_crc_errors++;
+      break;
+    }
+  
+  /* If any worth-while packets have been received, a mark_bh(NET_BH)
+   * has been done by netif_rx and Linux will handle them after we
+   * return.
+   */
+  
+  
 }
 
 
+
 /* Packet receiver for "standard" RFC1201-style packets
  */
 static void
 arcnetA_rx(struct device *dev,u_char *buf,
-	int length,u_char saddr, u_char daddr)
+	   int length, u_char saddr, u_char daddr)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	struct sk_buff *skb;
-	struct ClientData *arcsoft,*soft;
-
-	BUGMSG(D_DURING,"it's an RFC1201 packet (length=%d)\n",
-			length);
-
-	/* compensate for EXTRA_CLIENTDATA (which isn't actually in the
-	 * packet)
-	 */
-	arcsoft=(struct ClientData *)(buf-EXTRA_CLIENTDATA);
-	length+=EXTRA_CLIENTDATA;
-
-	if (arcsoft->split_flag==0xFF)  /* Exception Packet */
-	{
-		BUGMSG(D_DURING,"compensating for exception packet\n");
-
-		/* skip over 4-byte junkola */
-		arcsoft=(struct ClientData *)
-			((u_char *)arcsoft + 4);
-		length-=4;
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  struct sk_buff *skb;
+  struct ClientData *arcsoft,*soft;
+  
+  BUGMSG(D_DURING,"it's an RFC1201 packet (length=%d)\n",
+	 length);
+  
+  /* compensate for EXTRA_CLIENTDATA (which isn't actually in the
+   * packet)
+   */
+  arcsoft=(struct ClientData *)(buf-EXTRA_CLIENTDATA);
+  length+=EXTRA_CLIENTDATA;
+  
+  if (arcsoft->split_flag==0xFF)  /* Exception Packet */
+    {
+      BUGMSG(D_DURING,"compensating for exception packet\n");
+      
+      /* skip over 4-byte junkola */
+      arcsoft=(struct ClientData *)
+	((u_char *)arcsoft + 4);
+      length-=4;
+    }
+  
+  if (!arcsoft->split_flag)		/* not split */
+    {
+      struct Incoming *in=&lp->incoming[saddr];
+      
+      BUGMSG(D_RX,"incoming is not split (splitflag=%d)\n",
+	     arcsoft->split_flag);
+      
+      if (in->skb)	/* already assembling one! */
+	{
+	  BUGMSG(D_EXTRA,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+		 in->sequence,arcsoft->split_flag,
+		 arcsoft->sequence);
+	  lp->aborted_seq=arcsoft->sequence;
+	  kfree_skb(in->skb,FREE_WRITE);
+	  lp->stats.rx_errors++;
+	  lp->stats.rx_missed_errors++;
+	  in->skb=NULL;
+	}
+      
+      in->sequence=arcsoft->sequence;
+      
+      skb = alloc_skb(length, GFP_ATOMIC);
+      if (skb == NULL) {
+	BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
+	lp->stats.rx_dropped++;
+	return;
+      }
+      soft=(struct ClientData *)skb->data;
+      
+      skb_put(skb,length);
+      skb->dev = dev;
+      
+      memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+	     (u_char *)arcsoft+EXTRA_CLIENTDATA,
+	     length-EXTRA_CLIENTDATA);
+      soft->daddr=daddr;
+      soft->saddr=saddr;
+      
+      /* ARP packets have problems when sent from DOS.
+       * source address is always 0 on some systems!  So we take
+       * the hardware source addr (which is impossible to fumble)
+       * and insert it ourselves.
+       */
+      if (soft->protocol_id == ARC_P_ARP)
+	{
+	  struct arphdr *arp=(struct arphdr *)
+	    ((char *)soft+sizeof(struct ClientData));
+	  
+	  /* make sure addresses are the right length */
+	  if (arp->ar_hln==1 && arp->ar_pln==4)
+	    {
+	      char *cptr=(char *)(arp)+sizeof(struct arphdr);
+	      
+	      if (!*cptr)	/* is saddr = 00? */
+		{
+		  BUGMSG(D_EXTRA,"ARP source address was 00h, set to %02Xh.\n",
+			 saddr);
+		  lp->stats.rx_crc_errors++;
+		  *cptr=saddr;
+		}
+	      else
+		{
+		  BUGMSG(D_DURING,"ARP source address (%Xh) is fine.\n",
+			 *cptr);
+		}
+	    }
+	  else
+	    {
+	      BUGMSG(D_NORMAL,"funny-shaped ARP packet. (%Xh, %Xh)\n",
+		     arp->ar_hln,arp->ar_pln);
+	      lp->stats.rx_errors++;
+	      lp->stats.rx_crc_errors++;
+	    }
+	}
+      
+      BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+      
+      lp->stats.rx_bytes += skb->len;
+      skb->protocol=arcnetA_type_trans(skb,dev);
+      netif_rx(skb);
+    }
+  else			/* split packet */
+    {
+      /* NOTE:  MSDOS ARP packet correction should only need to
+       * apply to unsplit packets, since ARP packets are so short.
+       *
+       * My interpretation of the RFC1201 (ARCnet) document is that
+       * if a packet is received out of order, the entire assembly
+       * process should be aborted.
+       *
+       * The RFC also mentions "it is possible for successfully
+       * received packets to be retransmitted."  As of 0.40 all
+       * previously received packets are allowed, not just the
+       * most recent one.
+       *
+       * We allow multiple assembly processes, one for each
+       * ARCnet card possible on the network.  Seems rather like
+       * a waste of memory.  Necessary?
+       */
+      
+      struct Incoming *in=&lp->incoming[saddr];
+      
+      BUGMSG(D_RX,"packet is split (splitflag=%d, seq=%d)\n",
+	     arcsoft->split_flag,in->sequence);
+      
+      if (in->skb && in->sequence!=arcsoft->sequence)
+	{
+	  BUGMSG(D_EXTRA,"wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+		 saddr,in->sequence,arcsoft->sequence,
+		 arcsoft->split_flag);
+	  kfree_skb(in->skb,FREE_WRITE);
+	  in->skb=NULL;
+	  lp->stats.rx_errors++;
+	  lp->stats.rx_missed_errors++;
+	  in->lastpacket=in->numpackets=0;
+	}
+      
+      if (arcsoft->split_flag & 1)	/* first packet in split */
+	{
+	  BUGMSG(D_RX,"brand new splitpacket (splitflag=%d)\n",
+		 arcsoft->split_flag);
+	  if (in->skb)	/* already assembling one! */
+	    {
+	      BUGMSG(D_EXTRA,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
+		     in->sequence,arcsoft->split_flag,
+		     arcsoft->sequence);
+	      lp->stats.rx_errors++;
+	      lp->stats.rx_missed_errors++;
+	      kfree_skb(in->skb,FREE_WRITE);
+	    }
+	  
+	  in->sequence=arcsoft->sequence;
+	  in->numpackets=((unsigned)arcsoft->split_flag>>1)+2;
+	  in->lastpacket=1;
+	  
+	  if (in->numpackets>16)
+	    {
+	      BUGMSG(D_EXTRA,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+		     arcsoft->split_flag);
+	      lp->stats.rx_errors++;
+	      lp->stats.rx_length_errors++;
+	      return;
+	    }
+	  
+	  in->skb=skb=alloc_skb(508*in->numpackets
+				+ sizeof(struct ClientData),
+				GFP_ATOMIC);
+	  if (skb == NULL) {
+	    BUGMSG(D_NORMAL,"(split) memory squeeze, dropping packet.\n");
+	    lp->stats.rx_dropped++;
+	    return;
+	  }
+	  
+	  soft=(struct ClientData *)skb->data;
+	  
+	  skb_put(skb,sizeof(struct ClientData));
+	  skb->dev=dev;
+	  
+	  memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+		 (u_char *)arcsoft+EXTRA_CLIENTDATA,
+		 sizeof(struct ClientData)-EXTRA_CLIENTDATA);
+	  soft->split_flag=0; /* final packet won't be split */
+	}
+      else			/* not first packet */
+	{
+	  int packetnum=((unsigned)arcsoft->split_flag>>1) + 1;
+	  
+	  /* if we're not assembling, there's no point
+	   * trying to continue.
+	   */
+	  if (!in->skb)
+	    {
+	      if (lp->aborted_seq != arcsoft->sequence)
+		{
+		  BUGMSG(D_EXTRA,"can't continue split without starting first! (splitflag=%d, seq=%d, aborted=%d)\n",
+			 arcsoft->split_flag,arcsoft->sequence, lp->aborted_seq);
+		  lp->stats.rx_errors++;
+		  lp->stats.rx_missed_errors++;
+		}
+	      return;
+	    }
+	  
+	  in->lastpacket++;
+	  if (packetnum!=in->lastpacket) /* not the right flag! */
+	    {
+	      /* harmless duplicate? ignore. */
+	      if (packetnum<=in->lastpacket-1)
+		{
+		  BUGMSG(D_EXTRA,"duplicate splitpacket ignored! (splitflag=%d)\n",
+			 arcsoft->split_flag);
+		  lp->stats.rx_errors++;
+		  lp->stats.rx_frame_errors++;
+		  return;
+		}
+	      
+	      /* "bad" duplicate, kill reassembly */
+	      BUGMSG(D_EXTRA,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
+		     in->sequence,arcsoft->split_flag,
+		     arcsoft->sequence);
+	      lp->aborted_seq=arcsoft->sequence;
+	      kfree_skb(in->skb,FREE_WRITE);
+	      in->skb=NULL;
+	      lp->stats.rx_errors++;
+	      lp->stats.rx_missed_errors++;
+	      in->lastpacket=in->numpackets=0;
+	      return;
+	    }
+	  
+	  soft=(struct ClientData *)in->skb->data;
+	}
+      
+      skb=in->skb;
+      
+      memcpy(skb->data+skb->len,
+	     (u_char *)arcsoft+sizeof(struct ClientData),
+	     length-sizeof(struct ClientData));
+      skb_put(skb,length-sizeof(struct ClientData));
+      
+      soft->daddr=daddr;
+      soft->saddr=saddr;
+      
+      /* are we done? */
+      if (in->lastpacket == in->numpackets)
+	{
+	  if (!skb || !in->skb)
+	    {
+	      BUGMSG(D_NORMAL,"?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
+		     skb,in->skb);
+	    }
+	  else
+	    {
+	      in->skb=NULL;
+	      in->lastpacket=in->numpackets=0;
+	      
+	      BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+	      
+	      lp->stats.rx_bytes += skb->len;
+	      skb->protocol=arcnetA_type_trans(skb,dev);
+	      netif_rx(skb);
+	    }
 	}
-
-	if (!arcsoft->split_flag)		/* not split */
-	{
-		struct Incoming *in=&lp->incoming[saddr];
-
-		BUGMSG(D_RX,"incoming is not split (splitflag=%d)\n",
-			arcsoft->split_flag);
-
-		if (in->skb)	/* already assembling one! */
-        	{
-        		BUGMSG(D_EXTRA,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
-        			in->sequence,arcsoft->split_flag,
-        			arcsoft->sequence);
-        		kfree_skb(in->skb,FREE_WRITE);
-			lp->stats.rx_errors++;
-        		lp->stats.rx_missed_errors++;
-        		in->skb=NULL;
-        	}
-
-        	in->sequence=arcsoft->sequence;
-
-         	skb = alloc_skb(length, GFP_ATOMIC);
-         	if (skb == NULL) {
-         		BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
-         		lp->stats.rx_dropped++;
-         		return;
-         	}
-         	soft=(struct ClientData *)skb->data;
-
-         	skb->len = length;
-         	skb->dev = dev;
-
-         	memcpy((u_char *)soft+EXTRA_CLIENTDATA,
-         		(u_char *)arcsoft+EXTRA_CLIENTDATA,
-         		length-EXTRA_CLIENTDATA);
-         	soft->daddr=daddr;
-         	soft->saddr=saddr;
-
-         	/* ARP packets have problems when sent from DOS.
-         	 * source address is always 0 on some systems!  So we take
-         	 * the hardware source addr (which is impossible to fumble)
-         	 * and insert it ourselves.
-         	 */
-         	if (soft->protocol_id == ARC_P_ARP)
-         	{
-         		struct arphdr *arp=(struct arphdr *)
-         				((char *)soft+sizeof(struct ClientData));
-
-         		/* make sure addresses are the right length */
-         		if (arp->ar_hln==1 && arp->ar_pln==4)
-         		{
-         			char *cptr=(char *)(arp)+sizeof(struct arphdr);
-
-         			if (!*cptr)	/* is saddr = 00? */
-         			{
-         				BUGMSG(D_EXTRA,"ARP source address was 00h, set to %02Xh.\n",
-         						saddr);
-         				lp->stats.rx_crc_errors++;
-         				*cptr=saddr;
-         			}
-         			else
-         			{
-         				BUGMSG(D_DURING,"ARP source address (%Xh) is fine.\n",
-         					*cptr);
-         			}
-         		}
-         		else
-         		{
-         			BUGMSG(D_NORMAL,"funny-shaped ARP packet. (%Xh, %Xh)\n",
-         				arp->ar_hln,arp->ar_pln);
-         			lp->stats.rx_errors++;
-         			lp->stats.rx_crc_errors++;
-         		}
-         	}
-
-		BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
-		skb->protocol=arcnetA_type_trans(skb,dev);
-
-         	netif_rx(skb);
-         }
-         else			/* split packet */
-         {
-	         /* NOTE:  MSDOS ARP packet correction should only need to
-	          * apply to unsplit packets, since ARP packets are so short.
-	          *
-	          * My interpretation of the RFC1201 (ARCnet) document is that
-	          * if a packet is received out of order, the entire assembly
-	          * process should be aborted.
-	          *
-	          * The RFC also mentions "it is possible for successfully
-	          * received packets to be retransmitted."  As of 0.40 all
-	          * previously received packets are allowed, not just the
-	          * most recent one.
-	          *
-	          * We allow multiple assembly processes, one for each
-	          * ARCnet card possible on the network.  Seems rather like
-	          * a waste of memory.  Necessary?
-	          */
-
-	        struct Incoming *in=&lp->incoming[saddr];
-
-	        BUGMSG(D_RX,"packet is split (splitflag=%d, seq=%d)\n",
-	        	arcsoft->split_flag,in->sequence);
-
-		if (in->skb && in->sequence!=arcsoft->sequence)
-		{
-			BUGMSG(D_EXTRA,"wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
-				saddr,in->sequence,arcsoft->sequence,
-				arcsoft->split_flag);
-	        	kfree_skb(in->skb,FREE_WRITE);
-	        	in->skb=NULL;
-			lp->stats.rx_errors++;
-	        	lp->stats.rx_missed_errors++;
-	        	in->lastpacket=in->numpackets=0;
-	        }
-
-	        if (arcsoft->split_flag & 1)	/* first packet in split */
-	        {
-	        	BUGMSG(D_RX,"brand new splitpacket (splitflag=%d)\n",
-	        		arcsoft->split_flag);
-	        	if (in->skb)	/* already assembling one! */
-	        	{
-	        		BUGMSG(D_EXTRA,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
-	        			in->sequence,arcsoft->split_flag,
-	        			arcsoft->sequence);
-				lp->stats.rx_errors++;
-	        		lp->stats.rx_missed_errors++;
-	        		kfree_skb(in->skb,FREE_WRITE);
-	        	}
-
-			in->sequence=arcsoft->sequence;
-	        	in->numpackets=((unsigned)arcsoft->split_flag>>1)+2;
-	        	in->lastpacket=1;
-
-	        	if (in->numpackets>16)
-	        	{
-	        		BUGMSG(D_EXTRA,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
-	        			arcsoft->split_flag);
-	        		lp->stats.rx_errors++;
-	        		lp->stats.rx_length_errors++;
-	        		return;
-	        	}
-
-                	in->skb=skb=alloc_skb(508*in->numpackets
-                			+ sizeof(struct ClientData),
-	                		GFP_ATOMIC);
-                	if (skb == NULL) {
-                		BUGMSG(D_NORMAL,"(split) memory squeeze, dropping packet.\n");
-                		lp->stats.rx_dropped++;
-                		return;
-                	}
-
-                	soft=(struct ClientData *)skb->data;
-
-                	skb->len=sizeof(struct ClientData);
-                	skb->dev=dev;
-
-	         	memcpy((u_char *)soft+EXTRA_CLIENTDATA,
-        	 		(u_char *)arcsoft+EXTRA_CLIENTDATA,
-         			sizeof(struct ClientData)-EXTRA_CLIENTDATA);
-         		soft->split_flag=0; /* final packet won't be split */
-	        }
-	        else			/* not first packet */
-	        {
-			int packetnum=((unsigned)arcsoft->split_flag>>1) + 1;
-
-			/* if we're not assembling, there's no point
-			 * trying to continue.
-			 */
-			if (!in->skb)
-			{
-				BUGMSG(D_EXTRA,"can't continue split without starting first! (splitflag=%d, seq=%d)\n",
-					arcsoft->split_flag,arcsoft->sequence);
-				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors++;
-				return;
-			}
-
-	        	in->lastpacket++;
-	        	if (packetnum!=in->lastpacket) /* not the right flag! */
-	        	{
-				/* harmless duplicate? ignore. */
-				if (packetnum<=in->lastpacket-1)
-				{
-					BUGMSG(D_EXTRA,"duplicate splitpacket ignored! (splitflag=%d)\n",
-						arcsoft->split_flag);
-					lp->stats.rx_errors++;
-					lp->stats.rx_frame_errors++;
-					return;
-				}
-
-				/* "bad" duplicate, kill reassembly */
-				BUGMSG(D_EXTRA,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
-					in->sequence,arcsoft->split_flag,
-					arcsoft->sequence);
-	        		kfree_skb(in->skb,FREE_WRITE);
-	        		in->skb=NULL;
-				lp->stats.rx_errors++;
-	        		lp->stats.rx_missed_errors++;
-	        		in->lastpacket=in->numpackets=0;
-	        		return;
-	        	}
-
-	               	soft=(struct ClientData *)in->skb->data;
-	        }
-
-	        skb=in->skb;
-
-	        memcpy(skb->data+skb->len,
-	               (u_char *)arcsoft+sizeof(struct ClientData),
-	               length-sizeof(struct ClientData));
-
-         	skb->len+=length-sizeof(struct ClientData);
-
-         	soft->daddr=daddr;
-         	soft->saddr=saddr;
-
-         	/* are we done? */
-         	if (in->lastpacket == in->numpackets)
-         	{
-         		if (!skb || !in->skb)
-         		{
-         			BUGMSG(D_NORMAL,"?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
-         				skb,in->skb);
-         		}
-         		else
-         		{
-	         		in->skb=NULL;
-        	 		in->lastpacket=in->numpackets=0;
-
-				BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
-				skb->protocol=arcnetA_type_trans(skb,dev);
-
-	         		netif_rx(skb);
-	         	}
-        	}
-         }
+    }
 }
 
 
@@ -2574,99 +1359,83 @@
 
 static struct net_device_stats *arcnet_get_stats(struct device *dev)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
-	return &lp->stats;
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  return &lp->stats;
 }
 
-#if 0	/* standard ARCnet cards have no promiscuous mode */
-/* Set or clear the multicast filter for this adaptor.
- * num_addrs == -1	Promiscuous mode, receive all packets
- * num_addrs == 0	Normal mode, clear multicast list
- * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
- *			best-effort filtering.
- */
-static void
-set_multicast_list(struct device *dev)
-{
-	if (num_addrs) {
-		/* Enable promiscuous mode */
-	} else
-		/* Disable promiscuous mode, use normal mode */
-}
-#endif
 
 /* Create the ARCnet ClientData header for an arbitrary protocol layer
  *
  * saddr=NULL	means use device source address (always will anyway)
  * daddr=NULL	means leave destination address (eg unresolved arp)
  */
-int arcnetA_header(struct sk_buff *skb,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len)
+static int arcnetA_header(struct sk_buff *skb,struct device *dev,
+			  unsigned short type,void *daddr,void *saddr,unsigned len)
 {
-	struct ClientData *head = (struct ClientData *)
-		skb_push(skb,dev->hard_header_len);
-	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
-	BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
-			saddr ? *(u_char*)saddr : -1,
-			daddr ? *(u_char*)daddr : -1,
-			type,type,len);
-
-	/* set the protocol ID according to RFC1201 */
-	switch(type)
-	{
-	case ETH_P_IP:
-		head->protocol_id=ARC_P_IP;
-		break;
-	case ETH_P_ARP:
-		head->protocol_id=ARC_P_ARP;
-		break;
-	case ETH_P_RARP:
-		head->protocol_id=ARC_P_RARP;
-		break;
-	case ETH_P_IPX:
-	case ETH_P_802_3:
-	case ETH_P_802_2:
-		head->protocol_id=ARC_P_IPX;
-		break;
-	case ETH_P_ATALK:
-		head->protocol_id=ARC_P_ATALK;
-		break;
-	default:
-		BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
-			type,type);
-		lp->stats.tx_errors++;
-		lp->stats.tx_aborted_errors++;
-		return 0;
-	}
-
-	/*
-	 * Set the source hardware address.
-	 *
-	 * This is pretty pointless for most purposes, but it can help
-	 * in debugging.  saddr is stored in the ClientData header and
-	 * removed before sending the packet (since ARCnet does not allow
-	 * us to change the source address in the actual packet sent)
-	 */
-	if(saddr)
-		head->saddr=((u_char*)saddr)[0];
-	else
-		head->saddr=((u_char*)(dev->dev_addr))[0];
-
-	head->split_flag=0;	/* split packets are done elsewhere */
-	head->sequence=0;	/* so are sequence numbers */
-
-	/* supposedly if daddr is NULL, we should ignore it... */
-	if(daddr)
-	{
-		head->daddr=((u_char*)daddr)[0];
-		return dev->hard_header_len;
-	}
-	else
-		head->daddr=0;	/* better fill one in anyway */
-
-	return -dev->hard_header_len;
+  struct ClientData *head = (struct ClientData *)
+    skb_push(skb,dev->hard_header_len);
+  struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+  
+  BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
+	 saddr ? *(u_char*)saddr : -1,
+	 daddr ? *(u_char*)daddr : -1,
+	 type,type,len);
+  
+  /* set the protocol ID according to RFC1201 */
+  switch(type)
+    {
+    case ETH_P_IP:
+      head->protocol_id=ARC_P_IP;
+      break;
+    case ETH_P_ARP:
+      head->protocol_id=ARC_P_ARP;
+      break;
+    case ETH_P_RARP:
+      head->protocol_id=ARC_P_RARP;
+      break;
+    case ETH_P_IPX:
+    case ETH_P_802_3:
+    case ETH_P_802_2:
+      head->protocol_id=ARC_P_IPX;
+      break;
+    case ETH_P_ATALK:
+      head->protocol_id=ARC_P_ATALK;
+      break;
+    default:
+      BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+	     type,type);
+      lp->stats.tx_errors++;
+      lp->stats.tx_aborted_errors++;
+      return 0;
+    }
+  
+  /*
+   * Set the source hardware address.
+   *
+   * This is pretty pointless for most purposes, but it can help
+   * in debugging.  saddr is stored in the ClientData header and
+   * removed before sending the packet (since ARCnet does not allow
+   * us to change the source address in the actual packet sent)
+   */
+  if(saddr)
+    head->saddr=((u_char*)saddr)[0];
+  else
+    head->saddr=((u_char*)(dev->dev_addr))[0];
+  
+  head->split_flag=0;	/* split packets are done elsewhere */
+  head->sequence=0;	/* so are sequence numbers */
+  
+  /* supposedly if daddr is NULL, we should ignore it... */
+  if(daddr)
+    {
+      head->daddr=((u_char*)daddr)[0];
+      return dev->hard_header_len;
+    }
+  else
+    head->daddr=0;	/* better fill one in anyway */
+  
+  return -dev->hard_header_len;
 }
 
 
@@ -2675,42 +1444,42 @@
  * (or in future other address resolution) has completed on this
  * sk_buff. We now let ARP fill in the other fields.
  */
-int arcnetA_rebuild_header(struct sk_buff *skb)
+static int arcnetA_rebuild_header(struct sk_buff *skb)
 {
-	struct ClientData *head = (struct ClientData *)skb->data;
-	struct device *dev=skb->dev;
-	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-	int status;
-
-	/*
-	 * Only ARP and IP are currently supported
-	 *
-	 * FIXME: Anyone want to spec IPv6 over ARCnet ?
-	 */
-
-	if(head->protocol_id != ARC_P_IP)
-	{
-		BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
-			head->protocol_id,head->protocol_id);
-		lp->stats.tx_errors++;
-		lp->stats.tx_aborted_errors++;
-		head->daddr=0;
-		/*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
-		return 0;
-	}
-
-	/*
-	 * Try to get ARP to resolve the header.
-	 */
+  struct ClientData *head = (struct ClientData *)skb->data;
+  struct device *dev=skb->dev;
+  struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+  int status;
+  
+  /*
+   * Only ARP and IP are currently supported
+   *
+   * FIXME: Anyone want to spec IPv6 over ARCnet ?
+   */
+  
+  if(head->protocol_id != ARC_P_IP)
+    {
+      BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+	     head->protocol_id,head->protocol_id);
+      lp->stats.tx_errors++;
+      lp->stats.tx_aborted_errors++;
+      head->daddr=0;
+      /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+      return 0;
+    }
+  
+  /*
+   * Try to get ARP to resolve the header.
+   */
 #ifdef CONFIG_INET
-	BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
-			head->saddr,head->daddr,head->protocol_id);
-	status=arp_find(&(head->daddr),skb)? 1 : 0;
-	BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
-			head->saddr,head->daddr,head->protocol_id);
-	return status;
+  BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
+	 head->saddr,head->daddr,head->protocol_id);
+  status=arp_find(&(head->daddr),skb)? 1 : 0;
+  BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
+	 head->saddr,head->daddr,head->protocol_id);
+  return status;
 #else
-	return 0;
+  return 0;
 #endif
 }
 
@@ -2719,42 +1488,42 @@
  *
  * With ARCnet we have to convert everything to Ethernet-style stuff.
  */
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
+static unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
 {
-	struct ClientData *head;
-	struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
-
-	/* Pull off the arcnet header. */
-	skb->mac.raw=skb->data;
-	skb_pull(skb,dev->hard_header_len);
-	head=(struct ClientData *)skb->mac.raw;
-
-	if (head->daddr==0)
-		skb->pkt_type=PACKET_BROADCAST;
-	else if (dev->flags&IFF_PROMISC)
-	{
-		/* if we're not sending to ourselves :) */
-		if (head->daddr != dev->dev_addr[0])
-			skb->pkt_type=PACKET_OTHERHOST;
-	}
-
-	/* now return the protocol number */
-	switch (head->protocol_id)
-	{
-	case ARC_P_IP:		return htons(ETH_P_IP);
-	case ARC_P_ARP:		return htons(ETH_P_ARP);
-	case ARC_P_RARP:	return htons(ETH_P_RARP);
-
-	case ARC_P_IPX:
-	case ARC_P_NOVELL_EC:
-		return htons(ETH_P_802_3);
-	default:
-		lp->stats.rx_errors++;
-		lp->stats.rx_crc_errors++;
-		return 0;
-	}
-
-	return htons(ETH_P_IP);
+  struct ClientData *head;
+  struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
+  
+  /* Pull off the arcnet header. */
+  skb->mac.raw=skb->data;
+  skb_pull(skb,dev->hard_header_len);
+  head=(struct ClientData *)skb->mac.raw;
+  
+  if (head->daddr==0)
+    skb->pkt_type=PACKET_BROADCAST;
+  else if (dev->flags&IFF_PROMISC)
+    {
+      /* if we're not sending to ourselves :) */
+      if (head->daddr != dev->dev_addr[0])
+	skb->pkt_type=PACKET_OTHERHOST;
+    }
+  
+  /* now return the protocol number */
+  switch (head->protocol_id)
+    {
+    case ARC_P_IP:		return htons(ETH_P_IP);
+    case ARC_P_ARP:		return htons(ETH_P_ARP);
+    case ARC_P_RARP:	return htons(ETH_P_RARP);
+      
+    case ARC_P_IPX:
+    case ARC_P_NOVELL_EC:
+      return htons(ETH_P_802_3);
+    default:
+      lp->stats.rx_errors++;
+      lp->stats.rx_crc_errors++;
+      return 0;
+    }
+  
+  return htons(ETH_P_IP);
 }
 
 
@@ -2769,19 +1538,17 @@
  */
 static int arcnetE_init(struct device *dev)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
-	ether_setup(dev); /* we're emulating ether here, not ARCnet */
-	dev->dev_addr[0]=0;
-	dev->dev_addr[5]=lp->stationid;
-	dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len-1;
-	dev->open=arcnetE_open_close;
-	dev->stop=arcnetE_open_close;
-	dev->hard_start_xmit=arcnetE_send_packet;
-
-	BUGMSG(D_NORMAL,"ARCnet Ethernet-Encap protocol initialized.\n");
-
-	return 0;
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  ether_setup(dev); /* we're emulating ether here, not ARCnet */
+  dev->dev_addr[0]=0;
+  dev->dev_addr[5]=lp->stationid;
+  dev->mtu=512-sizeof(struct archdr)-dev->hard_header_len-1;
+  dev->open=arcnetE_open_close;
+  dev->stop=arcnetE_open_close;
+  dev->hard_start_xmit=arcnetE_send_packet;
+  
+  return 0;
 }
 
 
@@ -2790,7 +1557,7 @@
  */
 static int arcnetE_open_close(struct device *dev)
 {
-	return 0;
+  return 0;
 }
 
 
@@ -2799,97 +1566,80 @@
 static int
 arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR,bad;
-	union ArcPacket *arcpacket =
-		(union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
-	u_char *arcsoft,daddr;
-	short offset,length=skb->len+1;
-
-	lp->intx++;
-
-	bad=arcnet_send_packet_bad(skb,dev);
-	if (bad)
-	{
-		lp->intx--;
-		return bad;
-	}
-
-	TBUSY=1;
-
-	if (length>XMTU)
-	{
-		BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
-			length);
-		BUGMSG(D_NORMAL,"transmit aborted.\n");
-
-		dev_kfree_skb(skb,FREE_WRITE);
-		lp->intx--;
-		return 0;
-	}
-
-	BUGMSG(D_DURING,"starting tx sequence...\n");
-
-	lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate btw 2 & 3 */
-
-#ifndef SLOW_XMIT_COPY
-	/* clean out the page to make debugging make more sense :) */
-	BUGLVL(D_DURING)
-		memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
-#endif
-
-	/* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
-	if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
-		daddr=arcpacket->hardheader.destination=0;
-	else
-		daddr=arcpacket->hardheader.destination=
-			((struct ethhdr*)(skb->data))->h_dest[5];
-
-	/* load packet into shared memory */
-	offset=512-length;
-	if (length>MTU)		/* long/exception packet */
-	{
-		if (length<MinTU) offset-=3;
-		arcpacket->hardheader.offset1=0;
-		arcpacket->hardheader.offset2=offset;
-	}
-	else			/* short packet */
-	{
-		arcpacket->hardheader.offset1=(offset-=256);
-	}
-
-	BUGMSG(D_DURING," length=%Xh, offset=%Xh, offset1=%Xh, offset2=%Xh\n",
-			length,offset,arcpacket->hardheader.offset1,
-			arcpacket->hardheader.offset2);
-
-	arcsoft=&arcpacket->raw[offset];
-	arcsoft[0]=ARC_P_ETHER;
-	arcsoft++;
-
-	/* copy the packet into ARCnet shmem
-	 *  - the first bytes of ClientData header are skipped
-	 */
-	BUGMSG(D_DURING,"ready to memcpy\n");
-
-	memcpy(arcsoft,skb->data,skb->len);
-
-	BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
-			daddr,length);
-
-	BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>=240,"tx");
-
-	lp->lastload_dest=daddr;
-	lp->txready=lp->txbuf;	/* packet is ready for sending */
-
-	dev_kfree_skb(skb,FREE_WRITE);
-
-	if (arcnet_go_tx(dev,1))
-	{
-		/* inform upper layers */
-		TBUSY=0;
-		mark_bh(NET_BH);
-	}
-
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int bad,oldmask=0;
+  u_char daddr;
+  short offset,length=skb->len+1;
+  u_char proto=ARC_P_ETHER;
+  
+  lp->intx++;
+  
+  oldmask |= lp->intmask;
+  lp->intmask=0;
+  SETMASK;
+  
+  bad=arcnet_send_packet_bad(skb,dev);
+  if (bad)
+    {
+      lp->intx--;
+      lp->intmask=oldmask;
+      SETMASK;
+      return bad;
+    }
+  
+  /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
+
+  lp->intmask=oldmask;
+  SETMASK;
+  
+  if (length>XMTU)
+    {
+      BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
+	     length);
+      BUGMSG(D_NORMAL,"transmit aborted.\n");
+      
+      dev_kfree_skb(skb,FREE_WRITE);
+      lp->intx--;
+      return 0;
+    }
+  
+  BUGMSG(D_DURING,"starting tx sequence...\n");
+  
+  /* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
+  if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
+    daddr=0;
+  else
+    daddr=((struct ethhdr*)(skb->data))->h_dest[5];
+  
+  /* load packet into shared memory */
+  offset=512-length;
+  if (length>MTU)		/* long/exception packet */
+    {
+      if (length<MinTU) offset-=3;
+    }
+  else			/* short packet */
+    {
+      offset-=256;
+    }
+  
+  BUGMSG(D_DURING," length=%Xh, offset=%Xh\n",
+	 length,offset);
+  
+
+
+  (*lp->prepare_tx)(dev, &proto, 1, skb->data, length-1, daddr, 0, 
+			   offset);
+  
+  
+  
+  dev_kfree_skb(skb,FREE_WRITE);
+
+  if (arcnet_go_tx(dev,1))
+    {
+      /* inform upper layers */
+      arcnet_tx_done(lp->adev, lp);
+    }
+  
 	dev->trans_start=jiffies;
 	lp->intx--;
 
@@ -2920,15 +1670,16 @@
        		return;
        	}
 
-       	skb->len = length;
+       	skb_put(skb,length);
+
        	skb->dev = dev;
 
        	memcpy(skb->data,(u_char *)arcsoft+1,length-1);
 
         BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
 
+	lp->stats.rx_bytes += skb->len;
 	skb->protocol=eth_type_trans(skb,dev);
-
         netif_rx(skb);
 }
 
@@ -2945,23 +1696,22 @@
  */
 static int arcnetS_init(struct device *dev)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
-	arcnet_setup(dev);
-
-	/* And now fill particular fields with arcnet values */
-	dev->dev_addr[0]=lp->stationid;
-	dev->hard_header_len=sizeof(struct S_ClientData);
-	dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len
-		+ S_EXTRA_CLIENTDATA;
-	dev->open=arcnetS_open_close;
-	dev->stop=arcnetS_open_close;
-	dev->hard_start_xmit=arcnetS_send_packet;
-	dev->hard_header=arcnetS_header;
-	dev->rebuild_header=arcnetS_rebuild_header;
-	BUGMSG(D_NORMAL,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
-
-	return 0;
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  arcnet_setup(dev);
+  
+  /* And now fill particular fields with arcnet values */
+  dev->dev_addr[0]=lp->stationid;
+  dev->hard_header_len=sizeof(struct S_ClientData);
+  dev->mtu=512-sizeof(struct archdr)-dev->hard_header_len
+    + S_EXTRA_CLIENTDATA;
+  dev->open=arcnetS_open_close;
+  dev->stop=arcnetS_open_close;
+  dev->hard_start_xmit=arcnetS_send_packet;
+  dev->hard_header=arcnetS_header;
+  dev->rebuild_header=arcnetS_rebuild_header;
+  
+  return 0;
 }
 
 
@@ -2970,7 +1720,7 @@
  */
 static int arcnetS_open_close(struct device *dev)
 {
-	return 0;
+  return 0;
 }
 
 
@@ -2979,63 +1729,61 @@
 static int
 arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	int ioaddr=IOADDR,bad,length;
-	struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
-
-	lp->intx++;
-
-	bad=arcnet_send_packet_bad(skb,dev);
-	if (bad)
-	{
-		lp->intx--;
-		return bad;
-	}
-
-	TBUSY=1;
-
-	length = 1 < skb->len ? skb->len : 1;
-
-	BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
-
-	/* fits in one packet? */
-	if (length-S_EXTRA_CLIENTDATA<=XMTU)
-	{
-		arcnetAS_prepare_tx(dev,
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int bad,length;
+  struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
+  
+  lp->intx++;
+  
+  bad=arcnet_send_packet_bad(skb,dev);
+  if (bad)
+    {
+      lp->intx--;
+      return bad;
+    }
+
+  /* arcnet_send_packet_pad has already set tbusy - don't bother here. */
+  
+  length = 1 < skb->len ? skb->len : 1;
+  
+  BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"tx");
+  
+  /* fits in one packet? */
+  if (length-S_EXTRA_CLIENTDATA<=XMTU)
+    {
+      (*lp->prepare_tx)(dev,
 			skb->data+S_EXTRA_CLIENTDATA,
 			sizeof(struct S_ClientData)-S_EXTRA_CLIENTDATA,
 			skb->data+sizeof(struct S_ClientData),
 			length-sizeof(struct S_ClientData),
-			hdr->daddr,0);
-
-		/* done right away */
-		dev_kfree_skb(skb,FREE_WRITE);
-
-		if (arcnet_go_tx(dev,1))
-		{
-			/* inform upper layers */
-			TBUSY=0;
-			mark_bh(NET_BH);
-		}
-	}
-	else			/* too big for one - not accepted */
-	{
-		BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
-			length);
-		dev_kfree_skb(skb,FREE_WRITE);
-		lp->stats.tx_dropped++;
-		TBUSY=0;
-		mark_bh(NET_BH);
-	}
-
-	dev->trans_start=jiffies;
-	lp->intx--;
-
-	/* make sure we didn't ignore a TX IRQ while we were in here */
-	lp->intmask |= TXFREEflag;
-	SETMASK;
-
-	return 0;
+			hdr->daddr,0,0);
+      
+      /* done right away */
+      dev_kfree_skb(skb,FREE_WRITE);
+      
+      if (arcnet_go_tx(dev,1))
+	{
+	  /* inform upper layers */
+	  arcnet_tx_done(lp->adev, lp);
+	}
+    }
+  else			/* too big for one - not accepted */
+    {
+      BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
+	     length);
+      dev_kfree_skb(skb,FREE_WRITE);
+      lp->stats.tx_dropped++;
+      arcnet_tx_done(lp->adev, lp);
+    }
+  
+  dev->trans_start=jiffies;
+  lp->intx--;
+  
+  /* make sure we didn't ignore a TX IRQ while we were in here */
+  lp->intmask |= TXFREEflag;
+  SETMASK;
+  
+  return 0;
 }
 
 
@@ -3043,47 +1791,45 @@
  */
 static void
 arcnetS_rx(struct device *dev,u_char *buf,
-	int length,u_char saddr, u_char daddr)
+	   int length,u_char saddr, u_char daddr)
 {
-	struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-	struct sk_buff *skb;
-	struct S_ClientData *arcsoft,*soft;
-
-	arcsoft=(struct S_ClientData *)(buf-S_EXTRA_CLIENTDATA);
-	length+=S_EXTRA_CLIENTDATA;
-
-	BUGMSG(D_DURING,"it's an RFC1051 packet (length=%d)\n",
-			length);
-
-
-
-	{    /* was "if not split" in A protocol, S is never split */
-
-         	skb = alloc_skb(length, GFP_ATOMIC);
-         	if (skb == NULL) {
-         		BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
-         		lp->stats.rx_dropped++;
-         		return;
-         	}
-         	soft=(struct S_ClientData *)skb->data;
-		skb->len = length;
-		memcpy((u_char *)soft + sizeof(struct S_ClientData)
-				- S_EXTRA_CLIENTDATA,
-			(u_char *)arcsoft + sizeof(struct S_ClientData)
-				- S_EXTRA_CLIENTDATA,
-			length - sizeof(struct S_ClientData)
-				+ S_EXTRA_CLIENTDATA);
-		soft->protocol_id=arcsoft->protocol_id;
-         	soft->daddr=daddr;
-         	soft->saddr=saddr;
-         	skb->dev = dev;  /* is already lp->sdev */
-
-		BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
-
-		skb->protocol=arcnetS_type_trans(skb,dev);
-
-         	netif_rx(skb);
-         }
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  struct sk_buff *skb;
+  struct S_ClientData *arcsoft,*soft;
+  
+  arcsoft=(struct S_ClientData *)(buf-S_EXTRA_CLIENTDATA);
+  length+=S_EXTRA_CLIENTDATA;
+  
+  BUGMSG(D_DURING,"it's an RFC1051 packet (length=%d)\n",
+	 length);
+  
+  
+  
+  {    /* was "if not split" in A protocol, S is never split */
+    
+    skb = alloc_skb(length, GFP_ATOMIC);
+    if (skb == NULL) {
+      BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
+      lp->stats.rx_dropped++;
+      return;
+    }
+    soft=(struct S_ClientData *)skb->data;
+    skb_put(skb,length);
+
+    memcpy((u_char *)soft + sizeof(struct S_ClientData) - S_EXTRA_CLIENTDATA,
+	   (u_char *)arcsoft + sizeof(struct S_ClientData) -S_EXTRA_CLIENTDATA,
+	   length - sizeof(struct S_ClientData) + S_EXTRA_CLIENTDATA);
+    soft->protocol_id=arcsoft->protocol_id;
+    soft->daddr=daddr;
+    soft->saddr=saddr;
+    skb->dev = dev;  /* is already lp->sdev */
+    
+    BUGLVL(D_SKB) arcnet_dump_skb(dev,skb,"rx");
+    
+    lp->stats.rx_bytes += skb->len;
+    skb->protocol=arcnetS_type_trans(skb,dev);
+    netif_rx(skb);
+  }
 }
 
 
@@ -3092,55 +1838,55 @@
  * saddr=NULL	means use device source address (always will anyway)
  * daddr=NULL	means leave destination address (eg unresolved arp)
  */
-int arcnetS_header(struct sk_buff *skb,struct device *dev,
-		unsigned short type,void *daddr,void *saddr,unsigned len)
+static int arcnetS_header(struct sk_buff *skb,struct device *dev,
+			  unsigned short type,void *daddr,void *saddr,unsigned len)
 {
-	struct S_ClientData *head = (struct S_ClientData *)
-		skb_push(skb,dev->hard_header_len);
-	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
-	/* set the protocol ID according to RFC1051 */
-	switch(type)
-	{
-	case ETH_P_IP:
-		head->protocol_id=ARC_P_IP_RFC1051;
-		BUGMSG(D_DURING,"S_header: IP_RFC1051 packet.\n");
-		break;
-	case ETH_P_ARP:
-		head->protocol_id=ARC_P_ARP_RFC1051;
-		BUGMSG(D_DURING,"S_header: ARP_RFC1051 packet.\n");
-		break;
-	default:
-		BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
-			type,type);
-		lp->stats.tx_errors++;
-		lp->stats.tx_aborted_errors++;
-		return 0;
-	}
-
-	/*
-	 * Set the source hardware address.
-	 *
-	 * This is pretty pointless for most purposes, but it can help
-	 * in debugging.  saddr is stored in the ClientData header and
-	 * removed before sending the packet (since ARCnet does not allow
-	 * us to change the source address in the actual packet sent)
-	 */
-	if(saddr)
-		head->saddr=((u_char*)saddr)[0];
-	else
-		head->saddr=((u_char*)(dev->dev_addr))[0];
-
-	/* supposedly if daddr is NULL, we should ignore it... */
-	if(daddr)
-	{
+  struct S_ClientData *head = (struct S_ClientData *)
+    skb_push(skb,dev->hard_header_len);
+  struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+  
+  /* set the protocol ID according to RFC1051 */
+  switch(type)
+    {
+    case ETH_P_IP:
+      head->protocol_id=ARC_P_IP_RFC1051;
+      BUGMSG(D_DURING,"S_header: IP_RFC1051 packet.\n");
+      break;
+    case ETH_P_ARP:
+      head->protocol_id=ARC_P_ARP_RFC1051;
+      BUGMSG(D_DURING,"S_header: ARP_RFC1051 packet.\n");
+      break;
+    default:
+      BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+	     type,type);
+      lp->stats.tx_errors++;
+      lp->stats.tx_aborted_errors++;
+      return 0;
+    }
+  
+  /*
+   * Set the source hardware address.
+   *
+   * This is pretty pointless for most purposes, but it can help
+   * in debugging.  saddr is stored in the ClientData header and
+   * removed before sending the packet (since ARCnet does not allow
+   * us to change the source address in the actual packet sent)
+   */
+  if(saddr)
+    head->saddr=((u_char*)saddr)[0];
+  else
+    head->saddr=((u_char*)(dev->dev_addr))[0];
+  
+  /* supposedly if daddr is NULL, we should ignore it... */
+  if(daddr)
+    {
 		head->daddr=((u_char*)daddr)[0];
 		return dev->hard_header_len;
-	}
-	else
-		head->daddr=0;	/* better fill one in anyway */
-
-	return -dev->hard_header_len;
+    }
+  else
+    head->daddr=0;	/* better fill one in anyway */
+  
+  return -dev->hard_header_len;
 }
 
 
@@ -3148,34 +1894,34 @@
  * (or in future other address resolution) has completed on this
  * sk_buff. We now let ARP fill in the other fields.
  */
-int arcnetS_rebuild_header(struct sk_buff *skb)
+static int arcnetS_rebuild_header(struct sk_buff *skb)
 {
-	struct device *dev=skb->dev;
-	struct S_ClientData *head = (struct S_ClientData *)skb->data;
-	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
-	/*
-	 * Only ARP and IP are currently supported
-	 */
-
-	if(head->protocol_id != ARC_P_IP_RFC1051)
-	{
-		BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
-			head->protocol_id,head->protocol_id);
-		lp->stats.tx_errors++;
-		lp->stats.tx_aborted_errors++;
-		head->daddr=0;
-		/*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
-		return 0;
-	}
-
-	/*
-	 * Try to get ARP to resolve the header.
-	 */
+  struct device *dev=skb->dev;
+  struct S_ClientData *head = (struct S_ClientData *)skb->data;
+  struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+  
+  /*
+   * Only ARP and IP are currently supported
+   */
+  
+  if(head->protocol_id != ARC_P_IP_RFC1051)
+    {
+      BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+	     head->protocol_id,head->protocol_id);
+      lp->stats.tx_errors++;
+      lp->stats.tx_aborted_errors++;
+      head->daddr=0;
+      /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+      return 0;
+    }
+  
+  /*
+   * Try to get ARP to resolve the header.
+   */
 #ifdef CONFIG_INET
-	return arp_find(&(head->daddr),skb)? 1 : 0;
+  return arp_find(&(head->daddr),skb)? 1 : 0;
 #else
-	return 0;
+  return 0;
 #endif
 }
 
@@ -3186,36 +1932,36 @@
  */
 unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev)
 {
-	struct S_ClientData *head;
-	struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
-
-	/* Pull off the arcnet header. */
-	skb->mac.raw=skb->data;
-	skb_pull(skb,dev->hard_header_len);
-	head=(struct S_ClientData *)skb->mac.raw;
-
-	if (head->daddr==0)
-		skb->pkt_type=PACKET_BROADCAST;
-	else if (dev->flags&IFF_PROMISC)
-	{
-		/* if we're not sending to ourselves :) */
-		if (head->daddr != dev->dev_addr[0])
-			skb->pkt_type=PACKET_OTHERHOST;
-	}
-
-	/* now return the protocol number */
-	switch (head->protocol_id)
-	{
-	case ARC_P_IP_RFC1051:	return htons(ETH_P_IP);
-	case ARC_P_ARP_RFC1051:	return htons(ETH_P_ARP);
-	case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
-	default:
-		lp->stats.rx_errors++;
-		lp->stats.rx_crc_errors++;
-		return 0;
-	}
-
-	return htons(ETH_P_IP);
+  struct S_ClientData *head;
+  struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
+  
+  /* Pull off the arcnet header. */
+  skb->mac.raw=skb->data;
+  skb_pull(skb,dev->hard_header_len);
+  head=(struct S_ClientData *)skb->mac.raw;
+  
+  if (head->daddr==0)
+    skb->pkt_type=PACKET_BROADCAST;
+  else if (dev->flags&IFF_PROMISC)
+    {
+      /* if we're not sending to ourselves :) */
+      if (head->daddr != dev->dev_addr[0])
+	skb->pkt_type=PACKET_OTHERHOST;
+    }
+  
+  /* now return the protocol number */
+  switch (head->protocol_id)
+    {
+    case ARC_P_IP_RFC1051:	return htons(ETH_P_IP);
+    case ARC_P_ARP_RFC1051:	return htons(ETH_P_ARP);
+    case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
+    default:
+      lp->stats.rx_errors++;
+      lp->stats.rx_crc_errors++;
+      return 0;
+    }
+  
+  return htons(ETH_P_IP);
 }
 
 #endif	/* CONFIG_ARCNET_1051 */
@@ -3227,88 +1973,151 @@
  ****************************************************************************/
 
 
+
 #ifdef MODULE
 
-static char devicename[9] = "";
-static struct device thiscard = {
-  devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-  0, 0, 0, 0,
-  0, 0,  /* I/O address, IRQ */
-  0, 0, 0, NULL, arcnet_probe
-};
+void cleanup_module(void)
+{
+  printk("Generic arcnet support removed.\n");
+}
+
+void arcnet_use_count(int open)
+{ 
+  if (open)
+    MOD_INC_USE_COUNT;
+  else
+    MOD_DEC_USE_COUNT;
+}
+
+#else
 
+void arcnet_use_count(int open)
+{
+}
 
-static int io=0x0;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
-static int irqnum=0;	/* or use the insmod io= irq= shmem= options */
-static int irq=0;
-static int shmem=0;
-static char *device = NULL;	/* use eg. device="arc1" to change name */
+struct device arcnet_devs[MAX_ARCNET_DEVS];
+int arcnet_num_devs=0;
+char arcnet_dev_names[MAX_ARCNET_DEVS][10];
 
-MODULE_PARM(io, "i");
-MODULE_PARM(irqnum, "i");
-MODULE_PARM(shmem, "i");
+void arcnet_init(void)
+{
+  int c;
+  
+  (void) init_module();
 
-#ifdef RIM_I_MODE
-  static int node=0;	/* you must specify the node ID for RIM I cards */
+  /* Don't register_netdev here. The chain hasn't been initialised. */
+  
+#ifdef CONFIG_ARCNET_COM90xx
+  if ((!com90xx_explicit) && arcnet_num_devs < MAX_ARCNET_DEVS)
+    {
+      arcnet_devs[arcnet_num_devs].init=arc90xx_probe;
+      arcnet_devs[arcnet_num_devs].name=
+	(char *)&arcnet_dev_names[arcnet_num_devs];
+      arcnet_num_devs++;
+    }
 #endif
 
-int init_module(void)
-{
-	struct device *dev=&thiscard;
-	if (device)
-		strcpy(dev->name,device);
-	#ifndef CONFIG_ARCNET_ETHNAME
-	  else if (!dev->name[0]) strcpy(dev->name,"arc0");
-	#endif
+  if (!arcnet_num_devs)
+    {
+      printk("Don't forget to load the chipset driver.\n");
+      return;  
+    }
 
-	#ifdef RIM_I_MODE
-	if (node) io=node;
-	#endif
+  /* Link into the device chain */
 
-	dev->base_addr=io;
+  /* Q: Should we put ourselves at the beginning or the end of the chain? */
+  /* Probably the end, because we're not so fast, but... */
 
-	if (irq) irqnum=irq;
-	dev->irq=irqnum;
-	if (dev->irq==2) dev->irq=9;
+  for (c=0; c< (arcnet_num_devs-1); c++)
+    arcnet_devs[c].next=&arcnet_devs[c+1];
 
-	if (shmem)
-	{
-		dev->mem_start=shmem;
-		dev->mem_end=thiscard.mem_start+512*4-1;
-		dev->rmem_start=thiscard.mem_start+512*0;
-		dev->rmem_end=thiscard.mem_start+512*2-1;
-	}
+  arcnet_devs[c].next=dev_base;
+  dev_base=&arcnet_devs[0];
+  
+  /* Give names to those without them */
+  
+  for (c=0; c< arcnet_num_devs; c++)
+    if (!arcnet_dev_names[c][0])
+      arcnet_makename((char *)&arcnet_dev_names[c]);
 
-	if (register_netdev(dev) != 0)
-		return -EIO;
-	return 0;
 }
-
-void cleanup_module(void)
+static
+#endif /* MODULE */
+int init_module()
 {
-	struct device *dev=&thiscard;
-	int ioaddr=IOADDR;
+ 
+#if 1
+  BUGLVL(D_EXTRA)
+    {
+      printk("arcnet: ***\n");
+      printk("arcnet: * Read arcnet.txt for important release notes!\n");
+      printk("arcnet: *\n");
+      printk("arcnet: * This is an ALPHA version!  (Last stable release: v2.56)  E-mail me if\n");
+      printk("arcnet: * you have any questions, comments, or bug reports.\n");
+      printk("arcnet: ***\n");
+    }
+#endif
+
+  printk("%sGeneric arcnet support for Linux kernel.\n"
+	 "Available protocols: ARCnet RFC1201"
+#ifdef CONFIG_ARCNET_ETH
+	 ", Ethernet-Encap"
+#endif
+#ifdef CONFIG_ARCNET_1051
+	 ", ARCnet RFC1051"
+#endif
+#ifdef MODULE
+	 ".\nDon't forget to load the chipset driver"
+#endif
+	".\n",version);
+  return 0;
+}
+
 
-	if (dev->start) arcnet_close(dev);
 
-	/* Flush TX and disable RX */
-	if (ioaddr)
-	{
-		AINTMASK(0);		/* disable IRQ's */
-		ACOMMAND(NOTXcmd);	/* stop transmit */
-		ACOMMAND(NORXcmd);	/* disable receive */
-	}
 
-	if (dev->irq)
-	{
-		irq2dev_map[dev->irq] = NULL;
-		free_irq(dev->irq,NULL);
-	}
 
-	if (dev->base_addr) RELEASE_REGION(dev->base_addr,ARCNET_TOTAL_SIZE);
-	unregister_netdev(dev);
-	kfree(dev->priv);
-	dev->priv = NULL;
+void arcnet_makename(char *device)
+{
+  __u32 arcmask=0;
+  struct device *dev;
+  char *c;
+  int arcnum;
+
+  for (dev = dev_base; dev; dev=dev->next)
+    {
+      arcnum=0;
+
+      if (!strncmp(dev->name, "arc", 3))
+	{
+	  c = &dev->name[3];
+	  while ((*c)>='0' && (*c)<='9')
+	    {
+	      arcnum *= 10;
+	      arcnum += (*(c++)-'0');
+      	    }
+	  
+	  if (arcnum<32)              
+	    arcmask |= ((__u32)1 << arcnum);
+	}
+      
+      
+    }
+  
+  /* arcmask now holds a mask of the first 32 arcnet names available */
+  
+  if ((__u32)~arcmask)
+    {
+      for (arcnum=0; arcmask&1; arcnum++, arcmask >>=1)
+	;
+      
+      sprintf (device, "arc%d",arcnum);
+    } 
+  else
+    {
+      printk (KERN_INFO "arcnet: Can't find name for device\n");
+      sprintf (device, "arc???");
+    }
 }
 
-#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.52/linux/drivers/net/com20020.c linux/drivers/net/com20020.c
--- v2.1.52/linux/drivers/net/com20020.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/com20020.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,1144 @@
+/* com20020.c:
+        Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
+
+	Derived from the original arcnet.c,
+	Written 1994-1996 by Avery Pennarun,
+	which was in turn derived from skeleton.c by Donald Becker.
+
+	Contact Avery at: apenwarr@bond.net or
+	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+	**********************
+
+	The original copyright of skeleton.c was as follows:
+
+	skeleton.c Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the
+        Director, National Security Agency.  This software may only be used
+        and distributed according to the terms of the GNU Public License as
+        modified by SRC, incorporated herein by reference.
+
+	**********************
+
+	For more details, see drivers/net/arcnet.c
+
+	**********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.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/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+
+
+
+
+/* External functions from arcnet.c */
+
+
+
+#if ARCNET_DEBUG_MAX & D_SKB
+extern void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+			    char *desc);
+#else
+#define arcnet_dump_skb(dev,skb,desc) ;
+#endif
+
+#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
+extern void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+			       char *desc);
+#else
+#define arcnet_dump_packet(dev,buffer,ext,desc) ;
+#endif
+extern void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+extern void arcnet_makename(char *device);
+extern void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+extern void arcnet_setup(struct device *dev);
+extern int arcnet_go_tx(struct device *dev,int enable_irq);
+extern void arcnetA_continue_tx(struct device *dev);
+extern void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+extern void arcnet_use_count(int open);
+
+
+/* Internal function declarations */
+
+static int arc20020_probe(struct device *dev);
+static void arc20020_rx(struct device *dev,int recbuf);
+static int arc20020_found(struct device *dev,int ioaddr,int airq);
+static void arc20020_inthandler (struct device *dev);
+static int arc20020_reset (struct device *dev, int reset_delay);
+static void arc20020_setmask (struct device *dev, u_char mask);
+static void arc20020_command (struct device *dev, u_char command);
+static u_char arc20020_status (struct device *dev);
+static void arc20020_en_dis_able_TX (struct device *dev, int enable); 
+static void arc20020_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+       		      char *data,int length,int daddr,int exceptA, int offset);
+static  void arc20020_openclose(int open);
+static void arc20020_set_mc_list(struct device *dev);
+static u_char get_buffer_byte (struct device *dev, unsigned offset);
+static void put_buffer_byte (struct device *dev, unsigned offset, u_char datum);
+static void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+static void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int node=0;
+static int io=0x0;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0;	/* or use the insmod io= irq= shmem= options */
+static char *device;	/* use eg. device="arc1" to change name */
+static int timeout=3;
+static int backplane=0;
+static int clock=0;
+
+MODULE_PARM(node,"i");
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+MODULE_PARM(timeout,"i");
+MODULE_PARM(backplane,"i");
+MODULE_PARM(clock,"i");
+#else
+__initfunc(void com20020_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+
+
+/* Handy defines for ARCnet specific stuff */
+
+static char *clockrates[]={"2.5 Mb/s","1.25Mb/s","625 Kb/s","312.5 Kb/s",
+		    "156.25 Kb/s", "Reserved", "Reserved", 
+		    "Reserved"};
+
+
+/* The number of low I/O ports used by the ethercard. */
+#define ARCNET_TOTAL_SIZE 9
+
+#define _INTMASK (ioaddr+0)	/* writable */
+#define _STATUS  (ioaddr+0)	/* readable */
+#define _COMMAND (ioaddr+1)	/* writable, returns random vals on read (?) */
+#define _CONFIG  (ioaddr+6)      /* Configuration register */
+#define _DIAGSTAT (ioaddr+1)     /* Diagnostic status register */
+#define _MEMDATA  (ioaddr+4)     /* Data port for IO-mapped memory */
+#define _ADDR_HI  (ioaddr+2)     /* Control registers for said */
+#define _ADDR_LO  (ioaddr+3)
+
+
+#define RDDATAflag      0x80     /* Next access is a read/~write */
+#define NEWNXTIDflag    0x02     /* ID to which token is passed has changed */
+
+#define TXENflag        0x20     /* Enable TX (in CONFIG register) */
+
+#define PROMISCflag     0x10     /* Enable RCV_ALL (in SETUP register) */
+
+#define REGTENTID (lp->config &= ~3);
+#define REGNID (lp->config = (lp->config&~2)|1);
+#define REGSETUP (lp->config = (lp->config&~1)|2);
+#define REGNXTID (lp->config |= 3);
+
+#define ARCRESET { outb(lp->config | 0x80, _CONFIG); \
+		    udelay(5);                        \
+		    outb(lp->config , _CONFIG);       \
+                  }
+#define ARCRESET0 { outb(0x18 | 0x80, _CONFIG);   \
+		    udelay(5);                       \
+		    outb(0x18 , _CONFIG);            \
+                  }
+
+
+#define ARCSTATUS	inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk)	outb((msk),_INTMASK)
+#define SETCONF 	outb((lp->config),_CONFIG)
+
+
+/****************************************************************************
+ *                                                                          *
+ * IO-mapped operation routines                                             *
+ *                                                                          *
+ ****************************************************************************/
+
+u_char get_buffer_byte (struct device *dev, unsigned offset)
+{
+  int ioaddr=dev->base_addr;
+
+  outb(offset >> 8 | RDDATAflag,  _ADDR_HI); 
+  outb(offset & 0xff, _ADDR_LO);
+
+  return inb(_MEMDATA);
+}
+
+void put_buffer_byte (struct device *dev, unsigned offset, u_char datum)
+{
+  int ioaddr=dev->base_addr;
+
+  outb(offset >> 8,   _ADDR_HI); 
+  outb(offset & 0xff, _ADDR_LO);
+
+  outb(datum, _MEMDATA);
+}
+
+
+#undef ONE_AT_A_TIME_TX
+#undef ONE_AT_A_TIME_RX
+
+void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+  int ioaddr=dev->base_addr;
+
+  outb( (offset >> 8) | AUTOINCflag | RDDATAflag,   _ADDR_HI);
+  outb( offset & 0xff,                 _ADDR_LO);
+
+  while (length--)
+#ifdef ONE_AT_A_TIME_RX
+    *(dest++) = get_buffer_byte(dev,offset++);
+#else
+    *(dest++) = inb (_MEMDATA);
+#endif
+}
+
+void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+  int ioaddr=dev->base_addr;
+
+  outb( (offset >> 8) | AUTOINCflag,   _ADDR_HI);
+  outb( offset & 0xff,                 _ADDR_LO);
+ 
+  while (length--)
+#ifdef ONE_AT_A_TIME_TX
+    put_buffer_byte(dev,offset++,*(dest++));
+#else
+    outb (*(dest++), _MEMDATA);
+#endif
+}
+
+
+static const char *version =
+ "com20020.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+/****************************************************************************
+ *                                                                          *
+ * Probe and initialization                                                 *
+ *                                                                          *
+ ****************************************************************************/
+
+
+
+/* We cannot probe for an IO mapped card either, although we can check that
+ * it's where we were told it was, and even autoirq
+ */
+
+__initfunc(int arc20020_probe(struct device *dev))
+{
+  int ioaddr=dev->base_addr,status,delayval;
+  unsigned long airqmask;
+  
+  BUGLVL(D_NORMAL) printk(version);
+  
+  if (ioaddr<0x200)
+    {
+      BUGMSG(D_NORMAL,"No autoprobe for IO mapped cards; you "
+	     "must specify the base address!\n");
+      return -ENODEV;
+    }
+  
+  if (check_region(ioaddr, ARCNET_TOTAL_SIZE))
+    {
+      BUGMSG(D_NORMAL,"IO region %xh-%xh already allocated.\n",
+	     ioaddr,ioaddr+ARCNET_TOTAL_SIZE-1);
+      return -ENXIO;
+    }
+  
+  if (ARCSTATUS == 0xFF)
+    {
+      BUGMSG(D_NORMAL,"IO address %x empty\n",ioaddr);
+      return -ENODEV;
+    }
+  
+  ARCRESET0;
+  JIFFER(RESETtime);
+  
+  status=ARCSTATUS;
+  
+  if ((status & 0x99) 
+      !=  (NORXflag|TXFREEflag|RESETflag))
+    {
+      BUGMSG(D_NORMAL,"Status invalid (%Xh).\n",status);
+      return -ENODEV;
+    }
+  
+  
+  BUGMSG(D_INIT_REASONS,"Status after reset: %X\n",status);
+  
+  
+  /* Enable TX */
+  outb(0x39,_CONFIG);
+  outb(inb(ioaddr+8),ioaddr+7);
+  
+  
+  ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+  
+  BUGMSG(D_INIT_REASONS,"Status after reset acknowledged: %X\n",status);
+  
+  /* Reset card. */
+  
+  outb(0x98,_CONFIG);
+  udelay(5);
+  outb(0x18,_CONFIG);
+  
+  /* Read first loc'n of memory */
+  
+  outb(0 | RDDATAflag | AUTOINCflag ,_ADDR_HI);
+  outb(0,_ADDR_LO);
+  
+  if ((status=inb(_MEMDATA)) != 0xd1)
+    {
+      BUGMSG(D_NORMAL,"Signature byte not found.\n");
+      return -ENODEV;
+    }
+  
+  if (!dev->irq)
+    {
+      /* if we do this, we're sure to get an IRQ since the
+       * card has just reset and the NORXflag is on until
+       * we tell it to start receiving.
+       */
+      BUGMSG(D_INIT_REASONS, "intmask was %d:\n",inb(_INTMASK));
+      outb(0, _INTMASK);      
+      airqmask = probe_irq_on();
+      outb(NORXflag,_INTMASK);
+      udelay(1);
+      outb(0,_INTMASK);
+      dev->irq = probe_irq_off(airqmask);
+      
+      if (dev->irq<=0)
+	{
+	  BUGMSG(D_INIT_REASONS,"Autoprobe IRQ failed first time\n");
+          airqmask = probe_irq_on();
+          outb(NORXflag,_INTMASK);
+          udelay(5);
+          outb(0,_INTMASK);
+          dev->irq = probe_irq_off(airqmask);
+          if (dev->irq<=0)
+	    {
+	      BUGMSG(D_NORMAL,"Autoprobe IRQ failed.\n");
+	      return -ENODEV;
+            }
+	}
+    }
+  
+  return arc20020_found(dev,dev->base_addr,dev->irq);
+}
+
+
+
+/* Set up the struct device associated with this card.  Called after
+ * probing succeeds.
+ */
+__initfunc(int arc20020_found(struct device *dev,int ioaddr,int airq))
+{
+  struct arcnet_local *lp;
+  
+  /* reserve the irq */
+  if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM20020)",NULL))
+    {
+      BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+      return -ENODEV;
+    }
+  irq2dev_map[airq]=dev;
+  dev->irq=airq;
+  
+  /* reserve the I/O region - guaranteed to work by check_region */
+  request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (COM20020)");
+  dev->base_addr=ioaddr;
+  
+  
+  dev->mem_start=dev->mem_end=dev->rmem_start=dev->rmem_end=(long)NULL;
+  
+  
+  /* Initialize the rest of the device structure. */
+  
+  dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+  if (dev->priv == NULL)
+    {
+      irq2dev_map[airq] = NULL;
+      free_irq(airq,NULL);
+      release_region(ioaddr,ARCNET_TOTAL_SIZE);
+      return -ENOMEM;
+    }
+
+  memset(dev->priv,0,sizeof(struct arcnet_local));
+  lp=(struct arcnet_local *)(dev->priv);
+  lp->card_type = ARC_20020;
+  lp->card_type_str = "COM 20020";
+  
+  lp->arcnet_reset=arc20020_reset;
+  lp->asetmask=arc20020_setmask;
+  lp->astatus=arc20020_status;
+  lp->acommand=arc20020_command;
+  lp->en_dis_able_TX=arc20020_en_dis_able_TX;
+  lp->openclose_device=arc20020_openclose;
+  lp->prepare_tx=arc20020_prepare_tx;
+  lp->inthandler=arc20020_inthandler;
+  
+  
+  dev->set_multicast_list = arc20020_set_mc_list;
+  
+  /* Fill in the fields of the device structure with generic
+   * values.
+   */
+  arcnet_setup(dev);
+  
+  /* And now fill particular fields with arcnet values */
+  dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+  dev->hard_header_len=sizeof(struct ClientData);
+  lp->sequence=1;
+  lp->recbuf=0;
+  
+  BUGMSG(D_DURING,"ClientData header size is %d.\n",
+	 sizeof(struct ClientData));
+  BUGMSG(D_DURING,"HardHeader size is %d.\n",
+	 sizeof(struct archdr));
+  
+  /* get and check the station ID from offset 1 in shmem */
+  lp->timeout = dev->dev_addr[3] & 3; dev->dev_addr[3]=0;
+  lp->backplane =dev->dev_addr[1] & 1; dev->dev_addr[1]=0;
+  lp->setup = (dev->dev_addr[2] & 7) << 1; dev->dev_addr[2]=0;
+  
+  if (dev->dev_addr[0])
+    lp->stationid=dev->dev_addr[0];
+  else
+    lp->stationid=inb(ioaddr+8);            /* FIX ME - We should check that
+					       this is valid before using it */
+  lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2);
+  /* Default 0x38 + register: Node ID */
+  SETCONF;
+  outb(lp->stationid, ioaddr+7);
+  
+  REGSETUP;
+  SETCONF;
+  outb(lp->setup, ioaddr+7);
+  
+  if (!lp->stationid)
+    BUGMSG(D_NORMAL,"WARNING!  Station address 00 is reserved "
+	   "for broadcasts!\n");
+  else if (lp->stationid==255)
+    BUGMSG(D_NORMAL,"WARNING!  Station address FF may confuse "
+	   "DOS networking programs!\n");
+  dev->dev_addr[0]=lp->stationid;
+  
+  BUGMSG(D_NORMAL,"ARCnet COM20020: station %02Xh found at %03lXh, IRQ %d.\n",
+	 lp->stationid, dev->base_addr,dev->irq);
+  
+  if (lp->backplane)
+    BUGMSG (D_NORMAL, "Using backplane mode.\n");
+  
+  if (lp->timeout != 3)
+    BUGMSG (D_NORMAL, "Using Extended Timeout value of %d.\n",lp->timeout);
+  if (lp->setup)
+    {
+      BUGMSG (D_NORMAL, "Using CKP %d - Data rate %s.\n",
+	      lp->setup >>1,clockrates[lp->setup >> 1] );	
+    }
+  return 0;
+}
+
+
+/****************************************************************************
+ *                                                                          *
+ * Utility routines for arcnet.c                                            *
+ *                                                                          *
+ ****************************************************************************/
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc20020_reset(struct device *dev,int reset_delay)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  short ioaddr=dev->base_addr;
+  int delayval,recbuf=lp->recbuf;
+  
+  if (reset_delay==3)
+    {
+      ARCRESET;
+      return 0;
+    }
+  
+  /* no IRQ's, please! */
+  lp->intmask=0;
+  SETMASK;
+  
+  BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+	 dev->name,ARCSTATUS);
+  
+  lp->config = 0x20 | (lp->timeout<<3) | (lp->backplane<<2); 
+  /* power-up defaults */
+  SETCONF;
+  
+  if (reset_delay)
+    {
+      /* reset the card */
+      ARCRESET;
+      JIFFER(RESETtime);
+    }
+  
+  ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+  ACOMMAND(CFLAGScmd|CONFIGclear);
+  
+  /* verify that the ARCnet signature byte is present */
+  
+  if (get_buffer_byte(dev,0) != TESTvalue)
+    {
+      BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+      return 1;
+    }
+  
+  /* clear out status variables */
+  recbuf=lp->recbuf=0;
+  lp->txbuf=2;
+  
+  /* enable extended (512-byte) packets */
+  ACOMMAND(CONFIGcmd|EXTconf);
+  
+  /* and enable receive of our first packet to the first buffer */
+  EnableReceiver();
+  
+  /* re-enable interrupts */
+  lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+  lp->intmask|=RECONflag;
+#endif
+  SETMASK;
+  
+  /* done!  return success. */
+  return 0;
+}
+
+
+/* Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+ *			best-effort filtering.
+ *      FIX ME - do multicast stuff, not just promiscuous.
+ */
+static void
+arc20020_set_mc_list(struct device *dev)
+{
+  struct arcnet_local *lp=dev->priv;
+  int ioaddr=dev->base_addr;
+  
+  if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP))
+    {	/* Enable promiscuous mode */
+      if (!(lp->setup & PROMISCflag))
+	BUGMSG(D_NORMAL, "Setting promiscuous flag...\n");
+      REGSETUP;
+      SETCONF;
+      lp->setup|=PROMISCflag;
+      outb(lp->setup,ioaddr+7);
+    } else
+      /* Disable promiscuous mode, use normal mode */
+      {
+	if ((lp->setup & PROMISCflag))
+	  BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n");
+	REGSETUP;
+	SETCONF;
+	lp->setup &= ~PROMISCflag;
+	outb(lp->setup,ioaddr+7);
+      } 
+}
+
+
+static void arc20020_openclose(int open)
+{
+  if (open)
+    MOD_INC_USE_COUNT;
+  else
+    MOD_DEC_USE_COUNT;
+}
+
+static void arc20020_en_dis_able_TX(struct device *dev, int enable)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr;
+  
+  lp->config=enable?(lp->config | TXENflag):(lp->config & ~TXENflag);
+  SETCONF;
+}
+
+
+static void arc20020_setmask(struct device *dev, u_char mask)
+{
+  short ioaddr=dev->base_addr;
+  
+  AINTMASK(mask);
+}
+
+static u_char arc20020_status(struct device *dev)
+{
+  short ioaddr=dev->base_addr;
+
+  return ARCSTATUS;
+}
+
+static void arc20020_command(struct device *dev, u_char cmd)
+{
+  short ioaddr=dev->base_addr;
+
+  ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc20020_inthandler(struct device *dev)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr, status, boguscount = 3, didsomething, 
+    dstatus;
+  
+	
+    
+  AINTMASK(0);
+  
+  BUGMSG(D_DURING,"in arc20020_inthandler (status=%Xh, intmask=%Xh)\n",
+	 ARCSTATUS,lp->intmask);
+  
+  do
+    {
+      status = ARCSTATUS;
+      didsomething=0;
+      
+      
+      /* RESET flag was enabled - card is resetting and if RX
+       * is disabled, it's NOT because we just got a packet.
+       */
+      if (status & RESETflag)
+	{
+	  BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+		 status);
+	  arc20020_reset(dev,0);
+	  
+	  /* all other flag values are just garbage */
+	  break;
+	}
+      
+      
+      /* RX is inhibited - we must have received something. */
+      if (status & lp->intmask & NORXflag)
+	{
+	  int recbuf=lp->recbuf=!lp->recbuf;
+	  int oldaddr=0;
+	  
+	  BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+		 status);
+	  
+	  /* enable receive of our next packet */
+	  EnableReceiver();
+	  
+	  if (lp->intx)
+	    oldaddr=(inb(_ADDR_HI)<<8) | inb(_ADDR_LO);
+	  
+	  
+	  /* Got a packet. */
+	  arc20020_rx(dev,!recbuf);
+	  
+	  
+	  if (lp->intx)
+	    {
+	      outb( (oldaddr >> 8),  _ADDR_HI);
+	      outb( oldaddr & 0xff, _ADDR_LO);
+	    }
+	  
+	  didsomething++;
+	}
+      
+      /* it can only be an xmit-done irq if we're xmitting :) */
+      /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+      if (status & lp->intmask & TXFREEflag)
+	{
+	  struct Outgoing *out=&(lp->outgoing);
+	  int was_sending=lp->sending;
+	  
+	  lp->intmask &= ~TXFREEflag;
+	  
+	  lp->in_txhandler++;
+	  if (was_sending) lp->sending--;
+	  
+	  BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+		 status,out->numsegs,out->segnum,out->skb);
+	  
+	  if (was_sending && !(status&TXACKflag))
+	    {
+	      if (lp->lasttrans_dest != 0)
+		{
+		  BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+			 status,lp->lasttrans_dest);
+		  lp->stats.tx_errors++;
+		  lp->stats.tx_carrier_errors++;
+		}
+	      else
+		{
+		  BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+			 status,
+			 lp->lasttrans_dest);
+		}
+	    }
+	  
+	  /* send packet if there is one */
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	  
+	  if (lp->intx)
+	    {
+	      BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+		     ARCSTATUS,lp->intx);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  if (!lp->outgoing.skb)
+	    {
+	      BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  /* if more than one segment, and not all segments
+	   * are done, then continue xmit.
+	   */
+	  if (out->segnum<out->numsegs)
+	    arcnetA_continue_tx(dev);
+	  arcnet_go_tx(dev,0);
+	  
+	  /* if segnum==numsegs, the transmission is finished;
+	   * free the skb.
+	   */
+	  if (out->segnum>=out->numsegs)
+	    {
+	      /* transmit completed */
+	      out->segnum++;
+	      if (out->skb)
+		{
+		  lp->stats.tx_bytes += out->skb->len;
+		  dev_kfree_skb(out->skb,FREE_WRITE);
+		}
+	      out->skb=NULL;
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	    }
+	  didsomething++;
+	  
+	  lp->in_txhandler--;
+	}
+      else if (lp->txready && !lp->sending && !lp->intx)
+	{
+	  BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+		 status);
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	}
+      
+      if ((dstatus=inb(_DIAGSTAT)) & NEWNXTIDflag)
+	{
+	  REGNXTID;
+	  SETCONF;
+	  BUGMSG(D_EXTRA,"New NextID detected: %X\n",inb(ioaddr+7));
+	}
+      
+      
+#ifdef DETECT_RECONFIGS
+      if (status & (lp->intmask) & RECONflag)
+	{
+	  ACOMMAND(CFLAGScmd|CONFIGclear);
+	  lp->stats.tx_carrier_errors++;
+	  
+#ifdef SHOW_RECONFIGS
+	  
+	  BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh, diag status=%Xh, config=%X)\n",
+		 status,dstatus,lp->config);
+#endif /* SHOW_RECONFIGS */
+	  
+#ifdef RECON_THRESHOLD
+	  /* is the RECON info empty or old? */
+	  if (!lp->first_recon || !lp->last_recon ||
+	      jiffies-lp->last_recon > HZ*10)
+	    {
+	      if (lp->network_down)
+		BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+	      lp->first_recon=lp->last_recon=jiffies;
+	      lp->num_recons=lp->network_down=0;
+	      
+	      BUGMSG(D_DURING,"recon: clearing counters.\n");
+	    }
+	  else /* add to current RECON counter */
+	    {
+	      lp->last_recon=jiffies;
+	      lp->num_recons++;
+	      
+	      BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+		     lp->num_recons,
+		     (lp->last_recon-lp->first_recon)/HZ,
+		     lp->network_down);
+	      
+	      /* if network is marked up;
+	       * and first_recon and last_recon are 60+ sec
+	       *   apart;
+	       * and the average no. of recons counted is
+	       *   > RECON_THRESHOLD/min;
+	       * then print a warning message.
+	       */
+	      if (!lp->network_down
+		  && (lp->last_recon-lp->first_recon)<=HZ*60
+		  && lp->num_recons >= RECON_THRESHOLD)
+		{
+		  lp->network_down=1;
+		  BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+		}
+	      else if (!lp->network_down
+		       && lp->last_recon-lp->first_recon > HZ*60)
+		{
+		  /* reset counters if we've gone for
+		   * over a minute.
+		   */
+		  lp->first_recon=lp->last_recon;
+		  lp->num_recons=1;
+		}
+	    }
+	}
+      else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+	{
+	  if (lp->network_down)
+	    BUGMSG(D_NORMAL,"cabling restored?\n");
+	  lp->first_recon=lp->last_recon=0;
+	  lp->num_recons=lp->network_down=0;
+	  
+	  BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+	}
+#endif /* DETECT_RECONFIGS */
+    } while (--boguscount && didsomething);
+  
+  BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+	 ARCSTATUS,boguscount);
+  BUGMSG(D_DURING,"\n");
+  
+  SETMASK;	/* put back interrupt mask */
+  
+}
+
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */ 
+
+static void
+arc20020_rx(struct device *dev,int recbuf)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr;
+  union ArcPacket packetbuf;
+  union ArcPacket *arcpacket=&packetbuf;
+  u_char *arcsoft;
+  short length,offset;
+  u_char daddr,saddr;
+  
+  lp->stats.rx_packets++;
+  
+  get_whole_buffer(dev,recbuf*512,4,(char *)arcpacket);
+  
+  saddr=arcpacket->hardheader.source;
+  
+  /* if source is 0, it's a "used" packet! */
+  if (saddr==0)
+    {
+      BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+	     ARCSTATUS);
+      lp->stats.rx_errors++;
+      return;
+    }
+  /* Set source address to zero to mark it as old */
+  
+  put_buffer_byte(dev,recbuf*512,0);
+  
+  arcpacket->hardheader.source=0;
+  
+  daddr=arcpacket->hardheader.destination;
+  
+  if (arcpacket->hardheader.offset1) /* Normal Packet */
+    {
+      offset=arcpacket->hardheader.offset1;
+      arcsoft=&arcpacket->raw[offset];
+      length=256-offset;
+    }
+  else		/* ExtendedPacket or ExceptionPacket */
+    {
+      offset=arcpacket->hardheader.offset2;
+      arcsoft=&arcpacket->raw[offset];
+      length=512-offset;
+    }
+  
+  get_whole_buffer(dev,recbuf*512+offset,length,(char *)arcpacket+offset);
+  
+  arcnet_rx(lp, arcsoft, length, saddr, daddr);
+  
+  BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+}
+
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc20020_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+		    char *data,int length,int daddr,int exceptA, int offset)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  lp->txbuf=lp->txbuf^1;	/* XOR with 1 to alternate between 2 and 3 */
+  
+  length+=hdrlen;
+  
+  BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+	 hdr,length,data);
+  
+  put_buffer_byte(dev, lp->txbuf*512+1, daddr);
+
+  /* load packet into shared memory */
+  if (length<=MTU)	/* Normal (256-byte) Packet */
+    put_buffer_byte(dev, lp->txbuf*512+2, offset=offset?offset:256-length);
+  
+  else if (length>=MinTU || offset)	/* Extended (512-byte) Packet */
+    {
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=offset?offset:512-length);
+    }
+  else if (exceptA)		/* RFC1201 Exception Packet */
+    {
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=512-length-4);
+      
+      /* exception-specific stuff - these four bytes
+       * make the packet long enough to fit in a 512-byte
+       * frame.
+       */
+      
+      put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+      offset+=4;
+    }
+  else				/* "other" Exception packet */
+    {
+      /* RFC1051 - set 4 trailing bytes to 0 */
+      
+      put_whole_buffer(dev,lp->txbuf*512+508,4,"\0\0\0\0");
+      
+      /* now round up to MinTU */
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=512-MinTU);
+    }
+  
+  /* copy the packet into ARCnet shmem
+   *  - the first bytes of ClientData header are skipped
+   */
+  
+  put_whole_buffer(dev, 512*lp->txbuf+offset, hdrlen,(u_char *)hdr);
+  put_whole_buffer(dev, 512*lp->txbuf+offset+hdrlen,length-hdrlen,data);
+  
+  BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+	 daddr,length);
+  
+  lp->lastload_dest=daddr;
+  lp->txready=lp->txbuf;	/* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ *                                                                          *
+ * Kernel Loadable Module Support                                           *
+ *                                                                          *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static struct device *cards[16]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+			  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+
+int init_module(void)
+{
+  struct device *dev;
+  
+  cards[0]=dev=(struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
+  if (!dev)
+    return -ENOMEM;
+  
+  memset(dev, 0, sizeof(struct device));
+  
+  dev->name=(char *)kmalloc(9, GFP_KERNEL);
+  if (!dev->name)
+    {
+      kfree(dev);
+      return -ENOMEM;
+    }
+  dev->init=arc20020_probe;
+
+  if (device)
+    strcpy(dev->name,device);
+  else arcnet_makename(dev->name);
+
+  if (node && node != 0xff)
+    dev->dev_addr[0]=node;
+  
+  if (backplane) dev->dev_addr[1]=backplane?1:0;
+  if (clock) dev->dev_addr[2]=clock&7;
+  dev->dev_addr[3]=timeout&3;
+
+  dev->base_addr=io;
+  dev->irq=irq;
+  
+  if (dev->irq==2) dev->irq=9;
+
+  if (register_netdev(dev) != 0)
+    return -EIO;
+  
+  /* Increase use count of arcnet.o */
+  arcnet_use_count(1);
+
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  struct device *dev=cards[0];
+  int ioaddr=dev->base_addr;
+  
+  if (dev->start) (*dev->stop)(dev);
+  
+  /* Flush TX and disable RX */
+  if (ioaddr)
+    {
+      AINTMASK(0);		/* disable IRQ's */
+      ACOMMAND(NOTXcmd);	/* stop transmit */
+      ACOMMAND(NORXcmd);	/* disable receive */
+    }
+  
+  if (dev->irq)
+    {
+      irq2dev_map[dev->irq] = NULL;
+      free_irq(dev->irq,NULL);
+    }
+  
+  if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+  unregister_netdev(dev);
+  kfree(dev->priv);
+  dev->priv = NULL;
+  
+  /* Decrease use count of arcnet.o */
+  arcnet_use_count(0);
+}
+#else
+
+__initfunc(void com20020_setup (char *str, int *ints))
+{
+  struct device *dev;
+
+  if (arcnet_num_devs == MAX_ARCNET_DEVS)
+    {
+      printk("com20020: Too many ARCnet devices registered (max %d).\n",
+	     MAX_ARCNET_DEVS);
+      return;
+    }
+  
+  dev=&arcnet_devs[arcnet_num_devs];
+  
+  if (ints[0] < 1)
+    {
+      printk("com20020: You must give an IO address.\n");
+      return;
+    }
+
+  dev->dev_addr[3]=3;
+  dev->init=arc20020_probe;
+
+  switch(ints[0])
+    {
+    case 7: /* ERROR */
+      printk("com20020: Too many arguments.\n");
+
+    case 6: /* Timeout */
+      dev->dev_addr[3]=(u_char)ints[6];
+
+    case 5: /* CKP value */
+      dev->dev_addr[2]=(u_char)ints[5];
+
+    case 4: /* Backplane flag */
+      dev->dev_addr[1]=(u_char)ints[4];
+
+    case 3: /* Node ID */
+      dev->dev_addr[0]=(u_char)ints[3];
+
+    case 2: /* IRQ */
+      dev->irq=ints[2];
+      
+    case 1: /* IO address */
+      dev->base_addr=ints[1];
+    }
+
+  dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+  
+  if (str)
+    strncpy(dev->name, str, 9);
+  
+  arcnet_num_devs++;
+}
+#endif /* MODULE */
+
+
+
diff -u --recursive --new-file v2.1.52/linux/drivers/net/com90io.c linux/drivers/net/com90io.c
--- v2.1.52/linux/drivers/net/com90io.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/com90io.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,1019 @@
+/* com90io.c:
+        Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
+
+	Derived from the original arcnet.c,
+	Written 1994-1996 by Avery Pennarun,
+	which was in turn derived from skeleton.c by Donald Becker.
+
+	Contact Avery at: apenwarr@foxnet.net or
+	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+	**********************
+
+	The original copyright of skeleton.c was as follows:
+
+	skeleton.c Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the
+        Director, National Security Agency.  This software may only be used
+        and distributed according to the terms of the GNU Public License as
+        modified by SRC, incorporated herein by reference.
+
+	**********************
+
+	For more details, see drivers/net/arcnet.c
+
+	**********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.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/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+
+
+
+
+/* External functions from arcnet.c */
+
+
+
+#if ARCNET_DEBUG_MAX & D_SKB
+extern void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+			    char *desc);
+#else
+#define arcnet_dump_skb(dev,skb,desc) ;
+#endif
+
+#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
+extern void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+			       char *desc);
+#else
+#define arcnet_dump_packet(dev,buffer,ext,desc) ;
+#endif
+extern void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+extern void arcnet_makename(char *device);
+extern void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+extern void arcnet_setup(struct device *dev);
+extern int arcnet_go_tx(struct device *dev,int enable_irq);
+extern void arcnetA_continue_tx(struct device *dev);
+extern void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+extern void arcnet_use_count(int open);
+
+
+/* Internal function declarations */
+
+static int arc90io_probe(struct device *dev);
+static void arc90io_rx(struct device *dev,int recbuf);
+static int arc90io_found(struct device *dev,int ioaddr,int airq);
+static void arc90io_inthandler (struct device *dev);
+static int arc90io_reset (struct device *dev, int reset_delay);
+static void arc90io_setmask (struct device *dev, u_char mask);
+static void arc90io_command (struct device *dev, u_char command);
+static u_char arc90io_status (struct device *dev);
+static void arc90io_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+       		      char *data,int length,int daddr,int exceptA, int offset);
+static  void arc90io_openclose(int open);
+
+static u_char get_buffer_byte (struct device *dev, unsigned offset);
+static void put_buffer_byte (struct device *dev, unsigned offset, u_char datum);
+static void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+static void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int io=0x0;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0;	/* or use the insmod io= irq= shmem= options */
+static char *device;	/* use eg. device="arc1" to change name */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(device, "s");
+#else
+__initfunc(void com90io_setup (char *str, int *ints));
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+#endif
+
+
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the ethercard. */
+#define ARCNET_TOTAL_SIZE 16
+
+	/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0)	/* writable */
+#define _STATUS  (ioaddr+0)	/* readable */
+#define _COMMAND (ioaddr+1)	/* writable, returns random vals on read (?) */
+#define _RESET  (ioaddr+8)	/* software reset (on read) */
+#define _MEMDATA  (ioaddr+12)            /* Data port for IO-mapped memory */
+#define _ADDR_HI  (ioaddr+15)            /* Control registers for said */
+#define _ADDR_LO  (ioaddr+14)
+#define _CONFIG  (ioaddr+2)      /* Configuration register */
+
+#define ARCSTATUS	inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk)	outb((msk),_INTMASK)
+
+#define ARCRESET	inb(_RESET)
+
+
+#define SETCONF 	outb((lp->config),_CONFIG)
+
+
+/****************************************************************************
+ *                                                                          *
+ * IO-mapped operation routines                                             *
+ *                                                                          *
+ ****************************************************************************/
+
+u_char get_buffer_byte (struct device *dev, unsigned offset)
+{
+  int ioaddr=dev->base_addr;
+
+  outb(offset >> 8,  _ADDR_HI); 
+  outb(offset & 0xff, _ADDR_LO);
+
+  return inb(_MEMDATA);
+}
+
+void put_buffer_byte (struct device *dev, unsigned offset, u_char datum)
+{
+  int ioaddr=dev->base_addr;
+
+  outb(offset >> 8,   _ADDR_HI); 
+  outb(offset & 0xff, _ADDR_LO);
+
+  outb(datum, _MEMDATA);
+}
+
+
+#undef ONE_AT_A_TIME_TX
+#undef ONE_AT_A_TIME_RX
+
+void get_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+  int ioaddr=dev->base_addr;
+
+  outb( (offset >> 8) | AUTOINCflag,   _ADDR_HI);
+  outb( offset & 0xff,                 _ADDR_LO);
+
+  while (length--)
+#ifdef ONE_AT_A_TIME_RX
+    *(dest++) = get_buffer_byte(dev,offset++);
+#else
+    *(dest++) = inb (_MEMDATA);
+#endif
+}
+
+void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, char *dest)
+{
+  int ioaddr=dev->base_addr;
+
+  outb( (offset >> 8) | AUTOINCflag,   _ADDR_HI);
+  outb( offset & 0xff,                 _ADDR_LO);
+ 
+  while (length--)
+#ifdef ONE_AT_A_TIME_TX
+    put_buffer_byte(dev,offset++,*(dest++));
+#else
+    outb (*(dest++), _MEMDATA);
+#endif
+}
+
+
+static const char *version =
+ "com90io.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+/****************************************************************************
+ *                                                                          *
+ * Probe and initialization                                                 *
+ *                                                                          *
+ ****************************************************************************/
+
+
+
+/* We cannot probe for an IO mapped card either, although we can check that
+ * it's where we were told it was, and even autoirq
+ */
+
+__initfunc(int arc90io_probe(struct device *dev))
+{
+  int ioaddr=dev->base_addr,status,delayval;
+  unsigned long airqmask;
+  
+  BUGLVL(D_NORMAL) printk(version);
+  
+  if (ioaddr<0x200)
+    {
+      BUGMSG(D_NORMAL,"No autoprobe for IO mapped cards; you "
+	     "must specify the base address!\n");
+      return -ENODEV;
+    }
+  
+  if (check_region(ioaddr, ARCNET_TOTAL_SIZE))
+    {
+      BUGMSG(D_INIT_REASONS,"IO check_region %x-%x failed.\n",
+	     ioaddr,ioaddr+ARCNET_TOTAL_SIZE-1);
+      return -ENXIO;
+    }
+  
+  if (ARCSTATUS == 0xFF)
+    {
+      BUGMSG(D_INIT_REASONS,"IO address %x empty\n",ioaddr);
+      return -ENODEV;
+    }
+  
+  ARCRESET;
+  JIFFER(RESETtime);
+  
+  status=ARCSTATUS;
+  
+  if ((status & 0x9D)
+      !=  (NORXflag|RECONflag|TXFREEflag|RESETflag))
+    {
+      BUGMSG(D_INIT_REASONS,"Status invalid (%Xh).\n",status);
+      return -ENODEV;
+    }
+  
+  
+  BUGMSG(D_INIT_REASONS,"Status after reset: %X\n",status);
+  
+  
+  ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+  
+  BUGMSG(D_INIT_REASONS,"Status after reset acknowledged: %X\n",status);
+  
+  status=ARCSTATUS;
+  
+  if (status & RESETflag)
+    {
+      BUGMSG(D_INIT_REASONS,"Eternal reset (status=%Xh)\n",status);
+      return -ENODEV;
+    }
+  
+  outb((0x16 | IOMAPflag) &~ENABLE16flag, _CONFIG);
+  
+  
+  /* Read first loc'n of memory */
+  
+  outb(AUTOINCflag ,_ADDR_HI);
+  outb(0,_ADDR_LO);
+  
+  if ((status=inb(_MEMDATA)) != 0xd1)
+    {
+      BUGMSG(D_INIT_REASONS,"Signature byte not found"
+	     " (%Xh instead).\n", status);
+      return -ENODEV;
+    }
+  
+  if (!dev->irq)
+    {
+      /* if we do this, we're sure to get an IRQ since the
+       * card has just reset and the NORXflag is on until
+       * we tell it to start receiving.
+       */
+      
+      airqmask = probe_irq_on();
+      outb(NORXflag,_INTMASK);
+      udelay(1);
+      outb(0,_INTMASK);
+      dev->irq = probe_irq_off(airqmask);
+      
+      if (dev->irq<=0)
+	{
+	  BUGMSG(D_INIT_REASONS,"Autoprobe IRQ failed\n");
+	  return -ENODEV;
+	}
+    }
+  
+  return arc90io_found(dev,dev->base_addr,dev->irq);
+}
+
+
+
+/* Set up the struct device associated with this card.  Called after
+ * probing succeeds.
+ */
+__initfunc(int arc90io_found(struct device *dev,int ioaddr,int airq))
+{
+  struct arcnet_local *lp;
+  
+  /* reserve the irq */
+  if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM90xx-IO)",NULL))
+    {
+      BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+      return -ENODEV;
+    }
+  irq2dev_map[airq]=dev;
+  dev->irq=airq;
+  
+  /* reserve the I/O region - guaranteed to work by check_region */
+  request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (COM90xx-IO)");
+  dev->base_addr=ioaddr;
+  
+  
+  dev->mem_start=dev->mem_end=dev->rmem_start=dev->rmem_end=(long)NULL;
+  
+  
+  /* Initialize the rest of the device structure. */
+  
+  dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+  if (dev->priv == NULL)
+    {
+      irq2dev_map[airq] = NULL;
+      free_irq(airq,NULL);
+      release_region(ioaddr,ARCNET_TOTAL_SIZE);
+      return -ENOMEM;
+    }
+
+  memset(dev->priv,0,sizeof(struct arcnet_local));
+  lp=(struct arcnet_local *)(dev->priv);
+  lp->card_type = ARC_90xx_IO;
+  lp->card_type_str = "COM 90xx (IO)";
+  
+  lp->arcnet_reset=arc90io_reset;
+  lp->asetmask=arc90io_setmask;
+  lp->astatus=arc90io_status;
+  lp->acommand=arc90io_command;
+  lp->openclose_device=arc90io_openclose;
+  lp->prepare_tx=arc90io_prepare_tx;
+  lp->inthandler=arc90io_inthandler;
+  
+  
+
+  
+  /* Fill in the fields of the device structure with generic
+   * values.
+   */
+  arcnet_setup(dev);
+  
+  /* And now fill particular fields with arcnet values */
+  dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+  dev->hard_header_len=sizeof(struct ClientData);
+  lp->sequence=1;
+  lp->recbuf=0;
+  
+  BUGMSG(D_DURING,"ClientData header size is %d.\n",
+	 sizeof(struct ClientData));
+  BUGMSG(D_DURING,"HardHeader size is %d.\n",
+	 sizeof(struct archdr));
+  
+  lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
+  SETCONF;
+  
+  /* get and check the station ID from offset 1 in shmem */
+
+  lp->stationid = get_buffer_byte(dev,1);
+  
+  if (!lp->stationid)
+    BUGMSG(D_NORMAL,"WARNING!  Station address 00 is reserved "
+	   "for broadcasts!\n");
+  else if (lp->stationid==255)
+    BUGMSG(D_NORMAL,"WARNING!  Station address FF may confuse "
+	   "DOS networking programs!\n");
+  dev->dev_addr[0]=lp->stationid;
+  
+  BUGMSG(D_NORMAL,"ARCnet COM90xx in IO-mapped mode:  "
+	 "station %02Xh found at %03lXh, IRQ %d.\n",
+	 lp->stationid,
+	 dev->base_addr,dev->irq);
+  
+  return 0;
+}
+
+
+/****************************************************************************
+ *                                                                          *
+ * Utility routines for arcnet.c                                            *
+ *                                                                          *
+ ****************************************************************************/
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc90io_reset(struct device *dev,int reset_delay)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  short ioaddr=dev->base_addr;
+  int delayval,recbuf=lp->recbuf;
+  
+  if (reset_delay==3)
+    {
+      ARCRESET;
+      return 0;
+    }
+  
+  /* no IRQ's, please! */
+  lp->intmask=0;
+  SETMASK;
+  
+  BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+	 dev->name,ARCSTATUS);
+  
+  /* Set the thing to IO-mapped, 8-bit  mode */
+  lp->config = (0x1C|IOMAPflag) & ~ENABLE16flag;
+  SETCONF;
+  
+  if (reset_delay)
+    {
+      /* reset the card */
+      ARCRESET;
+      JIFFER(RESETtime);
+    }
+  
+  ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+  ACOMMAND(CFLAGScmd|CONFIGclear);
+  
+  /* verify that the ARCnet signature byte is present */
+  
+  if (get_buffer_byte(dev,0) != TESTvalue)
+    {
+      BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+      return 1;
+    }
+  
+  /* clear out status variables */
+  recbuf=lp->recbuf=0;
+  lp->txbuf=2;
+  
+  /* enable extended (512-byte) packets */
+  ACOMMAND(CONFIGcmd|EXTconf);
+  
+  /* and enable receive of our first packet to the first buffer */
+  EnableReceiver();
+  
+  /* re-enable interrupts */
+  lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+  lp->intmask|=RECONflag;
+#endif
+  SETMASK;
+  
+  /* done!  return success. */
+  return 0;
+}
+
+
+static void arc90io_openclose(int open)
+{
+  if (open)
+    MOD_INC_USE_COUNT;
+  else
+    MOD_DEC_USE_COUNT;
+}
+
+
+
+static void arc90io_setmask(struct device *dev, u_char mask)
+{
+  short ioaddr=dev->base_addr;
+  
+  AINTMASK(mask);
+}
+
+static u_char arc90io_status(struct device *dev)
+{
+  short ioaddr=dev->base_addr;
+
+  return ARCSTATUS;
+}
+
+static void arc90io_command(struct device *dev, u_char cmd)
+{
+  short ioaddr=dev->base_addr;
+
+  ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc90io_inthandler(struct device *dev)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr, status, boguscount = 3, didsomething; 
+
+  
+	
+    
+  AINTMASK(0);
+  
+  BUGMSG(D_DURING,"in arc90io_inthandler (status=%Xh, intmask=%Xh)\n",
+	 ARCSTATUS,lp->intmask);
+  
+  do
+    {
+      status = ARCSTATUS;
+      didsomething=0;
+      
+      
+      /* RESET flag was enabled - card is resetting and if RX
+       * is disabled, it's NOT because we just got a packet.
+       */
+      if (status & RESETflag)
+	{
+	  BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+		 status);
+	  arc90io_reset(dev,0);
+	  
+	  /* all other flag values are just garbage */
+	  break;
+	}
+      
+      
+      /* RX is inhibited - we must have received something. */
+      if (status & lp->intmask & NORXflag)
+	{
+	  int recbuf=lp->recbuf=!lp->recbuf;
+	  int oldaddr=0;
+	  
+	  BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+		 status);
+	  
+	  /* enable receive of our next packet */
+	  EnableReceiver();
+	  
+	  if (lp->intx)
+	    oldaddr=(inb(_ADDR_HI)<<8) | inb(_ADDR_LO);
+	  
+	  
+	  /* Got a packet. */
+	  arc90io_rx(dev,!recbuf);
+	  
+	  
+	  if (lp->intx)
+	    {
+	      outb( (oldaddr >> 8),  _ADDR_HI);
+	      outb( oldaddr & 0xff, _ADDR_LO);
+	    }
+	  
+	  didsomething++;
+	}
+      
+      /* it can only be an xmit-done irq if we're xmitting :) */
+      /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+      if (status & lp->intmask & TXFREEflag)
+	{
+	  struct Outgoing *out=&(lp->outgoing);
+	  int was_sending=lp->sending;
+	  
+	  lp->intmask &= ~TXFREEflag;
+	  
+	  lp->in_txhandler++;
+	  if (was_sending) lp->sending--;
+	  
+	  BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+		 status,out->numsegs,out->segnum,out->skb);
+	  
+	  if (was_sending && !(status&TXACKflag))
+	    {
+	      if (lp->lasttrans_dest != 0)
+		{
+		  BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+			 status,lp->lasttrans_dest);
+		  lp->stats.tx_errors++;
+		  lp->stats.tx_carrier_errors++;
+		}
+	      else
+		{
+		  BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+			 status,
+			 lp->lasttrans_dest);
+		}
+	    }
+	  
+	  /* send packet if there is one */
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	  
+	  if (lp->intx)
+	    {
+	      BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+		     ARCSTATUS,lp->intx);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  if (!lp->outgoing.skb)
+	    {
+	      BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	      lp->in_txhandler--;
+	      continue;
+	    }
+	  
+	  /* if more than one segment, and not all segments
+	   * are done, then continue xmit.
+	   */
+	  if (out->segnum<out->numsegs)
+	    arcnetA_continue_tx(dev);
+	  arcnet_go_tx(dev,0);
+	  
+	  /* if segnum==numsegs, the transmission is finished;
+	   * free the skb.
+	   */
+	  if (out->segnum>=out->numsegs)
+	    {
+	      /* transmit completed */
+	      out->segnum++;
+	      if (out->skb)
+		{
+		  lp->stats.tx_bytes += out->skb->len;
+		  dev_kfree_skb(out->skb,FREE_WRITE);
+		}
+	      out->skb=NULL;
+	      
+	      /* inform upper layers */
+	      if (!lp->txready) arcnet_tx_done(dev, lp);
+	    }
+	  didsomething++;
+	  
+	  lp->in_txhandler--;
+	}
+      else if (lp->txready && !lp->sending && !lp->intx)
+	{
+	  BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+		 status);
+	  arcnet_go_tx(dev,0);
+	  didsomething++;
+	}
+      
+      
+#ifdef DETECT_RECONFIGS
+      if (status & (lp->intmask) & RECONflag)
+	{
+	  ACOMMAND(CFLAGScmd|CONFIGclear);
+	  lp->stats.tx_carrier_errors++;
+	  
+#ifdef SHOW_RECONFIGS
+	  
+	  BUGMSG(D_NORMAL,"Network reconfiguration detected"
+		 " (status=%Xh, config=%X)\n",
+		 status,lp->config);
+#endif /* SHOW_RECONFIGS */
+	  
+#ifdef RECON_THRESHOLD
+	  /* is the RECON info empty or old? */
+	  if (!lp->first_recon || !lp->last_recon ||
+	      jiffies-lp->last_recon > HZ*10)
+	    {
+	      if (lp->network_down)
+		BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+	      lp->first_recon=lp->last_recon=jiffies;
+	      lp->num_recons=lp->network_down=0;
+	      
+	      BUGMSG(D_DURING,"recon: clearing counters.\n");
+	    }
+	  else /* add to current RECON counter */
+	    {
+	      lp->last_recon=jiffies;
+	      lp->num_recons++;
+	      
+	      BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+		     lp->num_recons,
+		     (lp->last_recon-lp->first_recon)/HZ,
+		     lp->network_down);
+	      
+	      /* if network is marked up;
+	       * and first_recon and last_recon are 60+ sec
+	       *   apart;
+	       * and the average no. of recons counted is
+	       *   > RECON_THRESHOLD/min;
+	       * then print a warning message.
+	       */
+	      if (!lp->network_down
+		  && (lp->last_recon-lp->first_recon)<=HZ*60
+		  && lp->num_recons >= RECON_THRESHOLD)
+		{
+		  lp->network_down=1;
+		  BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+		}
+	      else if (!lp->network_down
+		       && lp->last_recon-lp->first_recon > HZ*60)
+		{
+		  /* reset counters if we've gone for
+		   * over a minute.
+		   */
+		  lp->first_recon=lp->last_recon;
+		  lp->num_recons=1;
+		}
+	    }
+	}
+      else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+	{
+	  if (lp->network_down)
+	    BUGMSG(D_NORMAL,"cabling restored?\n");
+	  lp->first_recon=lp->last_recon=0;
+	  lp->num_recons=lp->network_down=0;
+	  
+	  BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+	}
+#endif /* DETECT_RECONFIGS */
+    } while (--boguscount && didsomething);
+  
+  BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+	 ARCSTATUS,boguscount);
+  BUGMSG(D_DURING,"\n");
+  
+  SETMASK;	/* put back interrupt mask */
+  
+}
+
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */ 
+
+static void
+arc90io_rx(struct device *dev,int recbuf)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr;
+  union ArcPacket packetbuf;
+  union ArcPacket *arcpacket=&packetbuf;
+  u_char *arcsoft;
+  short length,offset;
+  u_char daddr,saddr;
+  
+  lp->stats.rx_packets++;
+  
+  get_whole_buffer(dev,recbuf*512,4,(char *)arcpacket);
+  
+  saddr=arcpacket->hardheader.source;
+  
+  /* if source is 0, it's a "used" packet! */
+  if (saddr==0)
+    {
+      BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+	     ARCSTATUS);
+      lp->stats.rx_errors++;
+      return;
+    }
+  /* Set source address to zero to mark it as old */
+  
+  put_buffer_byte(dev,recbuf*512,0);
+  
+  arcpacket->hardheader.source=0;
+  
+  daddr=arcpacket->hardheader.destination;
+  
+  if (arcpacket->hardheader.offset1) /* Normal Packet */
+    {
+      offset=arcpacket->hardheader.offset1;
+      arcsoft=&arcpacket->raw[offset];
+      length=256-offset;
+    }
+  else		/* ExtendedPacket or ExceptionPacket */
+    {
+      offset=arcpacket->hardheader.offset2;
+      arcsoft=&arcpacket->raw[offset];
+      length=512-offset;
+    }
+  
+  get_whole_buffer(dev,recbuf*512+offset,length,(char *)arcpacket+offset);
+  
+  arcnet_rx(lp, arcsoft, length, saddr, daddr);
+  
+  BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+}
+
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc90io_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+		    char *data,int length,int daddr,int exceptA, int offset)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  
+  lp->txbuf=lp->txbuf^1;	/* XOR with 1 to alternate between 2 and 3 */
+  
+  length+=hdrlen;
+  
+  BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+	 hdr,length,data);
+  
+  put_buffer_byte(dev, lp->txbuf*512+1, daddr);
+
+  /* load packet into shared memory */
+  if (length<=MTU)	/* Normal (256-byte) Packet */
+    put_buffer_byte(dev, lp->txbuf*512+2, offset=offset?offset:256-length);
+  
+  else if (length>=MinTU || offset)	/* Extended (512-byte) Packet */
+    {
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=offset?offset:512-length);
+    }
+  else if (exceptA)		/* RFC1201 Exception Packet */
+    {
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=512-length-4);
+      
+      /* exception-specific stuff - these four bytes
+       * make the packet long enough to fit in a 512-byte
+       * frame.
+       */
+      
+      put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+      offset+=4;
+    }
+  else				/* "other" Exception packet */
+    {
+      /* RFC1051 - set 4 trailing bytes to 0 */
+      
+      put_whole_buffer(dev,lp->txbuf*512+508,4,"\0\0\0\0");
+      
+      /* now round up to MinTU */
+      put_buffer_byte(dev, lp->txbuf*512+2, 0);
+      put_buffer_byte(dev, lp->txbuf*512+3, offset=512-MinTU);
+    }
+  
+  /* copy the packet into ARCnet shmem
+   *  - the first bytes of ClientData header are skipped
+   */
+  
+  put_whole_buffer(dev, 512*lp->txbuf+offset, hdrlen,(u_char *)hdr);
+  put_whole_buffer(dev, 512*lp->txbuf+offset+hdrlen,length-hdrlen,data);
+  
+  BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+	 daddr,length);
+  
+  lp->lastload_dest=daddr;
+  lp->txready=lp->txbuf;	/* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ *                                                                          *
+ * Kernel Loadable Module Support                                           *
+ *                                                                          *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static struct device *cards[16]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+			  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
+
+
+int init_module(void)
+{
+  struct device *dev=cards[0];
+  
+  cards[0]=dev=(struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
+  if (!dev)
+    return -ENOMEM;
+  
+  memset(dev, 0, sizeof(struct device));
+  
+  dev->name=(char *)kmalloc(9, GFP_KERNEL);
+  if (!dev->name)
+    {
+      kfree(dev);
+      return -ENOMEM;
+    }
+  dev->init=arc90io_probe;
+  
+  if (device)
+    strcpy(dev->name,device);
+  else arcnet_makename(dev->name);
+
+  dev->base_addr=io;
+  dev->irq=irq;
+  
+  if (dev->irq==2) dev->irq=9;
+  
+  if (register_netdev(dev) != 0)
+    return -EIO;
+  
+  /* Increase use count of arcnet.o */
+  arcnet_use_count(1);
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  struct device *dev=cards[0];
+  int ioaddr=dev->base_addr;
+  
+  if (dev->start) (*dev->stop)(dev);
+  
+  /* Flush TX and disable RX */
+  if (ioaddr)
+    {
+      AINTMASK(0);		/* disable IRQ's */
+      ACOMMAND(NOTXcmd);	/* stop transmit */
+      ACOMMAND(NORXcmd);	/* disable receive */
+
+      /* Set the thing back to MMAP mode, in case the old
+	         driver is loaded later */
+      outb( (inb(_CONFIG)&~IOMAPflag),_CONFIG);
+    }
+  
+  if (dev->irq)
+    {
+      irq2dev_map[dev->irq] = NULL;
+      free_irq(dev->irq,NULL);
+    }
+  
+  if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+  unregister_netdev(dev);
+  kfree(dev->priv);
+  dev->priv = NULL;
+  
+  /* Decrease use count of arcnet.o */
+  arcnet_use_count(0);
+}
+#else
+
+
+__initfunc(void com90io_setup (char *str, int *ints))
+{
+  struct device *dev;
+
+  if (arcnet_num_devs == MAX_ARCNET_DEVS)
+    {
+      printk("com90xx IO-MAP: Too many ARCnet devices registered (max %d).\n",
+	     MAX_ARCNET_DEVS);
+      return;
+    }
+  
+  dev=&arcnet_devs[arcnet_num_devs];
+  
+  if (ints[0] < 1)
+    {
+      printk("com90xx IO-MAP: You must give an IO address.\n");
+      return;
+    }
+
+  dev->init=arc90io_probe;
+
+  switch(ints[0])
+    {
+    case 3: /* ERROR */
+      printk("com90xx IO-MAP: Too many arguments.\n");
+
+    case 2: /* IRQ */
+      dev->irq=ints[2];
+      
+    case 1: /* IO address */
+      dev->base_addr=ints[1];
+    }
+
+  dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+  
+  if (str)
+    strncpy(dev->name, str, 9);
+  
+  arcnet_num_devs++;
+}
+
+#endif /* MODULE */
+
+
+
diff -u --recursive --new-file v2.1.52/linux/drivers/net/com90xx.c linux/drivers/net/com90xx.c
--- v2.1.52/linux/drivers/net/com90xx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/com90xx.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,1335 @@
+/* com90xx.c:
+	Derived from the original arcnet.c,
+	Written 1994-1996 by Avery Pennarun,
+	which was in turn derived from skeleton.c by Donald Becker.
+
+	Contact Avery at: apenwarr@foxnet.net or
+	RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+	**********************
+
+	The original copyright of skeleton.c was as follows:
+
+	skeleton.c Written 1993 by Donald Becker.
+	Copyright 1993 United States Government as represented by the
+        Director, National Security Agency.  This software may only be used
+        and distributed according to the terms of the GNU Public License as
+        modified by SRC, incorporated herein by reference.
+
+	**********************
+
+	For more details, see drivers/net/arcnet.c
+
+	**********************
+*/
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.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/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/if_arcnet.h>
+#include <linux/arcdevice.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <net/arp.h>
+
+/**************************************************************************/
+
+/* On a fast computer, the buffer copy from memory to the ARCnet card during
+ * a transmit can hog the bus just a little too long.  SLOW_XMIT_COPY
+ * replaces the fast memcpy() with a slower for() loop that seems to solve
+ * my problems with ftape.
+ *
+ * Probably a better solution would be to use memcpy_toio (more portable
+ * anyway) and modify that routine to support REALLY_SLOW_IO-style
+ * defines; ARCnet probably is not the only driver that can screw up an
+ * ftape DMA transfer.
+ *
+ * Turn this on if you have timing-sensitive DMA (ie. a tape drive) and
+ * would like to sacrifice a little bit of network speed to reduce tape
+ * write retries or some related problem.
+ */
+#undef SLOW_XMIT_COPY
+
+
+/* Define this to speed up the autoprobe by assuming if only one io port and
+ * shmem are left in the list at Stage 5, they must correspond to each
+ * other.
+ *
+ * This is undefined by default because it might not always be true, and the
+ * extra check makes the autoprobe even more careful.  Speed demons can turn
+ * it on - I think it should be fine if you only have one ARCnet card
+ * installed.
+ *
+ * If no ARCnet cards are installed, this delay never happens anyway and thus
+ * the option has no effect.
+ */
+#undef FAST_PROBE
+
+
+
+
+
+
+/* External functions from arcnet.c */
+
+
+
+#if ARCNET_DEBUG_MAX & D_SKB
+extern void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+			    char *desc);
+#else
+#define arcnet_dump_skb(dev,skb,desc) ;
+#endif
+
+#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
+extern void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+			       char *desc);
+#else
+#define arcnet_dump_packet(dev,buffer,ext,desc) ;
+#endif
+
+
+extern void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+extern void arcnet_makename(char *device);
+extern void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+extern void arcnet_setup(struct device *dev);
+extern int arcnet_go_tx(struct device *dev,int enable_irq);
+extern void arcnetA_continue_tx(struct device *dev);
+extern void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+extern void arcnet_use_count(int open);
+
+
+
+/* Internal function declarations */
+#ifdef MODULE 
+static 
+#endif
+       int arc90xx_probe(struct device *dev);
+static void arc90xx_rx(struct device *dev,int recbuf);
+static int arc90xx_found(struct device *dev,int ioaddr,int airq,u_long shmem); 
+static void arc90xx_inthandler (struct device *dev);
+static int arc90xx_reset (struct device *dev, int reset_delay);
+static void arc90xx_setmask (struct device *dev, u_char mask);
+static void arc90xx_command (struct device *dev, u_char command);
+static u_char arc90xx_status (struct device *dev);
+static void arc90xx_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+		      char *data,int length,int daddr,int exceptA, int offset);
+static  void arc90xx_openclose(int open);
+
+
+/* Module parameters */
+
+#ifdef MODULE
+static int io=0x0;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=0;	/* or use the insmod io= irq= shmem= options */
+static int shmem=0;
+static char *device;	/* use eg. device="arc1" to change name */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(shmem, "i");
+MODULE_PARM(device, "s");
+#else
+__initfunc(void com90xx_setup (char *str, int *ints));
+char __initdata com90xx_explicit=0;
+
+extern struct device arcnet_devs[];
+extern char arcnet_dev_names[][10];
+extern int arcnet_num_devs;
+
+#endif
+
+
+
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the ethercard. */
+#define ARCNET_TOTAL_SIZE	16
+
+	/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0)	/* writable */
+#define _STATUS  (ioaddr+0)	/* readable */
+#define _COMMAND (ioaddr+1)	/* writable, returns random vals on read (?) */
+#define _RESET  (ioaddr+8)	/* software reset (on read) */
+#define _MEMDATA  (ioaddr+12)            /* Data port for IO-mapped memory */
+#define _ADDR_HI  (ioaddr+15)            /* Control registers for said */
+#define _ADDR_LO  (ioaddr+14)
+#define _CONFIG  (ioaddr+2)      /* Configuration register */
+
+#define RDDATAflag      0x00     /* Next access is a read/~write */
+
+  #define ARCSTATUS	inb(_STATUS)
+  #define ACOMMAND(cmd) outb((cmd),_COMMAND)
+  #define AINTMASK(msk)	outb((msk),_INTMASK)
+  #define SETCONF	outb(lp->config,_CONFIG)
+  #define ARCRESET	inb(_RESET)
+
+static const char *version =
+ "com90xx.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+
+
+/****************************************************************************
+ *                                                                          *
+ * Probe and initialization                                                 *
+ *                                                                          *
+ ****************************************************************************/
+
+/* Check for an ARCnet network adaptor, and return '0' if one exists.
+ *  If dev->base_addr == 0, probe all likely locations.
+ *  If dev->base_addr == 1, always return failure.
+ *  If dev->base_addr == 2, allocate space for the device and return success
+ *  			    (detachable devices only).
+ *
+ * NOTE: the list of possible ports/shmems is static, so it is retained
+ * across calls to arcnet_probe.  So, if more than one ARCnet probe is made,
+ * values that were discarded once will not even be tried again.
+ *
+ * FIXME: grab all devices in one shot and eliminate the big static array.
+ */
+
+static int ports[(0x3f0 - 0x200) / 16 + 1] __initdata = { 0 };
+static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] __initdata = { 0 };
+
+__initfunc(int arc90xx_probe(struct device *dev))
+{
+  static int init_once = 0;
+  static int numports=sizeof(ports)/sizeof(ports[0]),
+    numshmems=sizeof(shmems)/sizeof(shmems[0]);
+  
+  int count,status,delayval,ioaddr,numprint,airq,retval=-ENODEV,
+    openparen=0;
+  unsigned long airqmask;
+  int *port;
+  u_long *shmem;
+  
+  if (!init_once)
+    {
+      for (count=0x200; count<=0x3f0; count+=16)
+	ports[(count-0x200)/16] = count;
+      for (count=0xA0000; count<=0xFF800; count+=2048)
+	shmems[(count-0xA0000)/2048] = count;
+      init_once=1;
+    }
+  
+  BUGLVL(D_NORMAL) printk(version);
+  BUGMSG(D_DURING,"space used for probe buffers: %d+%d=%d bytes\n",
+	 sizeof(ports),sizeof(shmems),
+	 sizeof(ports)+sizeof(shmems));
+  
+ 
+  
+  BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
+	 dev->base_addr,dev->irq,dev->mem_start);
+  
+  if (dev->base_addr > 0x1ff)	/* Check a single specified port */
+    {
+      ports[0]=dev->base_addr;
+      numports=1;
+    }
+  else if (dev->base_addr > 0)	/* Don't probe at all. */
+    return -ENXIO;
+  
+  if (dev->mem_start)
+    {
+      shmems[0]=dev->mem_start;
+      numshmems=1;
+    }
+  
+  
+  /* Stage 1: abandon any reserved ports, or ones with status==0xFF
+	 * (empty), and reset any others by reading the reset port.
+	 */
+  BUGMSG(D_INIT,"Stage 1: ");
+  numprint=0;
+  for (port = &ports[0]; port-ports<numports; port++)
+    {
+      numprint++;
+      if (numprint>8)
+	{
+	  BUGMSG2(D_INIT,"\n");
+	  BUGMSG(D_INIT,"Stage 1: ");
+	  numprint=1;
+	}
+      BUGMSG2(D_INIT,"%Xh ",*port);
+      
+      ioaddr=*port;
+      
+      if (check_region(*port, ARCNET_TOTAL_SIZE))
+	{
+	  BUGMSG2(D_INIT_REASONS,"(check_region)\n");
+	  BUGMSG(D_INIT_REASONS,"Stage 1: ");
+	  BUGLVL(D_INIT_REASONS) numprint=0;
+	  *port=ports[numports-1];
+	  numports--;
+	  port--;
+	  continue;
+	}
+      
+      if (ARCSTATUS == 0xFF)
+	{
+			BUGMSG2(D_INIT_REASONS,"(empty)\n");
+			BUGMSG(D_INIT_REASONS,"Stage 1: ");
+			BUGLVL(D_INIT_REASONS) numprint=0;
+			*port=ports[numports-1];
+			numports--;
+			port--;
+			continue;
+	}
+      
+      ARCRESET;	/* begin resetting card */
+
+      BUGMSG2(D_INIT_REASONS,"\n");
+      BUGMSG(D_INIT_REASONS,"Stage 1: ");
+      BUGLVL(D_INIT_REASONS) numprint=0;
+    }
+  BUGMSG2(D_INIT,"\n");
+  
+  if (!numports)
+    {
+      BUGMSG(D_NORMAL,"Stage 1: No ARCnet cards found.\n");
+      return -ENODEV;
+    }
+  
+  
+  /* Stage 2: we have now reset any possible ARCnet cards, so we can't
+   * do anything until they finish.  If D_INIT, print the list of
+   * cards that are left.
+   */
+	BUGMSG(D_INIT,"Stage 2: ");
+	numprint=0;
+	for (port = &ports[0]; port-ports<numports; port++)
+	  {
+	    numprint++;
+	    if (numprint>8)
+	      {
+		BUGMSG2(D_INIT,"\n");
+		BUGMSG(D_INIT,"Stage 2: ");
+		numprint=1;
+	      }
+	    BUGMSG2(D_INIT,"%Xh ",*port);
+	  }
+	BUGMSG2(D_INIT,"\n");
+	JIFFER(RESETtime);
+	
+	
+	/* Stage 3: abandon any shmem addresses that don't have the signature
+	 * 0xD1 byte in the right place, or are read-only.
+	 */
+	BUGMSG(D_INIT,"Stage 3: ");
+	numprint=0;
+	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+	  {
+	    u_long ptr;
+	    
+		numprint++;
+		if (numprint>8)
+		  {
+		    BUGMSG2(D_INIT,"\n");
+		    BUGMSG(D_INIT,"Stage 3: ");
+		    numprint=1;
+		  }
+		BUGMSG2(D_INIT,"%lXh ",*shmem);
+		
+		ptr=(u_long)(*shmem);
+		
+		if (readb(ptr) != TESTvalue)
+		{
+		  BUGMSG2(D_INIT_REASONS,"(mem=%02Xh, not %02Xh)\n",
+			  readb(ptr),TESTvalue);
+		  BUGMSG(D_INIT_REASONS,"Stage 3: ");
+		  BUGLVL(D_INIT_REASONS) numprint=0;
+		  *shmem=shmems[numshmems-1];
+		  numshmems--;
+		  shmem--;
+		  continue;
+		}
+		
+		/* By writing 0x42 to the TESTvalue location, we also make
+		 * sure no "mirror" shmem areas show up - if they occur
+		 * in another pass through this loop, they will be discarded
+		 * because *cptr != TESTvalue.
+		 */
+		writeb(0x42,ptr);
+		if (readb(ptr) != 0x42)
+		  {
+		    BUGMSG2(D_INIT_REASONS,"(read only)\n");
+		    BUGMSG(D_INIT_REASONS,"Stage 3: ");
+		    *shmem=shmems[numshmems-1];
+		    numshmems--;
+		    shmem--;
+		    continue;
+		  }
+		
+		BUGMSG2(D_INIT_REASONS,"\n");
+		BUGMSG(D_INIT_REASONS,"Stage 3: ");
+		BUGLVL(D_INIT_REASONS) numprint=0;
+	  }
+	BUGMSG2(D_INIT,"\n");
+	
+	if (!numshmems)
+	  {
+	    BUGMSG(D_NORMAL,"Stage 3: No ARCnet cards found.\n");
+	    return -ENODEV;
+	  }
+	
+	/* Stage 4: something of a dummy, to report the shmems that are
+	 * still possible after stage 3.
+	 */
+	BUGMSG(D_INIT,"Stage 4: ");
+	numprint=0;
+	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+	  {
+	    numprint++;
+	    if (numprint>8)
+	      {
+		BUGMSG2(D_INIT,"\n");
+		BUGMSG(D_INIT,"Stage 4: ");
+		numprint=1;
+	      }
+	    BUGMSG2(D_INIT,"%lXh ",*shmem);
+	  }
+	BUGMSG2(D_INIT,"\n");
+	
+	
+	/* Stage 5: for any ports that have the correct status, can disable
+	 * the RESET flag, and (if no irq is given) generate an autoirq,
+	 * register an ARCnet device.
+	 *
+	 * Currently, we can only register one device per probe, so quit
+	 * after the first one is found.
+	 */
+	BUGMSG(D_INIT,"Stage 5: ");
+	numprint=0;
+	for (port = &ports[0]; port-ports<numports; port++)
+	  {
+	    numprint++;
+	    if (numprint>8)
+	      {
+		BUGMSG2(D_INIT,"\n");
+		BUGMSG(D_INIT,"Stage 5: ");
+		numprint=1;
+	      }
+	    BUGMSG2(D_INIT,"%Xh ",*port);
+	    
+	    ioaddr=*port;
+	    status=ARCSTATUS;
+	    
+	    if ((status & 0x9D)
+		!= (NORXflag|RECONflag|TXFREEflag|RESETflag))
+	      {
+		BUGMSG2(D_INIT_REASONS,"(status=%Xh)\n",status);
+		BUGMSG(D_INIT_REASONS,"Stage 5: ");
+		BUGLVL(D_INIT_REASONS) numprint=0;
+		*port=ports[numports-1];
+		numports--;
+		port--;
+		continue;
+		}
+	    
+	    ACOMMAND(CFLAGScmd|RESETclear|CONFIGclear);
+	    status=ARCSTATUS;
+	    if (status & RESETflag)
+	      {
+		BUGMSG2(D_INIT_REASONS," (eternal reset, status=%Xh)\n",
+			status);
+		BUGMSG(D_INIT_REASONS,"Stage 5: ");
+		BUGLVL(D_INIT_REASONS) numprint=0;
+			*port=ports[numports-1];
+			numports--;
+			port--;
+			continue;
+	      }
+	    
+	    /* skip this completely if an IRQ was given, because maybe
+	     * we're on a machine that locks during autoirq!
+	     */
+		if (!dev->irq)
+		  {
+		    /* if we do this, we're sure to get an IRQ since the
+		     * card has just reset and the NORXflag is on until
+		     * we tell it to start receiving.
+		     */
+		    airqmask = probe_irq_on();
+		    AINTMASK(NORXflag);
+		    udelay(1);
+		    AINTMASK(0);
+		    airq = probe_irq_off(airqmask);
+
+		    if (airq<=0)
+		      {
+			BUGMSG2(D_INIT_REASONS,"(airq=%d)\n",airq);
+			BUGMSG(D_INIT_REASONS,"Stage 5: ");
+			BUGLVL(D_INIT_REASONS) numprint=0;
+			*port=ports[numports-1];
+			numports--;
+			port--;
+				continue;
+		      }
+		  }
+		else
+		{
+		  airq=dev->irq;
+		}
+		
+		BUGMSG2(D_INIT,"(%d,", airq);
+		openparen=1;
+		
+		/* Everything seems okay.  But which shmem, if any, puts
+		 * back its signature byte when the card is reset?
+		 *
+		 * If there are multiple cards installed, there might be
+		 * multiple shmems still in the list.
+		 */
+#ifdef FAST_PROBE
+		if (numports>1 || numshmems>1)
+		  {
+		    ARCRESET;
+		    JIFFER(RESETtime);
+		  }
+		else
+		  {
+		    /* just one shmem and port, assume they match */
+		    writeb(TESTvalue,shmems[0]);
+		  }
+#else
+		ARCRESET;
+		JIFFER(RESETtime);
+#endif
+		
+		
+		for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+		  {
+		    u_long ptr;
+		    ptr=(u_long)(*shmem);
+		    
+		    if (readb(ptr) == TESTvalue)	/* found one */
+		      {
+			BUGMSG2(D_INIT,"%lXh)\n", *shmem);
+			openparen=0;
+			
+			/* register the card */
+			retval=arc90xx_found(dev,*port,airq,*shmem);
+			if (retval) openparen=0;
+			
+			/* remove shmem from the list */
+			*shmem=shmems[numshmems-1];
+			numshmems--;
+			
+			break;
+		      }
+		    else
+		      {
+			BUGMSG2(D_INIT_REASONS,"%Xh-", readb(ptr));
+		      }
+		  }
+		
+		if (openparen)
+		  {
+		    BUGMSG2(D_INIT,"no matching shmem)\n");
+		    BUGMSG(D_INIT_REASONS,"Stage 5: ");
+		    BUGLVL(D_INIT_REASONS) numprint=0;
+		  }
+		
+		*port=ports[numports-1];
+		numports--;
+		port--;
+		
+		if (!retval) break;
+	  }
+	BUGMSG(D_INIT_REASONS,"\n");
+	
+	/* Now put back TESTvalue on all leftover shmems.
+	 */
+	for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+	  writeb(TESTvalue,*shmem);
+	
+	if (retval) BUGMSG(D_NORMAL,"Stage 5: No ARCnet cards found.\n");
+	return retval;
+}
+
+/* Set up the struct device associated with this card.  Called after
+ * probing succeeds.
+ */
+__initfunc(int arc90xx_found(struct device *dev,int ioaddr,int airq, u_long shmem))
+{
+  struct arcnet_local *lp;
+  u_long first_mirror,last_mirror;
+  int mirror_size;
+  
+  /* reserve the irq */
+  if (request_irq(airq,&arcnet_interrupt,0,"arcnet (90xx)",NULL))
+    {
+      BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
+      return -ENODEV;
+    }
+  irq2dev_map[airq]=dev;
+  dev->irq=airq;
+  
+  
+  /* reserve the I/O region - guaranteed to work by check_region */
+  request_region(ioaddr,ARCNET_TOTAL_SIZE,"arcnet (90xx)");
+  dev->base_addr=ioaddr;
+  
+  /* find the real shared memory start/end points, including mirrors */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+  
+  /* guess the actual size of one "memory mirror" - the number of
+	 * bytes between copies of the shared memory.  On most cards, it's
+	 * 2k (or there are no mirrors at all) but on some, it's 4k.
+	 */
+  mirror_size=MIRROR_SIZE;
+  if (readb(shmem)==TESTvalue
+      && readb(shmem-mirror_size)!=TESTvalue
+      && readb(shmem-2*mirror_size)==TESTvalue)
+    mirror_size*=2;
+  
+  first_mirror=last_mirror=shmem;
+  while (readb(first_mirror)==TESTvalue) first_mirror-=mirror_size;
+  first_mirror+=mirror_size;
+  
+  while (readb(last_mirror)==TESTvalue) last_mirror+=mirror_size;
+  last_mirror-=mirror_size;
+  
+  dev->mem_start=first_mirror;
+  dev->mem_end=last_mirror+MIRROR_SIZE-1;
+  dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
+  dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
+  
+	/* Initialize the rest of the device structure. */
+  
+  dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+  if (dev->priv == NULL)
+    {
+      irq2dev_map[airq] = NULL;
+      free_irq(airq,NULL);
+      release_region(ioaddr,ARCNET_TOTAL_SIZE);
+      return -ENOMEM;
+    }
+  memset(dev->priv,0,sizeof(struct arcnet_local));
+  lp=(struct arcnet_local *)(dev->priv);
+  lp->card_type = ARC_90xx;
+  lp->card_type_str = "COM 90xx";
+  lp->arcnet_reset=arc90xx_reset;
+  lp->asetmask=arc90xx_setmask;
+  lp->astatus=arc90xx_status;
+  lp->acommand=arc90xx_command;
+  lp->openclose_device=arc90xx_openclose;
+  lp->prepare_tx=arc90xx_prepare_tx;
+  lp->inthandler=arc90xx_inthandler;
+  
+  
+  /* Fill in the fields of the device structure with generic
+   * values.
+   */
+  arcnet_setup(dev);
+  
+  /* And now fill particular fields with arcnet values */
+  dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
+  dev->hard_header_len=sizeof(struct ClientData);
+  lp->sequence=1;
+  lp->recbuf=0;
+  
+  BUGMSG(D_DURING,"ClientData header size is %d.\n",
+	 sizeof(struct ClientData));
+  BUGMSG(D_DURING,"HardHeader size is %d.\n",
+	 sizeof(struct archdr));
+  
+  /* get and check the station ID from offset 1 in shmem */
+  lp->stationid = readb(first_mirror+1);
+  
+  if (lp->stationid==0)
+    BUGMSG(D_NORMAL,"WARNING!  Station address 00 is reserved "
+	   "for broadcasts!\n");
+  else if (lp->stationid==255)
+    BUGMSG(D_NORMAL,"WARNING!  Station address FF may confuse "
+	   "DOS networking programs!\n");
+  dev->dev_addr[0]=lp->stationid;
+  
+  BUGMSG(D_NORMAL,"ARCnet COM90xx: station %02Xh found at %03lXh, IRQ %d, "
+	 "ShMem %lXh (%ld*%xh).\n",
+	 lp->stationid,
+	 dev->base_addr, dev->irq, dev->mem_start,
+	 (dev->mem_end-dev->mem_start+1)/mirror_size,mirror_size);
+
+#ifdef CONFIG_ARCNET_COM90xx
+  /* OK. We're finished. Now do we need to probe for other cards? */
+
+  if (!com90xx_explicit)
+    {
+      /* We need to check whether there's another card here.
+       * Just add another COM90xx driver to the device chain.
+       */
+      
+      if (arcnet_num_devs < MAX_ARCNET_DEVS)
+	{
+	  arcnet_devs[arcnet_num_devs].next=dev->next;
+	  dev->next=&arcnet_devs[arcnet_num_devs];
+	  dev->next->name=(char *)&arcnet_dev_names[arcnet_num_devs];
+	  arcnet_num_devs++;
+	}
+      else
+	{
+	  while (dev->next) dev=dev->next;
+	  
+	  dev->next=kmalloc(sizeof(struct device), GFP_KERNEL);
+	  if (!dev->next)
+	    {
+	      BUGMSG(D_NORMAL, "No memory for allocating next device - no more "
+		     "will be probed for.\n");
+	      return 0;
+	    }
+	  memset(dev->next,0,sizeof(struct device));
+	  dev->next->name=kmalloc(10, GFP_KERNEL);
+	  if (!dev->next->name)
+	    {
+	      BUGMSG(D_NORMAL, "No memory for allocating next device - no more "
+		     "will be probed for.\n");
+	      kfree(dev->next);
+	      dev->next=NULL;
+	      return 0;
+	    }
+	}
+      arcnet_makename(dev->next->name);
+      dev->next->init=arc90xx_probe;      
+    }
+#endif
+
+  return 0;
+}
+
+
+
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arc90xx_reset(struct device *dev,int reset_delay)
+{
+	struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+	short ioaddr=dev->base_addr;
+	int delayval,recbuf=lp->recbuf;
+
+  if (reset_delay==3)
+    {
+      ARCRESET;
+      return 0;
+    }
+  
+	/* no IRQ's, please! */
+	lp->intmask=0;
+	SETMASK;
+
+	BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+			dev->name,ARCSTATUS);
+
+	if (reset_delay)
+	{
+		/* reset the card */
+		ARCRESET;
+		JIFFER(RESETtime);
+	}
+
+	ACOMMAND(CFLAGScmd|RESETclear); /* clear flags & end reset */
+	ACOMMAND(CFLAGScmd|CONFIGclear);
+
+	/* verify that the ARCnet signature byte is present */
+	if (readb(dev->mem_start) != TESTvalue)
+	{
+		BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
+		return 1;
+	}
+
+	/* clear out status variables */
+	recbuf=lp->recbuf=0;
+	lp->txbuf=2;
+
+	/* enable extended (512-byte) packets */
+	ACOMMAND(CONFIGcmd|EXTconf);
+
+#ifndef SLOW_XMIT_COPY
+	/* clean out all the memory to make debugging make more sense :) */
+	BUGLVL(D_DURING)
+		memset_io(dev->mem_start,0x42,2048);
+#endif
+
+	/* and enable receive of our first packet to the first buffer */
+	EnableReceiver();
+
+	/* re-enable interrupts */
+	lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+	lp->intmask|=RECONflag;
+#endif
+	SETMASK;
+
+	/* done!  return success. */
+	return 0;
+}
+
+static void arc90xx_openclose(int open)
+{
+ if (open)
+   MOD_INC_USE_COUNT;
+ else
+   MOD_DEC_USE_COUNT;
+}
+
+
+static void arc90xx_setmask(struct device *dev, u_char mask)
+{
+  short ioaddr=dev->base_addr;
+
+  AINTMASK(mask);
+}
+
+static u_char arc90xx_status(struct device *dev)
+{
+  short ioaddr=dev->base_addr;
+
+  return ARCSTATUS;
+}
+
+static void arc90xx_command(struct device *dev, u_char cmd)
+{
+  short ioaddr=dev->base_addr;
+
+  ACOMMAND(cmd);
+}
+
+
+/* The actual interrupt handler routine - handle various IRQ's generated
+ * by the card.
+ */
+static void
+arc90xx_inthandler(struct device *dev)
+{
+  struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr, status, boguscount = 3, didsomething;
+  
+  
+  AINTMASK(0);
+  
+  BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
+	 ARCSTATUS,lp->intmask);
+  
+  do
+    {
+      status = ARCSTATUS;
+		didsomething=0;
+		
+		
+		/* RESET flag was enabled - card is resetting and if RX
+		 * is disabled, it's NOT because we just got a packet.
+		 */
+		if (status & RESETflag)
+		  {
+		    BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
+			   status);
+		    arc90xx_reset(dev,0);
+		    
+		    /* all other flag values are just garbage */
+		    break;
+		  }
+		
+		
+		/* RX is inhibited - we must have received something. */
+		if (status & lp->intmask & NORXflag)
+		  {
+		    int recbuf=lp->recbuf=!lp->recbuf;
+		    
+		    BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+			   status);
+		    
+		    /* enable receive of our next packet */
+		    EnableReceiver();
+		    
+		    /* Got a packet. */
+		    arc90xx_rx(dev,!recbuf);
+			
+		    didsomething++;
+		  }
+		
+		/* it can only be an xmit-done irq if we're xmitting :) */
+		/*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+		if (status & lp->intmask & TXFREEflag)
+		  {
+		    struct Outgoing *out=&(lp->outgoing);
+		    int was_sending=lp->sending;
+		    
+		    lp->intmask &= ~TXFREEflag;
+		    
+			lp->in_txhandler++;
+			if (was_sending) lp->sending--;
+			
+			BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+			       status,out->numsegs,out->segnum,out->skb);
+			
+			if (was_sending && !(status&TXACKflag))
+			  {
+			    if (lp->lasttrans_dest != 0)
+			      {
+				BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
+				       status,lp->lasttrans_dest);
+				lp->stats.tx_errors++;
+				lp->stats.tx_carrier_errors++;
+			      }
+			    else
+			      {
+				BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
+				       status,
+				       lp->lasttrans_dest);
+			      }
+			  }
+			
+			/* send packet if there is one */
+			arcnet_go_tx(dev,0);
+			didsomething++;
+			
+			if (lp->intx)
+			  {
+			    BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+				   ARCSTATUS,lp->intx);
+			    lp->in_txhandler--;
+			    continue;
+			  }
+			
+			if (!lp->outgoing.skb)
+			  {
+			    BUGMSG(D_DURING,"TX IRQ done: no split to continue.\n");
+			    
+			    /* inform upper layers */
+			    if (!lp->txready) arcnet_tx_done(dev, lp);
+			    lp->in_txhandler--;
+			    continue;
+			  }
+			
+			/* if more than one segment, and not all segments
+			 * are done, then continue xmit.
+			 */
+			if (out->segnum<out->numsegs)
+			  arcnetA_continue_tx(dev);
+			arcnet_go_tx(dev,0);
+			
+			/* if segnum==numsegs, the transmission is finished;
+			 * free the skb.
+			 */
+			if (out->segnum>=out->numsegs)
+			  {
+			    /* transmit completed */
+			    out->segnum++;
+			    if (out->skb)
+			      {
+				lp->stats.tx_bytes += out->skb->len;
+				dev_kfree_skb(out->skb,FREE_WRITE);
+			      }
+			    out->skb=NULL;
+			    
+			    /* inform upper layers */
+			    if (!lp->txready) arcnet_tx_done(dev, lp);
+			  }
+			didsomething++;
+			
+			lp->in_txhandler--;
+		  }
+		else if (lp->txready && !lp->sending && !lp->intx)
+		  {
+		    BUGMSG(D_NORMAL,"recovery from silent TX (status=%Xh)\n",
+			   status);
+		    arcnet_go_tx(dev,0);
+		    didsomething++;
+		  }
+
+#ifdef DETECT_RECONFIGS
+		if (status & (lp->intmask) & RECONflag)
+		  {
+		    ACOMMAND(CFLAGScmd|CONFIGclear);
+		    lp->stats.tx_carrier_errors++;
+		    
+#ifdef SHOW_RECONFIGS
+		    BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
+			   status);
+		    
+		    
+#endif /* SHOW_RECONFIGS */
+		    
+#ifdef RECON_THRESHOLD
+		    /* is the RECON info empty or old? */
+		    if (!lp->first_recon || !lp->last_recon ||
+			jiffies-lp->last_recon > HZ*10)
+		      {
+			if (lp->network_down)
+			  BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+			lp->first_recon=lp->last_recon=jiffies;
+			lp->num_recons=lp->network_down=0;
+			
+			BUGMSG(D_DURING,"recon: clearing counters.\n");
+		      }
+		    else /* add to current RECON counter */
+		      {
+			lp->last_recon=jiffies;
+			lp->num_recons++;
+			
+			BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+			       lp->num_recons,
+			       (lp->last_recon-lp->first_recon)/HZ,
+			       lp->network_down);
+			
+			/* if network is marked up;
+			 * and first_recon and last_recon are 60+ sec
+			 *   apart;
+			 * and the average no. of recons counted is
+			 *   > RECON_THRESHOLD/min;
+			 * then print a warning message.
+			 */
+			if (!lp->network_down
+			    && (lp->last_recon-lp->first_recon)<=HZ*60
+			    && lp->num_recons >= RECON_THRESHOLD)
+			  {
+			    lp->network_down=1;
+			    BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+			  }
+			else if (!lp->network_down
+				 && lp->last_recon-lp->first_recon > HZ*60)
+			  {
+			    /* reset counters if we've gone for
+			     * over a minute.
+			     */
+			    lp->first_recon=lp->last_recon;
+			    lp->num_recons=1;
+			  }
+		      }
+		  }
+		else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+		  {
+		    if (lp->network_down)
+		      BUGMSG(D_NORMAL,"cabling restored?\n");
+		    lp->first_recon=lp->last_recon=0;
+		    lp->num_recons=lp->network_down=0;
+		    
+		    BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+#endif
+		  }
+#endif /* DETECT_RECONFIGS */
+    } while (--boguscount && didsomething);
+  
+  BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n",
+	 ARCSTATUS,boguscount);
+  BUGMSG(D_DURING,"\n");
+  
+  SETMASK;	/* put back interrupt mask */
+  
+}
+
+
+
+/* A packet has arrived; grab it from the buffers and pass it to the generic
+ * arcnet_rx routing to deal with it.
+ */ 
+
+static void
+arc90xx_rx(struct device *dev,int recbuf)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  int ioaddr=dev->base_addr;
+  union ArcPacket *arcpacket=
+    (union ArcPacket *)phys_to_virt(dev->mem_start+recbuf*512);
+  u_char *arcsoft;
+  short length,offset;
+  u_char daddr,saddr;
+  
+  lp->stats.rx_packets++;
+  
+  saddr=arcpacket->hardheader.source;
+  
+  /* if source is 0, it's a "used" packet! */
+  if (saddr==0)
+    {
+      BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+	     ARCSTATUS);
+      lp->stats.rx_errors++;
+      return;
+    }
+  /* Set source address to zero to mark it as old */
+  
+  arcpacket->hardheader.source=0;
+  
+  daddr=arcpacket->hardheader.destination;
+  
+  if (arcpacket->hardheader.offset1) /* Normal Packet */
+    {
+      offset=arcpacket->hardheader.offset1;
+      arcsoft=&arcpacket->raw[offset];
+      length=256-offset;
+    }
+  else		/* ExtendedPacket or ExceptionPacket */
+    {
+      offset=arcpacket->hardheader.offset2;
+      arcsoft=&arcpacket->raw[offset];
+      
+      length=512-offset;
+    }
+  
+  
+  arcnet_rx(lp, arcsoft, length, saddr, daddr);
+
+
+  BUGLVL(D_RX) arcnet_dump_packet(lp->adev,arcpacket->raw,length>240,"rx");
+   
+
+#ifndef SLOW_XMIT_COPY
+  /* clean out the page to make debugging make more sense :) */
+  BUGLVL(D_DURING)
+    memset((void *)arcpacket->raw,0x42,512);
+#endif
+  
+}
+
+
+
+
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arc90xx_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+		char *data,int length,int daddr,int exceptA, int offset)
+{
+  struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+  union ArcPacket *arcpacket =
+    (union ArcPacket *)phys_to_virt(dev->mem_start+512*(lp->txbuf^1));
+  
+#ifdef SLOW_XMIT_COPY
+  char *iptr,*iend,*optr;
+#endif
+  
+  lp->txbuf=lp->txbuf^1;	/* XOR with 1 to alternate between 2 and 3 */
+  
+  length+=hdrlen;
+  
+  BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+	 hdr,length,data);
+  
+#ifndef SLOW_XMIT_COPY
+  /* clean out the page to make debugging make more sense :) */
+  BUGLVL(D_DURING)
+    memset_io(dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
+  
+  arcpacket->hardheader.destination=daddr;
+  
+  /* load packet into shared memory */
+  if (length<=MTU)	/* Normal (256-byte) Packet */
+    arcpacket->hardheader.offset1=offset=offset?offset:256-length;
+  
+  else if (length>=MinTU || offset)	/* Extended (512-byte) Packet */
+    {
+       arcpacket->hardheader.offset1=0;
+       arcpacket->hardheader.offset2=offset=offset?offset:512-length;   
+    }
+  else if (exceptA)		/* RFC1201 Exception Packet */
+    {
+       arcpacket->hardheader.offset1=0;
+       arcpacket->hardheader.offset2=offset=512-length-4;
+       
+       /* exception-specific stuff - these four bytes
+	* make the packet long enough to fit in a 512-byte
+	* frame.
+	*/
+       
+       arcpacket->raw[offset+0]=hdr[0];
+       arcpacket->raw[offset+1]=0xFF; /* FF flag */
+       arcpacket->raw[offset+2]=0xFF; /* FF padding */
+       arcpacket->raw[offset+3]=0xFF; /* FF padding */
+       offset+=4;
+    }
+  else				/* "other" Exception packet */
+    {
+      /* RFC1051 - set 4 trailing bytes to 0 */
+      memset(&arcpacket->raw[508],0,4);
+      
+      /* now round up to MinTU */
+      arcpacket->hardheader.offset1=0;
+      arcpacket->hardheader.offset2=offset=512-MinTU;
+    }
+  
+  
+  /* copy the packet into ARCnet shmem
+   *  - the first bytes of ClientData header are skipped
+   */
+
+  memcpy((u_char*)arcpacket+offset, (u_char*)hdr,hdrlen);
+#ifdef SLOW_XMIT_COPY
+  for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcpacket+offset+hdrlen;
+       iptr<iend; iptr++,optr++)
+    {
+      *optr=*iptr;
+      /*udelay(5);*/
+    }
+#else
+  memcpy((u_char*)arcpacket+offset+hdrlen, data,length-hdrlen);
+#endif
+   
+  BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+	 daddr,length);
+  
+  BUGLVL(D_TX) arcnet_dump_packet(dev,arcpacket->raw,length>MTU,"tx");
+  
+  lp->lastload_dest=daddr;
+  lp->txready=lp->txbuf;	/* packet is ready for sending */
+}
+
+
+/****************************************************************************
+ *                                                                          *
+ * Kernel Loadable Module Support                                           *
+ *                                                                          *
+ ****************************************************************************/
+
+
+#ifdef MODULE
+
+static char devicename[9] = "";
+static struct device thiscard = {
+  devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+  0, 0, 0, 0,
+  0, 0,  /* I/O address, IRQ */
+  0, 0, 0, NULL, arc90xx_probe
+};
+
+
+int init_module(void)
+{
+  struct device *dev=&thiscard;
+  if (device)
+    strcpy(dev->name,device);
+  else arcnet_makename(dev->name);
+  
+  dev->base_addr=io;
+  
+  dev->irq=irq;
+  if (dev->irq==2) dev->irq=9;
+  
+  if (shmem)
+    {
+      dev->mem_start=shmem;
+      dev->mem_end=thiscard.mem_start+512*4-1;
+      dev->rmem_start=thiscard.mem_start+512*0;
+      dev->rmem_end=thiscard.mem_start+512*2-1;
+    }
+  
+  if (register_netdev(dev) != 0)
+    return -EIO;
+  arcnet_use_count(1);
+  return 0;
+}
+
+void cleanup_module(void)
+{
+	struct device *dev=&thiscard;
+	int ioaddr=dev->mem_start;
+
+	if (dev->start) (*dev->stop)(dev);
+
+	/* Flush TX and disable RX */
+	if (ioaddr)
+	{
+		AINTMASK(0);		/* disable IRQ's */
+		ACOMMAND(NOTXcmd);	/* stop transmit */
+		ACOMMAND(NORXcmd);	/* disable receive */
+
+#if defined(IO_MAPPED_BUFFERS) && !defined(COM20020)
+		/* Set the thing back to MMAP mode, in case the old
+        		   driver is loaded later */
+		outb( (inb(_CONFIG)&~IOMAPflag),_CONFIG);
+#endif
+	}
+
+	if (dev->irq)
+	{
+		irq2dev_map[dev->irq] = NULL;
+		free_irq(dev->irq,NULL);
+	}
+
+	if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
+	unregister_netdev(dev);
+	kfree(dev->priv);
+	dev->priv = NULL;
+	arcnet_use_count(0);
+}
+#else
+
+__initfunc(void com90xx_setup (char *str, int *ints))
+{
+  struct device *dev;
+
+  if (arcnet_num_devs == MAX_ARCNET_DEVS)
+    {
+      printk("com90xx: Too many ARCnet devices registered (max %d).\n",
+	     MAX_ARCNET_DEVS);
+      return;
+    }
+  
+  if (!ints[0] && (!str || !*str))
+    {
+      printk("com90xx: Disabled.\n");
+      com90xx_explicit++;
+      return;
+    }
+
+  dev=&arcnet_devs[arcnet_num_devs];
+  
+  dev->dev_addr[3]=3;
+  dev->init=arc90xx_probe;
+
+  switch(ints[0])
+    {
+    case 4: /* ERROR */
+      printk("com20020: Too many arguments.\n");
+
+    case 3: /* Mem address */
+      dev->mem_start=ints[3];
+
+    case 2: /* IRQ */
+      dev->irq=ints[2];
+      
+    case 1: /* IO address */
+      dev->base_addr=ints[1];
+    }
+
+  dev->name = (char *)&arcnet_dev_names[arcnet_num_devs];
+  
+  if (str)
+    strncpy(dev->name, str, 9);
+
+  arcnet_num_devs++;
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.52/linux/drivers/net/defxx.c linux/drivers/net/defxx.c
--- v2.1.52/linux/drivers/net/defxx.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/net/defxx.c	Thu Sep  4 13:25:28 1997
@@ -2953,7 +2953,7 @@
 			 * the old EISA boards.
 			 */
 			newskb->data = (char *)((unsigned long)
-						(newskb->data+127) & ~128);
+						(newskb->data+127) & ~127);
 			bp->descr_block_virt->rcv_data[i+j].long_1 = virt_to_bus(newskb->data);
 			/*
 			 * p_rcv_buff_va is only used inside the
@@ -3065,7 +3065,7 @@
 					if (newskb){
 						rx_in_place = 1;
 
-						newskb->data = (char *)((unsigned long)(newskb->data+127) & ~128);
+						newskb->data = (char *)((unsigned long)(newskb->data+127) & ~127);
 						skb = (struct sk_buff *)bp->p_rcv_buff_va[entry];
 						skb->data += RCV_BUFF_K_PADDING;
 						bp->p_rcv_buff_va[entry] = (char *)newskb;
diff -u --recursive --new-file v2.1.52/linux/drivers/net/ethertap.c linux/drivers/net/ethertap.c
--- v2.1.52/linux/drivers/net/ethertap.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/ethertap.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,225 @@
+/*
+ *	Ethertap: A network device for bouncing packets via user space
+ *
+ *	This is a very simple ethernet driver. It bounces ethernet frames
+ *	to user space on /dev/tap0->/dev/tap15 and expects ethernet frames
+ *	to be written back to it. By default it does not ARP. If you turn ARP
+ *	on it will attempt to ARP the user space and reply to ARPS from the
+ *	user space.
+ *
+ *	As this is an ethernet device you cau use it for appletalk, IPX etc
+ *	even for building bridging tunnels.
+ */
+ 
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <net/netlink.h>
+
+/*
+ *	Index to functions.
+ */
+
+int	    ethertap_probe(struct device *dev);
+static int  ethertap_open(struct device *dev);
+static int  ethertap_start_xmit(struct sk_buff *skb, struct device *dev);
+static int  ethertap_close(struct device *dev);
+static struct net_device_stats *ethertap_get_stats(struct device *dev);
+static int ethertap_rx(int id, struct sk_buff *skb);
+
+static int ethertap_debug = 0;
+
+static struct device *tap_map[32];	/* Returns the tap device for a given netlink */
+
+/*
+ *	Board-specific info in dev->priv.
+ */
+
+struct net_local
+{
+	struct net_device_stats stats;
+};
+
+/*
+ *	To call this a probe is a bit misleading, however for real
+ *	hardware it would have to check what was present.
+ */
+ 
+__initfunc(int ethertap_probe(struct device *dev))
+{
+	memcpy(dev->dev_addr, "\xFD\xFD\x00\x00\x00\x00", 6);
+	if (dev->mem_start & 0xf)
+		ethertap_debug = dev->mem_start & 0x7;
+
+	/*
+	 *	Initialize the device structure.
+	 */
+
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	/*
+	 *	The tap specific entries in the device structure.
+	 */
+
+	dev->open = ethertap_open;
+	dev->hard_start_xmit = ethertap_start_xmit;
+	dev->stop = ethertap_close;
+	dev->get_stats = ethertap_get_stats;
+
+	/*
+	 *	Setup the generic properties
+	 */
+
+	ether_setup(dev);
+
+	dev->flags|=IFF_NOARP;	/* Need to set ARP - looks like there is a bug
+				   in the 2.1.x hard header code currently */
+	tap_map[dev->base_addr]=dev;
+	
+	return 0;
+}
+
+/*
+ *	Open/initialize the board.
+ */
+
+static int ethertap_open(struct device *dev)
+{
+	if (ethertap_debug > 2)
+		printk("%s: Doing ethertap_open()...", dev->name);
+	netlink_attach(dev->base_addr, ethertap_rx);
+	dev->start = 1;
+	dev->tbusy = 0;
+	/* Fill in the MAC based on the IP address. We do the same thing
+	   here as PLIP does */
+	memcpy(dev->dev_addr+2,&dev->pa_addr,4);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	We transmit by throwing the packet at netlink. We have to clone
+ *	it for 2.0 so that we dev_kfree_skb() the locked original.
+ */
+ 
+static int ethertap_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	struct sk_buff *tmp;
+	/* copy buffer to tap */
+	tmp=skb_clone(skb, GFP_ATOMIC);
+	if(tmp)
+	{
+		if(netlink_post(dev->base_addr, tmp)<0)
+			kfree_skb(tmp, FREE_WRITE);
+		lp->stats.tx_bytes+=skb->len;
+		lp->stats.tx_packets++;
+	}
+	dev_kfree_skb (skb, FREE_WRITE);
+	return 0;
+}
+
+/*
+ *	The typical workload of the driver:
+ *	Handle the ether interface interrupts.
+ *
+ *	(In this case handle the packets posted from user space..)
+ */
+
+static int ethertap_rx(int id, struct sk_buff *skb)
+{
+	struct device *dev = (struct device *)(tap_map[id]);
+	struct net_local *lp;
+	int len=skb->len;
+	
+	if(dev==NULL)
+	{
+		printk("%s: bad unit!\n",dev->name);
+		kfree_skb(skb, FREE_WRITE);
+		return -ENXIO;
+	}
+	lp = (struct net_local *)dev->priv;
+
+	if (ethertap_debug > 3)
+		printk("%s: ethertap_rx()\n", dev->name);
+	skb->dev = dev;
+	skb->protocol=eth_type_trans(skb,dev);
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes+=len;
+	netif_rx(skb);
+	return len;
+}
+
+static int ethertap_close(struct device *dev)
+{
+	if (ethertap_debug > 2)
+		printk("%s: Shutting down tap %ld.\n", dev->name, dev->base_addr);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct net_device_stats *ethertap_get_stats(struct device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	return &lp->stats;
+}
+
+#ifdef MODULE
+
+int unit;
+MODULE_PARM(unit,"i");
+
+static char devicename[9] = { 0, };
+
+static struct device dev_ethertap =
+{
+	devicename,
+	0, 0, 0, 0,
+	1, 5,
+	0, 0, 0, NULL, ethertap_probe
+};
+
+int init_module(void)
+{
+	dev_ethertap.base_addr=unit+NETLINK_TAPBASE;
+	sprintf(devicename,"tap%d",unit);
+	if (dev_get(devicename))
+	{
+		printk(KERN_INFO "ethertap: tap %d already loaded.\n", unit);
+		return -EBUSY;
+	}
+	if (register_netdev(&dev_ethertap) != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	tap_map[dev_ethertap.base_addr]=NULL;
+	unregister_netdev(&dev_ethertap);
+
+	/*
+	 *	Free up the private structure.
+	 */
+
+	kfree(dev_ethertap.priv);
+	dev_ethertap.priv = NULL;	/* gets re-allocated by ethertap_probe */
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.52/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.1.52/linux/drivers/net/ibmtr.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/ibmtr.c	Thu Sep  4 13:25:28 1997
@@ -58,6 +58,10 @@
  *
  *	Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk>
  *	+ Now compiles ok as a module again.
+ *
+ *	Changes by Paul Norton (pnorton@cts.com) :
+ *      + moved the header manipulation code in tr_tx and tr_rx to
+ *        net/802/tr.c. (July 12 1997)
  */
 
 #ifdef PCMCIA
@@ -87,7 +91,7 @@
 /* version and credits */
 static char *version =
 "ibmtr.c: v1.3.57  8/ 7/94 Peter De Schrijver and Mark Swanson\n"
-"         v2.1.35  5/ 1/97 Paul Norton <pnorton@cts.com>\n";
+"         v2.1.42  7/12/97 Paul Norton <pnorton@cts.com>\n";
 
 static char pcchannelid[] = {
 	0x05, 0x00, 0x04, 0x09,
@@ -168,8 +172,8 @@
 static int	ibmtr_probe1(struct device *dev, int ioaddr);
 static unsigned char	get_sram_size(struct tok_info *adapt_info);
 static int	tok_init_card(struct device *dev);
-static int	trdev_init(struct device *dev);
 void		tok_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int	trdev_init(struct device *dev);
 static void 	initial_tok_int(struct device *dev);
 static void 	open_sap(unsigned char type,struct device *dev);
 void		tok_open_adapter(unsigned long dev_addr);
@@ -1249,7 +1253,15 @@
 	   effective address where we will place data.*/
 	dhb=ti->sram
 		+ntohs(readw(ti->arb + offsetof(struct arb_xmit_req, dhb_address)));
-	llc = (struct trllc *) &(ti->current_skb->data[sizeof(struct trh_hdr)]);
+	
+	/* Figure out the size of the 802.5 header */
+	if (!(trhdr->saddr[0] & 0x80)) /* RIF present? */
+		hdr_len=sizeof(struct trh_hdr)-18;
+	else 
+		hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8)
+			+sizeof(struct trh_hdr)-18;
+
+	llc = (struct trllc *)(ti->current_skb->data + hdr_len);
 
 	xmit_command = readb(ti->srb + offsetof(struct srb_xmit, command));
 
@@ -1277,39 +1289,15 @@
 
 	}
 
-	/* the token ring packet is copied from sk_buff to the adapter
-	   buffer identified in the command data received with the
-	   interrupt.  The sk_buff area was set up with a maximum
-	   sized route information field so here we must compress
-	   out the extra (all) rif fields.   */
-	/* nb/dwm .... I re-arranged code here to avoid copy of extra
-	   bytes, ended up with fewer statements as well. */
-
-	/* TR arch. identifies if RIF present by high bit of source
-	   address.  So here we check if RIF present */
-
-	if (!(trhdr->saddr[0] & 0x80)) { /* RIF present : preserve it */
-		hdr_len=sizeof(struct trh_hdr)-18;
-
-#if TR_VERBOSE
-		DPRINTK("hdr_length: %d, frame length: %ld\n", hdr_len,
-			ti->current_skb->len-18);
-#endif
-	} else hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8)
-		  +sizeof(struct trh_hdr)-18;
-
-	/* header length including rif is computed above, now move the data
-	   and set fields appropriately. */
-	memcpy_toio(dhb, ti->current_skb->data, hdr_len);
-
+	/*
+	 *      the token ring packet is copied from sk_buff to the adapter
+	 *      buffer identified in the command data received with the interrupt.
+	 */
 	writeb(hdr_len, ti->asb + offsetof(struct asb_xmit_resp, hdr_length));
-	writew(htons(ti->current_skb->len-sizeof(struct trh_hdr)+hdr_len),
+	writew(htons(ti->current_skb->len),
 	       ti->asb + offsetof(struct asb_xmit_resp, frame_length));
 
-	/*  now copy the actual packet data next to hdr */
-	memcpy_toio(dhb + hdr_len,
-		    ti->current_skb->data + sizeof(struct trh_hdr),
-		    ti->current_skb->len - sizeof(struct trh_hdr));
+	memcpy_toio(dhb, ti->current_skb->data, ti->current_skb->len);
 
 	writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
 	dev->tbusy=0;
@@ -1328,7 +1316,7 @@
 	unsigned int rbuffer_len, lan_hdr_len, hdr_len, ip_len, length;
 	struct sk_buff *skb;
 	unsigned int skb_size = 0;
-	int	is8022 = 0;
+	int	IPv4_p = 0;
 	unsigned int chksum = 0;
 	struct iphdr *iph;
 
@@ -1347,7 +1335,7 @@
 
 	lan_hdr_len=readb(ti->arb + offsetof(struct arb_rec_req, lan_hdr_len));
 	
-	llc=(rbuffer+offsetof(struct rec_buf, data) + lan_hdr_len);
+	llc=(rbuffer + offsetof(struct rec_buf, data) + lan_hdr_len);
 
 #if TR_VERBOSE
 	DPRINTK("offsetof data: %02X lan_hdr_len: %02X\n",
@@ -1366,19 +1354,19 @@
 		(int)readw(llc + offsetof(struct trllc, ethertype)));
 #endif
 	if (readb(llc + offsetof(struct trllc, llc))!=UI_CMD) {
-			writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code));
-			ti->tr_stats.rx_dropped++;
-			writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
-			return;
+		writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code));
+		ti->tr_stats.rx_dropped++;
+		writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
+		return;
 	}
 
-       	if ((readb(llc + offsetof(struct trllc, dsap))!=0xAA) ||
-       	    (readb(llc + offsetof(struct trllc, ssap))!=0xAA)) {
-       		is8022 = 1;
+       	if ((readb(llc + offsetof(struct trllc, dsap))==0xAA) &&
+       	    (readb(llc + offsetof(struct trllc, ssap))==0xAA)) {
+       		IPv4_p = 1;
        	}
 
 #if TR_VERBOSE
-       	if (is8022){
+       	if (!IPv4_p){
 
        		__u32 trhhdr;
 
@@ -1405,11 +1393,8 @@
 #endif
 
        	length = ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len)));
-       	skb_size = length-lan_hdr_len+sizeof(struct trh_hdr);
-       	if (is8022) {
-       		skb_size += sizeof(struct trllc);
-       	}
-
+       	skb_size = length-lan_hdr_len+sizeof(struct trh_hdr)+sizeof(struct trllc);
+ 
        	if (!(skb=dev_alloc_skb(skb_size))) {
        		DPRINTK("out of memory. frame dropped.\n");
        		ti->tr_stats.rx_dropped++;
@@ -1418,53 +1403,37 @@
        		return;
        	}
 
-       	skb_put(skb, skb_size);
+      	skb_put(skb, length);
+	skb_reserve(skb, sizeof(struct trh_hdr)-lan_hdr_len+sizeof(struct trllc));
        	skb->dev=dev;
-
        	data=skb->data;
+	rbuffer_len=ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len)));
+	rbufdata = rbuffer + offsetof(struct rec_buf,data);
 
-	/* Copy the 802.5 MAC header */
-	memcpy_fromio(data, rbuffer + offsetof(struct rec_buf, data), lan_hdr_len);
-
-	if (lan_hdr_len<sizeof(struct trh_hdr))
-		memset(data+lan_hdr_len, 0, sizeof(struct trh_hdr)-lan_hdr_len);
-
-	data+=sizeof(struct trh_hdr);
-	rbuffer_len=ntohs(readw(rbuffer + offsetof(struct rec_buf, buf_len)))-lan_hdr_len;
-
-	if (is8022) {
-		/* create whitewashed LLC header in sk buffer */
-		struct trllc	*local_llc = (struct trllc *)data;
-		memset(local_llc, 0, sizeof(*local_llc));
-		local_llc->ethertype = htons(ETH_P_TR_802_2);
-		hdr_len = sizeof(struct trllc);
-		/* copy the real LLC header to the sk buffer */
-		data += hdr_len;
-		memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len);
-	} else {
-                /* Copy the LLC header and the IPv4 header */
-		hdr_len = sizeof(struct trllc) + sizeof(struct iphdr);
-		memcpy_fromio(data, rbuffer+offsetof(struct rec_buf, data)+lan_hdr_len,hdr_len);
+	if (IPv4_p) {
+                /* Copy the headers without checksumming */
+		hdr_len = lan_hdr_len + sizeof(struct trllc) + sizeof(struct iphdr);
+		memcpy_fromio(data, rbufdata, hdr_len);
 
 		/* Watch for padded packets and bogons */
-		iph=(struct iphdr*)(data+sizeof(struct trllc));
+		iph=(struct iphdr*)(data + lan_hdr_len + sizeof(struct trllc));
 		ip_len = ntohs(iph->tot_len) - sizeof(struct iphdr);
-		length -= lan_hdr_len + hdr_len;
+		length -= hdr_len;
 		if ((ip_len <= length) && (ip_len > 7))
 			length = ip_len;
+		data += hdr_len;
+		rbuffer_len -= hdr_len;
+		rbufdata += hdr_len;
         }
-	data        += hdr_len;
-	rbuffer_len -= hdr_len;
-	rbufdata     = rbuffer + offsetof(struct rec_buf,data) + lan_hdr_len + hdr_len;
 
 	/* Copy the payload... */
 	for (;;) {
-		if (is8022)
-			memcpy_fromio(data, rbufdata, rbuffer_len);
-		else
+		if (IPv4_p)
 			chksum = csum_partial_copy(bus_to_virt(rbufdata), data,
 						   length < rbuffer_len ? length : rbuffer_len,
 						   chksum);
+		else
+			memcpy_fromio(data, rbufdata, rbuffer_len);
 		rbuffer = ntohs(readw(rbuffer));
 		if (!rbuffer)
 			break;
@@ -1481,11 +1450,14 @@
 
        	ti->tr_stats.rx_packets++;
 
-	skb->protocol  = tr_type_trans(skb,dev);
-	if (!is8022){
+	tr_reformat(skb, lan_hdr_len); 
+	skb->protocol = tr_type_trans(skb,dev);
+
+ 	if (IPv4_p){
 		skb->csum      = chksum;
 		skb->ip_summed = 1;
 	}
+
 	netif_rx(skb);
 }
 
@@ -1610,7 +1582,7 @@
 			 irq2dev_map[dev_ibmtr[i]->irq] = NULL;
 			 release_region(dev_ibmtr[i]->base_addr, IBMTR_IO_EXTENT);
 			 kfree_s(dev_ibmtr[i]->priv, sizeof(struct tok_info));
-			 kfree_s(dev_ibmtr[i], sizeof(struct dev));
+			 kfree_s(dev_ibmtr[i], sizeof(struct device));
 			 dev_ibmtr[i] = NULL;
                 }
 }
diff -u --recursive --new-file v2.1.52/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.1.52/linux/drivers/net/myri_sbus.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/net/myri_sbus.c	Thu Sep  4 13:25:28 1997
@@ -19,7 +19,9 @@
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 #include <linux/init.h>
+
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -34,6 +36,7 @@
 #include <asm/auxio.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
+#include <asm/irq.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -275,7 +278,7 @@
 		mp->rx_skbs[i] = skb;
 		skb->dev = dev;
 		skb_put(skb, RX_ALLOC_SIZE);
-		rxd[i].myri_scatters[0].addr = (unsigned long) skb->data;
+		rxd[i].myri_scatters[0].addr = (u32) ((unsigned long)skb->data);
 		rxd[i].myri_scatters[0].len = RX_ALLOC_SIZE;
 		rxd[i].ctx = i;
 		rxd[i].num_sg = 1;
@@ -338,6 +341,11 @@
 		mp->tx_skbs[entry] = NULL;
 		mp->enet_stats.tx_packets++;
 
+#ifdef NEED_DMA_SYNCHRONIZATION
+		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+			     skb->len, mp->myri_sbus_dev->my_bus);
+#endif
+
 		entry = NEXT_TX(entry);
 	}
 	mp->tx_old = entry;
@@ -429,7 +437,8 @@
 			drops++;
 			DRX(("DROP "));
 			mp->enet_stats.rx_dropped++;
-			rxd->myri_scatters[0].addr = (unsigned long) skb->data;
+			rxd->myri_scatters[0].addr =
+				(u32) ((unsigned long)skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -437,6 +446,11 @@
 			goto next;
 		}
 
+#ifdef NEED_DMA_SYNCHRONIZATION
+		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+			     skb->len, mp->myri_sbus_dev->my_bus);
+#endif
+
 		DRX(("len[%d] ", len));
 		if(len > RX_COPY_THRESHOLD) {
 			struct sk_buff *new_skb;
@@ -450,7 +464,8 @@
 			mp->rx_skbs[index] = new_skb;
 			new_skb->dev = dev;
 			skb_put(new_skb, RX_ALLOC_SIZE);
-			rxd->myri_scatters[0].addr = (unsigned long) new_skb->data;
+			rxd->myri_scatters[0].addr =
+				(u32) ((unsigned long)new_skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -474,7 +489,8 @@
 
 			/* Reuse original ring buffer. */
 			DRX(("reuse "));
-			rxd->myri_scatters[0].addr = (unsigned long) skb->data;
+			rxd->myri_scatters[0].addr =
+				(u32) ((unsigned long)skb->data);
 			rxd->myri_scatters[0].len = RX_ALLOC_SIZE;
 			rxd->ctx = index;
 			rxd->num_sg = 1;
@@ -618,7 +634,8 @@
 	txd = &sq->myri_txd[entry];
 	mp->tx_skbs[entry] = skb;
 
-	txd->myri_gathers[0].addr = (unsigned long) skb->data;
+	txd->myri_gathers[0].addr =
+		(unsigned int) ((unsigned long)skb->data);
 	txd->myri_gathers[0].len = len;
 	txd->num_sg = 1;
 	txd->chan = KERNEL_CHANNEL;
@@ -837,12 +854,12 @@
 	case CPUVERS_3_0:
 	case CPUVERS_3_1:
 	case CPUVERS_3_2:
-		mp->reg_size = (3 * 128 * 1024) + PAGE_SIZE;
+		mp->reg_size = (3 * 128 * 1024) + 4096;
 		break;
 
 	case CPUVERS_4_0:
 	case CPUVERS_4_1:
-		mp->reg_size = ((PAGE_SIZE<<1) + mp->eeprom.ramsz);
+		mp->reg_size = ((4096<<1) + mp->eeprom.ramsz);
 		break;
 
 	case CPUVERS_4_2:
@@ -850,7 +867,7 @@
 	default:
 		printk("myricom: AIEEE weird cpu version %04x assuming pre4.0\n",
 		       mp->eeprom.cpuvers);
-		mp->reg_size = (3 * 128 * 1024) + PAGE_SIZE;
+		mp->reg_size = (3 * 128 * 1024) + 4096;
 	};
 }
 
@@ -1077,6 +1094,22 @@
 
 	/* Register interrupt handler now. */
 	DET(("Requesting MYRIcom IRQ line.\n"));
+#ifdef __sparc_v9__
+	if(sparc_cpu_model == sun4u) {
+		struct devid_cookie dcookie;
+
+		dcookie.real_dev_id = dev;
+		dcookie.imap = dcookie.iclr = 0;
+		dcookie.pil = -1;
+		dcookie.bus_cookie = sdev->my_bus;
+		if(request_irq(dev->irq, &myri_interrupt,
+			       (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+			       "MyriCOM Ethernet", &dcookie)) {
+			printk("MyriCOM: Cannot register interrupt handler.\n");
+			return ENODEV;
+		}
+	} else
+#endif
 	if(request_irq(dev->irq, &myri_interrupt,
 		       SA_SHIRQ, "MyriCOM Ethernet", (void *) dev)) {
 		printk("MyriCOM: Cannot register interrupt handler.\n");
diff -u --recursive --new-file v2.1.52/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.52/linux/drivers/net/slip.c	Tue May 13 22:41:11 1997
+++ linux/drivers/net/slip.c	Thu Sep  4 13:25:28 1997
@@ -106,6 +106,7 @@
 #ifdef CONFIG_SLIP_SMART
 static void sl_keepalive(unsigned long sls);
 static void sl_outfill(unsigned long sls);
+static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd);
 #endif
 
 /* Find a free SLIP channel, and link in this `tty' line. */
@@ -1015,8 +1016,6 @@
 #ifdef CONFIG_SLIP_SMART
 	/* VSV changes start here */
         case SIOCSKEEPALIVE:
-                if (sl->keepalive)
-                        (void)del_timer (&sl->keepalive_timer);
 		err = verify_area(VERIFY_READ, arg, sizeof(int));
 		if (err)  {
 			return -err;
@@ -1024,6 +1023,8 @@
 		get_user(tmp,(int *)arg);
                 if (tmp > 255) /* max for unchar */
 			return -EINVAL;
+                if (sl->keepalive)
+                        (void)del_timer (&sl->keepalive_timer);
 		if ((sl->keepalive = (unchar) tmp) != 0) {
 			sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
 			add_timer(&sl->keepalive_timer);
@@ -1040,8 +1041,6 @@
 		return 0;
 
         case SIOCSOUTFILL:
-                if (sl->outfill)
-                         (void)del_timer (&sl->outfill_timer);
 		err = verify_area(VERIFY_READ, arg, sizeof(int));
 		if (err)  {
 			return -err;
@@ -1049,6 +1048,8 @@
 		get_user(tmp,(int *)arg);
                 if (tmp > 255) /* max for unchar */
 			return -EINVAL;
+                if (sl->outfill)
+                         (void)del_timer (&sl->outfill_timer);
                 if ((sl->outfill = (unchar) tmp) != 0){
 			sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
 			add_timer(&sl->outfill_timer);
@@ -1076,6 +1077,58 @@
 	}
 }
 
+/* VSV changes start here */
+#ifdef CONFIG_SLIP_SMART
+/* function do_ioctl called from net/core/dev.c
+   to allow get/set outfill/keepalive parameter
+   by ifconfig                                 */
+
+static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd)
+{
+	struct slip *sl = (struct slip*)(dev->priv);
+
+	if (sl == NULL)		/* Allocation failed ?? */
+		return -ENODEV;
+
+	switch(cmd){
+        case SIOCSKEEPALIVE:
+		/* max for unchar */
+                if (((unsigned int)((unsigned long)rq->ifr_data)) > 255)
+			return -EINVAL;
+                if (sl->keepalive)
+                        (void)del_timer (&sl->keepalive_timer);
+		sl->keepalive = (unchar) ((unsigned long)rq->ifr_data);
+		if (sl->keepalive != 0) {
+			sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+			add_timer(&sl->keepalive_timer);
+			set_bit(SLF_KEEPTEST, &sl->flags);
+                }
+		break;
+
+        case SIOCGKEEPALIVE:
+		rq->ifr_data=(caddr_t)((unsigned long)sl->keepalive);
+		break;
+
+        case SIOCSOUTFILL:
+                if (((unsigned)((unsigned long)rq->ifr_data)) > 255) /* max for unchar */
+			return -EINVAL;
+                if (sl->outfill)
+			del_timer (&sl->outfill_timer);
+                if ((sl->outfill = (unchar)((unsigned long) rq->ifr_data)) != 0){
+			sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+			add_timer(&sl->outfill_timer);
+			set_bit(SLF_OUTWAIT, &sl->flags);
+		}
+                break;
+
+        case SIOCGOUTFILL:
+		rq->ifr_data=(caddr_t)((unsigned long)sl->outfill);
+	};
+	return 0;
+}
+#endif
+/* VSV changes end */
+
 static int sl_open_dev(struct device *dev)
 {
 	struct slip *sl = (struct slip*)(dev->priv);
@@ -1173,6 +1226,9 @@
 	dev->open		= sl_open_dev;
 	dev->stop		= sl_close;
 	dev->get_stats	        = sl_get_stats;
+#ifdef CONFIG_SLIP_SMART
+	dev->do_ioctl		= sl_ioctl;
+#endif
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
diff -u --recursive --new-file v2.1.52/linux/drivers/net/soundmodem/Makefile linux/drivers/net/soundmodem/Makefile
--- v2.1.52/linux/drivers/net/soundmodem/Makefile	Mon Jun 16 16:35:55 1997
+++ linux/drivers/net/soundmodem/Makefile	Thu Sep  4 13:25:28 1997
@@ -42,6 +42,9 @@
 
 M_OBJS		:= $(O_TARGET)
 
+all:		all_targets
+.PHONY:		all
+
 gentbl:		gentbl.c
 		$(HOSTCC) -Wall $< -o $@ -lm
 
diff -u --recursive --new-file v2.1.52/linux/drivers/net/soundmodem/sm_afsk2400_7.c linux/drivers/net/soundmodem/sm_afsk2400_7.c
--- v2.1.52/linux/drivers/net/soundmodem/sm_afsk2400_7.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm_afsk2400_7.c	Thu Sep  4 13:25:28 1997
@@ -34,7 +34,6 @@
  * a "legacy" link.
  */
 
-#include <linux/config.h>
 #include "sm.h"
 #include "sm_tbl_afsk2400_7.h"
 
diff -u --recursive --new-file v2.1.52/linux/drivers/net/soundmodem/sm_afsk2400_8.c linux/drivers/net/soundmodem/sm_afsk2400_8.c
--- v2.1.52/linux/drivers/net/soundmodem/sm_afsk2400_8.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm_afsk2400_8.c	Thu Sep  4 13:25:28 1997
@@ -34,7 +34,6 @@
  * a "legacy" link.
  */
 
-#include <linux/config.h>
 #include "sm.h"
 #include "sm_tbl_afsk2400_8.h"
 
diff -u --recursive --new-file v2.1.52/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.1.52/linux/drivers/net/sunhme.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/sunhme.c	Thu Sep  4 13:25:28 1997
@@ -10,6 +10,7 @@
 
 #include <linux/module.h>
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -20,6 +21,7 @@
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -35,11 +37,18 @@
 #include <asm/auxio.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
+#include <asm/irq.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/pbm.h>
+#endif
+
 #include "sunhme.h"
 
 #ifdef MODULE
@@ -68,30 +77,33 @@
 #define DEFAULT_JAMSIZE    4 /* Toe jam */
 
 /* Oh yes, the MIF BitBang is mighty fun to program.  BitBucket is more like it. */
-#define BB_PUT_BIT(tregs, bit) \
-do { (tregs)->bb_data = (bit); (tregs)->bb_clock = 0; (tregs)->bb_clock = 1; } while(0)
-
-#define BB_GET_BIT(tregs, internal)              \
-({                                               \
-	(tregs)->bb_clock = 0;                   \
-	(tregs)->bb_clock = 1;                   \
-	if(internal)                             \
-		((tregs)->cfg & TCV_CFG_MDIO0);  \
-	else                                     \
-		((tregs)->cfg & TCV_CFG_MDIO1);  \
+#define BB_PUT_BIT(hp, tregs, bit)						\
+do {	hme_write32(hp, &(tregs)->bb_data, (bit));				\
+	hme_write32(hp, &(tregs)->bb_clock, 0);					\
+	hme_write32(hp, &(tregs)->bb_clock, 1);					\
+} while(0)
+
+#define BB_GET_BIT(hp, tregs, internal)						\
+({										\
+	hme_write32(hp, &(tregs)->bb_clock, 0);					\
+	hme_write32(hp, &(tregs)->bb_clock, 1);					\
+	if(internal)								\
+		hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0;			\
+	else									\
+		hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1;			\
 })
 
-#define BB_GET_BIT2(tregs, internal)                      \
-({                                                        \
-	int retval;                                       \
-	(tregs)->bb_clock = 0;                            \
-	udelay(1);                                        \
-	if(internal)                                      \
-		retval = ((tregs)->cfg & TCV_CFG_MDIO0);  \
-	else                                              \
-		retval = ((tregs)->cfg & TCV_CFG_MDIO1);  \
-	(tregs)->bb_clock = 1;                            \
-	retval;                                           \
+#define BB_GET_BIT2(hp, tregs, internal)					\
+({										\
+	int retval;								\
+	hme_write32(hp, &(tregs)->bb_clock, 0);					\
+	udelay(1);								\
+	if(internal)								\
+		retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO0;		\
+	else									\
+		retval = hme_read32(hp, &(tregs)->cfg) & TCV_CFG_MDIO1;		\
+	hme_write32(hp, &(tregs)->bb_clock, 1);					\
+	retval;									\
 })
 
 #define TCVR_FAILURE      0x80000000     /* Impossible MIF read value */
@@ -107,38 +119,38 @@
 	ASD(("happy_meal_bb_read: reg=%d ", reg));
 
 	/* Enable the MIF BitBang outputs. */
-	tregs->bb_oenab = 1;
+	hme_write32(hp, &tregs->bb_oenab, 1);
 
 	/* Force BitBang into the idle state. */
 	for(i = 0; i < 32; i++)
-		BB_PUT_BIT(tregs, 1);
+		BB_PUT_BIT(hp, tregs, 1);
 
 	/* Give it the read sequence. */
-	BB_PUT_BIT(tregs, 0);
-	BB_PUT_BIT(tregs, 1);
-	BB_PUT_BIT(tregs, 1);
-	BB_PUT_BIT(tregs, 0);
+	BB_PUT_BIT(hp, tregs, 0);
+	BB_PUT_BIT(hp, tregs, 1);
+	BB_PUT_BIT(hp, tregs, 1);
+	BB_PUT_BIT(hp, tregs, 0);
 
 	/* Give it the PHY address. */
 	tmp = hp->paddr & 0xff;
 	for(i = 4; i >= 0; i--)
-		BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
 
 	/* Tell it what register we want to read. */
 	tmp = (reg & 0xff);
 	for(i = 4; i >= 0; i--)
-		BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
 
 	/* Close down the MIF BitBang outputs. */
-	tregs->bb_oenab = 0;
+	hme_write32(hp, &tregs->bb_oenab, 0);
 
 	/* Now read in the value. */
-	unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
+	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
 	for(i = 15; i >= 0; i--)
-		retval |= BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
-	unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
-	unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
-	unused = BB_GET_BIT2(tregs, (hp->tcvr_type == internal));
+		retval |= BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
+	unused = BB_GET_BIT2(hp, tregs, (hp->tcvr_type == internal));
 	ASD(("value=%x\n", retval));
 	return retval;
 }
@@ -153,37 +165,37 @@
 	ASD(("happy_meal_bb_write: reg=%d value=%x\n", reg, value));
 
 	/* Enable the MIF BitBang outputs. */
-	tregs->bb_oenab = 1;
+	hme_write32(hp, &tregs->bb_oenab, 1);
 
 	/* Force BitBang into the idle state. */
 	for(i = 0; i < 32; i++)
-		BB_PUT_BIT(tregs, 1);
+		BB_PUT_BIT(hp, tregs, 1);
 
 	/* Give it write sequence. */
-	BB_PUT_BIT(tregs, 0);
-	BB_PUT_BIT(tregs, 1);
-	BB_PUT_BIT(tregs, 0);
-	BB_PUT_BIT(tregs, 1);
+	BB_PUT_BIT(hp, tregs, 0);
+	BB_PUT_BIT(hp, tregs, 1);
+	BB_PUT_BIT(hp, tregs, 0);
+	BB_PUT_BIT(hp, tregs, 1);
 
 	/* Give it the PHY address. */
 	tmp = (hp->paddr & 0xff);
 	for(i = 4; i >= 0; i--)
-		BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
 
 	/* Tell it what register we will be writing. */
 	tmp = (reg & 0xff);
 	for(i = 4; i >= 0; i--)
-		BB_PUT_BIT(tregs, ((tmp >> i) & 1));
+		BB_PUT_BIT(hp, tregs, ((tmp >> i) & 1));
 
 	/* Tell it to become ready for the bits. */
-	BB_PUT_BIT(tregs, 1);
-	BB_PUT_BIT(tregs, 0);
+	BB_PUT_BIT(hp, tregs, 1);
+	BB_PUT_BIT(hp, tregs, 0);
 
 	for(i = 15; i >= 0; i--)
-		BB_PUT_BIT(tregs, ((value >> i) & 1));
+		BB_PUT_BIT(hp, tregs, ((value >> i) & 1));
 
 	/* Close down the MIF BitBang outputs. */
-	tregs->bb_oenab = 0;
+	hme_write32(hp, &tregs->bb_oenab, 0);
 }
 
 #define TCVR_READ_TRIES   16
@@ -205,14 +217,15 @@
 		return happy_meal_bb_read(hp, tregs, reg);
 	}
 
-	tregs->frame = (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18));
-	while(!(tregs->frame & 0x10000) && --tries)
+	hme_write32(hp, &tregs->frame,
+		    (FRAME_READ | (hp->paddr << 23) | ((reg & 0xff) << 18)));
+	while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)
 		udelay(20);
 	if(!tries) {
 		printk("happy meal: Aieee, transceiver MIF read bolixed\n");
 		return TCVR_FAILURE;
 	}
-	retval = tregs->frame & 0xffff;
+	retval = hme_read32(hp, &tregs->frame) & 0xffff;
 	ASD(("value=%04x\n", retval));
 	return retval;
 }
@@ -232,9 +245,10 @@
 		return happy_meal_bb_write(hp, tregs, reg, value);
 
 	/* Would you like fries with that? */
-	tregs->frame = (FRAME_WRITE | (hp->paddr << 23) |
-			((reg & 0xff) << 18) | (value & 0xffff));
-	while(!(tregs->frame & 0x10000) && --tries)
+	hme_write32(hp, &tregs->frame,
+		    (FRAME_WRITE | (hp->paddr << 23) |
+		     ((reg & 0xff) << 18) | (value & 0xffff)));
+	while(!(hme_read32(hp, &tregs->frame) & 0x10000) && --tries)
 		udelay(20);
 
 	/* Anything else? */
@@ -364,9 +378,13 @@
 	 * XXX Happy Meal front end for this to work every time.
 	 */
 	if(full)
-		hp->bigmacregs->tx_cfg |= BIGMAC_TXCFG_FULLDPLX;
+		hme_write32(hp, &hp->bigmacregs->tx_cfg,
+			    hme_read32(hp, &hp->bigmacregs->tx_cfg) |
+			    BIGMAC_TXCFG_FULLDPLX);
 	else
-		hp->bigmacregs->tx_cfg &= ~(BIGMAC_TXCFG_FULLDPLX);
+		hme_write32(hp, &hp->bigmacregs->tx_cfg,
+			    hme_read32(hp, &hp->bigmacregs->tx_cfg) &
+			    ~(BIGMAC_TXCFG_FULLDPLX));
 
 	return 0;
 no_response:
@@ -541,15 +559,16 @@
 #define TX_RESET_TRIES     32
 #define RX_RESET_TRIES     32
 
-static inline void happy_meal_tx_reset(struct hmeal_bigmacregs *bregs)
+static inline void happy_meal_tx_reset(struct happy_meal *hp,
+				       struct hmeal_bigmacregs *bregs)
 {
 	int tries = TX_RESET_TRIES;
 
 	HMD(("happy_meal_tx_reset: reset, "));
 
 	/* Would you like to try our SMCC Delux? */
-	bregs->tx_swreset = 0;
-	while((bregs->tx_swreset & 1) && --tries)
+	hme_write32(hp, &bregs->tx_swreset, 0);
+	while((hme_read32(hp, &bregs->tx_swreset) & 1) && --tries)
 		udelay(20);
 
 	/* Lettuce, tomato, buggy hardware (no extra charge)? */
@@ -560,15 +579,16 @@
 	HMD(("done\n"));
 }
 
-static inline void happy_meal_rx_reset(struct hmeal_bigmacregs *bregs)
+static inline void happy_meal_rx_reset(struct happy_meal *hp,
+				       struct hmeal_bigmacregs *bregs)
 {
 	int tries = RX_RESET_TRIES;
 
 	HMD(("happy_meal_rx_reset: reset, "));
 
 	/* We have a special on GNU/Viking hardware bugs today. */
-	bregs->rx_swreset = 0;
-	while((bregs->rx_swreset & 1) && --tries)
+	hme_write32(hp, &bregs->rx_swreset, 0);
+	while((hme_read32(hp, &bregs->rx_swreset) & 1) && --tries)
 		udelay(20);
 
 	/* Will that be all? */
@@ -581,15 +601,16 @@
 
 #define STOP_TRIES         16
 
-static inline void happy_meal_stop(struct hmeal_gregs *gregs)
+static inline void happy_meal_stop(struct happy_meal *hp,
+				   struct hmeal_gregs *gregs)
 {
 	int tries = STOP_TRIES;
 
 	HMD(("happy_meal_stop: reset, "));
 
 	/* We're consolidating our STB products, it's your lucky day. */
-	gregs->sw_reset = GREG_RESET_ALL;
-	while(gregs->sw_reset && --tries)
+	hme_write32(hp, &gregs->sw_reset, GREG_RESET_ALL);
+	while(hme_read32(hp, &gregs->sw_reset) && --tries)
 		udelay(20);
 
 	/* Come back next week when we are "Sun Microelectronics". */
@@ -605,19 +626,22 @@
 {
 	struct net_device_stats *stats = &hp->net_stats;
 
-	stats->rx_crc_errors += bregs->rcrce_ctr;
-	bregs->rcrce_ctr = 0;
+	stats->rx_crc_errors += hme_read32(hp, &bregs->rcrce_ctr);
+	hme_write32(hp, &bregs->rcrce_ctr, 0);
 
-	stats->rx_frame_errors += bregs->unale_ctr;
-	bregs->unale_ctr = 0;
+	stats->rx_frame_errors += hme_read32(hp, &bregs->unale_ctr);
+	hme_write32(hp, &bregs->unale_ctr, 0);
 
-	stats->rx_length_errors += bregs->gle_ctr;
-	bregs->gle_ctr = 0;
+	stats->rx_length_errors += hme_read32(hp, &bregs->gle_ctr);
+	hme_write32(hp, &bregs->gle_ctr, 0);
 
-	stats->tx_aborted_errors += bregs->ex_ctr;
-
-	stats->collisions += (bregs->ex_ctr + bregs->lt_ctr);
-	bregs->ex_ctr = bregs->lt_ctr = 0;
+	stats->tx_aborted_errors += hme_read32(hp, &bregs->ex_ctr);
+
+	stats->collisions +=
+		(hme_read32(hp, &bregs->ex_ctr) +
+		 hme_read32(hp, &bregs->lt_ctr));
+	hme_write32(hp, &bregs->ex_ctr, 0);
+	hme_write32(hp, &bregs->lt_ctr, 0);
 }
 
 static inline void happy_meal_poll_start(struct happy_meal *hp,
@@ -634,12 +658,12 @@
 
 	/* Start the MIF polling on the external transceiver. */
 	ASD(("polling on, "));
-	tmp = tregs->cfg;
+	tmp = hme_read32(hp, &tregs->cfg);
 	tmp &= ~(TCV_CFG_PDADDR | TCV_CFG_PREGADDR);
 	tmp |= ((hp->paddr & 0x1f) << 10);
 	tmp |= (TCV_PADDR_ETX << 3);
 	tmp |= TCV_CFG_PENABLE;
-	tregs->cfg = tmp;
+	hme_write32(hp, &tregs->cfg, tmp);
 
 	/* Let the bits set. */
 	udelay(200);
@@ -660,9 +684,9 @@
 	/* Listen only for the MIF interrupts we want to hear. */
 	ASD(("mif ints on, "));
 	if(speed == 100)
-		tregs->int_mask = 0xfffb;
+		hme_write32(hp, &tregs->int_mask, 0xfffb);
 	else
-		tregs->int_mask = 0xfff9;
+		hme_write32(hp, &tregs->int_mask, 0xfff9);
 	ASD(("done\n"));
 }
 
@@ -680,11 +704,12 @@
 
 	/* Shut up the MIF. */
 	ASD(("were polling, mif ints off, "));
-	tregs->int_mask = 0xffff;
+	hme_write32(hp, &tregs->int_mask, 0xffff);
 
 	/* Turn off polling. */
 	ASD(("polling off, "));
-	tregs->cfg &= ~(TCV_CFG_PENABLE);
+	hme_write32(hp, &tregs->cfg,
+		    hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_PENABLE));
 
 	/* We are no longer polling. */
 	hp->happy_flags &= ~(HFLAG_POLL);
@@ -706,11 +731,11 @@
 	unsigned long tconfig;
 	int result, tries = TCVR_RESET_TRIES;
 
-	tconfig = tregs->cfg;
+	tconfig = hme_read32(hp, &tregs->cfg);
 	ASD(("happy_meal_tcvr_reset: tcfg<%08lx> ", tconfig));
 	if(hp->tcvr_type == external) {
 		ASD(("external<"));
-		tregs->cfg = tconfig & ~(TCV_CFG_PSELECT);
+		hme_write32(hp, &tregs->cfg, tconfig & ~(TCV_CFG_PSELECT));
 		hp->tcvr_type = internal;
 		hp->paddr = TCV_PADDR_ITX;
 		ASD(("ISOLATE,"));
@@ -722,13 +747,13 @@
 			return -1;
 		}
 		ASD(("phyread_ok,PSELECT>"));
-		tregs->cfg = tconfig | TCV_CFG_PSELECT;
+		hme_write32(hp, &tregs->cfg, tconfig | TCV_CFG_PSELECT);
 		hp->tcvr_type = external;
 		hp->paddr = TCV_PADDR_ETX;
 	} else {
 		if(tconfig & TCV_CFG_MDIO1) {
 			ASD(("internal<PSELECT,"));
-			tregs->cfg = (tconfig | TCV_CFG_PSELECT);
+			hme_write32(hp, &tregs->cfg, (tconfig | TCV_CFG_PSELECT));
 			ASD(("ISOLATE,"));
 			happy_meal_tcvr_write(hp, tregs, DP83840_BMCR,
 					      (BMCR_LOOPBACK|BMCR_PDOWN|BMCR_ISOLATE));
@@ -738,7 +763,7 @@
 				return -1;
 			}
 			ASD(("phyread_ok,~PSELECT>"));
-			tregs->cfg = (tconfig & ~(TCV_CFG_PSELECT));
+			hme_write32(hp, &tregs->cfg, (tconfig & ~(TCV_CFG_PSELECT)));
 			hp->tcvr_type = internal;
 			hp->paddr = TCV_PADDR_ITX;
 		}
@@ -795,7 +820,7 @@
 static void happy_meal_transceiver_check(struct happy_meal *hp,
 					 struct hmeal_tcvregs *tregs)
 {
-	unsigned long tconfig = tregs->cfg;
+	unsigned long tconfig = hme_read32(hp, &tregs->cfg);
 
 	ASD(("happy_meal_transceiver_check: tcfg=%08lx ", tconfig));
 	if(hp->happy_flags & HFLAG_POLL) {
@@ -810,18 +835,20 @@
 				ASD(("<external>\n"));
 				tconfig &= ~(TCV_CFG_PENABLE);
 				tconfig |= TCV_CFG_PSELECT;
-				tregs->cfg = tconfig;
+				hme_write32(hp, &tregs->cfg, tconfig);
 			}
 		} else {
 			if(hp->tcvr_type == external) {
 				ASD(("<external> "));
-				if(!(tregs->status >> 16)) {
+				if(!(hme_read32(hp, &tregs->status) >> 16)) {
 					ASD(("<poll stop> "));
 					happy_meal_poll_stop(hp, tregs);
 					hp->paddr = TCV_PADDR_ITX;
 					hp->tcvr_type = internal;
 					ASD(("<internal>\n"));
-					tregs->cfg &= ~(TCV_CFG_PSELECT);
+					hme_write32(hp, &tregs->cfg,
+						    hme_read32(hp, &tregs->cfg) &
+						    ~(TCV_CFG_PSELECT));
 				}
 				ASD(("\n"));
 			} else {
@@ -829,18 +856,19 @@
 			}
 		}
 	} else {
-		unsigned long reread = tregs->cfg;
+		unsigned long reread = hme_read32(hp, &tregs->cfg);
 
 		/* Else we can just work off of the MDIO bits. */
 		ASD(("<not polling> "));
 		if(reread & TCV_CFG_MDIO1) {
-			tregs->cfg = tconfig | TCV_CFG_PSELECT;
+			hme_write32(hp, &tregs->cfg, tconfig | TCV_CFG_PSELECT);
 			hp->paddr = TCV_PADDR_ETX;
 			hp->tcvr_type = external;
 			ASD(("<external>\n"));
 		} else {
 			if(reread & TCV_CFG_MDIO0) {
-				tregs->cfg = tconfig & ~(TCV_CFG_PSELECT);
+				hme_write32(hp, &tregs->cfg,
+					    tconfig & ~(TCV_CFG_PSELECT));
 				hp->paddr = TCV_PADDR_ITX;
 				hp->tcvr_type = internal;
 				ASD(("<internal>\n"));
@@ -946,11 +974,17 @@
 		/* Because we reserve afterwards. */
 		skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
 
-		hb->happy_meal_rxd[i].rx_addr =
-			(u32) ((unsigned long)skb->data);
+		if(hp->happy_flags & HFLAG_PCI) {
+			pcihme_write_rxd(&hb->happy_meal_rxd[i],
+					 (RXFLAG_OWN |
+					  ((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+					 (u32)virt_to_bus((volatile void *)skb->data));
+		} else {
+			hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data);
+			hb->happy_meal_rxd[i].rx_flags =
+				(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
+		}
 		skb_reserve(skb, RX_OFFSET);
-		hb->happy_meal_rxd[i].rx_flags =
-			(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
 	}
 
 	HMD(("init txring, "));
@@ -1097,7 +1131,8 @@
 	unsigned long regtmp;
 	unsigned char *e = &hp->dev->dev_addr[0];
 
-	HMD(("happy_meal_init: "));
+	HMD(("happy_meal_init: happy_flags[%08x] ",
+	     hp->happy_flags));
 	if(!(hp->happy_flags & HFLAG_INIT)) {
 		HMD(("set HFLAG_INIT, "));
 		hp->happy_flags |= HFLAG_INIT;
@@ -1110,7 +1145,7 @@
 
 	/* Stop transmitter and receiver. */
 	HMD(("happy_meal_init: to happy_meal_stop\n"));
-	happy_meal_stop(gregs);
+	happy_meal_stop(hp, gregs);
 
 	/* Alloc and reset the tx/rx descriptor chains. */
 	HMD(("happy_meal_init: to happy_meal_init_rings\n"));
@@ -1120,16 +1155,21 @@
 		happy_meal_init_rings(hp, from_irq);
 
 	/* Shut up the MIF. */
-	HMD(("happy_meal_init: Disable all MIF irqs, "));
-	tregs->int_mask = 0xffff;
+	HMD(("happy_meal_init: Disable all MIF irqs (old[%08x]), ",
+	     hme_read32(hp, &tregs->int_mask)));
+	hme_write32(hp, &tregs->int_mask, 0xffff);
 
 	/* See if we can enable the MIF frame on this card to speak to the DP83840. */
 	if(hp->happy_flags & HFLAG_FENABLE) {
-		HMD(("use frame, "));
-		tregs->cfg &= ~(TCV_CFG_BENABLE);
+		HMD(("use frame old[%08x], ",
+		     hme_read32(hp, &tregs->cfg)));
+		hme_write32(hp, &tregs->cfg,
+			    hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_BENABLE));
 	} else {
-		HMD(("use bitbang, "));
-		tregs->cfg |= TCV_CFG_BENABLE;
+		HMD(("use bitbang old[%08x], ",
+		     hme_read32(hp, &tregs->cfg)));
+		hme_write32(hp, &tregs->cfg,
+			    hme_read32(hp, &tregs->cfg) | TCV_CFG_BENABLE);
 	}
 
 	/* Check the state of the transceiver. */
@@ -1147,13 +1187,13 @@
 	case internal:
 		/* Using the MII buffers. */
 		HMD(("internal, using MII, "));
-		bregs->xif_cfg = 0;
+		hme_write32(hp, &bregs->xif_cfg, 0);
 		break;
 
 	case external:
 		/* Not using the MII, disable it. */
 		HMD(("external, disable MII, "));
-		bregs->xif_cfg = BIGMAC_XCFG_MIIDISAB;
+		hme_write32(hp, &bregs->xif_cfg, BIGMAC_XCFG_MIIDISAB);
 		break;
 	};
 
@@ -1162,81 +1202,90 @@
 
 	/* Reset the Happy Meal Big Mac transceiver and the receiver. */
 	HMD(("tx/rx reset, "));
-	happy_meal_tx_reset(bregs);
-	happy_meal_rx_reset(bregs);
+	happy_meal_tx_reset(hp, bregs);
+	happy_meal_rx_reset(hp, bregs);
 
 	/* Set jam size and inter-packet gaps to reasonable defaults. */
 	HMD(("jsize/ipg1/ipg2, "));
-	bregs->jsize     = DEFAULT_JAMSIZE;
-	bregs->ipkt_gap1 = DEFAULT_IPG1;
-	bregs->ipkt_gap2 = DEFAULT_IPG2;
+	hme_write32(hp, &bregs->jsize, DEFAULT_JAMSIZE);
+	hme_write32(hp, &bregs->ipkt_gap1, DEFAULT_IPG1);
+	hme_write32(hp, &bregs->ipkt_gap2, DEFAULT_IPG2);
 
 	/* Load up the MAC address and random seed. */
 	HMD(("rseed/macaddr, "));
 
 	/* XXX use something less deterministic... */
-	bregs->rand_seed = 0xbd;
+	hme_write32(hp, &bregs->rand_seed, 0xbd);
 
-	bregs->mac_addr2 = ((e[4] << 8) | e[5]);
-	bregs->mac_addr1 = ((e[2] << 8) | e[3]);
-	bregs->mac_addr0 = ((e[0] << 8) | e[1]);
+	hme_write32(hp, &bregs->mac_addr2, ((e[4] << 8) | e[5]));
+	hme_write32(hp, &bregs->mac_addr1, ((e[2] << 8) | e[3]));
+	hme_write32(hp, &bregs->mac_addr0, ((e[0] << 8) | e[1]));
 
 	/* Ick, figure out how to properly program the hash table later... */
 	HMD(("htable, "));
-	bregs->htable3 = 0;
-	bregs->htable2 = 0;
-	bregs->htable1 = 0;
-	bregs->htable0 = 0;
+	hme_write32(hp, &bregs->htable3, 0);
+	hme_write32(hp, &bregs->htable2, 0);
+	hme_write32(hp, &bregs->htable1, 0);
+	hme_write32(hp, &bregs->htable0, 0);
 
 	/* Set the RX and TX ring ptrs. */
-	HMD(("ring ptrs\n"));
-	erxregs->rx_ring = hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0);
-	etxregs->tx_ring = hp->hblock_dvma + hblock_offset(happy_meal_txd, 0);
-
-	/* Set the supported SBUS burst sizes. */
-	HMD(("happy_meal_init: bursts<"));
+	HMD(("ring ptrs rxr[%08x] txr[%08x]\n",
+	     (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)),
+	     (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0))));
+	hme_write32(hp, &erxregs->rx_ring,
+		    (hp->hblock_dvma + hblock_offset(happy_meal_rxd, 0)));
+	hme_write32(hp, &etxregs->tx_ring,
+		    (hp->hblock_dvma + hblock_offset(happy_meal_txd, 0)));
+
+	/* Set the supported burst sizes. */
+	HMD(("happy_meal_init: old[%08x] bursts<",
+	     hme_read32(hp, &gregs->cfg)));
 
-#if 0 /* XXX take down huahaga and debug this... */
-	/* Grrr... */
+#ifdef __sparc_v9__
 	if(hp->happy_bursts & DMA_BURST64) {
 		HMD(("64>"));
-		gregs->cfg = GREG_CFG_BURST64;
+		hme_write32(hp, &gregs->cfg, GREG_CFG_BURST64);
 	} else
 #endif
 	if(hp->happy_bursts & DMA_BURST32) {
 		HMD(("32>"));
-		gregs->cfg = GREG_CFG_BURST32;
+		hme_write32(hp, &gregs->cfg, GREG_CFG_BURST32);
 	} else if(hp->happy_bursts & DMA_BURST16) {
 		HMD(("16>"));
-		gregs->cfg = GREG_CFG_BURST16;
+		hme_write32(hp, &gregs->cfg, GREG_CFG_BURST16);
 	} else {
 		HMD(("XXX>"));
-		gregs->cfg = 0;
+		hme_write32(hp, &gregs->cfg, 0);
 	}
 
 	/* Turn off interrupts we do not want to hear. */
 	HMD((", enable global interrupts, "));
-	gregs->imask = (GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP |
-			GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR);
+	hme_write32(hp, &gregs->imask,
+		    (GREG_IMASK_GOTFRAME | GREG_IMASK_RCNTEXP |
+		     GREG_IMASK_SENTFRAME | GREG_IMASK_TXPERR));
 
 	/* Set the transmit ring buffer size. */
-	HMD(("tx rsize=%d, ", (int)TX_RING_SIZE));
-	etxregs->tx_rsize = (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1;
+	HMD(("tx rsize=%d oreg[%08x], ", (int)TX_RING_SIZE,
+	     hme_read32(hp, &etxregs->tx_rsize)));
+	hme_write32(hp, &etxregs->tx_rsize, (TX_RING_SIZE >> ETX_RSIZE_SHIFT) - 1);
 
 	/* Enable transmitter DVMA. */
-	HMD(("tx dma enable, "));
-	etxregs->cfg |= ETX_CFG_DMAENABLE;
+	HMD(("tx dma enable old[%08x], ",
+	     hme_read32(hp, &etxregs->cfg)));
+	hme_write32(hp, &etxregs->cfg,
+		    hme_read32(hp, &etxregs->cfg) | ETX_CFG_DMAENABLE);
 
 	/* This chip really rots, for the receiver sometimes when you
 	 * write to it's control registers not all the bits get there
 	 * properly.  I cannot think of a sane way to provide complete
 	 * coverage for this hardware bug yet.
 	 */
-	HMD(("erx regs bug\n"));
-	erxregs->cfg = ERX_CFG_DEFAULT(RX_OFFSET);
-	regtmp = erxregs->cfg;
-	erxregs->cfg = ERX_CFG_DEFAULT(RX_OFFSET);
-	if(erxregs->cfg != ERX_CFG_DEFAULT(RX_OFFSET)) {
+	HMD(("erx regs bug old[%08x]\n",
+	     hme_read32(hp, &erxregs->cfg)));
+	hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET));
+	regtmp = hme_read32(hp, &erxregs->cfg);
+	hme_write32(hp, &erxregs->cfg, ERX_CFG_DEFAULT(RX_OFFSET));
+	if(hme_read32(hp, &erxregs->cfg) != ERX_CFG_DEFAULT(RX_OFFSET)) {
 		printk("happy meal: Eieee, rx config register gets greasy fries.\n");
 		printk("happy meal: Trying to set %08x, reread gives %08lx\n",
 		       ERX_CFG_DEFAULT(RX_OFFSET), regtmp);
@@ -1244,8 +1293,9 @@
 	}
 
 	/* Enable Big Mac hash table filter. */
-	HMD(("happy_meal_init: enable hash, "));
-	bregs->rx_cfg = BIGMAC_RXCFG_HENABLE;
+	HMD(("happy_meal_init: enable hash rx_cfg_old[%08x], ",
+	     hme_read32(hp, &bregs->rx_cfg)));
+	hme_write32(hp, &bregs->rx_cfg, BIGMAC_RXCFG_HENABLE);
 
 	/* Let the bits settle in the chip. */
 	udelay(10);
@@ -1255,7 +1305,7 @@
 	regtmp = 0;
 	if(hp->happy_flags & HFLAG_FULL)
 		regtmp |= BIGMAC_TXCFG_FULLDPLX;
-	bregs->tx_cfg = regtmp | BIGMAC_TXCFG_DGIVEUP;
+	hme_write32(hp, &bregs->tx_cfg, regtmp | BIGMAC_TXCFG_DGIVEUP);
 
 	/* Enable the output drivers no matter what. */
 	regtmp = BIGMAC_XCFG_ODENABLE;
@@ -1268,13 +1318,18 @@
 	if(hp->tcvr_type == external)
 		regtmp |= BIGMAC_XCFG_MIIDISAB;
 
-	HMD(("XIF config, "));
-	bregs->xif_cfg = regtmp;
+	HMD(("XIF config old[%08x], ",
+	     hme_read32(hp, &bregs->xif_cfg)));
+	hme_write32(hp, &bregs->xif_cfg, regtmp);
 
 	/* Start things up. */
-	HMD(("tx and rx ON!\n"));
-	bregs->tx_cfg |= BIGMAC_TXCFG_ENABLE;
-	bregs->rx_cfg |= BIGMAC_RXCFG_ENABLE;
+	HMD(("tx old[%08x] and rx [%08x] ON!\n",
+	     hme_read32(hp, &bregs->tx_cfg),
+	     hme_read32(hp, &bregs->rx_cfg)));
+	hme_write32(hp, &bregs->tx_cfg,
+		    hme_read32(hp, &bregs->tx_cfg) | BIGMAC_TXCFG_ENABLE);
+	hme_write32(hp, &bregs->rx_cfg,
+		    hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_ENABLE);
 
 	/* Get the autonegotiation started, and the watch timer ticking. */
 	happy_meal_begin_auto_negotiation(hp, tregs);
@@ -1289,21 +1344,23 @@
 	struct hmeal_bigmacregs *bregs	= hp->bigmacregs;
 	struct hmeal_gregs *gregs	= hp->gregs;
 
-	happy_meal_stop(gregs);
-	tregs->int_mask = 0xffff;
+	happy_meal_stop(hp, gregs);
+	hme_write32(hp, &tregs->int_mask, 0xffff);
 	if(hp->happy_flags & HFLAG_FENABLE)
-		tregs->cfg &= ~(TCV_CFG_BENABLE);
+		hme_write32(hp, &tregs->cfg,
+			    hme_read32(hp, &tregs->cfg) & ~(TCV_CFG_BENABLE));
 	else
-		tregs->cfg |= TCV_CFG_BENABLE;
+		hme_write32(hp, &tregs->cfg,
+			    hme_read32(hp, &tregs->cfg) | TCV_CFG_BENABLE);
 	happy_meal_transceiver_check(hp, tregs);
 	switch(hp->tcvr_type) {
 	case none:
 		return;
 	case internal:
-		bregs->xif_cfg = 0;
+		hme_write32(hp, &bregs->xif_cfg, 0);
 		break;
 	case external:
-		bregs->xif_cfg = BIGMAC_XCFG_MIIDISAB;
+		hme_write32(hp, &bregs->xif_cfg, BIGMAC_XCFG_MIIDISAB);
 		break;
 	};
 	if(happy_meal_tcvr_reset(hp, tregs))
@@ -1544,6 +1601,13 @@
 		hp->tx_skbs[elem] = NULL;
 		hp->net_stats.tx_bytes+=skb->len;
 		
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef CONFIG_PCI
+		if(!(hp->happy_flags & HFLAG_PCI))
+#endif
+			mmu_sync_dma(kva_to_hva(hp, skb->data),
+				     skb->len, hp->happy_sbus_dev->my_bus);
+#endif
 		dev_kfree_skb(skb, FREE_WRITE);
 
 		hp->net_stats.tx_packets++;
@@ -1553,6 +1617,39 @@
 	TXD((">"));
 }
 
+#ifdef CONFIG_PCI
+static inline void pci_happy_meal_tx(struct happy_meal *hp)
+{
+	struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
+	struct happy_meal_txd *this;
+	int elem = hp->tx_old;
+
+	TXD(("TX<"));
+	while(elem != hp->tx_new) {
+		struct sk_buff *skb;
+		unsigned int flags;
+
+		TXD(("[%d]", elem));
+		this = &txbase[elem];
+		__asm__ __volatile__("lduwa [%1] %2, %0"
+				     : "=r" (flags)
+				     : "r" (&this->tx_flags), "i" (ASI_PL));
+		if(flags & TXFLAG_OWN)
+			break;
+		skb = hp->tx_skbs[elem];
+		hp->tx_skbs[elem] = NULL;
+		hp->net_stats.tx_bytes+=skb->len;
+		
+		dev_kfree_skb(skb, FREE_WRITE);
+
+		hp->net_stats.tx_packets++;
+		elem = NEXT_TX(elem);
+	}
+	hp->tx_old = elem;
+	TXD((">"));
+}
+#endif
+
 static inline void sun4c_happy_meal_tx(struct happy_meal *hp)
 {
 	struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
@@ -1616,13 +1713,19 @@
 			/* Return it to the Happy meal. */
 	drop_it:
 			hp->net_stats.rx_dropped++;
-			this->rx_addr =
-				(u32) ((unsigned long)hp->rx_skbs[elem]->data);
+			this->rx_addr = kva_to_hva(hp, hp->rx_skbs[elem]->data);
 			this->rx_flags =
 				(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
 			goto next;
 		}
 		skb = hp->rx_skbs[elem];
+#ifdef NEED_DMA_SYNCHRONIZATION
+#ifdef CONFIG_PCI
+		if(!(hp->happy_flags & HFLAG_PCI))
+#endif
+			mmu_sync_dma(kva_to_hva(hp, skb->data),
+				     skb->len, hp->happy_sbus_dev->my_bus);
+#endif
 		if(len > RX_COPY_THRESHOLD) {
 			struct sk_buff *new_skb;
 
@@ -1636,8 +1739,7 @@
 			hp->rx_skbs[elem] = new_skb;
 			new_skb->dev = dev;
 			skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
-			rxbase[elem].rx_addr =
-				(u32) ((unsigned long)new_skb->data);
+			rxbase[elem].rx_addr = kva_to_hva(hp, new_skb->data);
 			skb_reserve(new_skb, RX_OFFSET);
 			rxbase[elem].rx_flags =
 				(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
@@ -1658,8 +1760,7 @@
 			memcpy(copy_skb->data, skb->data, len);
 
 			/* Reuse original ring buffer. */
-			rxbase[elem].rx_addr =
-				(u32) ((unsigned long)skb->data);
+			rxbase[elem].rx_addr = kva_to_hva(hp, skb->data);
 			rxbase[elem].rx_flags =
 				(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
 
@@ -1688,6 +1789,117 @@
 	RXD((">"));
 }
 
+#ifdef CONFIG_PCI
+static inline void pci_happy_meal_rx(struct happy_meal *hp, struct device *dev,
+				     struct hmeal_gregs *gregs)
+{
+	struct happy_meal_rxd *rxbase = &hp->happy_block->happy_meal_rxd[0];
+	struct happy_meal_rxd *this;
+	unsigned int flags;
+	int elem = hp->rx_new, drops = 0;
+
+	RXD(("RX<"));
+	this = &rxbase[elem];
+	__asm__ __volatile__("lduwa [%1] %2, %0"
+			     : "=r" (flags)
+			     : "r" (&this->rx_flags), "i" (ASI_PL));
+	while(!(flags & RXFLAG_OWN)) {
+		struct sk_buff *skb;
+		int len;
+		u16 csum;
+
+		RXD(("[%d ", elem));
+
+		len = flags >> 16;
+		csum = flags & RXFLAG_CSUM;
+
+		/* Check for errors. */
+		if((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) {
+			RXD(("ERR(%08lx)]", flags));
+			hp->net_stats.rx_errors++;
+			if(len < ETH_ZLEN)
+				hp->net_stats.rx_length_errors++;
+			if(len & (RXFLAG_OVERFLOW >> 16)) {
+				hp->net_stats.rx_over_errors++;
+				hp->net_stats.rx_fifo_errors++;
+			}
+
+			/* Return it to the Happy meal. */
+	drop_it:
+			hp->net_stats.rx_dropped++;
+			pcihme_write_rxd(this,
+				 (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+				 (u32) virt_to_bus((volatile void *)hp->rx_skbs[elem]->data));
+			goto next;
+		}
+		skb = hp->rx_skbs[elem];
+		if(len > RX_COPY_THRESHOLD) {
+			struct sk_buff *new_skb;
+
+			/* Now refill the entry, if we can. */
+			new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
+			if(!new_skb) {
+				drops++;
+				goto drop_it;
+			}
+
+			hp->rx_skbs[elem] = new_skb;
+			new_skb->dev = dev;
+			skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET));
+			pcihme_write_rxd(&rxbase[elem],
+				 (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+				  (u32)virt_to_bus((volatile void *)new_skb->data));
+			skb_reserve(new_skb, RX_OFFSET);
+
+			/* Trim the original skb for the netif. */
+			skb_trim(skb, len);
+		} else {
+			struct sk_buff *copy_skb = dev_alloc_skb(len+2);
+
+			if(!copy_skb) {
+				drops++;
+				goto drop_it;
+			}
+
+			copy_skb->dev = dev;
+			skb_reserve(copy_skb, 2);
+			skb_put(copy_skb, len);
+			memcpy(copy_skb->data, skb->data, len);
+
+			/* Reuse original ring buffer. */
+			pcihme_write_rxd(&rxbase[elem],
+				 (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
+				 (u32)virt_to_bus((volatile void *)skb->data));
+
+			skb = copy_skb;
+		}
+
+		/* This card is _fucking_ hot... */
+		if(!~(csum))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
+
+		RXD(("len=%d csum=%4x]", len, csum));
+		skb->protocol = eth_type_trans(skb, dev);
+		netif_rx(skb);
+
+		hp->net_stats.rx_packets++;
+		hp->net_stats.rx_bytes+=len;
+	next:
+		elem = NEXT_RX(elem);
+		this = &rxbase[elem];
+		__asm__ __volatile__("lduwa [%1] %2, %0"
+				     : "=r" (flags)
+				     : "r" (&this->rx_flags), "i" (ASI_PL));
+	}
+	hp->rx_new = elem;
+	if(drops)
+		printk("%s: Memory squeeze, deferring packet.\n", hp->dev->name);
+	RXD((">"));
+}
+#endif
+
 static inline void sun4c_happy_meal_rx(struct happy_meal *hp, struct device *dev,
 				       struct hmeal_gregs *gregs)
 {
@@ -1757,7 +1969,7 @@
 	struct happy_meal *hp         = (struct happy_meal *) dev->priv;
 	struct hmeal_gregs *gregs     = hp->gregs;
 	struct hmeal_tcvregs *tregs   = hp->tcvregs;
-	unsigned long happy_status    = gregs->stat;
+	unsigned int happy_status    = hme_read32(hp, &gregs->stat);
 
 	HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
 
@@ -1795,13 +2007,59 @@
 	HMD(("done\n"));
 }
 
+#ifdef CONFIG_PCI
+static void pci_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev            = (struct device *) dev_id;
+	struct happy_meal *hp         = (struct happy_meal *) dev->priv;
+	struct hmeal_gregs *gregs     = hp->gregs;
+	struct hmeal_tcvregs *tregs   = hp->tcvregs;
+	unsigned int happy_status     = readl((unsigned long)&gregs->stat);
+
+	HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
+
+	dev->interrupt = 1;
+
+	if(happy_status & GREG_STAT_ERRORS) {
+		HMD(("ERRORS "));
+		if(happy_meal_is_not_so_happy(hp, gregs, /* un- */ happy_status)) {
+			dev->interrupt = 0;
+			return;
+		}
+	}
+
+	if(happy_status & GREG_STAT_MIFIRQ) {
+		HMD(("MIFIRQ "));
+		happy_meal_mif_interrupt(hp, gregs, tregs);
+	}
+
+	if(happy_status & GREG_STAT_TXALL) {
+		HMD(("TXALL "));
+		pci_happy_meal_tx(hp);
+	}
+
+	if(happy_status & GREG_STAT_RXTOHOST) {
+		HMD(("RXTOHOST "));
+		pci_happy_meal_rx(hp, dev, gregs);
+	}
+
+	if(dev->tbusy && (TX_BUFFS_AVAIL(hp) >= 0)) {
+		hp->dev->tbusy = 0;
+		mark_bh(NET_BH);
+	}
+
+	dev->interrupt = 0;
+	HMD(("done\n"));
+}
+#endif
+
 static void sun4c_happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct device *dev            = (struct device *) dev_id;
 	struct happy_meal *hp         = (struct happy_meal *) dev->priv;
 	struct hmeal_gregs *gregs     = hp->gregs;
 	struct hmeal_tcvregs *tregs   = hp->tcvregs;
-	unsigned long happy_status    = gregs->stat;
+	unsigned int happy_status    = hme_read32(hp, &gregs->stat);
 
 	HMD(("happy_meal_interrupt: status=%08lx ", happy_status));
 
@@ -1852,7 +2110,41 @@
 			printk("happy meal: Can't order irq %d to go.\n", dev->irq);
 			return -EAGAIN;
 		}
-	} else {
+	}
+#ifdef __sparc_v9__
+	else if(sparc_cpu_model == sun4u) {
+		struct devid_cookie dcookie;
+
+#ifdef CONFIG_PCI
+		if(hp->happy_flags & HFLAG_PCI) {
+			if(request_irq(dev->irq, &pci_happy_meal_interrupt,
+				       SA_SHIRQ, "HAPPY MEAL (PCI)", dev)) {
+				HMD(("EAGAIN\n"));
+				printk("happy_meal(PCI: Can't order irq %d to go.\n",
+				       dev->irq);
+				return -EAGAIN;
+			}
+			goto v9_done;
+		}
+#endif
+		dcookie.real_dev_id = dev;
+		dcookie.imap = dcookie.iclr = 0;
+		dcookie.pil = -1;
+		dcookie.bus_cookie = hp->happy_sbus_dev->my_bus;
+		if(request_irq(dev->irq, &happy_meal_interrupt,
+			       (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+			       "HAPPY MEAL", &dcookie)) {
+			HMD(("EAGAIN\n"));
+			printk("happy_meal(SBUS): Can't order irq %d to go.\n",
+			       dev->irq);
+			return -EAGAIN;
+		}
+#ifdef CONFIG_PCI
+	v9_done:
+#endif
+	}
+#endif
+	else {
 		if(request_irq(dev->irq, &happy_meal_interrupt,
 			       SA_SHIRQ, "HAPPY MEAL", (void *) dev)) {
 			HMD(("EAGAIN\n"));
@@ -1874,8 +2166,12 @@
 {
 	struct happy_meal *hp = (struct happy_meal *) dev->priv;
 
-	happy_meal_stop(hp->gregs);
+	happy_meal_stop(hp, hp->gregs);
 	happy_meal_clean_rings(hp);
+
+	/* If auto-negotiation timer is running, kill it. */
+	del_timer(&hp->happy_timer);
+
 	free_irq(dev->irq, (void *)dev);
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -1917,15 +2213,14 @@
 
 	SXD(("SX<l[%d]e[%d]>", len, entry));
 	hp->tx_skbs[entry] = skb;
-	hp->happy_block->happy_meal_txd[entry].tx_addr =
-		(u32) ((unsigned long)skb->data);
+	hp->happy_block->happy_meal_txd[entry].tx_addr = kva_to_hva(hp, skb->data);
 	hp->happy_block->happy_meal_txd[entry].tx_flags =
 		(TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE));
 	hp->tx_new = NEXT_TX(entry);
 
 	/* Get it going. */
 	dev->trans_start = jiffies;
-	hp->etxregs->tx_pnding = ETX_TP_DMAWAKEUP;
+	hme_write32(hp, &hp->etxregs->tx_pnding, ETX_TP_DMAWAKEUP);
 
 	if(TX_BUFFS_AVAIL(hp))
 		dev->tbusy = 0;
@@ -1933,6 +2228,56 @@
 	return 0;
 }
 
+#ifdef CONFIG_PCI
+static int pci_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct happy_meal *hp = (struct happy_meal *) dev->priv;
+	int len, entry;
+
+	if(dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+	    
+		if (tickssofar < 40) {
+			return 1;
+		} else {
+			printk ("%s: transmit timed out, resetting\n", dev->name);
+			hp->net_stats.tx_errors++;
+			happy_meal_init(hp, 0);
+			dev->tbusy = 0;
+			dev->trans_start = jiffies;
+			return 0;
+		}
+	}
+
+	if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+		printk("happy meal: Transmitter access conflict.\n");
+		return 1;
+	}
+
+	if(!TX_BUFFS_AVAIL(hp))
+		return 1;
+
+	len = skb->len;
+	entry = hp->tx_new;
+
+	SXD(("SX<l[%d]e[%d]>", len, entry));
+	hp->tx_skbs[entry] = skb;
+	pcihme_write_txd(&hp->happy_block->happy_meal_txd[entry],
+			 (TXFLAG_OWN|TXFLAG_SOP|TXFLAG_EOP|(len & TXFLAG_SIZE)),
+			 (u32) virt_to_bus((volatile void *)skb->data));
+	hp->tx_new = NEXT_TX(entry);
+
+	/* Get it going. */
+	dev->trans_start = jiffies;
+	writel(ETX_TP_DMAWAKEUP, (unsigned long)&hp->etxregs->tx_pnding);
+
+	if(TX_BUFFS_AVAIL(hp))
+		dev->tbusy = 0;
+
+	return 0;
+}
+#endif
+
 static int sun4c_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct happy_meal *hp = (struct happy_meal *) dev->priv;
@@ -2023,12 +2368,13 @@
 	set_bit(0, (void *) &dev->tbusy);
 
 	if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
-		bregs->htable0 = 0xffff;
-		bregs->htable1 = 0xffff;
-		bregs->htable2 = 0xffff;
-		bregs->htable3 = 0xffff;
+		hme_write32(hp, &bregs->htable0, 0xffff);
+		hme_write32(hp, &bregs->htable1, 0xffff);
+		hme_write32(hp, &bregs->htable2, 0xffff);
+		hme_write32(hp, &bregs->htable3, 0xffff);
 	} else if(dev->flags & IFF_PROMISC) {
-		bregs->rx_cfg |= BIGMAC_RXCFG_PMISC;
+		hme_write32(hp, &bregs->rx_cfg,
+			    hme_read32(hp, &bregs->rx_cfg) | BIGMAC_RXCFG_PMISC);
 	} else {
 		u16 hash_table[4];
 
@@ -2056,19 +2402,20 @@
 			crc >>= 26;
 			hash_table[crc >> 4] |= 1 << (crc & 0xf);
 		}
-		bregs->htable0 = hash_table[0];
-		bregs->htable1 = hash_table[1];
-		bregs->htable2 = hash_table[2];
-		bregs->htable3 = hash_table[3];
+		hme_write32(hp, &bregs->htable0, hash_table[0]);
+		hme_write32(hp, &bregs->htable1, hash_table[1]);
+		hme_write32(hp, &bregs->htable2, hash_table[2]);
+		hme_write32(hp, &bregs->htable3, hash_table[3]);
 	}
 
 	/* Let us get going again. */
 	dev->tbusy = 0;
 }
 
+static unsigned hme_version_printed = 0;
+
 static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_device *sdev)
 {
-	static unsigned version_printed = 0;
 	struct happy_meal *hp;
 	int i;
 
@@ -2079,10 +2426,10 @@
 		if(dev->priv == NULL)
 			return -ENOMEM;
 	}
-	if(version_printed++ == 0)
+	if(hme_version_printed++ == 0)
 		printk(version);
 
-	printk("%s: HAPPY MEAL 10/100baseT Ethernet ", dev->name);
+	printk("%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ", dev->name);
 
 	dev->base_addr = (long) sdev;
 	for(i = 0; i < 6; i++)
@@ -2095,6 +2442,9 @@
 	memset(hp, 0, sizeof(*hp));
 
 	hp->happy_sbus_dev = sdev;
+#ifdef CONFIG_PCI
+	hp->happy_pci_dev = NULL;
+#endif
 
 	if(sdev->num_registers != 5) {
 		printk("happymeal: Device does not have 5 regs, it has %d.\n",
@@ -2211,6 +2561,124 @@
 	return 0;
 }
 
+#ifdef CONFIG_PCI
+__initfunc(int happy_meal_pci_init(struct device *dev, struct pci_dev *pdev))
+{
+	struct pcidev_cookie *pcp;
+	struct happy_meal *hp;
+	unsigned long hpreg_base;
+	unsigned short pci_command;
+	int i, node;
+
+	if(dev == NULL) {
+		dev = init_etherdev(0, sizeof(struct happy_meal));
+	} else {
+		dev->priv = kmalloc(sizeof(struct happy_meal), GFP_KERNEL);
+		if(dev->priv == NULL)
+			return -ENOMEM;
+	}
+	if(hme_version_printed++ == 0)
+		printk(version);
+
+	printk("%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ", dev->name);
+
+	dev->base_addr = (long) pdev;
+	for(i = 0; i < 6; i++)
+		printk("%2.2x%c",
+		       dev->dev_addr[i] = idprom->id_ethaddr[i],
+		       i == 5 ? ' ' : ':');
+
+	printk("\n");
+
+	hp = (struct happy_meal *)dev->priv;
+	memset(hp, 0, sizeof(*hp));
+
+	hp->happy_sbus_dev = NULL;
+	hp->happy_pci_dev = pdev;
+
+	hpreg_base = pdev->base_address[0];
+	if((hpreg_base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+		printk("happymeal(PCI): Cannot find proper PCI device base address.\n");
+		return ENODEV;
+	}
+	hpreg_base &= PCI_BASE_ADDRESS_MEM_MASK;
+
+	/* Now make sure pci_dev cookie is there. */
+	pcp = pdev->sysdata;
+	if(pcp == NULL || pcp->prom_node == -1) {
+		printk("happymeal(PCI): Some PCI device info missing\n");
+		return ENODEV;
+	}
+	node = pcp->prom_node;
+
+	/* Layout registers. */
+	hp->gregs      = (struct hmeal_gregs *)		(hpreg_base + 0x0000);
+	hp->etxregs    = (struct hmeal_etxregs *)	(hpreg_base + 0x2000);
+	hp->erxregs    = (struct hmeal_erxregs *)	(hpreg_base + 0x4000);
+	hp->bigmacregs = (struct hmeal_bigmacregs *)	(hpreg_base + 0x6000);
+	hp->tcvregs    = (struct hmeal_tcvregs *)	(hpreg_base + 0x7000);
+
+	hp->hm_revision = prom_getintdefault(node, "hm-rev", 0xff);
+	if(hp->hm_revision == 0xff)
+		hp->hm_revision = 0xa0;
+
+	/* Now enable the feature flags we can. */
+	if(hp->hm_revision == 0x20 || hp->hm_revision == 0x21)
+		hp->happy_flags = HFLAG_20_21;
+	else if(hp->hm_revision != 0xa0)
+		hp->happy_flags = HFLAG_NOT_A0;
+
+	/* And of course, indicate this is PCI. */
+	hp->happy_flags |= HFLAG_PCI;
+
+	/* Assume PCI happy meals can handle all burst sizes. */
+	hp->happy_bursts = DMA_BURSTBITS;
+
+	hp->happy_block = (struct hmeal_init_block *) get_free_page(GFP_DMA);
+	if(!hp->happy_block) {
+		printk("happymeal(PCI): Cannot get hme init block.\n");
+		return ENODEV;
+	}
+
+	hp->hblock_dvma = (u32) virt_to_bus(hp->happy_block);
+	hp->sun4c_buffers = 0;
+
+	hp->linkcheck = 0;
+	hp->timer_state = asleep;
+	hp->timer_ticks = 0;
+	happy_meal_set_initial_advertisement(hp);
+
+	hp->dev = dev;
+	dev->open = &happy_meal_open;
+	dev->stop = &happy_meal_close;
+	dev->hard_start_xmit = &pci_happy_meal_start_xmit;
+	dev->get_stats = &happy_meal_get_stats;
+	dev->set_multicast_list = &happy_meal_set_multicast;
+	dev->irq = pdev->irq;
+	dev->dma = 0;
+	ether_setup(dev);
+
+	/* If we don't do this, nothing works. */
+	pcibios_read_config_word(pdev->bus->number,
+				 pdev->devfn,
+				 PCI_COMMAND, &pci_command);
+	pci_command |= PCI_COMMAND_MASTER;
+	pcibios_write_config_word(pdev->bus->number,
+				  pdev->devfn,
+				  PCI_COMMAND, pci_command);
+
+#ifdef MODULE
+	/* We are home free at this point, link us in to the happy
+	 * module device list.
+	 */
+	dev->ifindex = dev_new_index();
+	hp->next_module = root_happy_dev;
+	root_happy_dev = hp;
+#endif
+	return 0;
+}
+#endif
+
 __initfunc(int happy_meal_probe(struct device *dev))
 {
 	struct linux_sbus *bus;
@@ -2224,7 +2692,8 @@
 
 	for_each_sbus(bus) {
 		for_each_sbusdev(sdev, bus) {
-			if(cards) dev = NULL;
+			if(cards)
+				dev = NULL;
 			if(!strcmp(sdev->prom_name, "SUNW,hme")) {
 				cards++;
 				if((v = happy_meal_ether_init(dev, sdev)))
@@ -2232,6 +2701,23 @@
 			}
 		}
 	}
+#ifdef CONFIG_PCI
+	if(pcibios_present()) {
+		struct pci_dev *pdev;
+
+		for(pdev = pci_devices; pdev; pdev = pdev->next) {
+			if(cards)
+				dev = NULL;
+			if((pdev->vendor == PCI_VENDOR_ID_SUN) &&
+			   (pdev->device == PCI_DEVICE_ID_SUN_HAPPYMEAL)) {
+				cards++;
+				if((v = happy_meal_pci_init(dev, pdev)))
+					return v;
+			}
+
+		}
+	}
+#endif
 	if(!cards)
 		return ENODEV;
 	return 0;
diff -u --recursive --new-file v2.1.52/linux/drivers/net/sunhme.h linux/drivers/net/sunhme.h
--- v2.1.52/linux/drivers/net/sunhme.h	Mon Apr 14 16:28:12 1997
+++ linux/drivers/net/sunhme.h	Thu Sep  4 13:25:28 1997
@@ -7,6 +7,8 @@
 #ifndef _SUNHME_H
 #define _SUNHME_H
 
+#include <linux/config.h>
+
 /* Happy Meal global registers. */
 struct hmeal_gregs {
 	volatile unsigned int sw_reset;      /* Software Reset  */
@@ -552,6 +554,9 @@
 
 	struct net_device_stats	  net_stats;      /* Statistical counters              */
 	struct linux_sbus_device *happy_sbus_dev; /* ;-)                               */
+#ifdef CONFIG_PCI
+	struct pci_dev		 *happy_pci_dev;
+#endif
 	struct device            *dev;            /* Backpointer                       */
 	struct happy_meal        *next_module;
 };
@@ -568,6 +573,7 @@
 #define HFLAG_RXCV                0x00000100      /* XXX RXCV ENABLE                   */
 #define HFLAG_INIT                0x00000200      /* Init called at least once         */
 #define HFLAG_LINKUP              0x00000400      /* 1 = Link is up                    */
+#define HFLAG_PCI                 0x00000800      /* PCI based Happy Meal              */
 
 #define HFLAG_20_21  (HFLAG_POLLENABLE | HFLAG_FENABLE)
 #define HFLAG_NOT_A0 (HFLAG_POLLENABLE | HFLAG_FENABLE | HFLAG_LANCE | HFLAG_RXCV)
@@ -588,5 +594,67 @@
 	}
 	return skb;
 }
+
+/* Register/DMA access stuff, used to cope with differences between
+ * PCI and SBUS happy meals.
+ */
+extern inline u32 kva_to_hva(struct happy_meal *hp, char *addr)
+{
+#ifdef CONFIG_PCI
+	if(hp->happy_flags & HFLAG_PCI)
+		return (u32) virt_to_bus((volatile void *)addr);
+	else
+#endif
+		return (u32) ((unsigned long)addr);
+}
+
+extern inline unsigned int hme_read32(struct happy_meal *hp,
+				      volatile unsigned int *reg)
+{
+#ifdef CONFIG_PCI
+	if(hp->happy_flags & HFLAG_PCI)
+		return readl((unsigned long)reg);
+	else
+#endif
+		return *reg;
+}
+
+extern inline void hme_write32(struct happy_meal *hp,
+			       volatile unsigned int *reg,
+			       unsigned int val)
+{
+#ifdef CONFIG_PCI
+	if(hp->happy_flags & HFLAG_PCI)
+		writel(val, (unsigned long)reg);
+	else
+#endif
+		*reg = val;
+}
+
+#ifdef CONFIG_PCI
+extern inline void pcihme_write_rxd(struct happy_meal_rxd *rp,
+				    unsigned int flags,
+				    unsigned int addr)
+{
+	__asm__ __volatile__("
+	stwa	%3, [%0] %2
+	stwa	%4, [%1] %2
+"	: /* no outputs */
+	: "r" (&rp->rx_addr), "r" (&rp->rx_flags),
+	  "i" (ASI_PL), "r" (addr), "r" (flags));
+}
+
+extern inline void pcihme_write_txd(struct happy_meal_txd *tp,
+				    unsigned int flags,
+				    unsigned int addr)
+{
+	__asm__ __volatile__("
+	stwa	%3, [%0] %2
+	stwa	%4, [%1] %2
+"	: /* no outputs */
+	: "r" (&tp->tx_addr), "r" (&tp->tx_flags),
+	  "i" (ASI_PL), "r" (addr), "r" (flags));
+}
+#endif
 
 #endif /* !(_SUNHME_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.52/linux/drivers/net/sunlance.c	Thu May 15 16:48:03 1997
+++ linux/drivers/net/sunlance.c	Thu Sep  4 13:25:28 1997
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.64 1997/05/14 20:46:40 davem Exp $
+/* $Id: sunlance.c,v 1.68 1997/08/15 06:44:36 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -75,11 +75,13 @@
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/pgtable.h>
 #include <linux/errno.h>
 #include <asm/byteorder.h>	/* Used by the checksum routines */
 
@@ -92,6 +94,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/auxio.h>		/* For tpe-link-test? setting */
+#include <asm/irq.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -241,6 +244,7 @@
 
 	struct device		 *dev;		  /* Backpointer	*/
 	struct lance_private	 *next_module;
+	struct linux_sbus        *sbus;
 };
 
 #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
@@ -661,6 +665,23 @@
 
 	last_dev = dev;
 
+#ifdef __sparc_v9__
+	if (sparc_cpu_model == sun4u) {
+		struct devid_cookie dcookie;
+
+		dcookie.real_dev_id = dev;
+		dcookie.imap = dcookie.iclr = 0;
+		dcookie.pil = -1;
+		dcookie.bus_cookie = lp->sbus;
+		if(request_irq(dev->irq, &lance_interrupt,
+			       (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+			       lancestr, &dcookie)) {
+			printk ("Lance: Can't get irq %d\n", dev->irq);
+			return -EAGAIN;
+		}
+
+	} else
+#endif
 	if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ,
 			 lancestr, (void *) dev)) {
 		printk ("Lance: Can't get irq %d\n", dev->irq);
@@ -978,6 +999,7 @@
 	/* Make certain the data structures used by the LANCE are aligned. */
 	dev->priv = (void *)(((unsigned long)dev->priv + 7) & ~7);
 	lp = (struct lance_private *) dev->priv;
+	lp->sbus = sdev->my_bus;
 	if (lebuffer){
 		prom_apply_sbus_ranges (lebuffer->my_bus,
 					&lebuffer->reg_addrs [0],
diff -u --recursive --new-file v2.1.52/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c
--- v2.1.52/linux/drivers/net/sunqe.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/sunqe.c	Thu Sep  4 13:25:28 1997
@@ -21,7 +21,9 @@
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
+#include <linux/delay.h>
 #include <linux/init.h>
+
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -36,6 +38,7 @@
 #include <asm/auxio.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
+#include <asm/irq.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -153,7 +156,7 @@
 		skb_reserve(skb, 34);
 
 		qb->qe_rxd[i].rx_addr =
-			(unsigned int) ((unsigned long)skb->data);
+			(u32) ((unsigned long)skb->data);
 		qb->qe_rxd[i].rx_flags =
 			(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 	}
@@ -444,6 +447,10 @@
 		skb = qep->tx_skbs[elem];
 		qep->tx_skbs[elem] = NULL;
 		qep->net_stats.tx_bytes+=skb->len;
+#ifdef NEED_DMA_SYNCHRONIZATION
+		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+			     skb->len, qep->qe_sbusdev->my_bus);
+#endif
 		dev_kfree_skb(skb, FREE_WRITE);
 
 		qep->net_stats.tx_packets++;
@@ -492,12 +499,16 @@
 			/* Return it to the QE. */
 			qep->net_stats.rx_dropped++;
 			this->rx_addr =
-				(unsigned int) ((unsigned long)qep->rx_skbs[elem]->data);
+				(u32) ((unsigned long)qep->rx_skbs[elem]->data);
 			this->rx_flags =
 				(RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH));
 			goto next;
 		}
 		skb = qep->rx_skbs[elem];
+#ifdef NEED_DMA_SYNCHRONIZATION
+		mmu_sync_dma(((u32)((unsigned long)skb->data)),
+			     skb->len, qep->qe_sbusdev->my_bus);
+#endif
 		if(len > RX_COPY_THRESHOLD) {
 			struct sk_buff *new_skb;
 
@@ -514,7 +525,7 @@
 			skb_reserve(new_skb, 34);
 
 			rxbase[elem].rx_addr =
-				(unsigned int) ((unsigned long)new_skb->data);
+				(u32) ((unsigned long)new_skb->data);
 			rxbase[elem].rx_flags =
 				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 
@@ -535,7 +546,7 @@
 
 			/* Reuse original ring buffer. */
 			rxbase[elem].rx_addr =
-				(unsigned int) ((unsigned long)skb->data);
+				(u32) ((unsigned long)skb->data);
 			rxbase[elem].rx_flags =
 				(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
 
@@ -738,8 +749,7 @@
 
 	qep->tx_skbs[entry] = skb;
 
-	/* FIX FOR ULTRA */
-	qep->qe_block->qe_txd[entry].tx_addr = (unsigned long) skb->data;
+	qep->qe_block->qe_txd[entry].tx_addr = (u32) ((unsigned long) skb->data);
 	qep->qe_block->qe_txd[entry].tx_flags =
 		(TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH));
 	qep->tx_new = NEXT_TX(entry);
@@ -1110,7 +1120,25 @@
 			res = EAGAIN;
 			goto qec_free_devs;
 		}
-	} else {
+	}
+#ifdef __sparc_v9__
+	else if(sparc_cpu_model == sun4u) {
+		struct devid_cookie dcookie;
+
+		dcookie.real_dev_id = qecp;
+		dcookie.imap = dcookie.iclr = 0;
+		dcookie.pil = -1;
+		dcookie.bus_cookie = sdev->my_bus;
+		if(request_irq(sdev->irqs[0].pri, &qec_interrupt,
+			       (SA_SHIRQ | SA_SBUS | SA_DCOOKIE),
+			       "QuadEther", &dcookie)) {
+			printk("QuadEther: Can't register QEC master irq handler.\n");
+			res = EAGAIN;
+			goto qec_free_devs;
+		}
+	}
+#endif
+	else {
 		if(request_irq(sdev->irqs[0].pri, &qec_interrupt,
 			       SA_SHIRQ, "QuadEther", (void *) qecp)) {
 			printk("QuadEther: Can't register QEC master irq handler.\n");
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.1.52/linux/drivers/sbus/char/Config.in	Wed Apr 23 19:01:21 1997
+++ linux/drivers/sbus/char/Config.in	Thu Sep  4 12:54:48 1997
@@ -41,6 +41,7 @@
 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 [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.1.52/linux/drivers/sbus/char/Makefile	Mon Aug  4 16:25:37 1997
+++ linux/drivers/sbus/char/Makefile	Thu Sep  4 12:54:48 1997
@@ -50,10 +50,14 @@
 #endif
 
 O_TARGET := sunchar.o
-O_OBJ    := ${FB_OBJS} suncons.o  sunfb.o
-O_OBJS   := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.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
 M_OBJS   :=
 
+ifeq ($(ARCH),sparc64)
+O_OBJS += su.o pcikbd.o
+endif
+
 ifeq ($(CONFIG_SUN_OPENPROMIO),y)
 O_OBJS += openprom.o
 else
@@ -84,6 +88,20 @@
   ifeq ($(CONFIG_SUN_VIDEOPIX),m)
   M_OBJS += vfc.o
   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)
+O_OBJS += mach64.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/cgfourteen.c linux/drivers/sbus/char/cgfourteen.c
--- v2.1.52/linux/drivers/sbus/char/cgfourteen.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/cgfourteen.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $
+/* $Id: cgfourteen.c,v 1.25 1997/08/20 07:38:36 davem Exp $
  * cgfourteen.c: Sun SparcStation console support.
  *
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -456,7 +456,7 @@
 	fb->setcurshape = cg14_setcurshape;
 	fb->ioctl = cg14_ioctl;
 	fb->switch_from_graph = cg14_switch_from_graph;
-	fb->postsetup = sun_cg_postsetup;
+	fb->postsetup = cg_postsetup;
 	fb->reset = cg14_reset;
 	fb->blank = 0;
 	fb->unblank = 0;
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/cgsix.c linux/drivers/sbus/char/cgsix.c
--- v2.1.52/linux/drivers/sbus/char/cgsix.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/cgsix.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $
+/* $Id: cgsix.c,v 1.37 1997/08/22 15:55:20 jj Exp $
  * cgsix.c: cgsix frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -409,8 +409,8 @@
 	struct cg6_info *cg6 = &(fb->info.cg6);
 	unsigned int rev, conf;
 
-	if (fb->setcursor)
-		sun_hw_hide_cursor ();
+	if (fb == &fbinfo[0])
+		sbus_hw_hide_cursor ();
 	/* Turn off stuff in the Transform Engine. */
 	cg6->tec->tec_matrix = 0;
 	cg6->tec->tec_clip = 0;
@@ -466,7 +466,7 @@
 	fb->setcursor = cg6_setcursor;
 	fb->setcursormap = cg6_setcursormap;
 	fb->setcurshape = cg6_setcurshape;
-	fb->postsetup = sun_cg_postsetup;
+	fb->postsetup = cg_postsetup;
 	fb->blitc = cg6_blitc;
 	fb->setw = cg6_setw;
 	fb->cpyw = cg6_cpyw;
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/cgthree.c linux/drivers/sbus/char/cgthree.c
--- v2.1.52/linux/drivers/sbus/char/cgthree.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/cgthree.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $
+/* $Id: cgthree.c,v 1.24 1997/08/20 07:38:37 davem Exp $
  * cgtree.c: cg3 frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -213,7 +213,7 @@
 	fb->type.fb_cmsize = 256;
 	fb->mmap = cg3_mmap;
 	fb->loadcmap = cg3_loadcmap;
-	fb->postsetup = sun_cg_postsetup;
+	fb->postsetup = cg_postsetup;
 	fb->ioctl = 0; /* no special ioctls */
 	fb->reset = 0;
 	fb->blank = cg3_blank;
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/creator.c linux/drivers/sbus/char/creator.c
--- v2.1.52/linux/drivers/sbus/char/creator.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/sbus/char/creator.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: creator.c,v 1.7 1997/07/17 02:21:47 davem Exp $
+/* $Id: creator.c,v 1.12 1997/08/25 07:50:27 jj Exp $
  * creator.c: Creator/Creator3D frame buffer driver
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -101,14 +101,24 @@
 #define FFB_UCSR_RP_BUSY       0x02000000
 
 struct ffb_fbc {
-	u8		xxx1[0x200];
+	u8		xxx1[0x60];
+	volatile u32	by;
+	volatile u32	bx;
+	u32		xxx2;
+	u32		xxx3;
+	volatile u32	bh;
+	volatile u32	bw;
+	u8		xxx4[0x188];
 	volatile u32	ppc;
-	u8		xxx2[0x50];
+	u32		xxx5;
+	volatile u32	fg;
+	volatile u32	bg;
+	u8		xxx6[0x44];
 	volatile u32	fbc;
 	volatile u32	rop;
-	u8		xxx3[0x34];
+	u8		xxx7[0x34];
 	volatile u32	pmask;
-	u8		xxx4[12];
+	u8		xxx8[12];
 	volatile u32	clip0min;
 	volatile u32	clip0max;
 	volatile u32	clip1min;
@@ -117,11 +127,17 @@
 	volatile u32	clip2max;
 	volatile u32	clip3min;
 	volatile u32	clip3max;
-	u8		xxx5[0x3c];
+	u8		xxx9[0x3c];
 	volatile u32	unk1;
-	u8		xxx6[0x500];
 	volatile u32	unk2;
-	u8		xxx7[0xfc];
+	u8		xxx10[0x10];
+	volatile u32	fontxy;
+	volatile u32	fontw;
+	volatile u32	fontinc;
+	volatile u32	font;
+	u8		xxx11[0x4dc];
+	volatile u32	unk3;
+	u8		xxx12[0xfc];
 	volatile u32	ucsr;
 };
 
@@ -141,6 +157,7 @@
 static void ffb_setw(int, int, unsigned short, int);
 static void ffb_cpyw(int, int, unsigned short *, int);
 static void ffb_fill(int, int, int *);
+static void ffb_penguin(int,int,int);
 
 static struct {
 	unsigned long voff;
@@ -176,6 +193,8 @@
 	uint size, page, r, map_size;
 	unsigned long map_offset = 0;
 	int i;
+	int alignment;
+	struct vm_area_struct *vmm;
 
 	size = vma->vm_end - vma->vm_start;
         if (vma->vm_offset & ~PAGE_MASK)
@@ -184,15 +203,18 @@
 	/* Try to align RAM */
 #define ALIGNMENT 0x400000
 	map_offset = vma->vm_offset + size;
+	alignment = 0;
 	if (vma->vm_offset < FFB_FBC_REGS_VOFF) {
-		struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
-		int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
-
-		if (alignment == ALIGNMENT) alignment = 0;
-		if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
-			vma->vm_start += alignment;
-			vma->vm_end += alignment;
-		}
+		vmm = find_vma(current->mm, vma->vm_start);
+		alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1));
+	} else if (vma->vm_offset >= FFB_DFB8R_VOFF && (vma->vm_offset & (ALIGNMENT - 1)) == 0x4000) {
+		vmm = find_vma(current->mm, vma->vm_start);
+		alignment = ALIGNMENT - (vma->vm_start & (ALIGNMENT - 1));
+	}
+	if (alignment == ALIGNMENT) alignment = 0;
+	if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) {
+		vma->vm_start += alignment;
+		vma->vm_end += alignment;
 	}
 #undef ALIGNMENT
 
@@ -205,7 +227,7 @@
 		for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++)
 			if (ffbmmap[i].voff == vma->vm_offset+page) {
 				map_size = ffbmmap[i].size;
-				map_offset = fb->info.ffb.physbase + ffbmmap[i].poff;
+				map_offset = (fb->info.ffb.physbase + ffbmmap[i].poff) & _PAGE_PADDR;
 			}
 
 		if (!map_size){
@@ -380,8 +402,8 @@
 	dac->type2 = 0x104;
 /* Should this be just 0x7ff?? Should I do some margin handling and setcurshape
    in that case? */
-	dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16)
-	              |((c->cpos.fby - c->chot.fby) & 0xffff);
+	dac->value2 = (((c->cpos.fby - c->chot.fby) & 0xffff) << 16)
+	              |((c->cpos.fbx - c->chot.fbx) & 0xffff);
 	ffb_curs_enable (fb, fb->cursor.enable);
 }
 
@@ -480,32 +502,14 @@
 void
 ffb_reset (fbinfo_t *fb)
 {
-	struct ffb_info *ffb = &(fb->info.ffb);
-	int fifo;
-
-	if (fb->setcursor)
-		sun_hw_hide_cursor ();
-	
-	while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8);
- 	ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) |
-			(FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) |
-			(FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) |
-			(FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) |
-			(FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) |
-			(FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT);
-	ffb->fbc->fbc = (FFB_FBC_WB_A        << FFB_FBC_WB_SHIFT) |
-			(FFB_FBC_PGE_MASK    << FFB_FBC_BE_SHIFT) |
-			(FFB_FBC_PGE_MASK    << FFB_FBC_GE_SHIFT) |
-			(FFB_FBC_PGE_MASK    << FFB_FBC_RE_SHIFT);
-	ffb->fbc->rop = (FFB_ROP_NEW         << FFB_ROP_RGB_SHIFT);
-	ffb->fbc->pmask = 0x00ffffff;
-	while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY);
+	if (fb == &fbinfo[0])
+		sbus_hw_hide_cursor ();
 }
 
 __initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start))
 {
 	fb->info.ffb.clut = (u32 *)(memory_start);
-        fb->color_map = (u8 *)(memory_start+256*4+256);
+        fb->color_map = (u8 *)(memory_start+256*4);
         return memory_start + 256*4 + 256*3;
 }
 
@@ -513,10 +517,11 @@
 {
 	struct ffb_info *ffbinfo;
 	struct linux_prom64_registers regs[2*PROMREG_MAX];
-	
+	int type;
+
 	if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0)
 		return;
-	ffb = regs[0].phys_addr;
+	ffb = (long)__va(regs[0].phys_addr);
 	printk ("creator%d at 0x%016lx ", slot, ffb);
 	
 	fb->base = ffb;	/* ??? */
@@ -536,9 +541,11 @@
 	fb->setw = ffb_setw;
 	fb->cpyw = ffb_cpyw;
 	fb->fill = ffb_fill;
+	fb->draw_penguin = ffb_penguin;
 	fb->ioctl = ffb_ioctl;
 	fb->cursor.hwsize.fbx = 64;
 	fb->cursor.hwsize.fby = 64;
+	fb->type.fb_depth = 24;
 	
 	ffbinfo = (struct ffb_info *) &fb->info.ffb;
 	
@@ -546,16 +553,18 @@
 
 	ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF);
 	ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF);
-	
+
 	ffbinfo->dac->type = 0x8000;
 	ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c);
 	
 	if (slot == sun_prom_console_id)
 		fb_restore_palette = ffb_restore_palette;
+		
+	type = prom_getintdefault (ffb_node, "board_type", 8);
 
 	/* Initialize Brooktree DAC */
 
-	printk("DAC %d\n", ffbinfo->dac_rev);
+	printk("TYPE %d DAC %d\n", type, ffbinfo->dac_rev);
 		
 	if (slot && sun_prom_console_id == slot)
 		return;
@@ -573,18 +582,152 @@
 
 extern unsigned char vga_font[];
 
+#define FFB_BLITC_START(attr) \
+	{ \
+		register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+		register u32 *clut = fbinfo[0].info.ffb.clut; \
+		int i; \
+		ffb->ppc = 0x203; \
+		ffb->fg = clut[attr & 0xf]; \
+		ffb->fbc = 0x2000707f; \
+		ffb->rop = 0x83; \
+		ffb->pmask = 0xffffffff; \
+		ffb->bg = clut[attr>>4];
+#define FFB_BLITC_BODY4(count,x,y,start,action) \
+		while (count >= 4) { \
+			count -= 4; \
+		    	ffb->fontw = 32; \
+		    	ffb->fontinc = 0x10000; \
+		    	ffb->fontxy = (y << 16) + x; \
+		    	x += 32; \
+		    	start; \
+		    	for (i = 0; i < CHAR_HEIGHT; i++) { \
+		    		action; \
+		    	} \
+		}
+#define FFB_BLITC_BODY1(x,y,action) \
+		    	ffb->fontw = 8; \
+		    	ffb->fontinc = 0x10000; \
+		    	ffb->fontxy = (y << 16) + x; \
+		    	x += 8; \
+		    	for (i = 0; i < CHAR_HEIGHT; i++) { \
+		    		action; \
+		    	}
+#define FFB_BLITC_END \
+	}
+	
 static void ffb_blitc(unsigned short charattr, int xoff, int yoff)
 {
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr);
+	unsigned char *p = &vga_font[((unsigned char)charattr) << 4];
+	FFB_BLITC_START(attrib)
+	FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p++) << 24))
+	FFB_BLITC_END
 }
 
 static void ffb_setw(int xoff, int yoff, unsigned short c, int count)
 {
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(c);
+	unsigned char *p = &vga_font[((unsigned char)c) << 4];
+	register unsigned char *q;
+	register uint l;
+	FFB_BLITC_START(attrib)
+	if (count >= 4) {
+		FFB_BLITC_BODY4(count, xoff, yoff, q = p, 
+			l = *q++;
+			l |= l << 8;
+			l |= l << 16;
+			ffb->font=l)
+	}
+	while (count) {
+		count--;
+		q = p;
+		FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*q++) << 24));
+	}
+	FFB_BLITC_END
 }
 
 static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count)
 {
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p);
+	unsigned char *p1, *p2, *p3, *p4;
+	FFB_BLITC_START(attrib)
+	if (count >= 4) {
+		FFB_BLITC_BODY4(count, xoff, yoff, 
+			p1 = &vga_font[((unsigned char)*p++) << 4];
+			p2 = &vga_font[((unsigned char)*p++) << 4];
+			p3 = &vga_font[((unsigned char)*p++) << 4];
+			p4 = &vga_font[((unsigned char)*p++) << 4], 
+			ffb->font=((uint)*p4++) | ((((uint)*p3++) | ((((uint)*p2++) | (((uint)*p1++) << 8)) << 8)) << 8))
+	}
+	while (count) {
+		count--;
+		p1 = &vga_font[((unsigned char)*p++) << 4];
+		FFB_BLITC_BODY1(xoff, yoff, ffb->font=((*p1++) << 24));
+	}
+	FFB_BLITC_END
 }
 
+#if 0
+#define FFB_FILL_START(attr) \
+	{ \
+		register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+		register u32 *clut = fbinfo[0].info.ffb.clut; \
+		ffb->ppc =0x1803; \
+		ffb->fg = clut[attr & 0xf]; \
+		ffb->fbc = 0x2000707f; \
+		ffb->rop = 0x83; \
+		ffb->pmask = 0xffffffff; \
+		ffb->unk2 = 8;
+#define FFB_FILL_END \
+	}
+#else
+#define FFB_FILL_START(attr) \
+	{ \
+		register struct ffb_fbc *ffb = fbinfo[0].info.ffb.fbc; \
+		ffb->ppc = 0x1803; \
+		ffb->fg = 0; \
+		ffb->fbc = 0x2000707f; \
+		ffb->rop = 0x83; \
+		ffb->pmask = 0xffffffff; \
+		ffb->unk2 = 8;
+#define FFB_FILL_END \
+	}
+#endif	
+	
 static void ffb_fill(int attrib, int count, int *boxes)
 {
+	attrib = 5;
+	FFB_FILL_START(attrib)
+	while (count-- > 0) {
+		ffb->by = boxes[1];
+		ffb->bx = boxes[0];
+		ffb->bw = boxes[2];
+		ffb->bh = boxes[3];
+		boxes += 4;
+	}
+	FFB_FILL_END
+}
+
+__initfunc(void ffb_penguin(int x_margin, int y_margin, int ncpus))
+{
+	int i, j, k;
+	u32 *p, *q;
+	unsigned char *r;
+	unsigned char c;
+	
+	p = (u32 *)(fbinfo[0].info.ffb.physbase + FFB_DFB24_POFF + y_margin*8192 + x_margin*4);
+	for (i = 0; i < 80; i++, p += 2048) {
+		q = p;
+		for (j = 0; j < ncpus; j++) {
+			r = linux_logo + 80 * i;
+			for (k = 0; k < 80; k++, r++) {
+				c = *r - 32;
+				*q++ = (linux_logo_red[c]) | 
+				       (linux_logo_green[c]<<8) | 
+				       (linux_logo_blue[c]<<16);
+			}
+			q += 8;
+		}
+	}
 }
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/fb.h linux/drivers/sbus/char/fb.h
--- v2.1.52/linux/drivers/sbus/char/fb.h	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/fb.h	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $
+/* $Id: fb.h,v 1.33 1997/08/25 07:50:29 jj Exp $
  * fb.h: contains the definitions of the structures that various sun
  *       frame buffer can use to do console driver stuff.
  *
@@ -122,6 +122,7 @@
 		struct tcx_info   tcx;
 		struct leo_info	  leo;
 		struct ffb_info   ffb;
+		void		 *private;
 	} info;		        /* per frame information */
 	int    space;           /* I/O space this card resides in */
 	int    blanked;		/* true if video blanked */
@@ -134,6 +135,7 @@
 	int    emulations[4];   /* possible emulations (-1 N/A) */
 	int    prom_node;	/* node of the device in prom tree */
 	int    base_depth;	/* depth of fb->base piece */
+	int    linebytes;	/* number of bytes in a row */
 	struct cg_cursor cursor;	/* kernel state of hw cursor */
 	int    (*mmap)(struct inode *, struct file *, struct vm_area_struct *,
 		       long fb_base, struct fbinfo *);
@@ -149,16 +151,20 @@
 	void   (*setcursormap)(struct fbinfo *, unsigned char *, 
 			unsigned char *, unsigned char *);
 	unsigned long (*postsetup)(struct fbinfo *, unsigned long);
+	void (*clear_fb)(int);
+	void (*set_other_palette)(int);
 	void	(*blitc)(unsigned short, int, int);
 	void	(*setw)(int, int, unsigned short, int);
 	void	(*cpyw)(int, int, unsigned short *, int);
 	void	(*fill)(int, int, int *);
+	void 	(*draw_penguin)(int,int,int);
 	unsigned char *color_map;
 	struct openpromfs_dev proc_entry;
 } fbinfo_t;
 
 #define CM(i, j) [3*(i)+(j)]
 
+extern unsigned char sparc_color_table[];
 extern unsigned char reverse_color_table[];
 
 #define CHARATTR_TO_SUNCOLOR(attr) \
@@ -196,13 +202,13 @@
 extern int get_iospace (unsigned long addr);
 extern void render_screen(void);
 
-extern void sun_hw_hide_cursor(void);
-extern void sun_hw_set_cursor(int, int);
-extern int sun_hw_scursor(struct fbcursor *,fbinfo_t *);
-extern int sun_hw_cursor_shown;
+extern void sbus_hw_hide_cursor(void);
+extern void sbus_hw_set_cursor(int, int);
+extern int sbus_hw_scursor(struct fbcursor *,fbinfo_t *);
+extern int sbus_hw_cursor_shown;
 extern int sun_prom_console_id;
 
-extern unsigned long sun_cg_postsetup(fbinfo_t *, unsigned long);
+extern unsigned long cg_postsetup(fbinfo_t *, unsigned long);
 
 #define FB_DEV(x) (MINOR(x) / 32)
 
@@ -215,5 +221,13 @@
 extern void tcx_setup (fbinfo_t *, int, int, u32, struct linux_sbus_device *);
 extern void creator_setup (fbinfo_t *, int, int, unsigned long, int);
 extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space);
+
+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 int linux_logo_colors;
+extern char logo_banner[];
 
 #endif __SPARC_FB_H_
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/leo.c linux/drivers/sbus/char/leo.c
--- v2.1.52/linux/drivers/sbus/char/leo.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/leo.c	Thu Sep  4 12:54:48 1997
@@ -1,4 +1,4 @@
-/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $
+/* $Id: leo.c,v 1.25 1997/08/22 17:33:58 jj Exp $
  * leo.c: SUNW,leo 24/8bit frame buffer driver
  *
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -9,12 +9,12 @@
 #include <linux/tty.h>
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
+#include <linux/delay.h>
 
 #include <asm/sbus.h>
 #include <asm/io.h>
 #include <asm/fbio.h>
 #include <asm/pgtable.h>
-#include <asm/delay.h>
 #include <asm/uaccess.h>
 
 /* These must be included after asm/fbio.h */
@@ -129,6 +129,7 @@
 static void leo_setw(int, int, unsigned short, int);
 static void leo_cpyw(int, int, unsigned short *, int);
 static void leo_fill(int, int, int *);
+static void leo_penguin(int,int,int);
 
 static void
 leo_restore_palette (fbinfo_t *fb)
@@ -496,8 +497,8 @@
 static void
 leo_reset (fbinfo_t *fb)
 {
-	if (fb->setcursor)
-		sun_hw_hide_cursor ();
+	if (fb == &fbinfo[0])
+		sbus_hw_hide_cursor ();
 }
 
 
@@ -537,6 +538,7 @@
 	fb->setw = leo_setw;
 	fb->cpyw = leo_cpyw;
 	fb->fill = leo_fill;
+	fb->draw_penguin = leo_penguin;
 	fb->base_depth = 0;
 	
 	leoinfo = (struct leo_info *) &fb->info.leo;
@@ -691,4 +693,10 @@
 		i = us->attrs;
 		us->fill = (boxes[0] & 0x7ff) | ((boxes[1] & 0x7ff) << 11) | ((i & 3) << 29) | ((i & 8) ? 0x80000000 : 0);
 	}
+}
+
+__initfunc(void leo_penguin(int x_margin, int y_margin, int ncpus))
+{
+	suncons_ops.clear_screen();
+	/* FIXME: Write this */
 }
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/mach64.c linux/drivers/sbus/char/mach64.c
--- v2.1.52/linux/drivers/sbus/char/mach64.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/mach64.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,203 @@
+/* $Id: mach64.c,v 1.8 1997/08/25 07:50:34 jj Exp $
+ * mach64.c: Ultra/PCI Mach64 console driver.
+ *
+ * Just about all of this is from the PPC/mac driver, see that for
+ * author info.  I'm only responsible for grafting it into working
+ * on PCI Ultra's.  The two drivers should be merged.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/proc_fs.h>
+
+#include <asm/oplib.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/sbus.h>
+
+#include "pcicons.h"
+#include "mach64.h"
+#include "fb.h"
+
+#define MACH64_REGOFF	0x7ffc00
+#define MACH64_FBOFF	0x800000
+
+static inline void mach64_waitq(int entries)
+{
+	unsigned short base = (0x8000 >> entries);
+
+	while((pcivga_readl(MACH64_REGOFF + FIFO_STAT) & 0xffff) > base)
+		barrier();
+}
+
+static inline void mach64_idle(void)
+{
+	mach64_waitq(16);
+	while(pcivga_readl(MACH64_REGOFF + GUI_STAT) & 1)
+		barrier();
+}
+
+#if 0	/* not used yet */
+static void mach64_st_514(int offset, char val)
+{
+	mach64_waitq(5);
+	pcivga_writeb(1,			MACH64_REGOFF + DAC_CNTL);
+	pcivga_writeb((offset & 0xff),		MACH64_REGOFF + DAC_W_INDEX);
+	pcivga_writeb(((offset>>8)&0xff),	MACH64_REGOFF + DAC_DATA);
+	pcivga_writeb(val,			MACH64_REGOFF + DAC_MASK);
+	pcivga_writeb(0,			MACH64_REGOFF + DAC_CNTL);
+}
+
+static void mach64_st_pll(int offset, char val)
+{
+	mach64_waitq(3);
+	pcivga_writeb(((offset<<2)|PLL_WR_EN),	MACH64_REGOFF + CLOCK_CNTL + 1);
+	pcivga_writeb(val,			MACH64_REGOFF + CLOCK_CNTL + 2);
+	pcivga_writeb(((offset<<2)&~PLL_WR_EN),	MACH64_REGOFF + CLOCK_CNTL + 1);
+}
+#endif
+
+static int
+mach64_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma,
+	    long base, fbinfo_t *fb)
+{
+	return -ENOSYS;
+}
+
+static void
+mach64_loadcmap(fbinfo_t *fb, int index, int count)
+{
+	unsigned char tmp;
+	int i;
+
+	mach64_waitq(2);
+	tmp = pcivga_readb(MACH64_REGOFF + DAC_CNTL);
+	pcivga_writeb(tmp & 0xfc, MACH64_REGOFF + DAC_CNTL);
+	pcivga_writeb(0xff, MACH64_REGOFF + DAC_MASK);
+	for(i = index; count--; i++) {
+		mach64_waitq(4);
+		pcivga_writeb(i, MACH64_REGOFF + DAC_W_INDEX);
+		pcivga_writeb(fb->color_map CM(i, 0), MACH64_REGOFF + DAC_DATA);
+		pcivga_writeb(fb->color_map CM(i, 1), MACH64_REGOFF + DAC_DATA);
+		pcivga_writeb(fb->color_map CM(i, 2), MACH64_REGOFF + DAC_DATA);
+	}
+}
+
+static void
+mach64_blank(fbinfo_t *fb)
+{
+	unsigned char gen_cntl;
+
+	gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+	gen_cntl |= 0x40;
+	pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static void
+mach64_unblank(fbinfo_t *fb)
+{
+	unsigned char gen_cntl;
+
+	gen_cntl = pcivga_readb(MACH64_REGOFF + CRTC_GEN_CNTL);
+	gen_cntl &= ~(0x4c);
+	pcivga_writeb(gen_cntl, MACH64_REGOFF + CRTC_GEN_CNTL);
+}
+
+static struct mach64_info mach64;
+
+int mach64_init(fbinfo_t *fb)
+{
+	struct pci_dev *pdev;
+	struct pcidev_cookie *cookie;
+	unsigned long addr;
+
+	memset(&mach64, 0, sizeof(mach64));
+	for(pdev = pci_devices; pdev; pdev = pdev->next) {
+		if((pdev->vendor == PCI_VENDOR_ID_ATI) &&
+		   (pdev->device == PCI_DEVICE_ID_ATI_264VT))
+			break;
+	}
+	if(!pdev)
+		return -1;
+
+	addr = pdev->base_address[0];
+	pcivga_iobase = pcivga_membase = 0;
+	if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+		pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+	else
+		pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+	addr = pdev->base_address[1];
+	if((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+		pcivga_iobase = addr & PCI_BASE_ADDRESS_IO_MASK;
+	else
+		pcivga_membase = addr & PCI_BASE_ADDRESS_MEM_MASK;
+
+	if(!pcivga_iobase || !pcivga_membase) {
+		prom_printf("mach64_init: I/O or MEM baseaddr is missing\n");
+		prom_printf("mach64_init: ba[0]=%016lx ba[1]=%016lx\n",
+			    pdev->base_address[0], pdev->base_address[1]);
+		prom_halt();
+	}
+
+	printk("mach64_init: IOBASE[%016lx] MEMBASE[%016lx]\n",
+		pcivga_iobase, pcivga_membase);
+
+	cookie = (struct pcidev_cookie *)pdev->sysdata;
+	fb->prom_node = cookie->prom_node;
+	fb->proc_entry.node = cookie->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;
+	}
+
+	if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID)
+					& CFG_CHIP_TYPE) == MACH64_VT_ID)
+		mach64.flags |= MACH64_MASK_VT;
+
+	printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n",
+		mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0);
+
+	fb->mmap = mach64_mmap;
+	fb->loadcmap = mach64_loadcmap;
+	fb->ioctl = 0;
+	fb->reset = 0;
+	fb->blank = mach64_blank;
+	fb->unblank = mach64_unblank;
+	fb->setcursor = 0;
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/mach64.h linux/drivers/sbus/char/mach64.h
--- v2.1.52/linux/drivers/sbus/char/mach64.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/mach64.h	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,587 @@
+/* $Id: mach64.h,v 1.3 1997/08/24 12:13:07 ecd Exp $
+ * mach64.h: Ultra/PCI mach64 driver constants etc.
+ *
+ * Copyright 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _MACH64_H
+#define _MACH64_H 1
+
+struct mach64_info {
+	unsigned int color_mode;
+	unsigned int flags;
+	unsigned int total_vram;
+};
+
+/* The mach64_info flag bits. */
+#define MACH64_MASK_VT	0x00000001
+
+/* NON-GUI MEMORY MAPPED Registers - expressed in BYTE offsets */
+
+#define CRTC_H_TOTAL_DISP       0x0000  /* Dword offset 00 */
+#define CRTC_H_SYNC_STRT_WID    0x0004  /* Dword offset 01 */
+#define CRTC_H_SYNC_STRT        0x0004
+#define CRTC_H_SYNC_DLY         0x0005
+#define CRTC_H_SYNC_WID         0x0006
+
+#define CRTC_V_TOTAL_DISP       0x0008  /* Dword offset 02 */
+#define CRTC_V_TOTAL            0x0008
+#define CRTC_V_DISP             0x000a
+#define CRTC_V_SYNC_STRT_WID    0x000C  /* Dword offset 03 */
+#define CRTC_V_SYNC_STRT        0x000c
+#define CRTC_V_SYNC_WID         0x000e
+
+#define CRTC_VLINE_CRNT_VLINE   0x0010  /* Dword offset 04 */
+#define CRTC_OFF_PITCH          0x0014  /* Dword offset 05 */
+#define CRTC_OFFSET             0x0014
+#define CRTC_PITCH              0x0016
+
+#define CRTC_INT_CNTL           0x0018  /* Dword offset 06 */
+#define CRTC_GEN_CNTL           0x001C  /* Dword offset 07 */
+#define CRTC_PIX_WIDTH          0x001d
+#define CRTC_FIFO               0x001e
+#define CRTC_EXT_DISP           0x001f
+
+#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 */
+
+#define CUR_CLR0                0x0060  /* Dword offset 18 */
+#define CUR_CLR1                0x0064  /* Dword offset 19 */
+#define CUR_OFFSET              0x0068  /* Dword offset 1A */
+#define CUR_HORZ_VERT_POSN      0x006C  /* Dword offset 1B */
+#define CUR_HORZ_VERT_OFF       0x0070  /* Dword offset 1C */
+
+#define SCRATCH_REG0            0x0080  /* Dword offset 20 */
+#define SCRATCH_REG1            0x0084  /* Dword offset 21 */
+
+#define CLOCK_CNTL              0x0090  /* Dword offset 24 */
+#define CLOCK_SEL_CNTL          0x0090  // Dword offset 24
+
+#define BUS_CNTL                0x00A0  /* Dword offset 28 */
+
+#define MEM_CNTL                0x00B0  /* Dword offset 2C */
+
+#define MEM_VGA_WP_SEL          0x00B4  /* Dword offset 2D */
+#define MEM_VGA_RP_SEL          0x00B8  /* Dword offset 2E */
+
+#define DAC_REGS                0x00C0  /* Dword offset 30 */
+#define DAC_W_INDEX             0x00C0  /* Dword offset 30 */
+#define DAC_DATA                0x00C1  /* Dword offset 30 */
+#define DAC_MASK                0x00C2  /* Dword offset 30 */
+#define DAC_R_INDEX             0x00C3  /* Dword offset 30 */
+#define DAC_CNTL                0x00C4  /* Dword offset 31 */
+
+#define GEN_TEST_CNTL           0x00D0  /* Dword offset 34 */
+
+#define CONFIG_CNTL		0x00DC	/* Dword offset 37 (CT, ET, VT) */
+#define CONFIG_CHIP_ID          0x00E0  /* Dword offset 38 */
+#define CONFIG_STAT0            0x00E4  /* Dword offset 39 */
+#define CONFIG_STAT1            0x00E8  /* Dword offset 3A */
+
+
+/* GUI MEMORY MAPPED Registers */
+
+#define DST_OFF_PITCH           0x0100  /* Dword offset 40 */
+#define DST_X                   0x0104  /* Dword offset 41 */
+#define DST_Y                   0x0108  /* Dword offset 42 */
+#define DST_Y_X                 0x010C  /* Dword offset 43 */
+#define DST_WIDTH               0x0110  /* Dword offset 44 */
+#define DST_HEIGHT              0x0114  /* Dword offset 45 */
+#define DST_HEIGHT_WIDTH        0x0118  /* Dword offset 46 */
+#define DST_X_WIDTH             0x011C  /* Dword offset 47 */
+#define DST_BRES_LNTH           0x0120  /* Dword offset 48 */
+#define DST_BRES_ERR            0x0124  /* Dword offset 49 */
+#define DST_BRES_INC            0x0128  /* Dword offset 4A */
+#define DST_BRES_DEC            0x012C  /* Dword offset 4B */
+#define DST_CNTL                0x0130  /* Dword offset 4C */
+
+#define SRC_OFF_PITCH           0x0180  /* Dword offset 60 */
+#define SRC_X                   0x0184  /* Dword offset 61 */
+#define SRC_Y                   0x0188  /* Dword offset 62 */
+#define SRC_Y_X                 0x018C  /* Dword offset 63 */
+#define SRC_WIDTH1              0x0190  /* Dword offset 64 */
+#define SRC_HEIGHT1             0x0194  /* Dword offset 65 */
+#define SRC_HEIGHT1_WIDTH1      0x0198  /* Dword offset 66 */
+#define SRC_X_START             0x019C  /* Dword offset 67 */
+#define SRC_Y_START             0x01A0  /* Dword offset 68 */
+#define SRC_Y_X_START           0x01A4  /* Dword offset 69 */
+#define SRC_WIDTH2              0x01A8  /* Dword offset 6A */
+#define SRC_HEIGHT2             0x01AC  /* Dword offset 6B */
+#define SRC_HEIGHT2_WIDTH2      0x01B0  /* Dword offset 6C */
+#define SRC_CNTL                0x01B4  /* Dword offset 6D */
+
+#define HOST_DATA0              0x0200  /* Dword offset 80 */
+#define HOST_DATA1              0x0204  /* Dword offset 81 */
+#define HOST_DATA2              0x0208  /* Dword offset 82 */
+#define HOST_DATA3              0x020C  /* Dword offset 83 */
+#define HOST_DATA4              0x0210  /* Dword offset 84 */
+#define HOST_DATA5              0x0214  /* Dword offset 85 */
+#define HOST_DATA6              0x0218  /* Dword offset 86 */
+#define HOST_DATA7              0x021C  /* Dword offset 87 */
+#define HOST_DATA8              0x0220  /* Dword offset 88 */
+#define HOST_DATA9              0x0224  /* Dword offset 89 */
+#define HOST_DATAA              0x0228  /* Dword offset 8A */
+#define HOST_DATAB              0x022C  /* Dword offset 8B */
+#define HOST_DATAC              0x0230  /* Dword offset 8C */
+#define HOST_DATAD              0x0234  /* Dword offset 8D */
+#define HOST_DATAE              0x0238  /* Dword offset 8E */
+#define HOST_DATAF              0x023C  /* Dword offset 8F */
+#define HOST_CNTL               0x0240  /* Dword offset 90 */
+
+#define PAT_REG0                0x0280  /* Dword offset A0 */
+#define PAT_REG1                0x0284  /* Dword offset A1 */
+#define PAT_CNTL                0x0288  /* Dword offset A2 */
+
+#define SC_LEFT                 0x02A0  /* Dword offset A8 */
+#define SC_RIGHT                0x02A4  /* Dword offset A9 */
+#define SC_LEFT_RIGHT           0x02A8  /* Dword offset AA */
+#define SC_TOP                  0x02AC  /* Dword offset AB */
+#define SC_BOTTOM               0x02B0  /* Dword offset AC */
+#define SC_TOP_BOTTOM           0x02B4  /* Dword offset AD */
+
+#define DP_BKGD_CLR             0x02C0  /* Dword offset B0 */
+#define DP_FRGD_CLR             0x02C4  /* Dword offset B1 */
+#define DP_WRITE_MASK           0x02C8  /* Dword offset B2 */
+#define DP_CHAIN_MASK           0x02CC  /* Dword offset B3 */
+#define DP_PIX_WIDTH            0x02D0  /* Dword offset B4 */
+#define DP_MIX                  0x02D4  /* Dword offset B5 */
+#define DP_SRC                  0x02D8  /* Dword offset B6 */
+
+#define CLR_CMP_CLR             0x0300  /* Dword offset C0 */
+#define CLR_CMP_MASK            0x0304  /* Dword offset C1 */
+#define CLR_CMP_CNTL            0x0308  /* Dword offset C2 */
+
+#define FIFO_STAT               0x0310  /* Dword offset C4 */
+
+#define CONTEXT_MASK            0x0320  /* Dword offset C8 */
+#define CONTEXT_LOAD_CNTL       0x032C  /* Dword offset CB */
+
+#define GUI_TRAJ_CNTL           0x0330  /* Dword offset CC */
+#define GUI_STAT                0x0338  /* Dword offset CE */
+
+
+/* CRTC control values (mostly CRTC_GEN_CNTL) */
+
+#define CRTC_H_SYNC_NEG		0x00200000
+#define CRTC_V_SYNC_NEG		0x00200000
+
+#define CRTC_DBL_SCAN_EN	0x00000001
+#define CRTC_INTERLACE_EN	0x00000002
+#define CRTC_HSYNC_DIS		0x00000004
+#define CRTC_VSYNC_DIS		0x00000008
+#define CRTC_CSYNC_EN		0x00000010
+#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_15BPP	0x00000300
+#define CRTC_PIX_WIDTH_16BPP	0x00000400
+#define CRTC_PIX_WIDTH_24BPP	0x00000500
+#define CRTC_PIX_WIDTH_32BPP	0x00000600
+
+#define CRTC_BYTE_PIX_ORDER	0x00000800
+#define CRTC_PIX_ORDER_MSN_LSN	0x00000000
+#define CRTC_PIX_ORDER_LSN_MSN	0x00000800
+
+#define CRTC_FIFO_LWM		0x000f0000
+#define CRTC_EXT_DISP_EN	0x01000000
+#define CRTC_EXT_EN		0x02000000
+
+#define CRTC_CRNT_VLINE		0x07f00000
+#define CRTC_VBLANK		0x00000001
+
+/* DAC control values */
+
+#define DAC_EXT_SEL_RS2		0x01
+#define DAC_EXT_SEL_RS3		0x02
+#define DAC_8BIT_EN		0x00000100
+#define DAC_PIX_DLY_MASK	0x00000600
+#define DAC_PIX_DLY_0NS		0x00000000
+#define DAC_PIX_DLY_2NS		0x00000200
+#define DAC_PIX_DLY_4NS		0x00000400
+#define DAC_BLANK_ADJ_MASK	0x00001800
+#define DAC_BLANK_ADJ_0		0x00000000
+#define DAC_BLANK_ADJ_1		0x00000800
+#define DAC_BLANK_ADJ_2		0x00001000
+
+
+/* Mix control values */
+
+#define MIX_NOT_DST                     0x0000
+#define MIX_0                           0x0001
+#define MIX_1                           0x0002
+#define MIX_DST                         0x0003
+#define MIX_NOT_SRC                     0x0004
+#define MIX_XOR                         0x0005
+#define MIX_XNOR                        0x0006
+#define MIX_SRC                         0x0007
+#define MIX_NAND                        0x0008
+#define MIX_NOT_SRC_OR_DST              0x0009
+#define MIX_SRC_OR_NOT_DST              0x000a
+#define MIX_OR                          0x000b
+#define MIX_AND                         0x000c
+#define MIX_SRC_AND_NOT_DST             0x000d
+#define MIX_NOT_SRC_AND_DST             0x000e
+#define MIX_NOR                         0x000f
+
+/* Maximum engine dimensions */
+#define ENGINE_MIN_X        0
+#define ENGINE_MIN_Y        0
+#define ENGINE_MAX_X        4095
+#define ENGINE_MAX_Y        16383
+
+/* Mach64 engine bit constants - these are typically ORed together */
+
+/* BUS_CNTL register constants */
+#define BUS_FIFO_ERR_ACK        0x00200000
+#define BUS_HOST_ERR_ACK        0x00800000
+
+/* GEN_TEST_CNTL register constants */
+#define GEN_OVR_OUTPUT_EN       0x20
+#define HWCURSOR_ENABLE         0x80
+#define GUI_ENGINE_ENABLE       0x100
+#define BLOCK_WRITE_ENABLE      0x200
+
+/* CLOCK_CNTL register constants */
+#define CLOCK_SEL		0x0f
+#define CLOCK_DIV		0x30
+#define CLOCK_DIV1		0x00
+#define CLOCK_DIV2		0x10
+#define CLOCK_DIV4		0x20
+#define CLOCK_STROBE		0x40
+#define PLL_WR_EN		0x02
+
+/* PLL registers */
+#define PLL_MACRO_CNTL		0x01
+#define PLL_REF_DIV		0x02
+#define PLL_GEN_CNTL		0x03
+#define MCLK_FB_DIV		0x04
+#define PLL_VCLK_CNTL		0x05
+#define VCLK_POST_DIV		0x06
+#define VCLK0_FB_DIV		0x07
+#define VCLK1_FB_DIV		0x08
+#define VCLK2_FB_DIV		0x09
+#define VCLK3_FB_DIV		0x0A
+#define PLL_XCLK_CNTL		0x0B
+#define PLL_TEST_CTRL		0x0E
+#define PLL_TEST_COUNT		0x0F
+
+/* Fields in PLL registers */
+#define PLL_PC_GAIN		0x07
+#define PLL_VC_GAIN		0x18
+#define PLL_DUTY_CYC		0xE0
+#define PLL_OVERRIDE		0x01
+#define PLL_MCLK_RST		0x02
+#define OSC_EN			0x04
+#define EXT_CLK_EN		0x08
+#define MCLK_SRC_SEL		0x70
+#define EXT_CLK_CNTL		0x80
+#define VCLK_SRC_SEL		0x03
+#define PLL_VCLK_RST		0x04
+#define VCLK_INVERT		0x08
+#define VCLK0_POST		0x03
+#define VCLK1_POST		0x0C
+#define VCLK2_POST		0x30
+#define VCLK3_POST		0xC0
+
+/* CONFIG_CNTL register constants */
+#define APERTURE_4M_ENABLE      1
+#define APERTURE_8M_ENABLE      2
+#define VGA_APERTURE_ENABLE     4
+
+/* CONFIG_STAT0 register constants (GX, CX) */
+#define CFG_BUS_TYPE		0x00000007
+#define CFG_MEM_TYPE		0x00000038
+#define CFG_INIT_DAC_TYPE	0x00000e00
+
+/* CONFIG_STAT0 register constants (CT, ET, VT) */
+#define CFG_MEM_TYPE_xT		0x00000007
+
+#define ISA			0
+#define EISA			1
+#define LOCAL_BUS		6
+#define PCI			7
+
+/* Memory types for GX, CX */
+#define DRAMx4			0
+#define VRAMx16			1
+#define VRAMx16ssr		2
+#define DRAMx16			3
+#define GraphicsDRAMx16		4
+#define EnhancedVRAMx16		5
+#define EnhancedVRAMx16ssr	6
+
+/* Memory types for CT, ET, VT, GT */
+#define DRAM			0
+#define EDO_DRAM		1
+#define PSEUDO_EDO		2
+#define SDRAM			3
+
+#define DAC_INTERNAL		0x00
+#define DAC_IBMRGB514		0x01
+#define DAC_ATI68875		0x02
+#define DAC_TVP3026_A		0x72
+#define DAC_BT476		0x03
+#define DAC_BT481		0x04
+#define DAC_ATT20C491		0x14
+#define DAC_SC15026		0x24
+#define DAC_MU9C1880		0x34
+#define DAC_IMSG174		0x44
+#define DAC_ATI68860_B		0x05
+#define DAC_ATI68860_C		0x15
+#define DAC_TVP3026_B		0x75
+#define DAC_STG1700		0x06
+#define DAC_ATT498		0x16
+#define DAC_STG1702		0x07
+#define DAC_SC15021		0x17
+#define DAC_ATT21C498		0x27
+#define DAC_STG1703		0x37
+#define DAC_CH8398		0x47
+#define DAC_ATT20C408		0x57
+
+#define CLK_ATI18818_0		0
+#define CLK_ATI18818_1		1
+#define CLK_STG1703		2
+#define CLK_CH8398		3
+#define CLK_INTERNAL		4
+#define CLK_ATT20C408		5
+#define CLK_IBMRGB514		6
+
+/* MEM_CNTL register constants */
+#define MEM_SIZE_ALIAS		0x00000007
+#define MEM_SIZE_512K		0x00000000
+#define MEM_SIZE_1M		0x00000001
+#define MEM_SIZE_2M		0x00000002
+#define MEM_SIZE_4M		0x00000003
+#define MEM_SIZE_6M		0x00000004
+#define MEM_SIZE_8M		0x00000005
+#define MEM_SIZE_ALIAS_GTB	0x0000000F
+#define MEM_SIZE_2M_GTB		0x00000003
+#define MEM_SIZE_4M_GTB		0x00000007
+#define MEM_SIZE_6M_GTB		0x00000009
+#define MEM_SIZE_8M_GTB		0x0000000B
+#define MEM_BNDRY               0x00030000
+#define MEM_BNDRY_0K            0x00000000
+#define MEM_BNDRY_256K          0x00010000
+#define MEM_BNDRY_512K          0x00020000
+#define MEM_BNDRY_1M            0x00030000
+#define MEM_BNDRY_EN            0x00040000
+
+/* ATI PCI constants */
+#define PCI_ATI_VENDOR_ID	0x1002
+#define PCI_MACH64_GX		0x4758
+#define PCI_MACH64_CX		0x4358
+#define PCI_MACH64_CT		0x4354
+#define PCI_MACH64_ET		0x4554
+#define PCI_MACH64_VT		0x5654
+#define PCI_MACH64_GT		0x4754
+
+/* CONFIG_CHIP_ID register constants */
+#define CFG_CHIP_TYPE		0x0000FFFF
+#define CFG_CHIP_CLASS		0x00FF0000
+#define CFG_CHIP_REV		0xFF000000
+#define CFG_CHIP_VERSION	0x07000000
+#define CFG_CHIP_FOUNDRY	0x38000000
+#define CFG_CHIP_REVISION	0xC0000000
+
+/* Chip IDs read from CONFIG_CHIP_ID */
+#define MACH64_GX_ID		0xD7
+#define MACH64_CX_ID		0x57
+#define MACH64_CT_ID		0x4354
+#define MACH64_ET_ID		0x4554
+#define MACH64_VT_ID		0x5654
+#define MACH64_GT_ID		0x4754
+
+/* Mach64 chip types */
+#define MACH64_UNKNOWN		0
+#define MACH64_GX		1
+#define MACH64_CX		2
+#define MACH64_CT		3
+#define MACH64_ET		4
+#define MACH64_VT		5
+#define MACH64_GT		6
+
+/* DST_CNTL register constants */
+#define DST_X_RIGHT_TO_LEFT     0
+#define DST_X_LEFT_TO_RIGHT     1
+#define DST_Y_BOTTOM_TO_TOP     0
+#define DST_Y_TOP_TO_BOTTOM     2
+#define DST_X_MAJOR             0
+#define DST_Y_MAJOR             4
+#define DST_X_TILE              8
+#define DST_Y_TILE              0x10
+#define DST_LAST_PEL            0x20
+#define DST_POLYGON_ENABLE      0x40
+#define DST_24_ROTATION_ENABLE  0x80
+
+/* SRC_CNTL register constants */
+#define SRC_PATTERN_ENABLE      1
+#define SRC_ROTATION_ENABLE     2
+#define SRC_LINEAR_ENABLE       4
+#define SRC_BYTE_ALIGN          8
+#define SRC_LINE_X_RIGHT_TO_LEFT 0
+#define SRC_LINE_X_LEFT_TO_RIGHT 0x10
+
+/* HOST_CNTL register constants */
+#define HOST_BYTE_ALIGN         1
+
+/* GUI_TRAJ_CNTL register constants */
+#define PAT_MONO_8x8_ENABLE     0x01000000
+#define PAT_CLR_4x2_ENABLE      0x02000000
+#define PAT_CLR_8x1_ENABLE      0x04000000
+
+/* DP_CHAIN_MASK register constants */
+#define DP_CHAIN_4BPP		0x8888
+#define DP_CHAIN_7BPP		0xD2D2
+#define DP_CHAIN_8BPP		0x8080
+#define DP_CHAIN_8BPP_RGB	0x9292
+#define DP_CHAIN_15BPP		0x4210
+#define DP_CHAIN_16BPP		0x8410
+#define DP_CHAIN_24BPP		0x8080
+#define DP_CHAIN_32BPP		0x8080
+
+/* DP_PIX_WIDTH register constants */
+#define DST_1BPP                0
+#define DST_4BPP                1
+#define DST_8BPP                2
+#define DST_15BPP               3
+#define DST_16BPP               4
+#define DST_32BPP               6
+#define SRC_1BPP                0
+#define SRC_4BPP                0x100
+#define SRC_8BPP                0x200
+#define SRC_15BPP               0x300
+#define SRC_16BPP               0x400
+#define SRC_32BPP               0x600
+#define HOST_1BPP               0
+#define HOST_4BPP               0x10000
+#define HOST_8BPP               0x20000
+#define HOST_15BPP              0x30000
+#define HOST_16BPP              0x40000
+#define HOST_32BPP              0x60000
+#define BYTE_ORDER_MSB_TO_LSB   0
+#define BYTE_ORDER_LSB_TO_MSB   0x1000000
+
+/* DP_MIX register constants */
+#define BKGD_MIX_NOT_D              0
+#define BKGD_MIX_ZERO               1
+#define BKGD_MIX_ONE                2
+#define BKGD_MIX_D                  3
+#define BKGD_MIX_NOT_S              4
+#define BKGD_MIX_D_XOR_S            5
+#define BKGD_MIX_NOT_D_XOR_S        6
+#define BKGD_MIX_S                  7
+#define BKGD_MIX_NOT_D_OR_NOT_S     8
+#define BKGD_MIX_D_OR_NOT_S         9
+#define BKGD_MIX_NOT_D_OR_S         10
+#define BKGD_MIX_D_OR_S             11
+#define BKGD_MIX_D_AND_S            12
+#define BKGD_MIX_NOT_D_AND_S        13
+#define BKGD_MIX_D_AND_NOT_S        14
+#define BKGD_MIX_NOT_D_AND_NOT_S    15
+#define BKGD_MIX_D_PLUS_S_DIV2      0x17
+#define FRGD_MIX_NOT_D              0
+#define FRGD_MIX_ZERO               0x10000
+#define FRGD_MIX_ONE                0x20000
+#define FRGD_MIX_D                  0x30000
+#define FRGD_MIX_NOT_S              0x40000
+#define FRGD_MIX_D_XOR_S            0x50000
+#define FRGD_MIX_NOT_D_XOR_S        0x60000
+#define FRGD_MIX_S                  0x70000
+#define FRGD_MIX_NOT_D_OR_NOT_S     0x80000
+#define FRGD_MIX_D_OR_NOT_S         0x90000
+#define FRGD_MIX_NOT_D_OR_S         0xa0000
+#define FRGD_MIX_D_OR_S             0xb0000
+#define FRGD_MIX_D_AND_S            0xc0000
+#define FRGD_MIX_NOT_D_AND_S        0xd0000
+#define FRGD_MIX_D_AND_NOT_S        0xe0000
+#define FRGD_MIX_NOT_D_AND_NOT_S    0xf0000
+#define FRGD_MIX_D_PLUS_S_DIV2      0x170000
+
+/* DP_SRC register constants */
+#define BKGD_SRC_BKGD_CLR           0
+#define BKGD_SRC_FRGD_CLR           1
+#define BKGD_SRC_HOST               2
+#define BKGD_SRC_BLIT               3
+#define BKGD_SRC_PATTERN            4
+#define FRGD_SRC_BKGD_CLR           0
+#define FRGD_SRC_FRGD_CLR           0x100
+#define FRGD_SRC_HOST               0x200
+#define FRGD_SRC_BLIT               0x300
+#define FRGD_SRC_PATTERN            0x400
+#define MONO_SRC_ONE                0
+#define MONO_SRC_PATTERN            0x10000
+#define MONO_SRC_HOST               0x20000
+#define MONO_SRC_BLIT               0x30000
+
+/* CLR_CMP_CNTL register constants */
+#define COMPARE_FALSE               0
+#define COMPARE_TRUE                1
+#define COMPARE_NOT_EQUAL           4
+#define COMPARE_EQUAL               5
+#define COMPARE_DESTINATION         0
+#define COMPARE_SOURCE              0x1000000
+
+/* FIFO_STAT register constants */
+#define FIFO_ERR                    0x80000000
+
+/* CONTEXT_LOAD_CNTL constants */
+#define CONTEXT_NO_LOAD             0
+#define CONTEXT_LOAD                0x10000
+#define CONTEXT_LOAD_AND_DO_FILL    0x20000
+#define CONTEXT_LOAD_AND_DO_LINE    0x30000
+#define CONTEXT_EXECUTE             0
+#define CONTEXT_CMD_DISABLE         0x80000000
+
+/* GUI_STAT register constants */
+#define ENGINE_IDLE                 0
+#define ENGINE_BUSY                 1
+#define SCISSOR_LEFT_FLAG           0x10
+#define SCISSOR_RIGHT_FLAG          0x20
+#define SCISSOR_TOP_FLAG            0x40
+#define SCISSOR_BOTTOM_FLAG         0x80
+
+/* ATI VGA Extended Regsiters */
+#define sioATIEXT	0x1ce
+#define bioATIEXT	0x3ce
+
+#define ATI2E		0xae
+#define ATI32		0xb2
+#define ATI36		0xb6
+
+/* VGA Graphics Controller Registers */
+#define VGAGRA		0x3ce
+#define GRA06		0x06
+
+/* VGA Seququencer Registers */
+#define VGASEQ		0x3c4
+#define SEQ02		0x02
+#define SEQ04		0x04
+
+#define MACH64_MAX_X	ENGINE_MAX_X
+#define MACH64_MAX_Y	ENGINE_MAX_Y
+
+#define INC_X           0x0020
+#define INC_Y           0x0080
+
+#define RGB16_555               0x0000
+#define RGB16_565               0x0040
+#define RGB16_655               0x0080
+#define RGB16_664               0x00c0
+
+#define POLY_TEXT_TYPE          0x0001
+#define IMAGE_TEXT_TYPE         0x0002
+#define TEXT_TYPE_8_BIT         0x0004
+#define TEXT_TYPE_16_BIT        0x0008
+#define POLY_TEXT_TYPE_8        (POLY_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define IMAGE_TEXT_TYPE_8       (IMAGE_TEXT_TYPE | TEXT_TYPE_8_BIT)
+#define POLY_TEXT_TYPE_16       (POLY_TEXT_TYPE | TEXT_TYPE_16_BIT)
+#define IMAGE_TEXT_TYPE_16      (IMAGE_TEXT_TYPE | TEXT_TYPE_16_BIT)
+
+#define MACH64_NUM_CLOCKS	16
+#define MACH64_NUM_FREQS	50
+
+#endif /* !(_MACH64_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/pcicons.c linux/drivers/sbus/char/pcicons.c
--- v2.1.52/linux/drivers/sbus/char/pcicons.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/pcicons.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,750 @@
+/* $Id: pcicons.c,v 1.9 1997/08/28 02:23:24 ecd Exp $
+ * pcicons.c: PCI specific probing and console operations layer.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_PCI
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/major.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#include <asm/smp.h>
+
+#include <linux/kd.h>
+#include <linux/console_struct.h>
+#include <linux/selection.h>
+#include <linux/vt_kern.h>
+
+#include "pcicons.h"
+#include "fb.h"
+
+static int x_margin = 0;
+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 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;
+
+static void pci_install_consops(void);
+static int (*fbuf_offset)(int);
+
+static int color_fbuf_offset_1152_128(int cindex)
+{
+	register int i = (cindex >> 7);
+	/* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+	return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128(int cindex)
+{
+	register int i = (cindex >> 7);
+	/* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+	return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static __u32 expand_bits_8[16] = {
+	0x00000000,
+	0x000000ff,
+	0x0000ff00,
+	0x0000ffff,
+	0x00ff0000,
+	0x00ff00ff,
+	0x00ffff00,
+	0x00ffffff,
+	0xff000000,
+	0xff0000ff,
+	0xff00ff00,
+	0xff00ffff,
+	0xffff0000,
+	0xffff00ff,
+	0xffffff00,
+	0xffffffff
+};
+
+static void pci_blitc(unsigned int charattr, unsigned long addr)
+{
+	static int cached_attr = -1;
+	static __u32 fg, bg;
+	fbinfo_t *fb = &fbinfo[0];
+	__u32 *screen;
+	unsigned char attrib;
+	unsigned char *fp;
+	unsigned long flags;
+	int i, idx;
+
+	if ((charattr & 0xff00) != cached_attr) {
+		cached_attr = charattr;
+		attrib = CHARATTR_TO_SUNCOLOR(charattr);
+		fg = attrib & 0x0f;
+		fg |= fg << 8;
+		fg |= fg << 16;
+		bg = attrib >> 4;
+		bg |= bg << 8;
+		bg |= bg << 16;
+		fg ^= bg;
+	}
+
+	idx = (addr - video_mem_base) >> 1;
+	save_flags(flags); cli();
+	if (cursor_pos == idx)
+		cursor_pos = -1;
+	restore_flags(flags);
+
+	screen = (__u32 *)(fb->base + fbuf_offset(idx));
+	fp = &vga_font[(charattr & 0xff) << 4];
+
+	for(i = 0; i < 16; i++) {
+		int bits = *fp++;
+
+		screen[0] = (expand_bits_8[bits >> 4] & fg) ^ bg;
+		screen[1] = (expand_bits_8[bits & 0x0f] & fg) ^ bg;
+		screen = (__u32 *) (((unsigned long)screen) + fb->linebytes);
+	}
+}
+
+
+static void pci_memsetw(void *s, unsigned short c, unsigned int count)
+{
+	unsigned short *p = (unsigned short *)s;
+
+	count >>= 1;
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+		while (count) {
+			--count;
+			*p++ = c;
+		}
+		return;
+	}
+	if ((unsigned long)(p + count) > video_mem_base &&
+	    (unsigned long)p < video_mem_term) {
+		for (; p < (unsigned short *)video_mem_base && count; count--)
+			*p++ = c;
+		for (; p < (unsigned short *)video_mem_term && count; count--) {
+			if (*p != c) {
+				*p = c;
+				pci_blitc(c, (unsigned long)p);
+			}
+			++p;
+		}
+	}
+	for (; count; count--)
+		*p++ = c;
+}
+
+static void pci_memcpyw(unsigned short *dst, unsigned short *src,
+			unsigned int count)
+{
+	unsigned short c;
+
+	count >>= 1;
+	if ((unsigned long)(dst + count) > video_mem_base &&
+	    (unsigned long)dst < video_mem_term) {
+		for (; dst < (unsigned short *)video_mem_base && count; count--)
+			*dst++ = *src++;
+		for (; dst < (unsigned short *)video_mem_term && count;
+		     count--) {
+			c = *src++;
+			if (*dst != c) {
+				*dst = c;
+				pci_blitc(c, (unsigned long)dst);
+			}
+			++dst;
+		}
+	}
+	for (; count; count--)
+		*dst++ = *src++;
+}
+
+static void pci_scr_writew(unsigned short val, unsigned short *addr)
+{
+	if (*addr != val) {
+		*addr = val;
+		if ((unsigned long)addr < video_mem_term &&
+                    (unsigned long)addr >= video_mem_base &&
+                    vt_cons[fg_console]->vc_mode == KD_TEXT)
+                        pci_blitc(val, (unsigned long) addr);
+	}
+}
+
+static unsigned short pci_scr_readw(unsigned short *addr)
+{
+	return *addr;
+}
+
+static void pci_get_scrmem(int currcons)
+{
+	memcpyw((unsigned short *)vc_scrbuf[currcons],
+		(unsigned short *)origin, video_screen_size);
+	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+	scr_end = video_mem_end = video_mem_start + video_screen_size;
+	pos = origin + y * video_size_row + (x << 1);
+}
+
+static void pci_set_scrmem(int currcons, long offset)
+{
+	if (video_mem_term - video_mem_base < offset + video_screen_size)
+		offset = 0;
+	memcpyw((unsigned short *)(video_mem_base + offset),
+		(unsigned short *) origin, video_screen_size);
+	video_mem_start = video_mem_base;
+	video_mem_end = video_mem_term;
+	origin = video_mem_base + offset;
+	scr_end = origin + video_screen_size;
+	pos = origin + y * video_size_row + (x << 1);
+}
+
+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);
+
+	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);
+	}
+
+	for (i = 0; i < 2; i++) {
+		screen[0] ^= mask;
+		screen[1] ^= mask;
+		screen = (__u32 *)((unsigned long)screen + fb->linebytes);
+	}
+}
+
+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);
+}
+
+static void pci_hide_cursor(void)
+{
+	unsigned long flags;
+
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+		return;
+
+	save_flags(flags); cli();
+	if (cursor_pos != -1) {
+		pci_invert_cursor(-1);
+		cursor_pos = -1;
+	}
+	cursor_off = 1;
+	restore_flags(flags);
+}
+
+static void pci_set_cursor(int currcons)
+{
+	unsigned long flags;
+	int old_cursor;
+
+	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+		return;
+
+	save_flags(flags); cli();
+	if (!deccm) {
+		pci_hide_cursor();
+	} else {
+		old_cursor = cursor_pos;
+		cursor_pos = (pos - video_mem_base) >> 1;
+		if (old_cursor != -1)
+			pci_invert_cursor(-1);
+		pci_invert_cursor(cursor_pos);
+		cursor_off = 0;
+	}
+	restore_flags(flags);
+}
+
+static void pci_set_palette(void)
+{
+	fbinfo_t *fb = &fbinfo[0];
+
+	if (console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+		return;
+
+	if (fb->loadcmap) {
+		int i, j;
+
+		for (i = 0; i < 16; i++) {
+			j = sparc_color_table[i];
+			fb->color_map CM(i, 0) = default_red[j];
+			fb->color_map CM(i, 1) = default_grn[j];
+			fb->color_map CM(i, 2) = default_blu[j];
+		}
+		fb->loadcmap(fb, 0, 16);
+	}
+}
+
+static void pci_set_other_palette(int n)
+{
+	fbinfo_t *fb = &fbinfo[n];
+
+	if (!n) {
+		pci_set_palette();
+		return;
+	}
+
+	if (fb->loadcmap) {
+		fb->color_map CM(0, 0) = 0;
+		fb->color_map CM(0, 1) = 0;
+		fb->color_map CM(0, 2) = 0;
+		fb->loadcmap(fb, 0, 1);
+	}
+}
+
+static void pci_restore_palette(void)
+{
+	if (fb_restore_palette)
+		fb_restore_palette(&fbinfo[0]);
+}
+
+static int pci_set_get_font(char *arg, int set, int ch512)
+{
+	int i, line;
+
+	if (!arg)
+		return -EINVAL;
+
+	if (!set) {
+		if (clear_user(arg, sizeof(vga_font)))
+			return -EFAULT;
+		for (i = 0; i < 256; i++) {
+			for (line = 0; line < CHAR_HEIGHT; line++) {
+				unsigned char value;
+
+				value = vga_font[i * CHAR_HEIGHT + line];
+				__put_user_ret(value, (arg + (i * 32 + line)),
+					       -EFAULT);
+			}
+		}
+		return 0;
+	}
+
+	if (verify_area(VERIFY_READ, arg, 256 * CHAR_HEIGHT))
+		return -EFAULT;
+	for (i = 0; i < 256; i++) {
+		for (line = 0; line < CHAR_HEIGHT; line++) {
+			unsigned char value;
+			__get_user_ret(value, (arg + (i + 32 + line)), -EFAULT);
+			vga_font[i * CHAR_HEIGHT + line] = value;
+		}
+	}
+	return 0;
+}
+
+static int pci_con_adjust_height(unsigned long fontheight)
+{
+	return -EINVAL;
+}
+
+static int pci_set_get_cmap(unsigned char *arg, int set)
+{
+	int i;
+
+	if (set)
+		i = VERIFY_READ;
+	else
+		i = VERIFY_WRITE;
+
+	if (verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+		return -EFAULT;
+
+	for (i = 0; i < 16; i++) {
+		if (set) {
+			__get_user_ret(default_red[i], (arg + 0), -EFAULT);
+			__get_user_ret(default_grn[i], (arg + 1), -EFAULT);
+			__get_user_ret(default_blu[i], (arg + 2), -EFAULT);
+		} else {
+			__put_user_ret(default_red[i], (arg + 0), -EFAULT);
+			__put_user_ret(default_grn[i], (arg + 1), -EFAULT);
+			__put_user_ret(default_blu[i], (arg + 2), -EFAULT);
+		}
+		arg += 3;
+	}
+
+	if (set) {
+		for (i = 0; i < MAX_NR_CONSOLES; i++) {
+			if (vc_cons_allocated(i)) {
+				int j, k;
+
+				for (j = k = 0; j < 16; j++) {
+					vc_cons[i].d->vc_palette[k++] =
+							default_red[i];
+					vc_cons[i].d->vc_palette[k++] =
+							default_grn[i];
+					vc_cons[i].d->vc_palette[k++] =
+							default_blu[i];
+				}
+			}
+		}
+		pci_set_palette();
+	}
+	return -EINVAL;
+}
+
+static void pci_clear_screen(void)
+{
+	fbinfo_t *fb = &fbinfo[0];
+
+	if (fb->base)
+		memset((void *)fb->base,
+		       (fb->type.fb_depth == 1) ?
+		       ~(0) : reverse_color_table[0],
+		       (fb->type.fb_depth * fb->type.fb_height
+					 * fb->type.fb_width) / 8);
+	memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+}
+
+static void pci_clear_fb(int n)
+{
+	fbinfo_t *fb = &fbinfo[n];
+
+	if (!n) {
+		pci_clear_screen();
+	} else if (fb->base) {
+		memset((void *)fb->base,
+		       (fb->type.fb_depth == 1) ?
+		       ~(0) : reverse_color_table[0],
+		       (fb->type.fb_depth * fb->type.fb_height
+					 * fb->type.fb_width) / 8);
+	}
+}
+
+static void pci_render_screen(void)
+{
+	int count;
+	unsigned short *p;
+
+	count = video_num_columns * video_num_lines;
+	p = (unsigned short *)video_mem_base;
+
+	for (; count--; p++)
+		pci_blitc(*p, (unsigned long)p);
+}
+
+static void pci_clear_margin(void)
+{
+	fbinfo_t *fb = &fbinfo[0];
+	unsigned long p;
+	int h, he;
+
+	memset((void *)fb->base,
+	       (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+	       skip_bytes - (x_margin << 1));
+	memset((void *)(fb->base + fb->linebytes * fb->type.fb_height
+	       - skip_bytes + (x_margin << 1)),
+	       (fb->type.fb_depth == 1) ? ~(0) : reverse_color_table[0],
+	       skip_bytes - (x_margin << 1));
+	he = fb->type.fb_height - 2 * y_margin;
+	if (fb->type.fb_depth == 1) {
+		for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+		     h < he; p += fb->linebytes, h++)
+			memset((void *)p, ~(0), (x_margin << 1));
+	} else {
+		for (p = fb->base + skip_bytes - (x_margin << 1), h = 0;
+		     h < he; p += fb->linebytes, h++)
+			memset((void *)p, reverse_color_table[0],
+			       (x_margin << 1));
+	}
+
+	if (fb->switch_from_graph)
+		fb->switch_from_graph();
+}
+
+static unsigned long
+pci_postsetup(fbinfo_t *fb, unsigned long memory_start)
+{
+	fb->color_map = (char *)memory_start;
+	pci_set_palette();
+	return memory_start + fb->type.fb_cmsize * 3;
+}
+
+__initfunc(static unsigned long
+pci_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+	can_do_color = 1;
+
+	video_type = VIDEO_TYPE_SUNPCI;
+	*display_desc = "SUNPCI";
+
+	if(!serial_console) {
+		/* If we fall back to PROM then our output
+		 * have to remain readable.
+		 */
+		prom_putchar('\033');
+		prom_putchar('[');
+		prom_putchar('H');
+
+		/*
+		 * Fake the screen memory with some CPU memory
+		 */
+		video_mem_base = kmem_start;
+		kmem_start += video_screen_size;
+		video_mem_term = kmem_start;
+	}
+
+	return kmem_start;
+}
+
+__initfunc(static void pci_con_type_init_finish(void))
+{
+	fbinfo_t *fb = &fbinfo[0];
+	unsigned char *p = (unsigned char *)fb->base + skip_bytes;
+	char q[2] = { 0, 5 };
+	unsigned short *ush;
+	int currcons = 0;
+	int cpu, ncpus;
+	int i;
+
+	if (serial_console)
+		return;
+
+	ncpus = linux_num_cpus;
+	if (ncpus > 4)
+		ncpus = 4;
+
+#if 0
+	if (fb->draw_penguin)
+		fb->draw_penguin(x_margin, y_margin, ncpus);
+	else
+#endif
+	if (fb->type.fb_depth == 8 && fb->loadcmap) {
+		for (i = 0; i < linux_logo_colors; i++) {
+			fb->color_map CM(i + 32, 0) = linux_logo_red[i];
+			fb->color_map CM(i + 32, 1) = linux_logo_green[i];
+			fb->color_map CM(i + 32, 2) = linux_logo_blue[i];
+		}
+		fb->loadcmap(fb, 32, linux_logo_colors);
+		for (i = 0; i < 80; i++, p += fb->linebytes) {
+			for (cpu = 0; cpu < ncpus; cpu++)
+				memcpy(p + (cpu * 88), linux_logo + 80 * i, 80);
+		}
+	} else if (fb->type.fb_depth == 1) {
+		for (i = 0; i < 80; i++, p += fb->linebytes) {
+			for (cpu = 0; cpu < ncpus; cpu++)
+				memcpy(p + (cpu * 11),
+				       linux_logo_bw + 10 * i, 10);
+		}
+	}
+	putconsxy(0, q);
+
+	ush = (unsigned short *)video_mem_base + video_num_columns * 2 + 20
+				+ 10 * (ncpus - 1);
+
+	for (p = logo_banner; *p; p++, ush++) {
+		*ush = (attr << 8) + *p;
+		pci_blitc(*ush, (unsigned long)ush);
+	}
+
+	for (i = 0; i < 5; i++) {
+		ush = (unsigned short *)video_mem_base + i * video_num_columns;
+		memset(ush, 0xff, 20);
+	}
+}
+
+unsigned long pcivga_iobase = 0;
+unsigned long pcivga_membase = 0;
+
+static struct {
+	int depth;
+	int resx, resy;
+	int x_margin, y_margin;
+} scr_def[] = {
+	{ 8, 1152, 900, 64, 18 },
+	{ 8, 1024, 768, 0, 0 },
+	{ 0 }
+};
+
+extern int mach64_init(fbinfo_t *fb);
+
+__initfunc(int pci_console_probe(void))
+{
+	fbinfo_t *fb = &fbinfo[0];
+	char *p;
+	int i;
+
+	if (1
+#if 1
+	    && mach64_init(fb)
+#endif
+	    && 1) {
+		return -ENODEV;
+	}
+	fbinfos++;
+
+	fb->clear_fb = pci_clear_fb;
+	fb->set_other_palette = pci_set_other_palette;
+	fb->postsetup = pci_postsetup;
+	fb->blanked = 0;
+
+	fb->type.fb_height = prom_getintdefault(fb->prom_node, "height", 900);
+	fb->type.fb_width = prom_getintdefault(fb->prom_node, "width", 1152);
+	fb->type.fb_depth = prom_getintdefault(fb->prom_node, "depth", 8);
+	fb->linebytes = prom_getintdefault(fb->prom_node, "linebytes", 1152);
+	fb->type.fb_size = PAGE_ALIGN(fb->linebytes * fb->type.fb_height);
+
+	fb->proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, 0);
+	fb->proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+	prom_getname(fb->prom_node, fb->proc_entry.name, 32 - 3);
+	p = strchr(fb->proc_entry.name, 0);
+	sprintf(p, ":%d", 0);
+
+	for (i = 0; scr_def[i].depth; i++) {
+		if ((scr_def[i].resx != fb->type.fb_width) ||
+		    (scr_def[i].resy != fb->type.fb_height) ||
+		    (scr_def[i].depth != fb->type.fb_depth))
+			continue;
+		x_margin = scr_def[i].x_margin;
+		y_margin = scr_def[i].y_margin;
+		skip_bytes = y_margin * fb->linebytes + x_margin;
+		switch (fb->type.fb_width) {
+			case 1152:
+				fbuf_offset = color_fbuf_offset_1152_128;
+				break;
+			case 1024:
+				fbuf_offset = color_fbuf_offset_1024_128;
+				break;
+			default:
+				prom_printf("can't handle console width %d\n",
+					    fb->type.fb_width);
+				prom_halt();
+		}
+	}
+
+	pci_install_consops();
+	return fb_init();
+}
+
+__initfunc(void pci_console_inithook(void))
+{
+	extern char *console_fb_path;
+	char prop[16];
+	int node = 0;
+	int width, height, depth, linebytes;
+	int x_margin, y_margin;
+	int i, len;
+
+	if (console_fb_path) {
+		char *p;
+		for (p = console_fb_path; *p && *p != ' '; p++) ;
+		*p = 0;
+		node = prom_pathtoinode(console_fb_path);
+	}
+	if (!node) {
+		node = prom_inst2pkg(prom_stdout);
+		if (!node) {
+			prom_printf("can't find output-device node\n");
+			prom_halt();
+		}
+		len = prom_getproperty(node, "device_type", prop, sizeof(prop));
+		if (len < 0) {
+			prom_printf("output-device doesn't have"
+				    " device_type property\n");
+			prom_halt();
+		}
+		if (len != sizeof("display") ||
+		    strncmp("display", prop, sizeof("display"))) {
+			prom_printf("output-device is %s"
+				    " not \"display\"\n", prop);
+			prom_halt();
+		}
+	}
+
+	depth = prom_getintdefault(node, "depth", 8);
+	width = prom_getintdefault(node, "width", 1152);
+	height = prom_getintdefault(node, "height", 900);
+	linebytes = prom_getintdefault(node, "linebytes", 1152);
+
+	for (i = 0; scr_def[i].depth; i++) {
+		if ((scr_def[i].resx != width) ||
+		    (scr_def[i].resy != height) ||
+		    (scr_def[i].depth != depth))
+			continue;
+		x_margin = scr_def[i].x_margin;
+		y_margin = scr_def[i].y_margin;
+
+		ORIG_VIDEO_COLS = width / 8 - 2 * x_margin / depth;
+		ORIG_VIDEO_LINES = (height - 2 * y_margin) / 16;
+	}
+
+	suncons_ops.con_type_init = pci_con_type_init;
+}
+
+__initfunc(static void pci_install_consops(void))
+{
+	suncons_ops.memsetw = pci_memsetw;
+	suncons_ops.memcpyw = pci_memcpyw;
+	suncons_ops.scr_writew = pci_scr_writew;
+	suncons_ops.scr_readw = pci_scr_readw;
+
+	suncons_ops.get_scrmem = pci_get_scrmem;
+	suncons_ops.set_scrmem = pci_set_scrmem;
+
+	suncons_ops.hide_cursor = pci_hide_cursor;
+	suncons_ops.set_cursor = pci_set_cursor;
+	suncons_ops.set_get_font = pci_set_get_font;
+	suncons_ops.con_adjust_height = pci_con_adjust_height;
+	suncons_ops.set_get_cmap = pci_set_get_cmap;
+	suncons_ops.set_palette = pci_set_palette;
+	suncons_ops.set_other_palette = pci_set_other_palette;
+	suncons_ops.console_restore_palette = pci_restore_palette;
+
+	suncons_ops.con_type_init = pci_con_type_init;
+	suncons_ops.con_type_init_finish = pci_con_type_init_finish;
+
+	suncons_ops.clear_screen = pci_clear_screen;
+	suncons_ops.render_screen = pci_render_screen;
+	suncons_ops.clear_margin = pci_clear_margin;
+}
+
+#endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/pcicons.h linux/drivers/sbus/char/pcicons.h
--- v2.1.52/linux/drivers/sbus/char/pcicons.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/pcicons.h	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,79 @@
+/* $Id: pcicons.h,v 1.2 1997/08/24 12:13:11 ecd Exp $
+ * pcicons.h: Stuff which is generic across all PCI console drivers.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef PCICONS_H
+#define PCICONS_H
+
+#include <linux/pci.h>
+#include <asm/asi.h>
+#include <asm/io.h>
+
+extern unsigned long pcivga_iobase;
+extern unsigned long pcivga_membase;
+
+extern unsigned char vga_font[8192];
+
+extern __inline__ unsigned int pcivga_inb(unsigned long off)
+{
+	return inb(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inw(unsigned long off)
+{
+	return inw(pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_inl(unsigned long off)
+{
+	return inl(pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outb(unsigned char val, unsigned long off)
+{
+	outb(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outw(unsigned short val, unsigned long off)
+{
+	outw(val, pcivga_iobase + off);
+}
+
+extern __inline__ void pcivga_outl(unsigned int val, unsigned long off)
+{
+	outl(val, pcivga_iobase + off);
+}
+
+extern __inline__ unsigned int pcivga_readb(unsigned long off)
+{
+	return readb(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readw(unsigned long off)
+{
+	return readw(pcivga_membase + off);
+}
+
+extern __inline__ unsigned int pcivga_readl(unsigned long off)
+{
+	return readl(pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writeb(unsigned char val, unsigned long off)
+{
+	writeb(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writew(unsigned short val, unsigned long off)
+{
+	writew(val, pcivga_membase + off);
+}
+
+extern __inline__ void pcivga_writel(unsigned int val, unsigned long off)
+{
+	writel(val, pcivga_membase + off);
+}
+
+#endif /* PCICONS_H */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.1.52/linux/drivers/sbus/char/pcikbd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/pcikbd.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,992 @@
+/* $Id: pcikbd.c,v 1.3 1997/09/04 05:50:35 ecd Exp $
+ * pcikbd.c: Ultra/AX PC keyboard support.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ * This code is mainly put together from various places in
+ * drivers/char, please refer to these sources for credits
+ * to the original authors.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/poll.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/miscdevice.h>
+#include <linux/kbd_ll.h>
+#include <linux/init.h>
+
+#include <asm/ebus.h>
+#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/keyboard.h>
+
+#include "pcikbd.h"
+#include "sunserial.h"
+
+static int kbd_node;
+static int beep_node;
+
+static unsigned long pcikbd_iobase = 0;
+static unsigned int pcikbd_irq;
+
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected = 0;
+static volatile unsigned char acknowledge = 0;
+static volatile unsigned char resend = 0;
+
+static inline void kb_wait(void)
+{
+	unsigned long start = jiffies;
+
+	do {
+		if(!(inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
+			return;
+	} while (jiffies - start < KBC_TIMEOUT);
+}
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ * The keycodes 1-88,96-111,119 are fairly standard, and
+ * should probably not be changed - changing might confuse X.
+ * X also interprets scancode 0x5d (KEY_Begin).
+ *
+ * For 1-88 keycode equals scancode.
+ */
+
+#define E0_KPENTER 96
+#define E0_RCTRL   97
+#define E0_KPSLASH 98
+#define E0_PRSCR   99
+#define E0_RALT    100
+#define E0_BREAK   101  /* (control-pause) */
+#define E0_HOME    102
+#define E0_UP      103
+#define E0_PGUP    104
+#define E0_LEFT    105
+#define E0_RIGHT   106
+#define E0_END     107
+#define E0_DOWN    108
+#define E0_PGDN    109
+#define E0_INS     110
+#define E0_DEL     111
+
+#define E1_PAUSE   119
+
+/*
+ * The keycodes below are randomly located in 89-95,112-118,120-127.
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85           /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86     124
+/* tfj@olivia.ping.dk:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+  RGN1, RGN2, RGN3, RGN4, 0, 0, 0,                   /* 0x59-0x5f */
+  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */
+  0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,          /* 0x68-0x6f */
+  0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3,    /* 0x70-0x77 */
+  FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,        /* 0x78-0x7b */
+  FOCUS_PF8, JAP_86, FOCUS_PF10, 0                   /* 0x7c-0x7f */
+};
+
+/* BTC */
+#define E0_MACRO   112
+/* LK450 */
+#define E0_F13     113
+#define E0_F14     114
+#define E0_HELP    115
+#define E0_DO      116
+#define E0_F17     117
+#define E0_KPMINPLUS 118
+/*
+ * My OmniKey generates e0 4c for  the "OMNI" key and the
+ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
+ */
+#define E0_OK	124
+/*
+ * New microsoft keyboard is rumoured to have
+ * e0 5b (left window button), e0 5c (right window button),
+ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW	125
+#define E0_MSRW	126
+#define E0_MSTM	127
+
+static unsigned char e0_keys[128] = {
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x00-0x07 */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x08-0x0f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x10-0x17 */
+  0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,	      /* 0x18-0x1f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x20-0x27 */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x28-0x2f */
+  0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,	      /* 0x30-0x37 */
+  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,	      /* 0x38-0x3f */
+  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,	      /* 0x40-0x47 */
+  E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
+  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,	      /* 0x50-0x57 */
+  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,	      /* 0x58-0x5f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x60-0x67 */
+  0, 0, 0, 0, 0, 0, 0, E0_MACRO,		      /* 0x68-0x6f */
+  0, 0, 0, 0, 0, 0, 0, 0,			      /* 0x70-0x77 */
+  0, 0, 0, 0, 0, 0, 0, 0			      /* 0x78-0x7f */
+};
+
+static unsigned int prev_scancode = 0;
+
+int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+	if(scancode < SC_LIM || scancode > 255 || keycode > 127)
+		return -EINVAL;
+	if(scancode < 128)
+		high_keys[scancode - SC_LIM] = keycode;
+	else
+		e0_keys[scancode - 128] = keycode;
+	return 0;
+}
+
+int pcikbd_getkeycode(unsigned int scancode)
+{
+	return
+		(scancode < SC_LIM || scancode > 255) ? -EINVAL :
+		(scancode < 128) ? high_keys[scancode - SC_LIM] :
+		e0_keys[scancode - 128];
+}
+
+int do_acknowledge(unsigned char scancode)
+{
+	if(reply_expected) {
+		if(scancode == KBD_REPLY_ACK) {
+			acknowledge = 1;
+			reply_expected = 0;
+			return 0;
+		} else if(scancode == KBD_REPLY_RESEND) {
+			resend = 1;
+			reply_expected = 0;
+			return 0;
+		}
+	}
+	if(scancode == 0) {
+		prev_scancode = 0;
+		return 0;
+	}
+	return 1;
+}
+
+int pcikbd_pretranslate(unsigned char scancode, char raw_mode)
+{
+	if(scancode == 0xff) {
+		prev_scancode = 0;
+		return 0;
+	}
+	if(scancode == 0xe0 || scancode == 0xe1) {
+		prev_scancode = scancode;
+		return 0;
+	}
+	return 1;
+}
+
+int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
+		     char raw_mode)
+{
+	if(prev_scancode) {
+		if(prev_scancode != 0xe0) {
+			if(prev_scancode == 0xe1 && scancode == 0x1d) {
+				prev_scancode = 0x100;
+				return 0;
+			} else if(prev_scancode == 0x100 && scancode == 0x45) {
+				*keycode = E1_PAUSE;
+				prev_scancode = 0;
+			} else {
+				prev_scancode = 0;
+				return 0;
+			}
+		} else {
+			prev_scancode = 0;
+			if(scancode == 0x2a || scancode == 0x36)
+				return 0;
+			if(e0_keys[scancode])
+				*keycode = e0_keys[scancode];
+			else
+				return 0;
+		}
+	} else if(scancode >= SC_LIM) {
+		*keycode = high_keys[scancode - SC_LIM];
+		if(!*keycode)
+			return 0;
+
+	} else
+		*keycode = scancode;
+	return 1;
+}
+
+char pcikbd_unexpected_up(unsigned char keycode)
+{
+	if(keycode >= SC_LIM || keycode == 85)
+		return 0;
+	else
+		return 0200;
+}
+
+static void
+pcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	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);
+	do {
+		unsigned char scancode;
+
+		if(status & kbd_read_mask & KBD_STAT_MOUSE_OBF)
+			break;
+		scancode = inb(pcikbd_iobase + KBD_DATA_REG);
+		if((status & KBD_STAT_OBF) && do_acknowledge(scancode))
+			/* handle_scancode(scancode) */;
+		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+	} while(status & KBD_STAT_OBF);
+	mark_bh(KEYBOARD_BH);
+}
+
+static int send_data(unsigned char data)
+{
+	int retries = 3;
+	unsigned long start;
+
+	do {
+		kb_wait();
+		acknowledge = resend = 0;
+		reply_expected = 1;
+		outb(data, pcikbd_iobase + KBD_DATA_REG);
+		start = jiffies;
+		do {
+			if(acknowledge)
+				return 1;
+			if(jiffies - start >= KBD_TIMEOUT)
+				return 0;
+		} while(!resend);
+	} while(retries-- > 0);
+	return 0;
+}
+
+void pcikbd_leds(unsigned char leds)
+{
+	if(!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))
+		send_data(KBD_CMD_ENABLE);
+		
+}
+
+__initfunc(static int pcikbd_wait_for_input(void))
+{
+	int status, data;
+	unsigned long start = jiffies;
+
+	do {
+		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+		if(!(status & KBD_STAT_OBF))
+			continue;
+		data = inb(pcikbd_iobase + KBD_DATA_REG);
+		if(status & (KBD_STAT_GTO | KBD_STAT_PERR))
+			continue;
+		return (data & 0xff);
+	} while(jiffies - start < KBD_INIT_TIMEOUT);
+	return -1;
+}
+
+__initfunc(static void pcikbd_write(int address, int data))
+{
+	int status;
+
+	do {
+		status = inb(pcikbd_iobase + KBD_STATUS_REG);
+	} while (status & KBD_STAT_IBF);
+	outb(data, pcikbd_iobase + address);
+}
+
+__initfunc(static char *do_pcikbd_hwinit(void))
+{
+	while(pcikbd_wait_for_input() != -1)
+		;
+
+	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST);
+	if(pcikbd_wait_for_input() != 0xff)
+		return "Keyboard failed self test";
+
+	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST);
+	if(pcikbd_wait_for_input() != 0x00)
+		return "Keyboard interface failed self test";
+
+	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_ENABLE);
+	pcikbd_write(KBD_DATA_REG, KBD_CMD_RESET);
+	if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+		return "Keyboard reset failed, no ACK";
+	if(pcikbd_wait_for_input() != KBD_REPLY_POR)
+		return "Keyboard reset failed, no ACK";
+
+	pcikbd_write(KBD_DATA_REG, KBD_CMD_DISABLE);
+	if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+		return "Disable keyboard: no ACK";
+
+	pcikbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
+	pcikbd_write(KBD_DATA_REG,
+		     (KBD_MODE_KBD_INT | KBD_MODE_SYS |
+		      KBD_MODE_DISABLE_MOUSE | KBD_MODE_KCC));
+	pcikbd_write(KBD_DATA_REG, KBD_CMD_ENABLE);
+	if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+		return "Enable keyboard: no ACK";
+
+	pcikbd_write(KBD_DATA_REG, KBD_CMD_SET_RATE);
+	if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+		return "Set rate: no ACK";
+	pcikbd_write(KBD_DATA_REG, 0x00);
+	if(pcikbd_wait_for_input() != KBD_REPLY_ACK)
+		return "Set rate: no ACK";
+
+	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))
+{
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev;
+	int node, index, irq;
+
+	for_all_ebusdev(edev, ebus) {
+		if(!strcmp(edev->prom_name, "8042")) {
+			node = prom_getchild(edev->prom_node);
+			node = prom_searchsiblings(node, "kb_ps2");
+			if (node == kbd_node)
+				goto found;
+		}
+	}
+	printk("pcikbd_init: no 8042 found\n");
+	return -ENODEV;
+
+found:
+	if (prom_getproperty(node, "reg", (char *)&index, sizeof(index)) !=
+							sizeof(index)) {
+		printk("8042: can't get property '%s' from '%s'\n",
+		       "reg", "kb_ps2");
+		return -ENODEV;
+	}
+
+	if (prom_getproperty(node, "interrupts", (char *)&irq, sizeof(irq)) !=
+							sizeof(irq)) {
+		printk("8042: can't get property '%s' from '%s'\n",
+		       "interrupts", "kb_ps2");
+		return -ENODEV;
+	}
+
+	pcikbd_iobase = edev->base_address[index];
+	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;
+	}
+
+	request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
+	printk("8042(kbd): iobase[%016lx] irq[%x]\n", pcikbd_iobase, irq);
+	pcikbd_irq = irq;
+
+	if (request_irq(pcikbd_irq, &pcikbd_interrupt,
+			SA_SHIRQ, "keyboard", NULL)) {
+		printk("8042: cannot register IRQ %x\n", pcikbd_irq);
+		return -ENODEV;
+	}
+
+	/* pcikbd_init(); */
+	kbd_read_mask = KBD_STAT_OBF;
+	return 0;
+}
+
+
+
+/*
+ * Here begins the Mouse Driver.
+ */
+
+static int ms_node;
+
+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 {
+	unsigned long head;
+	unsigned long tail;
+	struct wait_queue *proc_list;
+	struct fasync_struct *fasync;
+	unsigned char buf[AUX_BUF_SIZE];
+};
+
+static struct aux_queue *queue;
+static int aux_ready = 0;
+static int aux_count = 0;
+static int aux_present = 0;
+
+/*
+ *	Shared subroutines
+ */
+
+static unsigned int get_from_queue(void)
+{
+	unsigned int result;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	result = queue->buf[queue->tail];
+	queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
+	restore_flags(flags);
+	return result;
+}
+
+
+static inline int queue_empty(void)
+{
+	return queue->head == queue->tail;
+}
+
+static int fasync_aux(struct inode *inode, struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(inode, filp, on, &queue->fasync);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+/*
+ *	PS/2 Aux Device
+ */
+
+#define AUX_INTS_OFF	(KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | \
+			 KBD_MODE_SYS | KBD_MODE_KBD_INT)
+
+#define AUX_INTS_ON	(KBD_MODE_KCC | KBD_MODE_SYS | \
+			 KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
+
+#define MAX_RETRIES	60		/* some aux operations take long time*/
+
+/*
+ *	Status polling
+ */
+
+static int poll_aux_status(void)
+{
+	int retries=0;
+
+	while ((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)
+		    == AUX_STAT_OBF)
+			inb(pcimouse_iobase + KBD_DATA_REG);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (5*HZ + 99) / 100;
+		schedule();
+		retries++;
+	}
+	return (retries < MAX_RETRIES);
+}
+
+/*
+ * Write to aux device
+ */
+
+static void aux_write_dev(int val)
+{
+	poll_aux_status();
+	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 */
+}
+
+/*
+ * Write to device & handle returned ack
+ */
+
+__initfunc(static int aux_write_ack(int val))
+{
+	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));
+	return 0;
+}
+
+/*
+ * Write aux device command
+ */
+
+static void aux_write_cmd(int val)
+{
+	poll_aux_status();
+	outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+	poll_aux_status();
+	outb(val, pcimouse_iobase + KBD_DATA_REG);
+}
+
+/*
+ * AUX handler critical section start and end.
+ * 
+ * Only one process can be in the critical section and all keyboard sends are
+ * deferred as long as we're inside. This is necessary as we may sleep when
+ * waiting for the keyboard controller and other processes / BH's can
+ * preempt us. Please note that the input buffer must be flushed when
+ * aux_end_atomic() is called and the interrupt is no longer enabled as not
+ * doing so might cause the keyboard driver to ignore all incoming keystrokes.
+ */
+
+static struct semaphore aux_sema4 = MUTEX;
+
+static inline void aux_start_atomic(void)
+{
+	down(&aux_sema4);
+	disable_bh(KEYBOARD_BH);
+}
+
+static inline void aux_end_atomic(void)
+{
+	enable_bh(KEYBOARD_BH);
+	up(&aux_sema4);
+}
+
+/*
+ * Interrupt from the auxiliary device: a character
+ * is waiting in the keyboard/aux controller.
+ */
+
+void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	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)
+		return;
+
+	add_mouse_randomness(queue->buf[head] = inb(pcimouse_iobase + KBD_DATA_REG));
+	if (head != maxhead) {
+		head++;
+		head &= AUX_BUF_SIZE-1;
+	}
+	queue->head = head;
+	aux_ready = 1;
+	if (queue->fasync)
+		kill_fasync(queue->fasync, SIGIO);
+	wake_up_interruptible(&queue->proc_list);
+}
+
+static int release_aux(struct inode * inode, struct file * file)
+{
+	fasync_aux(inode, file, 0);
+	if (--aux_count)
+		return 0;
+	aux_start_atomic();
+
+	/* Disable controller ints */
+	aux_write_cmd(AUX_INTS_OFF);
+	poll_aux_status();
+
+	/* Disable Aux device */
+	outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
+	poll_aux_status();
+	aux_end_atomic();
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable auxiliary device.
+ */
+
+static int open_aux(struct inode * inode, struct file * file)
+{
+	if (!aux_present)
+		return -ENODEV;
+	aux_start_atomic();
+	if (aux_count++) {
+		aux_end_atomic();
+		return 0;
+	}
+	if (!poll_aux_status()) {		/* FIXME: Race condition */
+		aux_count--;
+		aux_end_atomic();
+		return -EBUSY;
+	}
+	queue->head = queue->tail = 0;		/* Flush input queue */
+
+	MOD_INC_USE_COUNT;
+
+	poll_aux_status();
+	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();
+	aux_end_atomic();
+
+	aux_ready = 0;
+	return 0;
+}
+
+/*
+ * Write to the aux device.
+ */
+
+static long write_aux(struct inode * inode, struct file * file,
+	const char * buffer, unsigned long count)
+{
+	int retval = 0;
+
+	if (count) {
+		int written = 0;
+
+		aux_start_atomic();
+		do {
+			char c;
+			if (!poll_aux_status())
+				break;
+			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);
+			written++;
+		} while (--count);
+		aux_end_atomic();
+		retval = -EIO;
+		if (written) {
+			retval = written;
+			inode->i_mtime = CURRENT_TIME;
+		}
+	}
+
+	return retval;
+}
+
+/*
+ *	Generic part continues...
+ */
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static long read_aux(struct inode * inode, struct file * file,
+	char * buffer, unsigned long count)
+{
+	struct wait_queue wait = { current, NULL };
+	int i = count;
+	unsigned char c;
+
+	if (queue_empty()) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&queue->proc_list, &wait);
+repeat:
+		current->state = TASK_INTERRUPTIBLE;
+		if (queue_empty() && !(current->signal & ~current->blocked)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&queue->proc_list, &wait);
+	}
+	while (i > 0 && !queue_empty()) {
+		c = get_from_queue();
+		put_user(c, buffer++);
+		i--;
+	}
+	aux_ready = !queue_empty();
+	if (count-i) {
+		inode->i_atime = CURRENT_TIME;
+		return count-i;
+	}
+	if (current->signal & ~current->blocked)
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int aux_poll(struct file *file, poll_table * wait)
+{
+	poll_wait(&queue->proc_list, wait);
+	if (aux_ready)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+struct file_operations psaux_fops = {
+	NULL,		/* seek */
+	read_aux,
+	write_aux,
+	NULL, 		/* readdir */
+	aux_poll,
+	NULL, 		/* ioctl */
+	NULL,		/* mmap */
+	open_aux,
+	release_aux,
+	NULL,
+	fasync_aux,
+};
+
+static struct miscdevice psaux_mouse = {
+	PSMOUSE_MINOR, "ps2aux", &psaux_fops
+};
+
+__initfunc(int pcimouse_init(void))
+{
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev;
+	int node, index, irq;
+
+	for_all_ebusdev(edev, ebus) {
+		if(!strcmp(edev->prom_name, "8042")) {
+			node = prom_getchild(edev->prom_node);
+			node = prom_searchsiblings(node, "kdmouse");
+			if (node == ms_node)
+				goto found;
+		}
+	}
+	printk("pcimouse_init: no 8042 found\n");
+	return -ENODEV;
+
+found:
+	if (prom_getproperty(node, "reg", (char *)&index, sizeof(index)) !=
+							sizeof(index)) {
+		printk("8042: can't get property '%s' from '%s'\n",
+		       "reg", "kdmouse");
+		return -ENODEV;
+	}
+
+	if (prom_getproperty(node, "interrupts", (char *)&irq, sizeof(irq)) !=
+							sizeof(irq)) {
+		printk("8042: can't get property '%s' from '%s'\n",
+		       "interrupts", "kdmouse");
+		return -ENODEV;
+	}
+
+	pcimouse_iobase = edev->base_address[index];
+
+	/*
+	 * Just in case the iobases for kbd/mouse ever differ...
+	 */
+	if (!check_region(pcimouse_iobase, sizeof(unsigned long)))
+		request_region(pcimouse_iobase, sizeof(unsigned long),
+			       "8042 controller");
+
+	printk("8042(mouse): iobase[%016lx] irq[%x]\n", pcimouse_iobase, irq);
+	pcimouse_irq = irq;
+
+	if (request_irq(pcimouse_irq, &pcimouse_interrupt,
+		        SA_SHIRQ, "mouse", NULL)) {
+		printk("8042: Cannot register IRQ %x\n", pcimouse_irq);
+		return -ENODEV;
+	}
+
+	printk("8042: PS/2 auxiliary pointing device detected.\n");
+	aux_present = 1;
+	kbd_read_mask = AUX_STAT_OBF;
+
+	misc_register(&psaux_mouse);
+	queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+	memset(queue, 0, sizeof(*queue));
+	queue->head = queue->tail = 0;
+	queue->proc_list = NULL;
+	aux_start_atomic();
+	outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
+	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);
+	poll_aux_status();
+	outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG);
+	poll_aux_status();
+	outb(AUX_INTS_OFF, KBD_DATA_REG);
+	poll_aux_status();
+	aux_end_atomic();
+
+	return 0;
+}
+
+
+__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;
+	int kbnode = 0, msnode = 0, bnode = 0;
+	int devices = 0;
+	char prop[128];
+	int len;
+
+	/*
+	 * Get the nodes for keyboard and mouse from 'aliases'...
+	 */
+        node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "aliases");
+	if (!node)
+		return -ENODEV;
+
+	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+	if (len > 0)
+		kbnode = prom_pathtoinode(prop);
+	if (!kbnode)
+		return -ENODEV;
+
+	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+	if (len > 0)
+		msnode = prom_pathtoinode(prop);
+	if (!msnode)
+		return -ENODEV;
+
+	/*
+	 * Find matching EBus nodes...
+	 */
+        node = prom_getchild(prom_root_node);
+	pnode = prom_searchsiblings(node, "pci");
+
+	/*
+	 * For each PCI bus...
+	 */
+	while (pnode) {
+		enode = prom_getchild(pnode);
+		enode = prom_searchsiblings(enode, "ebus");
+
+		/*
+		 * For each EBus on this PCI...
+		 */
+		while (enode) {
+			node = prom_getchild(enode);
+			bnode = prom_searchsiblings(node, "beeper");
+
+			node = prom_getchild(enode);
+			node = prom_searchsiblings(node, "8042");
+
+			/*
+			 * For each '8042' on this EBus...
+			 */
+			while (node) {
+				/*
+				 * Does it match?
+				 */
+				dnode = prom_getchild(node);
+				dnode = prom_searchsiblings(dnode, "kb_ps2");
+				if (dnode == kbnode) {
+					kbd_node = kbnode;
+					beep_node = bnode;
+					++devices;
+				}
+
+				dnode = prom_getchild(node);
+				dnode = prom_searchsiblings(dnode, "kdmouse");
+				if (dnode == msnode) {
+					ms_node = msnode;
+					++devices;
+				}
+
+				/*
+				 * Found everything we need?
+				 */
+				if (devices == 2)
+					goto found;
+
+				node = prom_getsibling(node);
+				node = prom_searchsiblings(node, "8042");
+			}
+			enode = prom_getsibling(enode);
+			enode = prom_searchsiblings(enode, "ebus");
+		}
+		pnode = prom_getsibling(pnode);
+		pnode = prom_searchsiblings(pnode, "pci");
+	}
+	return -ENODEV;
+
+found:
+        sunserial_setinitfunc(memory_start, ps2_init);
+	return 0;
+}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/pcikbd.h linux/drivers/sbus/char/pcikbd.h
--- v2.1.52/linux/drivers/sbus/char/pcikbd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/pcikbd.h	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,123 @@
+/* $Id: pcikbd.h,v 1.1 1997/08/24 02:53:25 davem Exp $
+ * pcikbd.h: PCI/PC 8042 keyboard/mouse driver stuff.  Mostly snarfed
+ *           from the existing driver by Martin Mares.
+ *
+ * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Ultra/AX specific hacks are:
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __PCIKBD_H
+#define __PCIKBD_H
+
+/*
+ *	Configuration Switches
+ */
+
+#define KBD_REPORT_ERR			/* Report keyboard errors */
+#define KBD_REPORT_UNKN			/* Report unknown scan codes */
+#define KBD_REPORT_TIMEOUTS		/* Report keyboard timeouts */
+
+#define KBD_INIT_TIMEOUT HZ		/* Timeout for initializing the keyboard */
+#define KBC_TIMEOUT (HZ/4)		/* Timeout for sending to keyboard controller */
+#define KBD_TIMEOUT (HZ/4)		/* Timeout for keyboard command acknowledge */
+
+/*
+ *	Internal variables of the driver
+ */
+
+extern unsigned char kbd_read_mask;
+extern unsigned char aux_device_present;
+
+extern unsigned long pcikbd_iobase;
+extern unsigned int pcikbd_irq;
+
+/*
+ *	Keyboard Controller Registers
+ *
+ * NOTE: These are offsets from pcikbd_iobase, not absolute.
+ */
+
+#define KBD_STATUS_REG		0x04
+#define KBD_CNTL_REG		KBD_STATUS_REG
+#define KBD_DATA_REG		0x00
+
+/*
+ *	Keyboard Controller Commands
+ */
+
+#define KBD_CCMD_READ_MODE	0x20	/* Read mode bits */
+#define KBD_CCMD_WRITE_MODE	0x60	/* Write mode bits */
+#define KBD_CCMD_GET_VERSION	0xA1	/* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE	0xA7	/* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE	0xA8	/* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE	0xA9	/* Mouse interface test */
+#define KBD_CCMD_SELF_TEST	0xAA	/* Controller self test */
+#define KBD_CCMD_KBD_TEST	0xAB	/* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE	0xAD	/* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE	0xAE	/* Keyboard interface enable */
+#define KBD_CCMD_WRITE_MOUSE	0xD4	/* Write the following byte to the mouse */
+
+/*
+ *	Keyboard Commands
+ */
+
+#define KBD_CMD_SET_LEDS	0xED	/* Set keyboard leds */
+#define KBD_CMD_SET_RATE	0xF3	/* Set typematic rate */
+#define KBD_CMD_ENABLE		0xF4	/* Enable scanning */
+#define KBD_CMD_DISABLE		0xF5	/* Disable scanning */
+#define KBD_CMD_RESET		0xFF	/* Reset */
+
+/*
+ *	Keyboard Replies
+ */
+
+#define KBD_REPLY_POR		0xAA	/* Power on reset */
+#define KBD_REPLY_ACK		0xFA	/* Command ACK */
+#define KBD_REPLY_RESEND	0xFE	/* Command NACK, send the cmd again */
+
+/*
+ *	Status Register Bits
+ */
+
+#define KBD_STAT_OBF 		0x01	/* Keyboard output buffer full */
+#define KBD_STAT_IBF 		0x02	/* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST	0x04	/* Self test successful */
+#define KBD_STAT_CMD		0x08	/* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED	0x10	/* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF	0x20	/* Mouse output buffer full */
+#define KBD_STAT_GTO 		0x40	/* General receive/xmit timeout */
+#define KBD_STAT_PERR 		0x80	/* Parity error */
+
+#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF)
+
+/*
+ *	Controller Mode Register Bits
+ */
+
+#define KBD_MODE_KBD_INT	0x01	/* Keyboard data generage IRQ1 */
+#define KBD_MODE_MOUSE_INT	0x02	/* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 		0x04	/* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK	0x08	/* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD	0x10	/* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE	0x20	/* Disable mouse interface */
+#define KBD_MODE_KCC 		0x40	/* Scan code conversion to PC format */
+#define KBD_MODE_RFU		0x80
+
+/*
+ *	Mouse Commands
+ */
+
+#define AUX_SET_RES		0xE8	/* Set resolution */
+#define AUX_SET_SCALE11		0xE6	/* Set 1:1 scaling */
+#define AUX_SET_SCALE21		0xE7	/* Set 2:1 scaling */
+#define AUX_GET_SCALE		0xE9	/* Get scaling factor */
+#define AUX_SET_STREAM		0xEA	/* Set stream mode */
+#define AUX_SET_SAMPLE		0xF3	/* Set sample rate */
+#define AUX_ENABLE_DEV		0xF4	/* Enable aux device */
+#define AUX_DISABLE_DEV		0xF5	/* Disable aux device */
+#define AUX_RESET		0xFF	/* Reset aux device */
+
+#endif /* __PCIKBD_H */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
--- v2.1.52/linux/drivers/sbus/char/sab82532.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/sab82532.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,2173 @@
+/* $Id: sab82532.c,v 1.4 1997/09/03 17:04:21 ecd Exp $
+ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/sab82532.h>
+#include <asm/uaccess.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int sab82532_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
+
+static void change_speed(struct sab82532 *info);
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * This assumes you have a 29.4912 MHz clock for your UART.
+ */
+#define BASE_BAUD ( 29491200 / 16 )
+
+static struct sab82532 *sab82532_chain = 0;
+static struct tty_struct *sab82532_table[NR_PORTS];
+static struct termios *sab82532_termios[NR_PORTS];
+static struct termios *sab82532_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * 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,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sab82532 *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null sab82532 for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds.
+ *
+ * The formula is:    Baud = BASE_BAUD / ((N + 1) * (1 << M)),
+ *
+ * with               0 <= N < 64 and 0 <= M < 16
+ *
+ * XXX:	Speeds with M = 0 might not work properly for XTAL frequencies
+ *      above 10 MHz.
+ */
+struct ebrg_struct {
+	int	baud;
+	int	n;
+	int	m;
+};
+
+static struct ebrg_struct ebrg_table[] = {
+	{       0,	0,	 0 },
+	{      50,	35,	10 },
+	{      75,	47,	 9 },
+	{     110,	32,	 9 },
+	{     134,	53,	 8 },
+	{     150,	47,	 8 },
+	{     200,	35,	 8 },
+	{     300,	47,	 7 },
+	{     600,	47,	 6 },
+	{    1200,	47,	 5 },
+	{    1800,	31,	 5 },
+	{    2400,	47,	 4 },
+	{    4800,	47,	 3 },
+	{    9600,	47,	 2 },
+	{   19200,	47,	 1 },
+	{   38400,	23,	 1 },
+	{   57600,	15,	 1 },
+	{  115200,	 7,	 1 },
+	{  230400,	 3,	 1 },
+	{  460800,	 1,	 1 },
+	{   76800,	11,	 1 },
+	{  153600,	 5,	 1 },
+	{  307200,	 3,	 1 },
+	{  614400,	 3,	 0 },
+	{  921600,	 0,	 1 },
+};
+
+#define NR_EBRG_VALUES	(sizeof(ebrg_table)/sizeof(struct ebrg_struct))
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_stop() and sab82532_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void sab82532_stop(struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
+		return;
+
+	save_flags(flags); cli();
+	info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+	info->regs->w.imr1 = info->interrupt_mask1;
+	restore_flags(flags);
+}
+
+static void sab82532_start(struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "sab82532_start"))
+		return;
+
+	save_flags(flags); cli();
+	info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+	info->regs->w.imr1 = info->interrupt_mask1;
+	restore_flags(flags);
+}
+
+static void batten_down_hatches(void)
+{
+	/* 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
+	 * first before giving up control.
+	 */
+	printk("\n");
+	flush_user_windows();
+#ifndef __sparc_v9__
+	if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) &&
+	    (((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR))
+		sp_enter_debugger();
+	else
+#endif
+		prom_cmdline();
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * sab82532_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: sab82532_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * sab82532_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static inline void sab82532_sched_event(struct sab82532 *info, int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static inline void receive_chars(struct sab82532 *info,
+				 union sab82532_irq_status *stat)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char buf[32];
+	unsigned char status;
+	int free_fifo = 0;
+	int i, count = 0;
+
+	/* Read number of BYTES (Character + Status) available. */
+	if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
+		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) {
+		if (info->regs->r.star & SAB82532_STAR_CEC)
+			udelay(1);
+		info->regs->w.cmdr = SAB82532_CMDR_RFRD;
+	}
+
+	/* Read the FIFO. */
+	for (i = 0; i < (count << 1); i++)
+		buf[i] = info->regs->r.rfifo[i];
+
+	/* Issue Receive Message Complete command. */
+	if (free_fifo) {
+		if (info->regs->r.star & SAB82532_STAR_CEC)
+			udelay(1);
+		info->regs->w.cmdr = SAB82532_CMDR_RMC;
+	}
+
+	for (i = 0; i < count; ) {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+#if 1
+			printk("sab82532: receive_chars: tty overrun\n");
+#endif
+			info->icount.buf_overrun++;
+			break;
+		}
+
+		tty->flip.count++;
+		*tty->flip.char_buf_ptr++ = buf[i++];
+		status = buf[i++];
+		info->icount.rx++;
+
+#ifdef SERIAL_DEBUG_INTR
+                printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
+#endif
+
+		if (status & SAB82532_RSTAT_PE) {
+			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+			info->icount.parity++;
+		} else if (status & SAB82532_RSTAT_FE) {
+			*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+			info->icount.frame++;
+		}
+#ifdef CMSPAR
+		else if (status & SAB82532_RSTAT_PARITY)
+			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+#endif
+		else
+			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+	}
+
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static inline void transmit_chars(struct sab82532 *info,
+				  union sab82532_irq_status *stat)
+{
+	int i;
+
+	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+	    info->tty->hw_stopped) {
+		if (stat->sreg.isr1 & SAB82532_ISR1_ALLS)
+			info->all_sent = 1;
+		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+		info->regs->w.imr1 = info->interrupt_mask1;
+		return;
+	}
+
+	/* Stuff 32 bytes into Transmit FIFO. */
+	info->all_sent = 0;
+	for (i = 0; i < info->xmit_fifo_size; i++) {
+		info->regs->w.xfifo[i] = info->xmit_buf[info->xmit_tail++];
+		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+		info->icount.tx++;
+		if (--info->xmit_cnt <= 0)
+			break;
+	}
+
+	/* Issue a Transmit Frame command. */
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_XF;
+
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (info->xmit_cnt <= 0) {
+		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
+		info->regs->w.imr1 = info->interrupt_mask1;
+	}
+}
+
+static inline void check_status(struct sab82532 *info,
+				union sab82532_irq_status *stat)
+{
+	struct tty_struct *tty = info->tty;
+	int modem_change = 0;
+
+	if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+		if (info->is_console) {
+			batten_down_hatches();
+			return;
+		}
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			info->icount.buf_overrun++;
+			goto check_modem;
+		}
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.brk++;
+	}
+
+	if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			info->icount.buf_overrun++;
+			goto check_modem;
+		}
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		*tty->flip.char_buf_ptr++ = 0;
+		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;
+		info->icount.dcd++;
+		modem_change++;
+#if 0
+		printk("DCD change: %d\n", info->icount.dcd);
+#endif
+	}
+	if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
+		info->cts = info->regs->r.star & SAB82532_STAR_CTS;
+		info->icount.cts++;
+		modem_change++;
+#if 0
+		printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
+#endif
+	}
+	if ((info->regs->r.pvr & info->pvr_dsr_bit) ^ info->dsr) {
+		info->dsr = info->regs->r.pvr & info->pvr_dsr_bit;
+		info->icount.dsr++;
+		modem_change++;
+#if 0
+		printk("DSR change: %d\n", info->icount.dsr);
+#endif
+	}
+	if (modem_change)
+		wake_up_interruptible(&info->delta_msr_wait);
+
+	if ((info->flags & ASYNC_CHECK_CD) &&
+	    (stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
+
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (info->dcd) ? "on" : "off");
+#endif		
+
+		if (info->dcd)
+			wake_up_interruptible(&info->open_wait);
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+
+			queue_task(&info->tqueue_hangup, &tq_scheduler);
+		}
+	}
+
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (info->cts) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				sab82532_sched_event(info,
+						     RS_EVENT_WRITE_WAKEUP);
+				info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+				info->regs->w.imr1 = info->interrupt_mask1;
+			}
+		} else {
+			if (!(info->cts)) {
+
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx stop...");
+#endif
+				info->tty->hw_stopped = 1;
+			}
+		}
+	}
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct sab82532 *info = dev_id;
+	union sab82532_irq_status status;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("sab82532_interrupt(%d)...", irq);
+#endif
+
+	status.stat = 0;
+	if (info->regs->r.gis & SAB82532_GIS_ISA0)
+		status.sreg.isr0 = info->regs->r.isr0;
+	if (info->regs->r.gis & SAB82532_GIS_ISA1)
+		status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("%d<%02x.%02x>", info->line,
+	       status.sreg.isr0, status.sreg.isr1);
+#endif
+
+	if (!status.stat)
+		goto next;
+
+	if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+				SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+		receive_chars(info, &status);
+	if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+	    (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+		check_status(info, &status);
+	if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+		transmit_chars(info, &status);
+
+next:
+	info = info->next;
+	status.stat = 0;
+	if (info->regs->r.gis & SAB82532_GIS_ISB0)
+		status.sreg.isr0 = info->regs->r.isr0;
+	if (info->regs->r.gis & SAB82532_GIS_ISB1)
+		status.sreg.isr1 = info->regs->r.isr1;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("%d<%02x.%02x>", info->line,
+	       status.sreg.isr0, status.sreg.isr1);
+#endif
+
+	if (!status.stat)
+		goto done;
+
+	if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+				SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+		receive_chars(info, &status);
+	if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+	    (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+		check_status(info, &status);
+	if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+		transmit_chars(info, &status);
+
+done:
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using sab82532_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct sab82532	*info = (struct sab82532 *)private_;
+	struct tty_struct *tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * 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() -> sab82532_hangup()
+ * 
+ */
+static void do_serial_hangup(void *private_)
+{
+	struct sab82532	*info = (struct sab82532 *) private_;
+	struct tty_struct *tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+static int startup(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
+
+	/*
+	 * Clear the FIFO buffers.
+	 */
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_RRES;
+	if (info->regs->r.star & SAB82532_STAR_CEC)
+		udelay(1);
+	info->regs->w.cmdr = SAB82532_CMDR_XRES;
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	stat = info->regs->r.isr0;
+	stat = info->regs->r.isr1;
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	info->regs->w.ccr0 = 0;				/* power-down */
+	info->regs->w.ccr0 = SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
+			     SAB82532_CCR0_SM_ASYNC;
+	info->regs->w.ccr1 = SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7;
+	info->regs->w.ccr2 = SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
+			     SAB82532_CCR2_TOE;
+	info->regs->w.ccr3 = 0;
+	info->regs->w.ccr4 = SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG;
+	info->regs->w.mode = SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
+			     SAB82532_MODE_RAC;
+	info->regs->w.rfc = SAB82532_RFC_DPS | SAB82532_RFC_RFDF;
+	switch (info->recv_fifo_size) {
+		case 1:
+			info->regs->w.rfc |= SAB82532_RFC_RFTH_1;
+			break;
+		case 4:
+			info->regs->w.rfc |= SAB82532_RFC_RFTH_4;
+			break;
+		case 16:
+			info->regs->w.rfc |= SAB82532_RFC_RFTH_16;
+			break;
+		default:
+			info->recv_fifo_size = 32;
+			/* fall through */
+		case 32:
+			info->regs->w.rfc |= SAB82532_RFC_RFTH_32;
+			break;
+	}
+	info->regs->rw.ccr0 |= SAB82532_CCR0_PU;	/* power-up */
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+				SAB82532_IMR0_PLLA;
+	info->regs->w.imr0 = info->interrupt_mask0;
+	info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
+				SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
+				SAB82532_IMR1_XPR;
+	info->regs->w.imr1 = info->interrupt_mask1;
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+	
+errout:
+	restore_flags(flags);
+	return -ENODEV;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct sab82532 *info)
+{
+	unsigned long flags;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d...", info->line);
+#endif
+
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+	wake_up_interruptible(&info->delta_msr_wait);
+
+	if (info->xmit_buf) {
+		free_page((unsigned long)info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	/* Disable Interrupts */
+	info->interrupt_mask0 = 0xff;
+	info->regs->w.imr0 = info->interrupt_mask0;
+	info->interrupt_mask1 = 0xff;
+	info->regs->w.imr1 = info->interrupt_mask1;
+
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->regs->rw.mode |= SAB82532_MODE_FRTS;
+		info->regs->rw.mode |= SAB82532_MODE_RTS;
+		info->regs->rw.pvr |= info->pvr_dtr_bit;
+	}
+
+	/* Disable break condition */
+	info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+
+	/* Disable Receiver */	
+	info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+
+	/* Power Down */	
+	info->regs->rw.ccr0 &= ~(SAB82532_CCR0_PU);
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sab82532 *info)
+{
+	unsigned long	flags;
+	unsigned int	ebrg;
+	tcflag_t	cflag;
+	unsigned char	dafo;
+	int		i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+
+	/* 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;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  dafo = SAB82532_DAFO_CHL5; break;
+	}
+
+	if (cflag & CSTOPB)
+		dafo |= SAB82532_DAFO_STOP;
+
+	if (cflag & PARENB)
+		dafo |= SAB82532_DAFO_PARE;
+
+	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);
+
+	/* 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;
+	if (info->tty)
+		info->tty->hw_stopped = 0;
+
+	/*
+	 * Set up parity check flag
+	 * XXX: not implemented, yet.
+	 */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+	/*
+	 * Characters to ignore
+	 * XXX: not implemented, yet.
+	 */
+
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 * XXX: not implemented, yet.
+	 */
+	if ((cflag & CREAD) == 0)
+		info->ignore_status_mask |= SAB82532_ISR0_RPF |
+					    SAB82532_ISR0_TCD |
+					    SAB82532_ISR0_TIME;
+
+	save_flags(flags); cli();
+	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);
+}
+
+static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_put_char"))
+		return;
+
+	if (!tty || !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= SERIAL_XMIT_SIZE-1;
+	info->xmit_cnt++;
+	restore_flags(flags);
+}
+
+static void sab82532_flush_chars(struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+	info->regs->w.imr1 = info->interrupt_mask1;
+	restore_flags(flags);
+}
+
+static int sab82532_write(struct tty_struct * tty, int from_user,
+			  const unsigned char *buf, int count)
+{
+	int c, ret = 0;
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		return 0;
+	    
+	if (from_user)
+		down(&tmp_buf_sem);
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			c -= copy_from_user(tmp_buf, buf, c);
+			if (!c) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    (info->interrupt_mask1 & SAB82532_IMR1_XPR)) {
+		info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+		info->regs->w.imr1 = info->interrupt_mask1;
+	}
+
+	restore_flags(flags);
+	return ret;
+}
+
+static int sab82532_write_room(struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	int ret;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int sab82532_chars_in_buffer(struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+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();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void sab82532_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
+		return;
+
+	if (info->regs->r.star & SAB82532_STAR_TEC)
+		udelay(1);
+	info->regs->w.tic = ch;
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void sab82532_throttle(struct tty_struct * tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		sab82532_send_xchar(tty, STOP_CHAR(tty));
+#if 0
+	if (tty->termios->c_cflag & CRTSCTS)
+		info->regs->rw.mode |= SAB82532_MODE_RTS;
+#endif
+}
+
+static void sab82532_unthrottle(struct tty_struct * tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			sab82532_send_xchar(tty, START_CHAR(tty));
+	}
+
+#if 0
+	if (tty->termios->c_cflag & CRTSCTS)
+		info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sab82532 *info,
+			   struct serial_struct *retinfo)
+{
+	struct serial_struct tmp;
+   
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = (unsigned long)info->regs;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.xmit_fifo_size = info->xmit_fifo_size;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	tmp.hub6 = 0;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int set_serial_info(struct sab82532 *info,
+			   struct serial_struct *new_info)
+{
+	return 0;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct sab82532 * info, unsigned int *value)
+{
+	unsigned int result;
+
+	result = info->all_sent ? TIOCSER_TEMT : 0;
+	return put_user(result, value);
+}
+
+
+static int get_modem_info(struct sab82532 * info, unsigned int *value)
+{
+	unsigned int result;
+
+	result =  ((info->regs->r.mode & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
+		| ((info->regs->r.pvr & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
+		| ((info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
+		| ((info->regs->r.pvr & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
+		| ((info->regs->r.star & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
+	return put_user(result,value);
+}
+
+static int set_modem_info(struct sab82532 * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+
+	error = get_user(arg, value);
+	if (error)
+		return error;
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS) {
+			info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+			info->regs->rw.mode |= SAB82532_MODE_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			info->regs->rw.mode |= SAB82532_MODE_FRTS;
+			info->regs->rw.mode |= SAB82532_MODE_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->regs->rw.pvr |= info->pvr_dtr_bit;
+		}
+		break;
+	case TIOCMSET:
+		if (arg & TIOCM_RTS) {
+			info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+			info->regs->rw.mode |= SAB82532_MODE_RTS;
+		} else {
+			info->regs->rw.mode |= SAB82532_MODE_FRTS;
+			info->regs->rw.mode |= SAB82532_MODE_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+		} else {
+			info->regs->rw.pvr |= info->pvr_dtr_bit;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(	struct sab82532 * info, int duration)
+{
+	if (!info->regs)
+		return;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("sab82532_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
+	cli();
+	info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+	schedule();
+	info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+	sti();
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("done jiffies=%lu\n", jiffies);
+#endif
+}
+
+/*
+ * This routine sets the break condition on the serial port.
+ */
+static void begin_break(struct sab82532 * info)
+{
+	if (!info->regs)
+		return;
+	info->regs->rw.dafo |= SAB82532_DAFO_XBRK;
+}
+
+/*
+ * This routine clears the break condition on the serial port.
+ */
+static void end_break(struct sab82532 * info)
+{
+	if (!info->regs)
+		return;
+	info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK);
+}
+
+static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct sab82532 * info = (struct sab82532 *)tty->driver_data;
+	int retval;
+	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+
+	if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	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 (current->signal & ~current->blocked)
+				return -EINTR;
+			if (!arg) {
+				send_break(info, HZ/4);	/* 1/4 second */
+				if (current->signal & ~current->blocked)
+					return -EINTR;
+			}
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			return 0;
+		case TIOCSBRK:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			begin_break(info);
+			return 0;
+		case TIOCCBRK:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			end_break(info);
+			return 0;
+		case TIOCGSOFTCAR:
+			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+		case TIOCSSOFTCAR:
+			error = get_user(arg, (unsigned int *) arg);
+			if (error)
+				return error;
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERGSTRUCT:
+			if (copy_to_user((struct sab82532 *) arg,
+					 info, sizeof(struct sab82532)))
+				return -EFAULT;
+			return 0;
+				
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+ 		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		 case TIOCMIWAIT:
+			cli();
+			/* note the counters on entry */
+			cprev = info->icount;
+			sti();
+			while (1) {
+				interruptible_sleep_on(&info->delta_msr_wait);
+				/* see if a signal did it */
+				if (current->signal & ~current->blocked)
+					return -ERESTARTSYS;
+				cli();
+				cnow = info->icount; /* atomic copy */
+				sti();
+				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+					return -EIO; /* no change => error */
+				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+					return 0;
+				}
+				cprev = cnow;
+			}
+			/* NOTREACHED */
+
+		/* 
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			cli();
+			cnow = info->icount;
+			sti();
+			p_cuser = (struct serial_icounter_struct *) arg;
+			error = put_user(cnow.cts, &p_cuser->cts);
+			if (error) return error;
+			error = put_user(cnow.dsr, &p_cuser->dsr);
+			if (error) return error;
+			error = put_user(cnow.rng, &p_cuser->rng);
+			if (error) return error;
+			error = put_user(cnow.dcd, &p_cuser->dcd);
+			if (error) return error;
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void sab82532_set_termios(struct tty_struct *tty,
+				 struct termios *old_termios)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+
+	if (   (tty->termios->c_cflag == old_termios->c_cflag)
+	    && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	change_speed(info);
+
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->regs->w.mode |= SAB82532_MODE_FRTS;
+		info->regs->w.mode |= SAB82532_MODE_RTS;
+		info->regs->w.pvr |= info->pvr_dtr_bit;
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    (tty->termios->c_cflag & CBAUD)) {
+		info->regs->w.pvr &= ~(info->pvr_dtr_bit);
+		if (!tty->hw_stopped ||
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			info->regs->w.mode &= ~(SAB82532_MODE_FRTS);
+			info->regs->w.mode |= SAB82532_MODE_RTS;
+		}
+	}
+	
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		sab82532_start(tty);
+	}
+
+#if 0
+	/*
+	 * No need to wake up processes in open wait, since they
+	 * sample the CLOCAL flag once, and don't recheck it.
+	 * XXX  It's not clear whether the current behavior is correct
+	 * or not.  Hence, this may change.....
+	 */
+	if (!(old_termios->c_cflag & CLOCAL) &&
+	    (tty->termios->c_cflag & CLOCAL))
+		wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void sab82532_close(struct tty_struct *tty, struct file * filp)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
+		return;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("sab82532_close: bad serial port count; tty->count is 1,"
+		       " info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("sab82532_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and turn off
+	 * the receiver.
+	 */
+	info->interrupt_mask0 |= SAB82532_IMR0_TCD;
+	info->regs->w.imr0 = info->interrupt_mask0;
+	info->regs->rw.mode &= ~(SAB82532_MODE_RAC);
+	if (info->flags & ASYNC_INITIALIZED) {
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		sab82532_wait_until_sent(tty, info->timeout);
+	}
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
+	restore_flags(flags);
+}
+
+/*
+ * sab82532_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (serial_paranoia_check(info,tty->device,"sab82532_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.
+	 * 
+	 * Note: we have to use pretty tight timings here to satisfy
+	 * the NIST-PCTS.
+	 */
+	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);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time);
+	printk("jiff=%lu...", jiffies);
+#endif
+
+	/* XXX: Implement this... */
+
+	current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+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;
+
+	sab82532_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab82532_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct sab82532 *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int retval;
+	int do_clocal = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * sab82532_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	cli();
+	if (!tty_hung_up_p(filp)) 
+		info->count--;
+	sti();
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (tty->termios->c_cflag & CBAUD)) {
+			info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+			info->regs->rw.mode |= SAB82532_MODE_FRTS;
+			info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ASYNC_CLOSING) &&
+		    (do_clocal || !(info->regs->r.vstr & SAB82532_VSTR_CD)))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
+		       info->line, info->count, info->flags, do_clocal, info->regs->r.vstr);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int sab82532_open(struct tty_struct *tty, struct file * filp)
+{
+	struct sab82532	*info = sab82532_chain;
+	int retval, line;
+	unsigned long page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("sab82532_open: count = %d\n", info->count);
+#endif
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENODEV;
+
+	while (info) {
+		if (info->line == line)
+			break;
+		info = info->next;
+	}
+	if (!info) {
+		printk("sab82532_open: can't find info for line %d\n",
+		       line);
+		return -ENODEV;
+	}
+
+	info->count++;
+	if (serial_paranoia_check(info, tty->device, "sab82532_open"))
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+	
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	MOD_INC_USE_COUNT;
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("sab82532_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) &&
+	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
+#endif
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct tty_struct *tty)
+{
+	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	int ret;
+
+	ret = sprintf(buf, "%d: uart:SAB82532 ", info->line);
+	switch (info->type) {
+		case 0:
+			ret += sprintf(buf+ret, "V1.0 ");
+			break;
+		case 1:
+			ret += sprintf(buf+ret, "V2.0 ");
+			break;
+		case 2:
+			ret += sprintf(buf+ret, "V3.2 ");
+			break;
+		default:
+			ret += sprintf(buf+ret, "V?.? ");
+			break;
+	}
+	ret += sprintf(buf+ret, "port:%lX irq:%d",
+		       (unsigned long)info->regs, info->irq);
+
+	if (!info->regs) {
+		ret += sprintf(buf+ret, "\n");
+		return ret;
+	}
+
+	/*
+	 * Figure out the current RS-232 lines
+	 */
+
+	ret += sprintf(buf+ret, "\n");
+	return ret;
+}
+
+int sab82532_read_proc(char *page, char **start, off_t off, int count,
+		       int *eof, void *data)
+{
+	int i, len = 0;
+	off_t	begin = 0;
+
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.4 $");
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		len += line_info(page + len, sab82532_table[i]);
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * sab82532_init() and friends
+ *
+ * sab82532_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+__initfunc(static int get_sab82532(void))
+{
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev;
+	struct sab82532 *sab;
+	unsigned long regs, offset;
+	int i;
+
+	for_all_ebusdev(edev, ebus)
+		if (!strcmp(edev->prom_name, "se"))
+			break;
+	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;
+		}
+		memset(sab, 0, sizeof(struct sab82532));
+
+		sab->regs = (union sab82532_async_regs *)(regs + offset);
+		sab->irq = edev->irqs[0];
+
+		if (check_region((unsigned long)sab->regs,
+				 sizeof(union sab82532_async_regs))) {
+			kfree(sab);
+			continue;
+		}
+		request_region((unsigned long)sab->regs,
+			       sizeof(union sab82532_async_regs),
+			       "serial(sab82532)");
+
+		sab->regs->w.ipc = SAB82532_IPC_IC_ACT_LOW;
+
+		sab->next = sab82532_chain;
+		sab82532_chain = sab;
+
+		offset -= sizeof(union sab82532_async_regs);
+	}
+	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)
+{
+	prom_printf("sab82532: kgdb support is not implemented, yet\n");
+	prom_halt();
+}
+
+__initfunc(static inline void show_serial_version(void))
+{
+	char *revision = "$Revision: 1.4 $";
+	char *version, *p;
+
+	version = strchr(revision, ' ');
+	p = strchr(++version, ' ');
+	*p = '\0';
+	printk("SAB82532 serial driver version %s\n", version);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int sab82532_init(void))
+{
+	struct sab82532 *info;
+	int i;
+
+	if (!sab82532_chain)
+		get_sab82532();
+	if (!sab82532_chain)
+		return -ENODEV;
+
+	init_bh(SERIAL_BH, do_serial_bh);
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "serial";
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NR_PORTS;
+	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;
+	serial_driver.refcount = &sab82532_refcount;
+	serial_driver.table = sab82532_table;
+	serial_driver.termios = sab82532_termios;
+	serial_driver.termios_locked = sab82532_termios_locked;
+
+	serial_driver.open = sab82532_open;
+	serial_driver.close = sab82532_close;
+	serial_driver.write = sab82532_write;
+	serial_driver.put_char = sab82532_put_char;
+	serial_driver.flush_chars = sab82532_flush_chars;
+	serial_driver.write_room = sab82532_write_room;
+	serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
+	serial_driver.flush_buffer = sab82532_flush_buffer;
+	serial_driver.ioctl = sab82532_ioctl;
+	serial_driver.throttle = sab82532_throttle;
+	serial_driver.unthrottle = sab82532_unthrottle;
+	serial_driver.send_xchar = sab82532_send_xchar;
+	serial_driver.set_termios = sab82532_set_termios;
+	serial_driver.stop = sab82532_stop;
+	serial_driver.start = sab82532_start;
+	serial_driver.hangup = sab82532_hangup;
+	serial_driver.wait_until_sent = sab82532_wait_until_sent;
+	serial_driver.read_proc = sab82532_read_proc;
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register serial driver\n");
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+
+	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));
+		info->regs->w.pim = 0xff;
+		if (info->line == 0) {
+			info->pvr_dsr_bit = (1 << 0);
+			info->pvr_dtr_bit = (1 << 1);
+		} else {
+			info->pvr_dsr_bit = (1 << 3);
+			info->pvr_dtr_bit = (1 << 2);
+		}
+		info->regs->w.pvr = (1 << 1) | (1 << 2) | (1 << 4);
+		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;
+		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;
+		info->close_wait = 0;
+		info->delta_msr_wait = 0;
+		info->icount.cts = info->icount.dsr = 
+			info->icount.rng = info->icount.dcd = 0;
+		info->icount.rx = info->icount.tx = 0;
+		info->icount.frame = info->icount.parity = 0;
+		info->icount.overrun = info->icount.brk = 0;
+
+		if (!(info->line & 0x01)) {
+			if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
+					"serial(sab82532)", info)) {
+				printk("sab82532: can't get IRQ %x\n",
+				       info->irq);
+				panic("sab82532 initialization failed");
+			}
+		}
+	
+		printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a %s\n",
+		       info->line, (unsigned long)info->regs, info->irq,
+		       "SAB82532");
+	}
+	return 0;
+}
+
+__initfunc(int sab82532_probe(unsigned long *memory_start))
+{
+	int node, enode, snode;
+
+        node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "pci");
+
+	/*
+	 * For each PCI bus...
+	 */
+	while (node) {
+		enode = prom_getchild(node);
+		enode = prom_searchsiblings(enode, "ebus");
+
+		/*
+		 * For each EBus on this PCI...
+		 */
+		while (enode) {
+			snode = prom_getchild(enode);
+			snode = prom_searchsiblings(snode, "se");
+			if (snode)
+				goto found;
+
+			enode = prom_getsibling(enode);
+			enode = prom_searchsiblings(enode, "ebus");
+		}
+		node = prom_getsibling(node);
+		node = prom_searchsiblings(node, "pci");
+	}
+	return -ENODEV;
+
+found:
+	sunserial_setinitfunc(memory_start, sab82532_init);
+	rs_ops.rs_cons_hook = sab82532_cons_hook;
+	rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	if (get_sab82532())
+		return -ENODEV;
+
+	return sab82532_init();
+}
+
+void cleanup_module(void) 
+{
+	unsigned long flags;
+	int e1, e2;
+	int i;
+
+	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+	save_flags(flags);
+	cli();
+	timer_active &= ~(1 << RS_TIMER);
+	timer_table[RS_TIMER].fn = NULL;
+	timer_table[RS_TIMER].expires = 0;
+        remove_bh(SERIAL_BH);
+	if ((e1 = tty_unregister_driver(&serial_driver)))
+		printk("SERIAL: failed to unregister serial driver (%d)\n",
+		       e1);
+	if ((e2 = tty_unregister_driver(&callout_driver)))
+		printk("SERIAL: failed to unregister callout driver (%d)\n", 
+		       e2);
+	restore_flags(flags);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		if (sab82532_table[i].type != PORT_UNKNOWN)
+			release_region(sab82532_table[i].port, 8);
+	}
+	if (tmp_buf) {
+		free_page((unsigned long) tmp_buf);
+		tmp_buf = NULL;
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sbuscons.c linux/drivers/sbus/char/sbuscons.c
--- v2.1.52/linux/drivers/sbus/char/sbuscons.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/sbuscons.c	Thu Sep  4 12:54:48 1997
@@ -0,0 +1,1644 @@
+/* $Id: sbuscons.c,v 1.7 1997/08/28 09:30:07 davem Exp $
+ * sbuscons.c: Routines specific to SBUS frame buffer consoles.
+ *
+ * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ * Copyright (C) 1995,1996,1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Added render_screen and faster scrolling Nov/27, miguel
+ * Added console palette code for cg6 Dec/13/95, miguel
+ * Added generic frame buffer support Dec/14/95, miguel
+ * Added cgsix and bwtwo drivers Jan/96, miguel
+ * Added 4m, and cg3 driver Feb/96, miguel
+ * Fixed the cursor on color displays Feb/96, miguel.
+ * Cleaned up the detection code, generic 8bit depth display 
+ *   code, Mar/96 miguel
+ * Hacked support for cg14 video cards -- Apr/96, miguel.
+ * Color support for cg14 video cards -- May/96, miguel.
+ * Code split, Dave Redman, May/96
+ * Be more VT change friendly, May/96, miguel.
+ * Support for hw cursor and graphics acceleration, Jun/96, jj.
+ * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
+ * Added TCX support (8/24bit), Aug/96, jj.
+ * Support for multiple framebuffers, Sep/96, jj.
+ * Fix bwtwo inversion and handle inverse monochrome cells in
+ *   sun_blitc, Nov/96, ecd.
+ * Fix sun_blitc and screen size on displays other than 1152x900, 
+ *   128x54 chars, Nov/96, jj.
+ * Fix cursor spots left on some non-accelerated fbs, changed
+ *   software cursor to be like the hw one, Nov/96, jj.
+ * 
+ * Much of this driver is derived from the DEC TGA driver by
+ * Jay Estabrook who has done a nice job with the console
+ * driver abstraction btw.
+ *
+ * We try to make everything a power of two if possible to
+ * speed up the bit blit.  Doing multiplies, divides, and
+ * remainder routines end up calling software library routines
+ * since not all Sparcs have the hardware to do it.
+ *
+ * TODO:
+ * do not blank the screen when frame buffer is mapped.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/fbio.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
+
+#include "fb.h"
+
+#define cmapsz 8192
+
+#include "suncons_font.h"
+
+#define ASM_BLITC
+
+int sbus_hw_cursor_shown = 0;
+
+void sbus_hw_hide_cursor(void);
+void sbus_hw_set_cursor(int,int);
+
+static int sbus_blitc(uint, unsigned long);
+
+static void sbus_install_consops(void);
+
+extern void register_console(void (*proc)(const char *));
+extern void console_print(const char *);
+extern void putconsxy(int, char *);
+extern unsigned char vga_font[];
+extern int serial_console;
+extern char *console_fb_path;
+
+/* The following variables describe a Sparc console. */
+
+/* Screen dimensions and color depth. */
+static int con_depth, con_width, con_height, con_type;
+
+/* Base address of first line. */
+static unsigned char *con_fb_base;
+
+/* Screen parameters: we compute those at startup to make the code faster */
+static int chars_per_line;	/* number of bytes per line */
+static int ints_per_line;	/* number of ints per  line */
+static int ints_per_cursor;  	/* 14 * ints_per_line */
+static int skip_bytes;		/* number of bytes we skip for the y margin + x_margin */
+static int x_margin, y_margin;	/* the x and y margins */
+static int bytes_per_row;	/* bytes used by one screen line (of 16 scan lines)  */
+int sun_prom_console_id = 0;
+
+/* Functions used by the SPARC dependent console code
+ * to perform the fb_restore_palette function.
+ */
+extern void (*fb_restore_palette)(fbinfo_t *fbinfo);
+static void sbus_set_palette (void);
+
+ /* Our screen looks like at 1152 X 900:
+ *
+ *  0,0
+ *      ------------------------------------------------------------------
+ *      |                          ^^^^^^^^^^^                           |
+ *      |                          18 y-pixels                           |
+ *      |                          ^^^^^^^^^^^                           |
+ *   13 | <-64 pixels->|  <-- 128 8x16 characters -->    | <-64 pixels-> |
+ *    ....
+ *                         54 chars from top to bottom
+ *    ....
+ *  888 | <-64 pixels->|  <-- 128 8x16 characters -->    | <-64 pixels-> |
+ *      |                          ^^^^^^^^^^^                           |
+ *      |                          18 y-pixels                           |
+ *      |                          ^^^^^^^^^^^                           |
+ *      ------------------------------------------------------------------
+ */
+/* First for MONO displays. */
+#define SCREEN_WIDTH     1152     /* Screen width in pixels  */
+#define SCREEN_HEIGHT    900      /* Screen height in pixels */
+#define CHARS_PER_LINE   144      /* Make this empirical for speed */
+#define NICE_Y_MARGIN    18       /* We skip 18 y-pixels at top/bottom */
+#define NICE_X_MARGIN    8        /* We skip 64 x-pixels at left/right */
+#define FBUF_TOP_SKIP    2592     /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
+#define CHAR_HEIGHT      16
+#define ONE_ROW          2304     /* CHARS_PER_LINE * CHAR_HEIGHT */
+
+/* Now we have this, to compute the base frame buffer position
+ * for a new character to be rendered. 1 and 8 bit depth.
+ */
+#define FBUF_OFFSET(cindex) \
+        (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
+	 ((NICE_X_MARGIN) + (((cindex)&127))))
+
+
+#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
+
+/* These four routines are optimizations for the _generic routine for
+ * the most common cases.
+ * I guess doing twice sll is much faster than doing .mul, sra faster
+ * than doing .div, and the disadvantage that someone has to call it
+ * (it cannot be inline) runs away, 'cause otherwise it would have to
+ * call .mul anyway. 
+ * The shifting + addition only routines won't eat any stack frame :))
+ * Names come from width, screen_num_columns.
+ */
+static int color_fbuf_offset_1280_144 (int cindex)
+{
+	register int i = (cindex/144);
+	/* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
+	return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+}
+
+static int color_fbuf_offset_1152_128 (int cindex)
+{
+	register int i = (cindex>>7);
+	/* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
+	return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_1024_128 (int cindex)
+{
+	register int i = (cindex>>7);
+	/* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
+	return skip_bytes + (i << 14) + ((cindex & 127) << 3);
+}
+
+static int color_fbuf_offset_800_96 (int cindex)
+{
+	register int i = (cindex / 96);
+	/* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
+	return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
+}
+
+static int color_fbuf_offset_640_80 (int cindex)
+{
+	register int i = (cindex/80);
+	/* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
+	return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
+}
+	 
+static int color_fbuf_offset_generic (int cindex)
+{
+	return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
+}
+
+static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
+	 
+static int do_accel = 0;
+
+/* For the cursor, we just invert the 8x16 block at the cursor
+ * location.  Easy enough...
+ *
+ * Hide the cursor from view, during blanking, usually...
+ */
+static int cursor_pos = -1;
+
+static unsigned int under_cursor[4];
+
+static void sbus_hide_cursor(void)
+{
+	unsigned long flags;
+	int j;
+
+	if (fbinfo[0].setcursor) {
+		sbus_hw_hide_cursor();
+		return;
+	}
+	
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+		return; /* Don't paint anything on fb which is not ours,
+			   but turn off the hw cursor in such case */
+	
+	__save_and_cli(flags);
+
+	if(cursor_pos == -1) {
+		__restore_flags (flags);
+		return;
+	}
+	switch (con_depth){
+	case 1: {
+		unsigned char *dst;
+		dst = (unsigned char *)((unsigned long)con_fb_base +
+					FBUF_OFFSET(cursor_pos));
+		for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
+			*dst = ~(*dst);
+		break;
+	}
+	case 8: {
+		unsigned int *dst;
+		
+		dst = (unsigned int *)((unsigned long)con_fb_base +
+					COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
+		dst[0] = under_cursor[0];
+		dst[1] = under_cursor[1];
+		dst[ints_per_line] = under_cursor[2];
+		dst[ints_per_line+1] = under_cursor[3];
+		break;
+	}
+	default:
+		break;
+	}
+	cursor_pos = -1;
+	__restore_flags(flags);
+}
+
+static void sbus_set_cursor(int currcons)
+{
+	int j, idx, oldpos;
+	unsigned long flags;
+
+	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+		return;
+
+	if (fbinfo[0].setcursor) {
+		if (!deccm)
+			sbus_hide_cursor();
+		else {
+			idx = (pos - video_mem_base) >> 1;
+			
+			sbus_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
+		}
+		return;
+	}
+
+	__save_and_cli(flags);
+
+	idx = (pos - video_mem_base) >> 1;
+	oldpos = cursor_pos;
+	if (!deccm) {
+		sbus_hide_cursor ();
+		__restore_flags (flags);
+		return;
+	}
+	cursor_pos = idx;
+	switch (con_depth){
+	case 1: {
+		unsigned char *dst, *opos;
+		
+		dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
+		opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
+		if(oldpos != -1) {
+			/* Restore what was at the old position */
+			for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
+				*opos = ~*opos;
+			}
+		}
+		for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
+			*dst = ~*dst;
+		}
+		break;
+	}
+	case 8: {
+		unsigned int *dst, *opos;
+		dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
+			
+		if(oldpos != -1) {
+			opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
+			opos[0] = under_cursor[0];
+			opos[1] = under_cursor[1];
+			opos[ints_per_line] = under_cursor[2];
+			opos[ints_per_line+1] = under_cursor[3];
+		}
+		under_cursor[0] = dst[0];
+		under_cursor[1] = dst[1];
+		under_cursor[2] = dst[ints_per_line];
+		under_cursor[3] = dst[ints_per_line+1];
+		dst[0] = 0x00000000;
+		dst[1] = 0x00000000;
+		dst[ints_per_line] = 0x00000000;
+		dst[ints_per_line+1] = 0x00000000;
+		break;
+	}
+	default:
+	}
+	__restore_flags(flags);
+}
+
+/*
+ * Render the current screen
+ * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
+ * to avoid the caching that is being done in selection.h
+ */
+
+static void sbus_render_screen(void)
+{
+    int count;
+    unsigned short *contents;
+
+    count = video_num_columns * video_num_lines;
+    contents = (unsigned short *) video_mem_base;
+
+    for (;count--; contents++)
+	sbus_blitc (*contents, (unsigned long) contents);
+}
+
+__initfunc(static unsigned long
+sbus_con_type_init(unsigned long kmem_start, const char **display_desc))
+{
+        can_do_color = (con_type != FBTYPE_SUN2BW);
+
+        video_type = VIDEO_TYPE_SUN;
+        *display_desc = "SUN";
+
+	if (!serial_console) {
+		/* If we fall back to PROM then our output have to remain readable. */
+		prom_putchar('\033');  prom_putchar('[');  prom_putchar('H');
+
+		/*
+		 * fake the screen memory with some CPU memory
+		 */
+		video_mem_base = kmem_start;
+		kmem_start += video_screen_size;
+		video_mem_term = kmem_start;
+	}
+	return kmem_start;
+}
+
+__initfunc(static void sbus_con_type_init_finish(void))
+{
+	int i, cpu;
+	char *p = con_fb_base + skip_bytes;
+	char q[2] = {0,5};
+	int currcons = 0;
+	unsigned short *ush;
+	int ncpus;
+
+	if (serial_console)
+		return;
+	ncpus = linux_num_cpus;
+	if (ncpus > 4) ncpus = 4;
+	if (fbinfo[0].draw_penguin) {
+		(*fbinfo[0].draw_penguin)(x_margin, y_margin, ncpus);
+	} else if (con_depth == 8 && fbinfo[0].loadcmap) {
+		for (i = 0; i < linux_logo_colors; i++) {
+			fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
+			fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
+			fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
+		}
+		(*fbinfo [0].loadcmap)(&fbinfo [0], 0, linux_logo_colors + 32);
+		for (i = 0; i < 80; i++, p += chars_per_line){
+		        for (cpu = 0; cpu < ncpus; cpu++){
+				memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
+			}
+		}
+	} else if (con_depth == 1) {
+		for (i = 0; i < 80; i++, p += chars_per_line)
+			memcpy (p, linux_logo_bw + 10 * i, 10);
+	}
+	putconsxy(0, q);
+	ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (ncpus - 1);
+
+	p = logo_banner;
+	for (; *p; p++, ush++) {
+		*ush = (attr << 8) + *p;
+		sbus_blitc (*ush, (unsigned long) ush);
+	}
+	for (i = 0; i < 5; i++) {
+		ush = (unsigned short *) video_mem_base + i * video_num_columns;
+		memset (ush, 0, 20);
+	}
+}
+
+/*
+ * NOTE: get_scrmem() and set_scrmem() are here only because
+ * the VGA version of set_scrmem() has some direct VGA references.
+ */
+static void sbus_get_scrmem(int currcons)
+{
+	memcpyw((unsigned short *)vc_scrbuf[currcons],
+		(unsigned short *)origin, video_screen_size);
+	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
+	scr_end = video_mem_end = video_mem_start + video_screen_size;
+	pos = origin + y*video_size_row + (x<<1);
+}
+
+static void sbus_set_scrmem(int currcons, long offset)
+{
+	if (video_mem_term - video_mem_base < offset + video_screen_size)
+		offset = 0;
+	memcpyw((unsigned short *)(video_mem_base + offset),
+		(unsigned short *) origin, video_screen_size);
+	video_mem_start = video_mem_base;
+	video_mem_end = video_mem_term;
+	origin = video_mem_base + offset;
+	scr_end = origin + video_screen_size;
+	pos = origin + y*video_size_row + (x<<1);
+}
+
+/*
+ * PIO_FONT support.
+ */
+static int sbus_set_get_font(char * arg, int set, int ch512)
+{
+	int i, line;
+
+	if (!arg)
+		return -EINVAL;
+
+	/* download the current font */
+	if (!set){
+		if(clear_user(arg, cmapsz))
+			return -EFAULT;
+		for (i = 0; i < 256; i++) {
+			for (line = 0; line < CHAR_HEIGHT; line++) {
+				unsigned char value = vga_font[i];
+
+				/* Access checked by the above clear_user */
+				__put_user_ret (value, (arg + (i * 32 + line)),
+						-EFAULT);
+			}
+		}
+		return 0;
+	}
+	
+        /* set the font */
+        
+        if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
+	for (i = 0; i < 256; i++) {
+		for (line = 0; line < CHAR_HEIGHT; line++){
+			unsigned char value;
+			__get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
+			vga_font [i*CHAR_HEIGHT + line] = value;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Adjust the screen to fit a font of a certain height
+ *
+ * Returns < 0 for error, 0 if nothing changed, and the number
+ * of lines on the adjusted console if changed.
+ *
+ * for now, we only support the built-in font...
+ */
+static int sbus_con_adjust_height(unsigned long fontheight)
+{
+	return -EINVAL;
+}
+
+static int sbus_set_get_cmap(unsigned char * arg, int set)
+{
+	int i;
+
+	if(set)
+		i = VERIFY_READ;
+	else
+		i = VERIFY_WRITE;
+	if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
+		return -EFAULT;
+	for (i=0; i<16; i++) {
+		if (set) {
+			__get_user_ret(default_red[i], (arg+0),-EFAULT);
+			__get_user_ret(default_grn[i], (arg+1),-EFAULT);
+			__get_user_ret(default_blu[i], (arg+2),-EFAULT);
+		} else {
+			__put_user_ret(default_red[i], (arg+0),-EFAULT);
+			__put_user_ret(default_grn[i], (arg+1),-EFAULT);
+			__put_user_ret(default_blu[i], (arg+2),-EFAULT);
+		}
+		arg += 3;
+	}
+	if (set) {
+		for (i=0; i<MAX_NR_CONSOLES; i++)
+			if (vc_cons_allocated(i)) {
+				int j, k ;
+				for (j=k=0; j<16; j++) {
+					vc_cons[i].d->vc_palette[k++] = default_red[j];
+					vc_cons[i].d->vc_palette[k++] = default_grn[j];
+					vc_cons[i].d->vc_palette[k++] = default_blu[j];
+				}
+			}
+		sbus_set_palette();
+	}
+
+	return 0;
+}
+
+static void sbus_clear_screen(void)
+{
+	if (fbinfo[0].fill) {
+		int rects [4];
+		
+		rects [0] = 0;
+		rects [1] = 0;
+		rects [2] = con_width;
+		rects [3] = con_height;
+		(*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
+	} else if (fbinfo[0].base && fbinfo[0].base_depth)
+		memset (con_fb_base,
+			(con_depth == 1) ? ~(0) : reverse_color_table[0],
+			(con_depth * con_height * con_width) / 8);
+	/* also clear out the "shadow" screen memory */
+	memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
+	cursor_pos = -1;
+}
+
+static void sbus_clear_fb(int n)
+{
+	if (!n) {
+		sbus_clear_screen ();
+	} else if (fbinfo[n].base && fbinfo[n].base_depth) {
+		memset((void *)fbinfo[n].base,
+		       (fbinfo[n].base_depth == 1) ?
+			~(0) : reverse_color_table[0],
+		       (fbinfo[n].base_depth * fbinfo[n].type.fb_height
+					     * fbinfo[n].type.fb_width) / 8);
+	}
+}
+
+static void sbus_clear_margin(void)
+{
+	int h, he, i;
+	unsigned char *p;
+
+	if (fbinfo[0].fill) {
+		int rects [16];
+
+		rects [0] = 0;
+		rects [1] = 0;
+		rects [2] = con_width;
+		rects [3] = y_margin;
+		rects [4] = 0;
+		rects [5] = y_margin;
+		rects [6] = x_margin;
+		rects [7] = con_height;
+		rects [8] = con_width - x_margin;
+		rects [9] = y_margin;
+		rects [10] = con_width;
+		rects [11] = con_height;
+		rects [12] = x_margin;
+		rects [13] = con_height - y_margin;
+		rects [14] = con_width - x_margin;
+		rects [15] = con_height;
+		(*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
+	} else {
+		memset (con_fb_base, 
+			(con_depth == 1) ? ~(0) : reverse_color_table[0],
+			skip_bytes - (x_margin<<1));
+		memset (con_fb_base + chars_per_line * con_height
+					- skip_bytes + (x_margin<<1),
+			(con_depth == 1) ? ~(0) : reverse_color_table[0],
+			skip_bytes - (x_margin<<1));
+		he = con_height - 2 * y_margin;
+		i = 2 * x_margin;
+		if (con_depth == 1) {
+			for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+			     h <= he; p += chars_per_line, h++)
+				memset (p, ~(0), i);			
+		} else {
+			for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
+			     h <= he; p += chars_per_line, h++)
+				memset (p, reverse_color_table[0], i);
+		}
+	}
+	if (fbinfo [0].switch_from_graph)
+		(*fbinfo [0].switch_from_graph)();
+}
+
+/* Call the frame buffer routine for setting the palette */
+static void sbus_set_palette (void)
+{
+	if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
+		return;
+
+	if (fbinfo [0].loadcmap){
+		int i, j;
+	
+		/* First keep color_map with the palette colors */
+		for (i = 0; i < 16; i++){
+			j = sparc_color_table [i];
+			fbinfo[0].color_map CM(i,0) = default_red [j];
+			fbinfo[0].color_map CM(i,1) = default_grn [j];
+			fbinfo[0].color_map CM(i,2) = default_blu [j];
+		}
+		(*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
+	}
+}
+
+static void sbus_set_other_palette (int n)
+{
+	if (!n) {
+		sbus_set_palette ();
+		return;
+	}
+	if (fbinfo [n].loadcmap){
+		fbinfo[n].color_map CM(0,0) = 0;
+		fbinfo[n].color_map CM(0,1) = 0;
+		fbinfo[n].color_map CM(0,2) = 0;
+		(*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
+	}
+}
+
+/* Called when returning to prom */
+static void sbus_console_restore_palette (void)
+{
+        if (fb_restore_palette)
+	        (*fb_restore_palette) (&fbinfo[0]);
+}
+
+__initfunc(unsigned long cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+{
+	fb->color_map = (char *)start_mem;
+	return start_mem + 256*3;
+}
+
+static char *known_cards [] __initdata = {
+	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
+	"cgfourteen", "SUNW,leo", "SUNW,ffb", 0
+};
+static char *v0_known_cards [] __initdata = {
+	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
+};
+
+__initfunc(static int known_card (char *name, char **known_cards))
+{
+	int i;
+
+	for (i = 0; known_cards [i]; i++)
+		if (strcmp (name, known_cards [i]) == 0)
+			return 1;
+	return 0;
+}
+
+static struct {
+	int depth;
+	int resx, resy;
+	int x_margin, y_margin;
+} scr_def [] = {
+	{ 8, 1280, 1024, 64, 80 },
+	{ 8, 1152, 1024, 64, 80 },
+	{ 8, 1152, 900,  64, 18 },
+	{ 8, 1024, 768,  0,  0 },
+	{ 8, 800, 600, 16, 12 },
+	{ 8, 640, 480, 0, 0 },
+	{ 1, 1152, 900,  8,  18 },
+	{ 0 },
+};
+
+__initfunc(static int cg14_present(void))
+{
+	int root, n;
+
+	root = prom_getchild (prom_root_node);
+	if ((n = prom_searchsiblings (root, "obio")) == 0)
+		return 0;
+
+	n = prom_getchild (n);
+	if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
+		return 0;
+	return n;
+}
+
+__initfunc(static int creator_present (void))
+{
+#ifdef __sparc_v9__
+	int root, n;
+
+	root = prom_getchild (prom_root_node);
+	if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
+		return 0;
+	return n;
+#else
+	return 0;
+#endif
+}
+
+__initfunc(static void
+	   sparc_framebuffer_setup(int primary, int con_node,
+				   int type, struct linux_sbus_device *sbdp, 
+				   uint base, unsigned long con_base, int prom_fb,
+				   int parent_node))
+{
+	static int frame_buffers = 1;
+	int n, i;
+	int linebytes;
+	uint io = 0;
+	char *p;
+	
+	if (primary)
+		n = 0;
+	else {
+		if (frame_buffers == FRAME_BUFFERS)
+			return; /* Silently ignore */
+		n = frame_buffers++;
+	}
+	
+	if (prom_fb) sun_prom_console_id = n;
+		
+	if (sbdp)
+		io = sbdp->reg_addrs [0].which_io;
+
+	/* Fill in common fb information */
+	fbinfo [n].clear_fb       = sbus_clear_fb;
+	fbinfo [n].set_other_palette = sbus_set_other_palette;
+	fbinfo [n].type.fb_type   = type;
+	fbinfo [n].real_type	  = type;
+	fbinfo [n].prom_node	  = con_node;
+	memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
+	fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
+	fbinfo [n].type.fb_width  = prom_getintdefault(con_node, "width", 1152);
+	fbinfo [n].type.fb_depth  = (type == FBTYPE_SUN2BW) ? 1 : 8;
+	linebytes = prom_getint(con_node, "linebytes");
+	if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
+	fbinfo [n].type.fb_size   = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
+	fbinfo [n].space = io;
+	fbinfo [n].blanked = 0;
+	if (con_base >= PAGE_OFFSET)
+		fbinfo [n].base = con_base;
+	else
+		fbinfo [n].base = 0;
+	fbinfo [n].cursor.hwsize.fbx = 32;
+	fbinfo [n].cursor.hwsize.fby = 32;
+	fbinfo [n].proc_entry.node = parent_node;
+	fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
+	fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
+	prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
+	p = strchr (fbinfo [n].proc_entry.name, 0);
+        sprintf (p, ":%d", n);
+	
+	/* Should be filled in for supported video cards */
+	fbinfo [n].mmap = 0; 
+	fbinfo [n].loadcmap = 0;
+	fbinfo [n].ioctl = 0;
+	fbinfo [n].reset = 0;
+	fbinfo [n].blank = 0;
+	fbinfo [n].unblank = 0;
+	fbinfo [n].setcursor = 0;
+	fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
+	
+	/* Per card setup */
+	switch (fbinfo [n].type.fb_type){
+#ifdef SUN_FB_CGTHREE
+	case FBTYPE_SUN3COLOR:
+		cg3_setup (&fbinfo [n], n, base, io, sbdp);
+		break;
+#endif
+#ifdef SUN_FB_TCX
+	case FBTYPE_TCXCOLOR:
+		tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
+		break;
+#endif
+#ifdef SUN_FB_CGSIX
+	case FBTYPE_SUNFAST_COLOR:
+		cg6_setup (&fbinfo [n], n, base, io);
+		break;
+#endif
+#ifdef SUN_FB_BWTWO
+	case FBTYPE_SUN2BW:
+		bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
+		break;
+#endif
+#ifdef SUN_FB_CGFOURTEEN
+	case FBTYPE_MDICOLOR:
+		cg14_setup (&fbinfo [n], n, con_node, base, io);
+		break;
+#endif
+#ifdef SUN_FB_LEO
+	case FBTYPE_SUNLEO:
+		leo_setup (&fbinfo [n], n, base, io);
+		break;
+#endif
+#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
+	case FBTYPE_CREATOR:
+		creator_setup (&fbinfo [n], n, con_node, base, io);
+		break;
+#endif
+	default:
+		fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
+		return;
+	}
+
+	if (n)
+		return;
+	
+	/* Code below here is just executed for the first frame buffer */
+	con_type = type;
+	con_height = fbinfo [n].type.fb_height;
+	con_width = fbinfo [n].type.fb_width;
+	con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
+	for (i = 0; scr_def [i].depth; i++){
+		if ((scr_def [i].resx != con_width) ||
+		    (scr_def [i].resy != con_height))
+			continue;
+		if (scr_def [i].depth != con_depth)
+			continue;
+		x_margin = scr_def [i].x_margin;
+		y_margin = scr_def [i].y_margin;
+		chars_per_line = (con_width * con_depth) / 8;
+		skip_bytes = chars_per_line * y_margin + x_margin;
+		ints_per_line = chars_per_line / 4;
+		ints_per_cursor = 14 * ints_per_line;
+		bytes_per_row = CHAR_HEIGHT * chars_per_line;
+		ORIG_VIDEO_COLS = con_width / 8 -
+			2 * x_margin / con_depth;
+		ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
+		switch (chars_per_line) {
+		case 1280:
+			if (ORIG_VIDEO_COLS == 144)
+				color_fbuf_offset =
+					color_fbuf_offset_1280_144;
+			break;
+		case 1152:
+			if (ORIG_VIDEO_COLS == 128)
+				color_fbuf_offset =
+					color_fbuf_offset_1152_128;
+			break;
+		case 1024:
+			if (ORIG_VIDEO_COLS == 128)
+				color_fbuf_offset =
+					color_fbuf_offset_1024_128;
+			break;
+		case 800:
+			if (ORIG_VIDEO_COLS == 96)
+				color_fbuf_offset =
+					color_fbuf_offset_800_96;
+			break;
+		case 640:
+			if (ORIG_VIDEO_COLS == 80)
+				color_fbuf_offset =
+					color_fbuf_offset_640_80;
+			break;
+		}
+		break;
+	}
+	
+	if (!scr_def [i].depth){
+		x_margin = y_margin = 0;
+		prom_printf ("console: unknown video resolution %dx%d,"
+			     " depth %d\n",
+			     con_width, con_height, con_depth);
+		prom_halt ();
+	}
+	
+	/* P3: I fear this strips 15inch 1024/768 PC-like
+	 * monitors out. */
+	if ((linebytes*8) / con_depth != con_width) {
+		prom_printf("console: unusual video, linebytes=%d, "
+			    "width=%d, height=%d depth=%d\n",
+			    linebytes, con_width, con_height,
+			    con_depth);
+		prom_halt ();
+	}
+}
+
+__initfunc(int sbus_console_probe(void))
+{
+	int propl, con_node, default_node = 0;
+	char prop[16];
+	struct linux_sbus_device *sbdp, *sbdprom;
+	struct linux_sbus *sbus;
+	int creator = 0, cg14 = 0;
+	char prom_name[40];	
+	int type, card_found = 0;
+	unsigned long con_base;
+	u32 tmp;
+	u32 prom_console_node = 0;
+
+	if(SBus_chain == 0)
+		return -1;
+
+	sbdprom = 0;
+	switch(prom_vers) {
+	case PROM_V0:
+		/* V0 proms are at sun4c only. Can skip many checks. */
+		con_type = FBTYPE_NOTYPE;
+		for_each_sbusdev(sbdp, SBus_chain) {
+			/* If no "address" than it is not the PROM console. */
+			if(sbdp->num_vaddrs) {
+				if(known_card(sbdp->prom_name, v0_known_cards)) {
+				   	sbdprom = sbdp;
+					strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
+					break;
+				}
+			}
+		}
+		if(!sbdprom) return -1;
+		for_each_sbusdev(sbdp, SBus_chain) {
+			con_node = sbdp->prom_node;
+
+			if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
+			   !strncmp(sbdp->prom_name, "cgthree+", 8)) {
+				type = FBTYPE_SUNFAST_COLOR;
+			} else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
+			   !strncmp(sbdp->prom_name, "cgRDI", 5)) {
+				type = FBTYPE_SUN3COLOR;
+			} else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
+				type = FBTYPE_SUN2BW;
+			} else
+				continue;
+			sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp, 
+						 (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
+						 sbdp->my_bus->prom_node);
+			/* XXX HACK */
+			if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+				break;
+		}
+		break;
+	case PROM_V2:
+	case PROM_V3:
+	case PROM_P1275:
+		if (console_fb_path) {
+			char *q, c;
+
+			for (q = console_fb_path; *q && *q != ' '; q++);
+			c = *q;
+			*q = 0;
+			default_node = prom_pathtoinode(console_fb_path);
+			if (default_node) {
+				prom_printf ("Using %s for console\n", console_fb_path);
+				prom_console_node = prom_inst2pkg(prom_stdout);
+				if (prom_console_node == default_node)
+					prom_console_node = 0;
+			}
+		}
+		if (!default_node)
+			default_node = prom_inst2pkg(prom_stdout);
+		propl = prom_getproperty(default_node, "device_type",
+					 prop, sizeof (prop));
+		if (propl < 0) {
+			prom_printf ("output-device doesn't have device_type property\n");
+			prom_halt ();
+		} else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
+		    	prop [propl] = 0;
+			prom_printf ("console_probe: output-device is %s"
+				     " (not \"display\")\n", prop);
+			prom_halt ();
+		}
+		for_all_sbusdev(sbdp, sbus) {
+			if ((sbdp->prom_node == default_node)
+			    && known_card (sbdp->prom_name, known_cards)) {
+			    	sbdprom = sbdp;
+				break;
+			}
+		}
+		if (sbdprom)
+		    card_found = 1;
+		if (!card_found)
+		    card_found = cg14 = cg14_present ();
+		if (!card_found){
+			card_found = creator = creator_present ();
+		}
+		if (!card_found){
+			prom_printf ("Could not find a known video card on this machine\n");
+			prom_halt ();
+		}
+		
+		for_all_sbusdev(sbdp, sbus) {
+			if (!known_card (sbdp->prom_name, known_cards))
+				continue;
+			con_node = sbdp->prom_node;
+			prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
+						sbdp->num_registers, sbdp);
+
+			propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
+			con_base = tmp;
+			if (propl != 4) con_base = 0;
+			propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
+			if (propl < 0 || propl >= sizeof (prom_name)) {
+				/* Early cg3s had no "emulation". */
+				propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
+				if (propl < 0) {
+					prom_printf("console: no device name!!\n");
+					return -1;
+				}
+			}
+			prom_name [sizeof (prom_name) - 1] = 0;
+			if(!strcmp(prom_name, "cgsix") ||
+			   !strcmp(prom_name, "cgthree+")) {
+				type = FBTYPE_SUNFAST_COLOR;
+			} else if(!strcmp(prom_name, "cgthree") ||
+			   !strcmp(prom_name, "cgRDI")) {
+				type = FBTYPE_SUN3COLOR;
+			} else if(!strcmp(prom_name, "cgfourteen")) {
+				type = FBTYPE_MDICOLOR;
+			} else if(!strcmp(prom_name, "SUNW,leo")) {
+				type = FBTYPE_SUNLEO;
+			} else if(!strcmp(prom_name, "bwtwo")) {
+				type = FBTYPE_SUN2BW;
+			} else if(!strcmp(prom_name,"SUNW,tcx")){
+				sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
+							 (uint)sbdp->reg_addrs [10].phys_addr, con_base, 
+							 prom_console_node == con_node, sbdp->my_bus->prom_node);
+				continue;
+			} else {
+				prom_printf("console: \"%s\" is unsupported\n", prom_name);
+				continue;
+			}
+			sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
+						 (uint)sbdp->reg_addrs [0].phys_addr, con_base,
+						 prom_console_node == con_node, sbdp->my_bus->prom_node);
+			/* XXX HACK */
+			if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
+				break;
+		}
+		if (cg14) {
+			sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR, 
+						 0, 0, 0, prom_console_node == cg14,
+						 prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
+		}
+		if (creator){
+			sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
+						 0, 0, 0, prom_console_node == creator,
+						 prom_root_node);
+		}
+		break;
+	default:
+		return -1;
+	}
+	
+	if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
+		prom_printf ("Couldn't setup your primary frame buffer.\n");
+		prom_halt ();
+	}
+
+	if (fbinfo [0].blitc)
+		do_accel = 1;
+	
+	con_fb_base = (unsigned char *)fbinfo[0].base;
+	if (!con_fb_base){
+		prom_printf ("PROM does not have an 'address' property for this\n"
+			     "frame buffer and the Linux drivers do not know how\n"
+			     "to map the video of this device\n");
+		prom_halt ();
+	}
+	sbus_install_consops();
+	return fb_init ();
+}
+
+/*
+ * sbus_blitc
+ *
+ * Displays an ASCII character at a specified character cell
+ *  position.
+ *
+ * Called from scr_writew() when the destination is
+ *  the "shadow" screen
+ */
+static uint
+fontmask_bits[16] = {
+    0x00000000,
+    0x000000ff,
+    0x0000ff00,
+    0x0000ffff,
+    0x00ff0000,
+    0x00ff00ff,
+    0x00ffff00,
+    0x00ffffff,
+    0xff000000,
+    0xff0000ff,
+    0xff00ff00,
+    0xff00ffff,
+    0xffff0000,
+    0xffff00ff,
+    0xffffff00,
+    0xffffffff
+};
+
+static int sbus_blitc(uint charattr, unsigned long addr)
+{
+	unsigned int fgmask, bgmask;
+	unsigned char attrib;
+	int j, idx;
+	unsigned char *font_row;
+
+	if (do_accel) {
+		(*fbinfo[0].blitc)(charattr, 
+			        x_margin + (((addr - video_mem_base) % video_size_row)<<2),
+		 	        y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
+		return 0;
+	}
+
+  	/* Invalidate the cursor position if necessary. */
+	idx = (addr - video_mem_base) >> 1;
+
+	attrib = CHARATTR_TO_SUNCOLOR(charattr);
+	font_row = &vga_font[(j = (charattr & 0xff)) << 4];
+
+	switch (con_depth){
+	case 1: {
+		register unsigned char *dst;
+		unsigned long flags;
+		
+		dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
+
+		__save_and_cli(flags);
+		if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
+			for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+				*dst = ~(*font_row);
+		} else {
+			for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
+				*dst = *font_row;
+		}
+		__restore_flags(flags);
+		break;
+	}
+	case 8: {
+#ifdef ASM_BLITC		
+		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");
+#else		
+		const int ipl = ints_per_line;
+		unsigned int data2, data3, data4;
+		unsigned int data, rowbits;
+		register unsigned int *dst;
+		unsigned long flags;
+#endif		
+		const uint *fontm_bits = fontmask_bits;
+		
+		dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
+		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"
+#define BLITC_SPC \
+		"\n\t std	%0, [%1]" \
+		"\n\t std	%0, [%1 + %2]"
+
+			x1 = attrib >> 4;
+			x1 |= x1 << 8;
+			x1 |= x1 << 16;
+			x3 = cpl << 1;
+			
+			__asm__ __volatile__ (
+				"\n\t mov	%2, %3"
+				BLITC_SPACE
+				BLITC_SPACE
+				BLITC_SPACE
+				BLITC_SPACE
+				BLITC_SPACE
+				BLITC_SPACE
+				BLITC_SPACE
+					: : "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));
+			else
+				__asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
+			__restore_flags (flags);
+#else
+			bgmask = attrib >> 4;
+			bgmask |= bgmask << 8;
+			bgmask |= bgmask << 16;
+			
+	                for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+        	                *dst = bgmask;
+        	                *(dst+1) = bgmask;
+                	}
+                	/* Prevent cursor spots left on the screen */
+			__save_and_cli(flags);
+			if (idx != cursor_pos) {
+	                	*dst = bgmask;
+        	        	*(dst+1) = bgmask;
+                		dst += ipl;
+	                	*dst = bgmask;
+        	        	*(dst+1) = bgmask;
+                	} else {
+	                	under_cursor [0] = bgmask;
+        	        	under_cursor [1] = bgmask;
+                		under_cursor [2] = bgmask;
+	                	under_cursor [3] = bgmask;
+        	        }
+	                __restore_flags(flags);
+#endif
+		} else /* non-space */ {
+			fgmask = attrib & 0x0f;
+			bgmask = attrib >> 4;
+			fgmask |= fgmask << 8;
+			fgmask |= fgmask << 16;
+			bgmask |= bgmask << 8;
+			bgmask |= bgmask << 16;
+
+#ifdef ASM_BLITC
+#define BLITC_INIT \
+			"\n\t ld	[%0], %%g2"
+#define BLITC_BODY(ST1,SC1,ST2,SC2)  \
+			"\n\t " #ST1 "	%%g2, " #SC1 ", %%g7"  \
+			"\n\t " #ST2 "	%%g2, " #SC2 ", %7"  \
+			"\n\t and	%%g7, 0x3c, %%g7"  \
+			"\n\t and	%7, 0x3c, %7"  \
+			"\n\t ld	[%1 + %%g7], %6"  \
+			"\n\t and	%6, %2, %%g7"  \
+			"\n\t andn	%3, %6, %6"  \
+			"\n\t or	%%g7, %6, %6"  \
+			"\n\t ld	[%1 + %7], %7"  \
+			"\n\t and	%7, %2, %%g7"  \
+			"\n\t andn	%3, %7, %7"  \
+			"\n\t or	%%g7, %7, %7"
+#define BLITC_BODYEND \
+			"\n\t sll	%3, 2, %%g7"  \
+			"\n\t srl	%3, 2, %3"  \
+			"\n\t and	%%g7, 0x3c, %%g7"  \
+			"\n\t and	%3, 0x3c, %3"  \
+			"\n\t ld	[%0 + %%g7], %4"  \
+			"\n\t and	%4, %1, %%g7"  \
+			"\n\t andn	%2, %4, %4"  \
+			"\n\t or	%%g7, %4, %4"  \
+			"\n\t ld	[%0 + %3], %3"  \
+			"\n\t and	%3, %1, %%g7"  \
+			"\n\t andn	%2, %3, %3"  \
+			"\n\t or	%%g7, %3, %3"
+#define BLITC_STOREIT \
+			"\n\t std	%6, [%5]"  \
+			"\n\t add	%5, %4, %5"  \
+			"\n\t" 
+#define BLITC_STORE \
+			"\n\t std	%%g4, [%0]"  \
+			"\n\t std	%%g2, [%0 + %1]"
+	
+			for (j = 0; j < 3; j++, font_row+=4) {
+				__asm__ __volatile__ (BLITC_INIT
+					BLITC_BODY(srl, 26, srl, 22)
+					BLITC_STOREIT
+					BLITC_BODY(srl, 18, srl, 14)
+					BLITC_STOREIT
+					BLITC_BODY(srl, 10, srl, 6)
+					BLITC_STOREIT
+					BLITC_BODY(srl, 2, sll, 2)
+					BLITC_STOREIT
+					: : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+					    "r" (x1), "r" (x2));
+			}
+			__asm__ __volatile__ (BLITC_INIT
+				BLITC_BODY(srl, 26, srl, 22)
+				BLITC_STOREIT
+				BLITC_BODY(srl, 18, srl, 14)
+				BLITC_STOREIT
+				/* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
+				BLITC_BODY(srl, 10, srl, 6)
+				: : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
+				    "r" (x1), "r" (x2));
+			/* Prepare the data the bottom line (and put it into g2,g3) */
+			__asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
+								"r" (x3), "r" (x4));
+			__save_and_cli(flags);
+			if (idx != cursor_pos)
+				__asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
+			else
+				__asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
+			__restore_flags (flags);
+#else
+	                for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
+        	                rowbits = *font_row;
+                	        data = fontm_bits[(rowbits>>4)&0xf];
+	                        data = (data & fgmask) | (~data & bgmask);
+        	                *dst = data;
+                	        data = fontm_bits[rowbits&0xf];
+	                        data = (data & fgmask) | (~data & bgmask);
+        	                *(dst+1) = data;
+                	}
+	                rowbits = *font_row;
+        	        data = fontm_bits[(rowbits>>4)&0xf];
+                	data = (data & fgmask) | (~data & bgmask);
+	                data2 = fontm_bits[rowbits&0xf];
+        	        data2 = (data2 & fgmask) | (~data2 & bgmask);
+                	rowbits = font_row[1];
+	                data3 = fontm_bits[(rowbits>>4)&0xf];
+        	        data3 = (data3 & fgmask) | (~data3 & bgmask);
+                	data4 = fontm_bits[rowbits&0xf];
+	                data4 = (data4 & fgmask) | (~data4 & bgmask);
+        	        
+                	/* Prevent cursor spots left on the screen */
+			__save_and_cli(flags);
+			
+			if (idx != cursor_pos) {
+	                	*dst = data;
+        	        	*(dst+1) = data2;
+                		dst += ipl;
+	                	*dst = data3;
+        	        	*(dst+1) = data4;
+                	} else {
+	                	under_cursor [0] = data;
+        	        	under_cursor [1] = data2;
+                		under_cursor [2] = data3;
+	                	under_cursor [3] = data4;
+        	        }
+                	
+	                __restore_flags(flags);
+#endif
+		}
+		break;
+	} /* case */
+	} /* switch */
+	return (0);
+}
+
+static void sbus_scr_writew(unsigned short val, unsigned short * addr)
+{
+        /*
+         * always deposit the char/attr, then see if it was to "screen" mem.
+	 * if so, then render the char/attr onto the real screen.
+         */
+	if (*addr != val) {
+        	*addr = val;
+		if ((unsigned long)addr < video_mem_term &&
+		    (unsigned long)addr >= video_mem_base &&
+		    vt_cons [fg_console]->vc_mode == KD_TEXT)
+			sbus_blitc(val, (unsigned long) addr);
+	}
+}
+
+static unsigned short sbus_scr_readw(unsigned short * addr)
+{
+	return *addr;
+}
+
+static void sbus_memsetw(void * s, unsigned short c, unsigned int count)
+{
+	unsigned short * addr = (unsigned short *) s;
+
+	count >>= 1;
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+		while (count) {
+			count--;
+			*addr++ = c;
+		}
+		return;
+	}
+	if ((unsigned long) addr + count > video_mem_term ||
+	    (unsigned long) addr < video_mem_base) {
+	    	if ((unsigned long) addr + count <= video_mem_term ||
+	    	    (unsigned long) addr > video_mem_base) {
+			while (count) {
+				count--;
+				*addr++ = c;
+			}
+			return;
+	    	} else {
+			while (count) {
+				count--;
+				scr_writew(c, addr++);
+			}
+		}
+#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
+	} else if (do_accel) {
+		int yoff = (((long)addr - (long)video_mem_base) >> 1) / video_num_columns;
+		int xoff = (((long)addr - (long)video_mem_base) >> 1) % video_num_columns;
+		unsigned short * last = addr;
+		
+		while (count) {
+			count--;
+			if (*addr != c) {
+				if (xoff == video_num_columns) {
+					if (last != addr)
+						GX_SETW
+					xoff = 0;
+					yoff++;
+					last = addr;
+				}
+				*addr++ = c;
+				xoff++;
+			} else {
+				if (last != addr)
+					GX_SETW
+				if (xoff == video_num_columns) {
+					xoff = 0;
+					yoff++;
+				}
+				addr++;
+				xoff++;
+				last = addr;
+			}
+		}
+		if (last != addr)
+			GX_SETW
+	} else {
+		while (count) {
+			count--;
+			if (*addr != c) {
+				sbus_blitc(c, (unsigned long)addr);
+				*addr++ = c;
+			} else
+				addr++;
+		}
+	}
+}
+
+static void sbus_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+{
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
+		memcpy(to, from, count);
+		return;
+	}
+	if ((unsigned long) to + count > video_mem_term ||
+	    (unsigned long) to < video_mem_base) {
+	    	if ((unsigned long) to + count <= video_mem_term ||
+	    	    (unsigned long) to > video_mem_base)
+	    	    	memcpy(to, from, count);
+	    	else {
+	    		count >>= 1;
+			while (count) {
+				count--;
+				scr_writew(scr_readw(from++), to++);
+			}
+		}
+#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
+	} else if (do_accel) {
+		int yoff = (((long)to - (long)video_mem_base) >> 1) / video_num_columns;
+		int xoff = (((long)to - (long)video_mem_base) >> 1) % video_num_columns;
+		unsigned short * last = to;
+		
+		count >>=1;
+		while (count) {
+			count--;
+			if (*to != *from) {
+				if (xoff == video_num_columns) {
+					if (last != to)
+						GX_CPYW
+					xoff = 0;
+					yoff++;
+					last = to;
+				} else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
+					GX_CPYW
+					last = to;
+				}
+				*to++ = *from++;
+				xoff++;
+			} else {
+				if (last != to)
+					GX_CPYW
+				if (xoff == video_num_columns) {
+					xoff = 0;
+					yoff++;
+				}
+				to++;
+				xoff++;
+				last = to;
+				from++;
+			}
+		}
+		if (last != to)
+			GX_CPYW
+	} else {
+		count >>= 1;
+		while (count) {
+			count--;
+			if (*to != *from) {
+				sbus_blitc(*from, (unsigned long)to);
+				*to++ = *from++;
+			} else {
+				from++;
+				to++;
+			}
+		}
+	}
+}
+
+#undef pos
+int sbus_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
+{
+	int op;
+	int i, bytes = 0;
+	struct fbcursor f;
+	char red[2], green[2], blue[2];
+	
+	if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+		return -EFAULT;
+	op = f.set;
+	if (op & FB_CUR_SETSHAPE){
+		if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
+			return -EINVAL;
+		if ((uint) f.size.fby > fb->cursor.hwsize.fby)
+			return -EINVAL;
+		if (f.size.fbx > 32)
+			bytes = f.size.fby << 3;
+		else
+			bytes = f.size.fby << 2;
+	}
+	if (op & FB_CUR_SETCMAP){
+		if (f.cmap.index || f.cmap.count != 2)
+			return -EINVAL;
+		if (copy_from_user (red, f.cmap.red, 2) ||
+		    copy_from_user (green, f.cmap.green, 2) ||
+		    copy_from_user (blue, f.cmap.blue, 2))
+			return -EFAULT;
+	}
+	if (op & FB_CUR_SETCMAP)
+		(*fb->setcursormap) (fb, red, green, blue);
+	if (op & FB_CUR_SETSHAPE){
+		uint u;
+		
+		fb->cursor.size = f.size;
+		memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+		if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+		    copy_from_user (fb->cursor.bits [1], f.image, bytes))
+			return -EFAULT;
+		if (f.size.fbx <= 32) {
+			u = ~(0xffffffff >> f.size.fbx);
+			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+				fb->cursor.bits [0][i] &= u;
+				fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+			}
+		} else {
+			u = ~(0xffffffff >> (f.size.fbx - 32));
+			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+				fb->cursor.bits [0][2*i+1] &= u;
+				fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+				fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+			}
+		}
+		(*fb->setcurshape) (fb);
+	}
+	if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+		if (op & FB_CUR_SETCUR)
+			fb->cursor.enable = f.enable;
+		if (op & FB_CUR_SETPOS)
+			fb->cursor.cpos = f.pos;
+		if (op & FB_CUR_SETHOT)
+			fb->cursor.chot = f.hot;
+		(*fb->setcursor) (fb);
+	}
+	return 0;
+}
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+void sbus_hw_hide_cursor (void)
+{
+	fbinfo[0].cursor.enable = 0;
+	(*fbinfo[0].setcursor)(&fbinfo[0]);
+	sbus_hw_cursor_shown = 0;
+}
+
+void sbus_hw_set_cursor (int xoff, int yoff)
+{
+	if (!sbus_hw_cursor_shown) {
+		fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
+		fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
+		fbinfo[0].cursor.chot.fbx = 0;
+		fbinfo[0].cursor.chot.fby = 0;
+		fbinfo[0].cursor.enable = 1;
+		memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
+		fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
+		fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
+		fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
+		fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
+		(*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+		(*fbinfo[0].setcurshape) (&fbinfo[0]);
+		sbus_hw_cursor_shown = 1;
+	}
+	fbinfo[0].cursor.cpos.fbx = xoff;
+	fbinfo[0].cursor.cpos.fby = yoff;
+	(*fbinfo[0].setcursor)(&fbinfo[0]);
+}
+
+__initfunc(static void sbus_install_consops(void))
+{
+	suncons_ops.memsetw = sbus_memsetw;
+	suncons_ops.memcpyw = sbus_memcpyw;
+	suncons_ops.scr_writew = sbus_scr_writew;
+	suncons_ops.scr_readw = sbus_scr_readw;
+
+	suncons_ops.get_scrmem = sbus_get_scrmem;
+	suncons_ops.set_scrmem = sbus_set_scrmem;
+
+	suncons_ops.hide_cursor = sbus_hide_cursor;
+	suncons_ops.set_cursor = sbus_set_cursor;
+	suncons_ops.set_get_font = sbus_set_get_font;
+	suncons_ops.con_adjust_height = sbus_con_adjust_height;
+	suncons_ops.set_get_cmap = sbus_set_get_cmap;
+	suncons_ops.set_palette = sbus_set_palette;
+	suncons_ops.set_other_palette = sbus_set_other_palette;
+	suncons_ops.console_restore_palette = sbus_console_restore_palette;
+
+	suncons_ops.con_type_init = sbus_con_type_init;
+	suncons_ops.con_type_init_finish = sbus_con_type_init_finish;
+
+	suncons_ops.clear_screen = sbus_clear_screen;
+	suncons_ops.render_screen = sbus_render_screen;
+	suncons_ops.clear_margin = sbus_clear_margin;
+}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.1.52/linux/drivers/sbus/char/su.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/su.c	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,678 @@
+/* $Id: su.c,v 1.3 1997/09/03 11:54:56 ecd Exp $
+ * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ * This is mainly a very stripped down version of drivers/char/serial.c,
+ * credits go to authors mentioned therein.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <asm/oplib.h>
+#include <asm/io.h>
+#include <asm/ebus.h>
+
+#include "sunserial.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static char *serial_name = "kbd/mouse serial driver";
+static char *serial_version = "1.0";
+
+/* Set of debugging defines */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+
+/* We are on a NS PC87303 clocked with 24.0 MHz, which results
+ * in a UART clock of 1.8462 MHz.
+ */
+#define BAUD_BASE	(1846200 / 16)
+
+struct su_struct {
+	int		 magic;
+	unsigned long	 port;
+	int		 baud_base;
+	int		 type;
+	int		 irq;
+	int		 flags;
+	unsigned char	 IER;
+	unsigned char	 MCR;
+	int		 line;
+	int		 cflag;
+	int		 kbd_node;
+	int		 ms_node;
+	char		 name[16];
+};
+
+static struct su_struct su_table[] = {
+	{ 0, 0, BAUD_BASE, PORT_UNKNOWN },
+	{ 0, 0, BAUD_BASE, PORT_UNKNOWN }
+};
+
+#define NR_PORTS (sizeof(su_table) / sizeof(struct su_struct))
+
+static void autoconfig(struct su_struct * info);
+static void change_speed(struct su_struct *info);
+
+/*
+ * Here we define the default xmit fifo size used for each type of
+ * UART
+ */
+static struct serial_uart_config uart_config[] = {
+	{ "unknown", 1, 0 }, 
+	{ "8250", 1, 0 }, 
+	{ "16450", 1, 0 }, 
+	{ "16550", 1, 0 }, 
+	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, 
+	{ "cirrus", 1, 0 }, 
+	{ "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH }, 
+	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+		  UART_STARTECH }, 
+	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+	{ 0, 0}
+};
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+static inline
+unsigned int su_inb(struct su_struct *info, unsigned long offset)
+{
+	return inb(info->port + offset);
+}
+
+static inline void
+su_outb(struct su_struct *info, unsigned long offset, int value)
+{
+	outb(value, info->port + offset);
+}
+
+static inline void
+receive_chars(struct su_struct *info, struct pt_regs *regs)
+{
+	unsigned char status = 0;
+	unsigned char ch;
+
+	do {
+		ch = su_inb(info, UART_RX);
+
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x...", ch, status);
+#endif
+
+		if (info->kbd_node) {
+			if(ch == SUNKBD_RESET) {
+                        	l1a_state.kbd_id = 1;
+                        	l1a_state.l1_down = 0;
+                	} else if(l1a_state.kbd_id) {
+                        	l1a_state.kbd_id = 0;
+                	} else if(ch == SUNKBD_L1) {
+                        	l1a_state.l1_down = 1;
+                	} else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+                        	l1a_state.l1_down = 0;
+                	} else if(ch == SUNKBD_A && l1a_state.l1_down) {
+                        	/* whee... */
+                        	batten_down_hatches();
+                        	/* Continue execution... */
+                        	l1a_state.l1_down = 0;
+                        	l1a_state.kbd_id = 0;
+                        	return;
+                	}
+                	sunkbd_inchar(ch, regs);
+		} else {
+			sun_mouse_inbyte(ch);
+		}
+
+		status = su_inb(info, UART_LSR);
+	} while (status & UART_LSR_DR);
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void
+su_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	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
+
+	if (su_inb(info, UART_IIR) & UART_IIR_NO_INT)
+		return;
+
+	status = su_inb(info, UART_LSR);
+#ifdef SERIAL_DEBUG_INTR
+	printk("status = %x...", status);
+#endif
+	if (status & UART_LSR_DR)
+		receive_chars(info, regs);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+static int
+startup(struct su_struct * info)
+{
+	unsigned long flags;
+	int	retval=0;
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		goto errout;
+	}
+
+	if (!info->port || !info->type) {
+		goto errout;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up su%d (irq %x)...", info->line, info->irq);
+#endif
+
+	if (info->type == PORT_16750)
+		su_outb(info, UART_IER, 0);
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	if (uart_config[info->type].flags & UART_CLEAR_FIFO)
+		su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+					     UART_FCR_CLEAR_XMIT));
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (su_inb(info, UART_LSR) == 0xff) {
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Allocate the IRQ if necessary
+	 */
+	retval = request_irq(info->irq, su_interrupt, SA_SHIRQ,
+			     info->name, info);
+	if (retval)
+		goto errout;
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	su_inb(info, UART_RX);
+	su_inb(info, UART_IIR);
+	su_inb(info, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	su_outb(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+
+	info->MCR = UART_MCR_OUT2;
+	su_outb(info, UART_MCR, info->MCR);
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->IER = UART_IER_RLSI | UART_IER_RDI;
+	su_outb(info, UART_IER, info->IER);	/* enable interrupts */
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	su_inb(info, UART_LSR);
+	su_inb(info, UART_RX);
+	su_inb(info, UART_IIR);
+	su_inb(info, UART_MSR);
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+	
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+change_speed(struct su_struct *info)
+{
+	unsigned char cval, fcr = 0;
+	int quot = 0;
+	unsigned long flags;
+	int i;
+
+	/* byte size and parity */
+	switch (info->cflag & CSIZE) {
+		case CS5: cval = 0x00; break;
+		case CS6: cval = 0x01; break;
+		case CS7: cval = 0x02; break;
+		case CS8: cval = 0x03; break;
+		/* Never happens, but GCC is too dumb to figure it out */
+		default:  cval = 0x00; break;
+	}
+	if (info->cflag & CSTOPB) {
+		cval |= 0x04;
+	}
+	if (info->cflag & PARENB) {
+		cval |= UART_LCR_PARITY;
+	}
+	if (!(info->cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (info->cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/* Determine divisor based on baud rate */
+	i = info->cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 4) 
+			info->cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (!quot) {
+		if (baud_table[i] == 134)
+			/* Special case since 134 is really 134.5 */
+			quot = (2 * info->baud_base / 269);
+		else if (baud_table[i])
+			quot = info->baud_base / baud_table[i];
+		/* If the quotient is ever zero, default to 1200 bps */
+		if (!quot)
+			quot = info->baud_base / 1200;
+	}
+
+	/* Set up FIFO's */
+	if (uart_config[info->type].flags & UART_USE_FIFO)
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+
+	su_outb(info, UART_IER, info->IER);
+
+	save_flags(flags); cli();
+	su_outb(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	su_outb(info, UART_DLL, quot & 0xff);	/* LS of divisor */
+	su_outb(info, UART_DLM, quot >> 8);		/* MS of divisor */
+	if (info->type == PORT_16750)
+		su_outb(info, UART_FCR, fcr); 	/* set fcr */
+	su_outb(info, UART_LCR, cval);		/* reset DLAB */
+	if (info->type != PORT_16750)
+		su_outb(info, UART_FCR, fcr); 	/* set fcr */
+	restore_flags(flags);
+}
+
+static void
+su_put_char(unsigned char c)
+{
+	struct su_struct *info = su_table;
+	int lsr;
+
+	if (!info->kbd_node)
+		++info;
+
+	do {
+		lsr = inb(info->port + UART_LSR);
+	} while (!(lsr & UART_LSR_THRE));
+
+	/* Send the character out. */
+	su_outb(info, UART_TX, c);
+}
+
+static void
+su_change_mouse_baud(int baud)
+{
+	struct su_struct *info = su_table;
+
+	if (!info->ms_node)
+		++info;
+	if (!info)
+		return;
+
+	info->cflag &= ~(CBAUDEX | CBAUD);
+	switch(baud) {
+		case 1200:
+			info->cflag |= B1200;
+			break;
+		case 2400:
+			info->cflag |= B2400;
+			break;
+		case 4800:
+			info->cflag |= B4800;
+			break;
+		case 9600:
+			info->cflag |= B9600;
+			break;
+		default:
+			printk("su_change_mouse_baud: unknown baud rate %d, "
+			       "defaulting to 1200\n", baud);
+			info->cflag |= 1200;
+			break;
+	}
+	change_speed(info);
+}
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void
+show_su_version(void)
+{
+ 	printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+/*
+ * This routine is called by su_init() to initialize a specific serial
+ * port.  It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void
+autoconfig(struct su_struct *info)
+{
+	unsigned char status1, status2, scratch, scratch2;
+	struct linux_ebus_device *dev;
+	struct linux_ebus *ebus;
+	unsigned long flags;
+
+	for_all_ebusdev(dev, ebus) {
+		if (!strncmp(dev->prom_name, "su", 2)) {
+			if (dev->prom_node == info->kbd_node)
+				break;
+			if (dev->prom_node == info->ms_node)
+				break;
+		}
+	}
+	if (!dev)
+		return;
+
+	info->port = dev->base_address[0];
+	if (check_region(info->port, 8))
+		return;
+
+	info->irq = dev->irqs[0];
+
+#ifdef DEBUG_SERIAL_OPEN
+	printk("Found 'su' at %016lx IRQ %08x\n",
+	       dev->base_address[0], dev->irqs[0]);
+#endif
+
+	info->magic = SERIAL_MAGIC;
+
+	save_flags(flags); cli();
+	
+	/*
+	 * Do a simple existence test first; if we fail this, there's
+	 * no point trying anything else.
+	 *
+	 * 0x80 is used as a nonsense port to prevent against false
+	 * positives due to ISA bus float.  The assumption is that
+	 * 0x80 is a non-existent port; which should be safe since
+	 * include/asm/io.h also makes this assumption.
+	 */
+	scratch = su_inb(info, UART_IER);
+	su_outb(info, UART_IER, 0);
+	scratch2 = su_inb(info, UART_IER);
+	su_outb(info, UART_IER, scratch);
+	if (scratch2) {
+		restore_flags(flags);
+		return;		/* We failed; there's nothing here */
+	}
+
+	scratch = su_inb(info, UART_MCR);
+	su_outb(info, UART_MCR, UART_MCR_LOOP | scratch);
+	scratch2 = su_inb(info, UART_MSR);
+	su_outb(info, UART_MCR, UART_MCR_LOOP | 0x0A);
+	status1 = su_inb(info, UART_MSR) & 0xF0;
+	su_outb(info, UART_MCR, scratch);
+	su_outb(info, UART_MSR, scratch2);
+	if (status1 != 0x90) {
+		restore_flags(flags);
+		return;
+	} 
+
+	scratch2 = su_inb(info, UART_LCR);
+	su_outb(info, UART_LCR, 0xBF);	/* set up for StarTech test */
+	su_outb(info, UART_EFR, 0);	/* EFR is the same as FCR */
+	su_outb(info, UART_LCR, 0);
+	su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+	scratch = su_inb(info, UART_IIR) >> 6;
+	switch (scratch) {
+		case 0:
+			info->type = PORT_16450;
+			break;
+		case 1:
+			info->type = PORT_UNKNOWN;
+			break;
+		case 2:
+			info->type = PORT_16550;
+			break;
+		case 3:
+			info->type = PORT_16550A;
+			break;
+	}
+	if (info->type == PORT_16550A) {
+		/* Check for Startech UART's */
+		su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+		if (su_inb(info, UART_EFR) == 0) {
+			info->type = PORT_16650;
+		} else {
+			su_outb(info, UART_LCR, 0xBF);
+			if (su_inb(info, UART_EFR) == 0)
+				info->type = PORT_16650V2;
+		}
+	}
+	if (info->type == PORT_16550A) {
+		/* Check for TI 16750 */
+		su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+		su_outb(info, UART_FCR,
+			    UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+		scratch = su_inb(info, UART_IIR) >> 5;
+		if (scratch == 7) {
+			su_outb(info, UART_LCR, 0);
+			su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+			scratch = su_inb(info, UART_IIR) >> 5;
+			if (scratch == 6)
+				info->type = PORT_16750;
+		}
+		su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+	}
+	su_outb(info, UART_LCR, scratch2);
+	if (info->type == PORT_16450) {
+		scratch = su_inb(info, UART_SCR);
+		su_outb(info, UART_SCR, 0xa5);
+		status1 = su_inb(info, UART_SCR);
+		su_outb(info, UART_SCR, 0x5a);
+		status2 = su_inb(info, UART_SCR);
+		su_outb(info, UART_SCR, scratch);
+
+		if ((status1 != 0xa5) || (status2 != 0x5a))
+			info->type = PORT_8250;
+	}
+
+	if (info->type == PORT_UNKNOWN) {
+		restore_flags(flags);
+		return;
+	}
+
+	sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
+	request_region(info->port, 8, info->name);
+
+	/*
+	 * Reset the UART.
+	 */
+	su_outb(info, UART_MCR, 0x00);
+	su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+				     UART_FCR_CLEAR_XMIT));
+	su_inb(info, UART_RX);
+
+	restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+__initfunc(int su_init(void))
+{
+	int i;
+	struct su_struct *info;
+	
+	show_su_version();
+
+	for (i = 0, info = su_table; i < NR_PORTS; i++, info++) {
+		info->line = i;
+		if (info->kbd_node)
+			info->cflag = B1200 | CS8 | CREAD;
+		else
+			info->cflag = B4800 | CS8 | CREAD;
+
+		autoconfig(info);
+		if (info->type == PORT_UNKNOWN)
+			continue;
+
+		printk(KERN_INFO "%s at %16lx (irq = %08x) is a %s\n",
+		       info->name, info->port, info->irq,
+		       uart_config[info->type].name);
+
+		startup(info);
+		if (info->kbd_node)
+			keyboard_zsinit(su_put_char);
+		else
+			sun_mouse_zsinit();
+	}
+	return 0;
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+	struct su_struct *info = su_table;
+        int node, enode, sunode;
+	int kbnode = 0, msnode = 0;
+	int devices = 0;
+	char prop[128];
+	int len;
+
+	/*
+	 * Get the nodes for keyboard and mouse from 'aliases'...
+	 */
+        node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "aliases");
+	if (!node)
+		return -ENODEV;
+
+	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
+	if (len > 0)
+		kbnode = prom_pathtoinode(prop);
+	if (!kbnode)
+		return -ENODEV;
+
+	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
+	if (len > 0)
+		msnode = prom_pathtoinode(prop);
+	if (!msnode)
+		return -ENODEV;
+
+	/*
+	 * Find matching EBus nodes...
+	 */
+        node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "pci");
+
+	/*
+	 * For each PCI bus...
+	 */
+	while (node) {
+		enode = prom_getchild(node);
+		enode = prom_searchsiblings(enode, "ebus");
+
+		/*
+		 * For each EBus on this PCI...
+		 */
+		while (enode) {
+			sunode = prom_getchild(enode);
+			sunode = prom_searchsiblings(sunode, "su");
+
+			/*
+			 * For each 'su' on this EBus...
+			 */
+			while (sunode) {
+				/*
+				 * Does it match?
+				 */
+				if (sunode == kbnode) {
+					info->kbd_node = kbnode;
+					++info;
+					++devices;
+				}
+				if (sunode == msnode) {
+					info->ms_node = msnode;
+					++info;
+					++devices;
+				}
+
+				/*
+				 * Found everything we need?
+				 */
+				if (devices == NR_PORTS)
+					goto found;
+
+				sunode = prom_getsibling(sunode);
+				sunode = prom_searchsiblings(sunode, "su");
+			}
+			enode = prom_getsibling(enode);
+			enode = prom_searchsiblings(enode, "ebus");
+		}
+		node = prom_getsibling(node);
+		node = prom_searchsiblings(node, "pci");
+	}
+	return -ENODEV;
+
+found:
+        sunserial_setinitfunc(memory_start, su_init);
+        rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+	return 0;
+}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.1.52/linux/drivers/sbus/char/suncons.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/sbus/char/suncons.c	Thu Sep  4 12:54:49 1997
@@ -1,1742 +1,376 @@
-/* $Id: suncons.c,v 1.67 1997/07/20 05:59:42 davem Exp $
- *
- * suncons.c: Sun SparcStation console support.
- *
- * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- *
- * Added font loading Nov/21, Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Added render_screen and faster scrolling Nov/27, miguel
- * Added console palette code for cg6 Dec/13/95, miguel
- * Added generic frame buffer support Dec/14/95, miguel
- * Added cgsix and bwtwo drivers Jan/96, miguel
- * Added 4m, and cg3 driver Feb/96, miguel
- * Fixed the cursor on color displays Feb/96, miguel.
- * Cleaned up the detection code, generic 8bit depth display 
- *   code, Mar/96 miguel
- * Hacked support for cg14 video cards -- Apr/96, miguel.
- * Color support for cg14 video cards -- May/96, miguel.
- * Code split, Dave Redman, May/96
- * Be more VT change friendly, May/96, miguel.
- * Support for hw cursor and graphics acceleration, Jun/96, jj.
- * Added TurboGX+ detection (cgthree+), Aug/96, Iain Lea (iain@sbs.de)
- * Added TCX support (8/24bit), Aug/96, jj.
- * Support for multiple framebuffers, Sep/96, jj.
- * Fix bwtwo inversion and handle inverse monochrome cells in
- *   sun_blitc, Nov/96, ecd.
- * Fix sun_blitc and screen size on displays other than 1152x900, 
- *   128x54 chars, Nov/96, jj.
- * Fix cursor spots left on some non-accelerated fbs, changed
- *   software cursor to be like the hw one, Nov/96, jj.
- * 
- * Much of this driver is derived from the DEC TGA driver by
- * Jay Estabrook who has done a nice job with the console
- * driver abstraction btw.
- *
- * We try to make everything a power of two if possible to
- * speed up the bit blit.  Doing multiplies, divides, and
- * remainder routines end up calling software library routines
- * since not all Sparcs have the hardware to do it.
- *
- * TODO:
- * do not blank the screen when frame buffer is mapped.
+/* $Id: suncons.c,v 1.73 1997/08/25 07:50:33 jj Exp $
+ * suncons.c: Sparc platform console generic layer.
  *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
  */
 
-
 #include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
 #include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kd.h>
-#include <linux/malloc.h>
-#include <linux/major.h>
-#include <linux/mm.h>
 #include <linux/types.h>
-#include <linux/version.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/vt.h>
+#include <linux/selection.h>
 #include <linux/proc_fs.h>
 
-#include <asm/system.h>
-#include <asm/uaccess.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/bitops.h>
-#include <asm/oplib.h>
 #include <asm/sbus.h>
 #include <asm/fbio.h>
-#include <asm/io.h>
-#include <asm/smp.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/consolemap.h>
-#include <linux/selection.h>
-#include <linux/console_struct.h>
 
 #include "fb.h"
 
-#define cmapsz 8192
-
-#include "suncons_font.h"
 #include <asm/linux_logo.h>
 
 fbinfo_t *fbinfo;
 int fbinfos;
+unsigned int linux_logo_colors __initdata = LINUX_LOGO_COLORS;
+char logo_banner[] __initdata = linux_logo_banner;
 
-#define ASM_BLITC
-
-int sun_hw_cursor_shown = 0;
-
-void sun_hw_hide_cursor(void);
-void sun_hw_set_cursor(int,int);
-
-extern void register_console(void (*proc)(const char *));
-extern void console_print(const char *);
-extern void putconsxy(int, char *);
-extern unsigned char vga_font[];
-extern int serial_console;
-char *console_fb_path = NULL; /* Set in setup.c */
-
-/* The following variables describe a Sparc console. */
-
-/* Screen dimensions and color depth. */
-static int con_depth, con_width, con_height, con_type;
+extern struct console vt_console_driver;
 
-/* Base address of first line. */
-static unsigned char *con_fb_base;
-
-/* Screen parameters: we compute those at startup to make the code faster */
-static int chars_per_line;	/* number of bytes per line */
-static int ints_per_line;	/* number of ints per  line */
-static int ints_per_cursor;  	/* 14 * ints_per_line */
-static int skip_bytes;		/* number of bytes we skip for the y margin + x_margin */
-static int x_margin, y_margin;	/* the x and y margins */
-static int bytes_per_row;	/* bytes used by one screen line (of 16 scan lines)  */
-int sun_prom_console_id = 0;
-
-/* Functions used by the SPARC dependent console code
- * to perform the fb_restore_palette function.
- */
-void (*fb_restore_palette)(fbinfo_t *fbinfo);
-void set_palette (void);
+/* Infrastructure. */
 
- /* Our screen looks like at 1152 X 900:
- *
- *  0,0
- *      ------------------------------------------------------------------
- *      |                          ^^^^^^^^^^^                           |
- *      |                          18 y-pixels                           |
- *      |                          ^^^^^^^^^^^                           |
- *   13 | <-64 pixels->|  <-- 128 8x16 characters -->    | <-64 pixels-> |
- *    ....
- *                         54 chars from top to bottom
- *    ....
- *  888 | <-64 pixels->|  <-- 128 8x16 characters -->    | <-64 pixels-> |
- *      |                          ^^^^^^^^^^^                           |
- *      |                          18 y-pixels                           |
- *      |                          ^^^^^^^^^^^                           |
- *      ------------------------------------------------------------------
- */
-/* First for MONO displays. */
-#define SCREEN_WIDTH     1152     /* Screen width in pixels  */
-#define SCREEN_HEIGHT    900      /* Screen height in pixels */
-#define CHARS_PER_LINE   144      /* Make this empirical for speed */
-#define NICE_Y_MARGIN    18       /* We skip 18 y-pixels at top/bottom */
-#define NICE_X_MARGIN    8        /* We skip 64 x-pixels at left/right */
-#define FBUF_TOP_SKIP    2592     /* Empirical, (CHARS_PER_LINE * NICE_Y_MARGIN) */
-#define CHAR_HEIGHT      16
-#define ONE_ROW          2304     /* CHARS_PER_LINE * CHAR_HEIGHT */
-
-/* Now we have this, to compute the base frame buffer position
- * for a new character to be rendered. 1 and 8 bit depth.
- */
-#define FBUF_OFFSET(cindex) \
-        (((FBUF_TOP_SKIP) + (((cindex)>>7) * ONE_ROW)) + \
-	 ((NICE_X_MARGIN) + (((cindex)&127))))
-
-
-#define COLOR_FBUF_OFFSET(cindex) (*color_fbuf_offset)(cindex)
-
-/* These four routines are optimizations for the _generic routine for
- * the most common cases.
- * I guess doing twice sll is much faster than doing .mul, sra faster
- * than doing .div, and the disadvantage that someone has to call it
- * (it cannot be inline) runs away, 'cause otherwise it would have to
- * call .mul anyway. 
- * The shifting + addition only routines won't eat any stack frame :))
- * Names come from width, screen_num_columns.
- */
-static int
-color_fbuf_offset_1280_144 (int cindex)
+static void nop_memsetw(void *s, unsigned short c, unsigned int count)
 {
-	register int i = (cindex/144);
-	/* (1280 * CHAR_HEIGHT) == 101.0000.0000.0000 */
-	return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
 }
 
-static int
-color_fbuf_offset_1152_128 (int cindex)
+static void nop_memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
 {
-	register int i = (cindex>>7);
-	/* (1152 * CHAR_HEIGHT) == 100.1000.0000.0000 */
-	return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
 }
 
-static int
-color_fbuf_offset_1024_128 (int cindex)
+static void nop_scr_writew(unsigned short val, unsigned short *addr)
 {
-	register int i = (cindex>>7);
-	/* (1024 * CHAR_HEIGHT) == 100.0000.0000.0000 */
-	return skip_bytes + (i << 14) + ((cindex & 127) << 3);
 }
 
-static int
-color_fbuf_offset_800_96 (int cindex)
+static unsigned short nop_scr_readw(unsigned short *addr)
 {
-	register int i = (cindex / 96);
-	/* (800 * CHAR_HEIGHT) == 11.0010.0000.0000 */
-	return skip_bytes + (i<<13) + (i<<12) + (i<<9) + ((cindex % 96)<<3);
+	return 0;
 }
 
-static int
-color_fbuf_offset_640_80 (int cindex)
+static void nop_get_scrmem(int a)
 {
-	register int i = (cindex/80);
-	/* (640 * CHAR_HEIGHT) == 10.1000.0000.0000 */
-	return skip_bytes + (i << 13) + (i << 11) + ((cindex % 80) << 3);
-}
-	 
-static int
-color_fbuf_offset_generic (int cindex)
-{
-	return skip_bytes + (cindex / video_num_columns) * bytes_per_row + ((cindex % video_num_columns) << 3);
 }
 
-static int (*color_fbuf_offset)(int) = color_fbuf_offset_generic;
-	 
-static int do_accel = 0;
-
-void
-__set_origin(unsigned short offset)
+static void nop_set_scrmem(int a, long b)
 {
-	/*
-	 * should not be called, but if so, do nothing...
-	 */
 }
 
-/* For the cursor, we just invert the 8x16 block at the cursor
- * location.  Easy enough...
- *
- * Hide the cursor from view, during blanking, usually...
- */
-static int cursor_pos = -1;
-
-static unsigned int under_cursor[4];
-
-void
-hide_cursor(void)
+static void nop_set_origin(unsigned short offset)
 {
-	unsigned long flags;
-	int j;
-
-	if (fbinfo[0].setcursor) {
-		sun_hw_hide_cursor();
-		return;
-	}
-	
-	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
-		return; /* Don't paint anything on fb which is not ours,
-			   but turn off the hw cursor in such case */
-	
-	__save_and_cli(flags);
-
-	if(cursor_pos == -1) {
-		__restore_flags (flags);
-		return;
-	}
-	switch (con_depth){
-	case 1: {
-		unsigned char *dst;
-		dst = (unsigned char *)((unsigned long)con_fb_base +
-					FBUF_OFFSET(cursor_pos));
-		for(j = 0; j < CHAR_HEIGHT; j++, dst += CHARS_PER_LINE)
-			*dst = ~(*dst);
-		break;
-	}
-	case 8: {
-		unsigned int *dst;
-		
-		dst = (unsigned int *)((unsigned long)con_fb_base +
-					COLOR_FBUF_OFFSET(cursor_pos)) + ints_per_cursor;
-		dst[0] = under_cursor[0];
-		dst[1] = under_cursor[1];
-		dst[ints_per_line] = under_cursor[2];
-		dst[ints_per_line+1] = under_cursor[3];
-		break;
-	}
-	default:
-		break;
-	}
-	cursor_pos = -1;
-	__restore_flags(flags);
 }
 
-void
-set_cursor(int currcons)
+static void nop_hide_cursor(void)
 {
-	int j, idx, oldpos;
-	unsigned long flags;
-
-	if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
-		return;
-
-#if 0
-/* This is a nop anyway */
-	if (__real_origin != __origin)
-		__set_origin(__real_origin);
-#endif
-		
-	if (fbinfo[0].setcursor) {
-		if (!deccm)
-			hide_cursor();
-		else {
-			idx = (pos - video_mem_base) >> 1;
-			
-			sun_hw_set_cursor(x_margin + ((idx % video_num_columns) << 3), y_margin + ((idx / video_num_columns) * CHAR_HEIGHT));
-		}
-		return;
-	}
-
-	__save_and_cli(flags);
-
-	idx = (pos - video_mem_base) >> 1;
-	oldpos = cursor_pos;
-	if (!deccm) {
-		hide_cursor ();
-		__restore_flags (flags);
-		return;
-	}
-	cursor_pos = idx;
-	switch (con_depth){
-	case 1: {
-		unsigned char *dst, *opos;
-		
-		dst = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(idx));
-		opos = (unsigned char *)((unsigned long)con_fb_base + FBUF_OFFSET(oldpos));
-		if(oldpos != -1) {
-			/* Restore what was at the old position */
-			for(j=0; j < CHAR_HEIGHT; j++, opos += CHARS_PER_LINE) {
-				*opos = ~*opos;
-			}
-		}
-		for(j=0; j < 16; j++, dst+=CHARS_PER_LINE) {
-			*dst = ~*dst;
-		}
-		break;
-	}
-	case 8: {
-		unsigned int *dst, *opos;
-		dst = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(idx)) + ints_per_cursor;
-			
-		if(oldpos != -1) {
-			opos = (unsigned int *)((unsigned long)con_fb_base + COLOR_FBUF_OFFSET(oldpos)) + ints_per_cursor;
-			opos[0] = under_cursor[0];
-			opos[1] = under_cursor[1];
-			opos[ints_per_line] = under_cursor[2];
-			opos[ints_per_line+1] = under_cursor[3];
-		}
-		under_cursor[0] = dst[0];
-		under_cursor[1] = dst[1];
-		under_cursor[2] = dst[ints_per_line];
-		under_cursor[3] = dst[ints_per_line+1];
-		dst[0] = 0x00000000;
-		dst[1] = 0x00000000;
-		dst[ints_per_line] = 0x00000000;
-		dst[ints_per_line+1] = 0x00000000;
-		break;
-	}
-	default:
-	}
-	__restore_flags(flags);
 }
 
-/*
- * Render the current screen
- * Only used at startup and when switching from KD_GRAPHICS to KD_TEXT
- * to avoid the caching that is being done in selection.h
- */
-void
-render_screen(void)
+static void nop_set_cursor(int c)
 {
-    int count;
-    unsigned short *contents;
-
-    count = video_num_columns * video_num_lines;
-    contents = (unsigned short *) video_mem_base;
-
-    for (;count--; contents++)
-	sun_blitc (*contents, (unsigned long) contents);
-}
-
-__initfunc(void serial_finish_init(void (*printfunc)(const char *, int)))
-{
-	char buffer[2048];
-	
-	sprintf (buffer, linux_serial_image, UTS_RELEASE);
-	(*printfunc)(buffer, strlen(buffer));
-}
-
-__initfunc(void con_type_init_finish(void))
-{
-	int i, cpu;
-	char *p = con_fb_base + skip_bytes;
-	char q[2] = {0,5};
-	int currcons = 0;
-	unsigned short *ush;
-
-	if (serial_console)
-		return;
-	if (con_type == FBTYPE_SUNLEO) {
-		int rects [4];
-		
-		rects [0] = 0;
-		rects [1] = 0;
-		rects [2] = con_width;
-		rects [3] = con_height;
-		(*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
-		return; /* Dunno how to display logo on leo/zx yet */
-	}
-	if (con_depth == 8 && fbinfo[0].loadcmap) {
-		for (i = 0; i < LINUX_LOGO_COLORS; i++) {
-			fbinfo[0].color_map CM(i+32,0) = linux_logo_red [i];
-			fbinfo[0].color_map CM(i+32,1) = linux_logo_green [i];
-			fbinfo[0].color_map CM(i+32,2) = linux_logo_blue [i];
-		}
-		(*fbinfo [0].loadcmap)(&fbinfo [0], 0, LINUX_LOGO_COLORS + 32);
-		for (i = 0; i < 80; i++, p += chars_per_line){
-		        for (cpu = 0; cpu < linux_num_cpus; cpu++){
-				memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
-			}
-		}
-	} else if (con_depth == 1) {
-		for (i = 0; i < 80; i++, p += chars_per_line)
-			memcpy (p, linux_logo_bw + 10 * i, 10);
-	}
-	putconsxy(0, q);
-	ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (linux_num_cpus - 1);
-
-	p = linux_logo_banner;
-	for (; *p; p++, ush++) {
-		*ush = (attr << 8) + *p;
-		sun_blitc (*ush, (unsigned long) ush);
-	}
-	for (i = 0; i < 5; i++) {
-		ush = (unsigned short *) video_mem_base + i * video_num_columns;
-		memset (ush, 0, 20);
-	}
 }
 
-__initfunc(unsigned long
-con_type_init(unsigned long kmem_start, const char **display_desc))
+static int nop_set_get_font(char *a, int b, int c)
 {
-        can_do_color = (con_type != FBTYPE_SUN2BW);
-
-        video_type = VIDEO_TYPE_SUN;
-        *display_desc = "SUN";
-
-	if (!serial_console) {
-		/* If we fall back to PROM then our output have to remain readable. */
-		prom_putchar('\033');  prom_putchar('[');  prom_putchar('H');
-
-		/*
-		 * fake the screen memory with some CPU memory
-		 */
-		video_mem_base = kmem_start;
-		kmem_start += video_screen_size;
-		video_mem_term = kmem_start;
-	}
-	return kmem_start;
+	return 0;
 }
 
-/*
- * NOTE: get_scrmem() and set_scrmem() are here only because
- * the VGA version of set_scrmem() has some direct VGA references.
- */
-void
-get_scrmem(int currcons)
+static int nop_con_adjust_height(unsigned long arg)
 {
-	memcpyw((unsigned short *)vc_scrbuf[currcons],
-		(unsigned short *)origin, video_screen_size);
-	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
-	scr_end = video_mem_end = video_mem_start + video_screen_size;
-	pos = origin + y*video_size_row + (x<<1);
-}
-
-void
-set_scrmem(int currcons, long offset)
-{
-	if (video_mem_term - video_mem_base < offset + video_screen_size)
-		offset = 0;
-	memcpyw((unsigned short *)(video_mem_base + offset),
-		(unsigned short *) origin, video_screen_size);
-	video_mem_start = video_mem_base;
-	video_mem_end = video_mem_term;
-	origin = video_mem_base + offset;
-	scr_end = origin + video_screen_size;
-	pos = origin + y*video_size_row + (x<<1);
+	return -EINVAL;
 }
 
-/*
- * PIO_FONT support.
- */
-int
-set_get_font(char * arg, int set, int ch512)
+static int nop_set_get_cmap(unsigned char *arg, int a)
 {
-	int i, line;
-
-	if (!arg)
-		return -EINVAL;
-
-	/* download the current font */
-	if (!set){
-		if(clear_user(arg, cmapsz))
-			return -EFAULT;
-		for (i = 0; i < 256; i++) {
-			for (line = 0; line < CHAR_HEIGHT; line++) {
-				unsigned char value = vga_font[i];
-
-				/* Access checked by the above clear_user */
-				__put_user_ret (value, (arg + (i * 32 + line)),
-						-EFAULT);
-			}
-		}
-		return 0;
-	}
-	
-        /* set the font */
-        
-        if (verify_area (VERIFY_READ, arg, 256 * CHAR_HEIGHT)) return -EFAULT;
-	for (i = 0; i < 256; i++) {
-		for (line = 0; line < CHAR_HEIGHT; line++){
-			unsigned char value;
-			__get_user_ret(value, (arg + (i * 32 + line)),-EFAULT);
-			vga_font [i*CHAR_HEIGHT + line] = value;
-		}
-	}
 	return 0;
 }
 
-/*
- * Adjust the screen to fit a font of a certain height
- *
- * Returns < 0 for error, 0 if nothing changed, and the number
- * of lines on the adjusted console if changed.
- *
- * for now, we only support the built-in font...
- */
-int
-con_adjust_height(unsigned long fontheight)
+static void nop_set_palette(void)
 {
-	return -EINVAL;
 }
 
-int
-set_get_cmap(unsigned char * arg, int set)
+static void nop_set_other_palette(int a)
 {
-	int i;
+}
 
-	if(set)
-		i = VERIFY_READ;
-	else
-		i = VERIFY_WRITE;
-	if(verify_area(i, arg, (16 * 3 * sizeof(unsigned char))))
-		return -EFAULT;
-	for (i=0; i<16; i++) {
-		if (set) {
-			__get_user_ret(default_red[i], (arg+0),-EFAULT);
-			__get_user_ret(default_grn[i], (arg+1),-EFAULT);
-			__get_user_ret(default_blu[i], (arg+2),-EFAULT);
-		} else {
-			__put_user_ret(default_red[i], (arg+0),-EFAULT);
-			__put_user_ret(default_grn[i], (arg+1),-EFAULT);
-			__put_user_ret(default_blu[i], (arg+2),-EFAULT);
-		}
-		arg += 3;
-	}
-	if (set) {
-		for (i=0; i<MAX_NR_CONSOLES; i++)
-			if (vc_cons_allocated(i)) {
-				int j, k ;
-				for (j=k=0; j<16; j++) {
-					vc_cons[i].d->vc_palette[k++] = default_red[j];
-					vc_cons[i].d->vc_palette[k++] = default_grn[j];
-					vc_cons[i].d->vc_palette[k++] = default_blu[j];
-				}
-			}
-		set_palette();
-	}
+static void nop_console_restore_palette(void)
+{
+}
 
-	return 0;
+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;
 }
 
-void
-sun_clear_screen(void)
+static void nop_con_type_init_finish(void)
 {
-	if (fbinfo[0].fill) {
-		int rects [4];
-		
-		rects [0] = 0;
-		rects [1] = 0;
-		rects [2] = con_width;
-		rects [3] = con_height;
-		(*fbinfo[0].fill)(reverse_color_table[0], 1, rects);
-	} else if (fbinfo[0].base && fbinfo[0].base_depth)
-		memset (con_fb_base,
-			(con_depth == 1) ? ~(0) : reverse_color_table[0],
-			(con_depth * con_height * con_width) / 8);
-	/* also clear out the "shadow" screen memory */
-	memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base));
-	cursor_pos = -1;
-}
-
-void
-sun_clear_fb(int n)
-{
-	if (!n) sun_clear_screen ();
-#if 0
-/* This makes in some configurations serious problems. 
- * Who cares if other screens are cleared?
- */ 
-	else if (fbinfo[n].fill) { 
-		int rects [4];
-		
-		rects [0] = 0;
-		rects [1] = 0;
-		rects [2] = fbinfo[n].type.fb_width;
-		rects [3] = fbinfo[n].type.fb_height;
-		(*fbinfo[n].fill)(reverse_color_table[0], 1, rects);
-	} 
-#endif		
-	else if (fbinfo[n].base && fbinfo[n].base_depth) {
-		memset((void *)fbinfo[n].base,
-		       (fbinfo[n].base_depth == 1) ?
-			~(0) : reverse_color_table[0],
-		       (fbinfo[n].base_depth * fbinfo[n].type.fb_height
-					     * fbinfo[n].type.fb_width) / 8);
-	}
+	prom_printf("YIEEE: nop_con_type_init_finish called!\n");
 }
 
-void
-sun_clear_margin(void)
+static void nop_vesa_blank(void)
 {
-	int h, he, i;
-	unsigned char *p;
+}
 
-	if (fbinfo[0].fill) {
-		int rects [16];
+static void nop_vesa_unblank(void)
+{
+}
 
-		memset (rects, 0, sizeof (rects));
-		rects [2] = con_width;
-		rects [3] = y_margin;
-		rects [5] = y_margin;
-		rects [6] = x_margin;
-		rects [7] = con_height;
-		rects [8] = con_width - x_margin;
-		rects [9] = y_margin;
-		rects [10] = con_width;
-		rects [11] = con_height;
-		rects [12] = x_margin;
-		rects [13] = con_height - y_margin;
-		rects [14] = con_width - x_margin;
-		rects [15] = con_height;
-		(*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
-	} else {
-		memset (con_fb_base, 
-			(con_depth == 1) ? ~(0) : reverse_color_table[0],
-			skip_bytes - (x_margin<<1));
-		memset (con_fb_base + chars_per_line * con_height
-					- skip_bytes + (x_margin<<1),
-			(con_depth == 1) ? ~(0) : reverse_color_table[0],
-			skip_bytes - (x_margin<<1));
-		he = con_height - 2 * y_margin;
-		i = 2 * x_margin;
-		if (con_depth == 1) {
-			for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
-			     h <= he; p += chars_per_line, h++)
-				memset (p, ~(0), i);			
-		} else {
-			for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
-			     h <= he; p += chars_per_line, h++)
-				memset (p, reverse_color_table[0], i);
-		}
-	}
-	if (fbinfo [0].switch_from_graph)
-		(*fbinfo [0].switch_from_graph)();
+static void nop_set_vesa_blanking(const unsigned long arg)
+{
 }
 
-/*
- * dummy routines for the VESA blanking code, which is VGA only,
- * so we don't have to carry that stuff around for the Sparc...
- */
-void vesa_blank(void)
+static void nop_vesa_powerdown(void)
 {
 }
 
-void vesa_unblank(void)
+static void nop_clear_screen(void)
 {
 }
 
-void set_vesa_blanking(const unsigned long arg)
+static void nop_render_screen(void)
 {
 }
 
-void vesa_powerdown(void)
+static void nop_clear_margin(void)
 {
 }
 
-/*
- * We permutate the colors, so we match the PROM's idea of
- * black and white.
- */
-unsigned char reverse_color_table[] = {
-	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
+struct suncons_operations suncons_ops = {
+	nop_memsetw,
+	nop_memcpyw,
+	nop_scr_writew,
+	nop_scr_readw,
+	nop_get_scrmem,
+	nop_set_scrmem,
+	nop_set_origin,
+	nop_hide_cursor,
+	nop_set_cursor,
+	nop_set_get_font,
+	nop_con_adjust_height,
+	nop_set_get_cmap,
+	nop_set_palette,
+	nop_set_other_palette,
+	nop_console_restore_palette,
+	nop_con_type_init,
+	nop_con_type_init_finish,
+	nop_vesa_blank,
+	nop_vesa_unblank,
+	nop_set_vesa_blanking,
+	nop_vesa_powerdown,
+	nop_clear_screen,
+	nop_render_screen,
+	nop_clear_margin
 };
 
-static unsigned char sparc_color_table[] = {
-	15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11
-};
+/* Entry points. */
 
-/* Call the frame buffer routine for setting the palette */
-void
-set_palette (void)
-{
-	if (console_blanked || vt_cons [fg_console]->vc_mode == KD_GRAPHICS)
-		return;
-
-	if (fbinfo [0].loadcmap){
-		int i, j;
-	
-		/* First keep color_map with the palette colors */
-		for (i = 0; i < 16; i++){
-			j = sparc_color_table [i];
-			fbinfo[0].color_map CM(i,0) = default_red [j];
-			fbinfo[0].color_map CM(i,1) = default_grn [j];
-			fbinfo[0].color_map CM(i,2) = default_blu [j];
-		}
-		(*fbinfo [0].loadcmap)(&fbinfo [0], 0, 16);
-	}
+void get_scrmem(int a)
+{
+	suncons_ops.get_scrmem(a);
 }
 
-void
-set_other_palette (int n)
+void set_scrmem(int a, long b)
 {
-	if (!n) {
-		set_palette ();
-		return;
-	}
-	if (fbinfo [n].loadcmap){
-		fbinfo[n].color_map CM(0,0) = 0;
-		fbinfo[n].color_map CM(0,1) = 0;
-		fbinfo[n].color_map CM(0,2) = 0;
-		(*fbinfo [n].loadcmap)(&fbinfo [n], 0, 1);
-	}
+	suncons_ops.set_scrmem(a, b);
 }
 
-/* Called when returning to prom */
-void
-console_restore_palette (void)
+void __set_origin(unsigned short offset)
 {
-        if (fb_restore_palette)
-	        (*fb_restore_palette) (&fbinfo[0]);
+	suncons_ops.set_origin(offset);
 }
 
-unsigned long
-get_phys (unsigned long addr)
+void hide_cursor(void)
 {
-	return __get_phys(addr);
+	suncons_ops.hide_cursor();
 }
 
-int
-get_iospace (unsigned long addr)
+void set_cursor(int currcons)
 {
-	return __get_iospace(addr);
+	suncons_ops.set_cursor(currcons);
 }
 
-__initfunc(unsigned long sun_cg_postsetup(fbinfo_t *fb, unsigned long start_mem))
+int set_get_font(char *arg, int set, int ch512)
 {
-	fb->color_map = (char *)start_mem;
-	return start_mem + 256*3;
+	return suncons_ops.set_get_font(arg, set, ch512);
 }
 
-static char *known_cards [] __initdata = {
-	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
-	"cgfourteen", "SUNW,leo", "SUNW,ffb", 0
-};
-static char *v0_known_cards [] __initdata = {
-	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
-};
+int con_adjust_height(unsigned long fontheight)
+{
+	return suncons_ops.con_adjust_height(fontheight);
+}
 
-__initfunc(static int known_card (char *name, char **known_cards))
+int set_get_cmap(unsigned char *arg, int set)
 {
-	int i;
+	return suncons_ops.set_get_cmap(arg, set);
+}
 
-	for (i = 0; known_cards [i]; i++)
-		if (strcmp (name, known_cards [i]) == 0)
-			return 1;
-	return 0;
+void set_palette(void)
+{
+	suncons_ops.set_palette();
 }
 
-static struct {
-	int depth;
-	int resx, resy;
-	int x_margin, y_margin;
-} scr_def [] = {
-	{ 8, 1280, 1024, 64, 80 },
-	{ 8, 1152, 1024, 64, 80 },
-	{ 8, 1152, 900,  64, 18 },
-	{ 8, 1024, 768,  0,  0 },
-	{ 8, 800, 600, 16, 12 },
-	{ 8, 640, 480, 0, 0 },
-	{ 1, 1152, 900,  8,  18 },
-	{ 0 },
-};
+void set_other_palette(int n)
+{
+	suncons_ops.set_other_palette(n);
+}
 
-__initfunc(static int cg14_present(void))
+void console_restore_palette(void)
 {
-	int root, n;
+	suncons_ops.console_restore_palette();
+}
 
-	root = prom_getchild (prom_root_node);
-	if ((n = prom_searchsiblings (root, "obio")) == 0)
-		return 0;
+unsigned long con_type_init(unsigned long mem_start, const char **disp_desc)
+{
+	return suncons_ops.con_type_init(mem_start, disp_desc);
+}
 
-	n = prom_getchild (n);
-	if ((n = prom_searchsiblings (n, "cgfourteen")) == 0)
-		return 0;
-	return n;
+void con_type_init_finish(void)
+{
+	suncons_ops.con_type_init_finish();
 }
 
-__initfunc(static int creator_present (void))
+void vesa_blank(void)
 {
-	int root, n;
+	suncons_ops.vesa_blank();
+}
 
-#ifdef __sparc_v9__
-	root = prom_getchild (prom_root_node);
-	if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0)
-		return 0;
-	return n;
-#else
-	return 0;
-#endif
+void vesa_unblank(void)
+{
+	suncons_ops.vesa_unblank();
 }
 
-__initfunc(static void
-	   sparc_framebuffer_setup(int primary, int con_node,
-				   int type, struct linux_sbus_device *sbdp, 
-				   uint base, unsigned long con_base, int prom_fb,
-				   int parent_node))
-{
-	static int frame_buffers = 1;
-	int n, i;
-	int linebytes;
-	uint io = 0;
-	char *p;
-	
-	if (primary)
-		n = 0;
-	else {
-		if (frame_buffers == FRAME_BUFFERS)
-			return; /* Silently ignore */
-		n = frame_buffers++;
-	}
-	
-	if (prom_fb) sun_prom_console_id = n;
-		
-	if (sbdp)
-		io = sbdp->reg_addrs [0].which_io;
-
-	/* Fill in common fb information */
-	fbinfo [n].type.fb_type   = type;
-	fbinfo [n].real_type	  = type;
-	fbinfo [n].prom_node	  = con_node;
-	memset (&(fbinfo [n].emulations), 0xff, sizeof (fbinfo [n].emulations));
-	fbinfo [n].type.fb_height = prom_getintdefault(con_node, "height", 900);
-	fbinfo [n].type.fb_width  = prom_getintdefault(con_node, "width", 1152);
-	fbinfo [n].type.fb_depth  = (type == FBTYPE_SUN2BW) ? 1 : 8;
-	linebytes = prom_getint(con_node, "linebytes");
-	if (linebytes == -1) linebytes = fbinfo [n].type.fb_width;
-	fbinfo [n].type.fb_size   = PAGE_ALIGN((linebytes) * (fbinfo [n].type.fb_height));
-	fbinfo [n].space = io;
-	fbinfo [n].blanked = 0;
-	if (con_base >= PAGE_OFFSET)
-		fbinfo [n].base = con_base;
-	else
-		fbinfo [n].base = 0;
-	fbinfo [n].cursor.hwsize.fbx = 32;
-	fbinfo [n].cursor.hwsize.fby = 32;
-	fbinfo [n].proc_entry.node = parent_node;
-	fbinfo [n].proc_entry.rdev = MKDEV(GRAPHDEV_MAJOR, n);
-	fbinfo [n].proc_entry.mode = S_IFCHR | S_IRUSR | S_IWUSR;
-	prom_getname (con_node, fbinfo [n].proc_entry.name, 32 - 3);
-	p = strchr (fbinfo [n].proc_entry.name, 0);
-        sprintf (p, ":%d", n);
-	
-	/* Should be filled in for supported video cards */
-	fbinfo [n].mmap = 0; 
-	fbinfo [n].loadcmap = 0;
-	fbinfo [n].ioctl = 0;
-	fbinfo [n].reset = 0;
-	fbinfo [n].blank = 0;
-	fbinfo [n].unblank = 0;
-	fbinfo [n].setcursor = 0;
-	fbinfo [n].base_depth = fbinfo [n].type.fb_depth;
-	
-	/* Per card setup */
-	switch (fbinfo [n].type.fb_type){
-#ifdef SUN_FB_CGTHREE
-	case FBTYPE_SUN3COLOR:
-		cg3_setup (&fbinfo [n], n, base, io, sbdp);
-		break;
-#endif
-#ifdef SUN_FB_TCX
-	case FBTYPE_TCXCOLOR:
-		tcx_setup (&fbinfo [n], n, con_node, base, sbdp);
-		break;
-#endif
-#ifdef SUN_FB_CGSIX
-	case FBTYPE_SUNFAST_COLOR:
-		cg6_setup (&fbinfo [n], n, base, io);
-		break;
-#endif
-#ifdef SUN_FB_BWTWO
-	case FBTYPE_SUN2BW:
-		bwtwo_setup (&fbinfo [n], n, base, io, sbdp);
-		break;
-#endif
-#ifdef SUN_FB_CGFOURTEEN
-	case FBTYPE_MDICOLOR:
-		cg14_setup (&fbinfo [n], n, con_node, base, io);
-		break;
-#endif
-#ifdef SUN_FB_LEO
-	case FBTYPE_SUNLEO:
-		leo_setup (&fbinfo [n], n, base, io);
-		break;
-#endif
-#if defined(SUN_FB_CREATOR) && defined(__sparc_v9__)
-	case FBTYPE_CREATOR:
-		creator_setup (&fbinfo [n], n, con_node, base, io);
-		break;
-#endif
-	default:
-		fbinfo [n].type.fb_type = FBTYPE_NOTYPE;
-		return;
-	}
+void set_vesa_blanking(const unsigned long arg)
+{
+	suncons_ops.set_vesa_blanking(arg);
+}
 
-	if (n)
-		return;
-	
-	/* Code below here is just executed for the first frame buffer */
-	con_type = type;
-	con_height = fbinfo [n].type.fb_height;
-	con_width = fbinfo [n].type.fb_width;
-	con_depth = (type == FBTYPE_SUN2BW) ? 1 : 8;
-	for (i = 0; scr_def [i].depth; i++){
-		if ((scr_def [i].resx != con_width) ||
-		    (scr_def [i].resy != con_height))
-			continue;
-		if (scr_def [i].depth != con_depth)
-			continue;
-		x_margin = scr_def [i].x_margin;
-		y_margin = scr_def [i].y_margin;
-		chars_per_line = (con_width * con_depth) / 8;
-		skip_bytes = chars_per_line * y_margin + x_margin;
-		ints_per_line = chars_per_line / 4;
-		ints_per_cursor = 14 * ints_per_line;
-		bytes_per_row = CHAR_HEIGHT * chars_per_line;
-		ORIG_VIDEO_COLS = con_width / 8 -
-			2 * x_margin / con_depth;
-		ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
-		switch (chars_per_line) {
-		case 1280:
-			if (ORIG_VIDEO_COLS == 144)
-				color_fbuf_offset =
-					color_fbuf_offset_1280_144;
-			break;
-		case 1152:
-			if (ORIG_VIDEO_COLS == 128)
-				color_fbuf_offset =
-					color_fbuf_offset_1152_128;
-			break;
-		case 1024:
-			if (ORIG_VIDEO_COLS == 128)
-				color_fbuf_offset =
-					color_fbuf_offset_1024_128;
-			break;
-		case 800:
-			if (ORIG_VIDEO_COLS == 96)
-				color_fbuf_offset =
-					color_fbuf_offset_800_96;
-			break;
-		case 640:
-			if (ORIG_VIDEO_COLS == 80)
-				color_fbuf_offset =
-					color_fbuf_offset_640_80;
-			break;
-		}
-		break;
-	}
-	
-	if (!scr_def [i].depth){
-		x_margin = y_margin = 0;
-		prom_printf ("console: unknown video resolution %dx%d,"
-			     " depth %d\n",
-			     con_width, con_height, con_depth);
-		prom_halt ();
-	}
-	
-	/* P3: I fear this strips 15inch 1024/768 PC-like
-	 * monitors out. */
-	if ((linebytes*8) / con_depth != con_width) {
-		prom_printf("console: unusual video, linebytes=%d, "
-			    "width=%d, height=%d depth=%d\n",
-			    linebytes, con_width, con_height,
-			    con_depth);
-		prom_halt ();
-	}
+void vesa_powerdown(void)
+{
+	suncons_ops.vesa_powerdown();
 }
 
-__initfunc(static int sparc_console_probe(void))
+void render_screen(void)
 {
-	int propl, con_node, default_node = 0, i;
-	char prop[16];
-	struct linux_sbus_device *sbdp, *sbdprom;
-	struct linux_sbus *sbus;
-	int creator = 0, cg14 = 0;
-	char prom_name[40];	
-	int type, card_found = 0;
-	unsigned long con_base;
-	u32 tmp;
-	u32 prom_console_node = 0;
+	suncons_ops.render_screen();
+}
 
-	for (i = 0; i < FRAME_BUFFERS; i++)
-		fbinfo [i].type.fb_type = FBTYPE_NOTYPE;
-	sbdprom = 0;
-	switch(prom_vers) {
-	case PROM_V0:
-		/* V0 proms are at sun4c only. Can skip many checks. */
-		con_type = FBTYPE_NOTYPE;
-		if(SBus_chain == 0) {
-			prom_printf("SBUS chain is NULL, bailing out...\n");
-			prom_halt();
-		}
-		for_each_sbusdev(sbdp, SBus_chain) {
-			/* If no "address" than it is not the PROM console. */
-			if(sbdp->num_vaddrs) {
-				if(known_card(sbdp->prom_name, v0_known_cards)) {
-				   	sbdprom = sbdp;
-					strncpy(prom_name, sbdp->prom_name, sizeof (prom_name));
-					break;
-				}
-			}
-		}
-		if(!sbdprom) return -1;
-		for_each_sbusdev(sbdp, SBus_chain) {
-			con_node = sbdp->prom_node;
-
-			if(!strncmp(sbdp->prom_name, "cgsix", 5) ||
-			   !strncmp(sbdp->prom_name, "cgthree+", 8)) {
-				type = FBTYPE_SUNFAST_COLOR;
-			} else if(!strncmp(sbdp->prom_name, "cgthree", 7) ||
-			   !strncmp(sbdp->prom_name, "cgRDI", 5)) {
-				type = FBTYPE_SUN3COLOR;
-			} else if (!strncmp(sbdp->prom_name, "bwtwo", 5)) {
-				type = FBTYPE_SUN2BW;
-			} else
-				continue;
-			sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp, 
-						 (uint)sbdp->reg_addrs [0].phys_addr, sbdp->sbus_vaddrs[0], 0,
-						 sbdp->my_bus->prom_node);
-			/* XXX HACK */
-			if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
-				break;
-		}
-		break;
-	case PROM_V2:
-	case PROM_V3:
-	case PROM_P1275:
-		if (console_fb_path) {
-			char *q, c;
-
-			for (q = console_fb_path; *q && *q != ' '; q++);
-			c = *q;
-			*q = 0;
-			default_node = prom_pathtoinode(console_fb_path);
-			if (default_node) {
-				prom_printf ("Using %s for console\n", console_fb_path);
-				prom_console_node = prom_inst2pkg(prom_stdout);
-				if (prom_console_node == default_node)
-					prom_console_node = 0;
-			}
-		}
-		if (!default_node)
-			default_node = prom_inst2pkg(prom_stdout);
-		propl = prom_getproperty(default_node, "device_type",
-					 prop, sizeof (prop));
-		if (propl < 0) {
-			prom_printf ("output-device doesn't have device_type property\n");
-			prom_halt ();
-		} else if (propl != sizeof("display") || strncmp("display", prop, sizeof("display"))) {
-		    	prop [propl] = 0;
-			prom_printf ("console_probe: output-device is %s"
-				     " (not \"display\")\n", prop);
-			prom_halt ();
-		}
-		for_all_sbusdev(sbdp, sbus) {
-			if ((sbdp->prom_node == default_node)
-			    && known_card (sbdp->prom_name, known_cards)) {
-			    	sbdprom = sbdp;
-				break;
-			}
-		}
-		if (sbdprom)
-		    card_found = 1;
-		if (!card_found)
-		    card_found = cg14 = cg14_present ();
-		if (!card_found){
-			card_found = creator = creator_present ();
-		}
-		if (!card_found){
-			prom_printf ("Could not find a known video card on this machine\n");
-			prom_halt ();
-		}
-		
-		for_all_sbusdev(sbdp, sbus) {
-			if (!known_card (sbdp->prom_name, known_cards))
-				continue;
-			con_node = sbdp->prom_node;
-			prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
-						sbdp->num_registers, sbdp);
-
-			propl = prom_getproperty(con_node, "address", (char *) &tmp, 4);
-			con_base = tmp;
-			if (propl != 4) con_base = 0;
-			propl = prom_getproperty(con_node, "emulation", prom_name, sizeof (prom_name));
-			if (propl < 0 || propl >= sizeof (prom_name)) {
-				/* Early cg3s had no "emulation". */
-				propl = prom_getproperty(con_node, "name", prom_name, sizeof (prom_name));
-				if (propl < 0) {
-					prom_printf("console: no device name!!\n");
-					return -1;
-				}
-			}
-			prom_name [sizeof (prom_name) - 1] = 0;
-			if(!strcmp(prom_name, "cgsix") ||
-			   !strcmp(prom_name, "cgthree+")) {
-				type = FBTYPE_SUNFAST_COLOR;
-			} else if(!strcmp(prom_name, "cgthree") ||
-			   !strcmp(prom_name, "cgRDI")) {
-				type = FBTYPE_SUN3COLOR;
-			} else if(!strcmp(prom_name, "cgfourteen")) {
-				type = FBTYPE_MDICOLOR;
-			} else if(!strcmp(prom_name, "SUNW,leo")) {
-				type = FBTYPE_SUNLEO;
-			} else if(!strcmp(prom_name, "bwtwo")) {
-				type = FBTYPE_SUN2BW;
-			} else if(!strcmp(prom_name,"SUNW,tcx")){
-				sparc_framebuffer_setup (sbdprom == sbdp, con_node, FBTYPE_TCXCOLOR, sbdp,
-							 (uint)sbdp->reg_addrs [10].phys_addr, con_base, 
-							 prom_console_node == con_node, sbdp->my_bus->prom_node);
-				continue;
-			} else {
-				prom_printf("console: \"%s\" is unsupported\n", prom_name);
-				continue;
-			}
-			sparc_framebuffer_setup (sbdprom == sbdp, con_node, type, sbdp,
-						 (uint)sbdp->reg_addrs [0].phys_addr, con_base,
-						 prom_console_node == con_node, sbdp->my_bus->prom_node);
-			/* XXX HACK */
-			if (sbdprom == sbdp && !strncmp(sbdp->prom_name, "cgRDI", 5))
-				break;
-		}
-		if (cg14) {
-			sparc_framebuffer_setup (!sbdprom, cg14, FBTYPE_MDICOLOR, 
-						 0, 0, 0, prom_console_node == cg14,
-						 prom_searchsiblings (prom_getchild (prom_root_node), "obio"));
-		}
-		if (creator){
-			sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR,
-						 0, 0, 0, prom_console_node == creator,
-						 prom_root_node);
-		}
-		break;
-	default:
-		return -1;
-	}
-	
-	if (fbinfo [0].type.fb_type == FBTYPE_NOTYPE) {
-		prom_printf ("Couldn't setup your primary frame buffer.\n");
-		prom_halt ();
-	}
+/*
+ * We permutate the colors, so we match the PROM's idea of
+ * black and white.
+ */
+unsigned char reverse_color_table[] = {
+	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
+};
 
-	if (fbinfo [0].blitc)
-		do_accel = 1;
-	
-	con_fb_base = (unsigned char *)fbinfo[0].base;
-	if (!con_fb_base){
-		prom_printf ("PROM does not have an 'address' property for this\n"
-			     "frame buffer and the Linux drivers do not know how\n"
-			     "to map the video of this device\n");
-		prom_halt ();
-	}
-	return fb_init ();
+unsigned char sparc_color_table[] = {
+	15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11
+};
+
+/* Probing engine. */
+
+char *console_fb_path = NULL;
+void (*fb_restore_palette)(fbinfo_t *fbinfo) = NULL;
+
+unsigned long
+get_phys (unsigned long addr)
+{
+	return __get_phys(addr);
 }
 
-/* video init code, called from within the SBUS bus scanner at
- * boot time.
- */
-__initfunc(unsigned long sun_console_init(unsigned long memory_start))
+extern int sbus_console_probe(void);
+extern int serial_console;
+
+__initfunc(static unsigned long finish_console_init(unsigned long memory_start))
 {
+	static int confinish_has_run = 0;
 	int i, j;
-	if(serial_console)
-		return memory_start;
 
-	fbinfo = (fbinfo_t *)memory_start;
-	memset (fbinfo, 0, FRAME_BUFFERS * sizeof (fbinfo_t));
-	if(sparc_console_probe()) {
-		prom_printf("Could not probe console, bailing out...\n");
-		prom_halt();
+	if(confinish_has_run != 0) {
+		printk("finish_console_init: Someone tries to run me twice.\n");
+		return memory_start;
 	}
-	
-	sun_clear_screen();
-	for (i = FRAME_BUFFERS; i > 1; i--)
-		if (fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE) break;
+	for(i = FRAME_BUFFERS; i > 1; i--)
+		if(fbinfo[i - 1].type.fb_type != FBTYPE_NOTYPE)
+			break;
 	fbinfos = i;
-	memory_start = memory_start + i * sizeof (fbinfo_t);
-	for (j = 0; j < i; j++)
+
+	for(j = 0; j < i; j++)
 		if (fbinfo[j].postsetup)
 			memory_start = (*fbinfo[j].postsetup)(fbinfo+j, memory_start);
-	for (j = 1; j < i; j++)
-		if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
-			sun_clear_fb(j);
-			set_other_palette(j);
+
+	suncons_ops.clear_screen();
+
+	for(j = 1; j < i; j++)
+		if(fbinfo[j].type.fb_type != FBTYPE_NOTYPE) {
+			fbinfo[j].clear_fb(j);
+			fbinfo[j].set_other_palette(j);
 		}
-#if defined(CONFIG_PROC_FS) && ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
+#if defined(CONFIG_PROC_FS) && \
+    ( defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) )
 	for (j = 0; j < i; j++)
 		if (fbinfo[j].type.fb_type != FBTYPE_NOTYPE)
 			proc_openprom_regdev (&fbinfo[j].proc_entry);
 #endif
+
+	confinish_has_run = 1;
+
 	return memory_start;
 }
 
-/*
- * sun_blitc
- *
- * Displays an ASCII character at a specified character cell
- *  position.
- *
- * Called from scr_writew() when the destination is
- *  the "shadow" screen
- */
-static uint
-fontmask_bits[16] = {
-    0x00000000,
-    0x000000ff,
-    0x0000ff00,
-    0x0000ffff,
-    0x00ff0000,
-    0x00ff00ff,
-    0x00ffff00,
-    0x00ffffff,
-    0xff000000,
-    0xff0000ff,
-    0xff00ff00,
-    0xff00ffff,
-    0xffff0000,
-    0xffff00ff,
-    0xffffff00,
-    0xffffffff
-};
+#ifdef CONFIG_PCI
+extern void pci_console_inithook(void);
+#endif
 
-int
-sun_blitc(uint charattr, unsigned long addr)
+__initfunc(unsigned long sun_console_init(unsigned long memory_start))
 {
-	unsigned int fgmask, bgmask;
-	unsigned char attrib;
-	int j, idx;
-	unsigned char *font_row;
-
-	if (do_accel) {
-		(*fbinfo[0].blitc)(charattr, 
-			        x_margin + (((addr - video_mem_base) % video_size_row)<<2),
-		 	        y_margin + CHAR_HEIGHT * ((addr - video_mem_base) / video_size_row));
-		return 0;
-	}
+	int i;
 
-  	/* Invalidate the cursor position if necessary. */
-	idx = (addr - video_mem_base) >> 1;
+	/* Nothing to do in this case. */
+	if(serial_console)
+		return memory_start;
 
-	attrib = CHARATTR_TO_SUNCOLOR(charattr);
-	font_row = &vga_font[(j = (charattr & 0xff)) << 4];
+	fbinfo = (fbinfo_t *)memory_start;
+	memset(fbinfo, 0, FRAME_BUFFERS * sizeof(fbinfo_t));
+	memory_start += (FRAME_BUFFERS * sizeof(fbinfo_t));
+	fbinfos = 0;
 
-	switch (con_depth){
-	case 1: {
-		register unsigned char *dst;
-		unsigned long flags;
-		
-		dst = (unsigned char *)(((unsigned long)con_fb_base) + FBUF_OFFSET(idx));
-
-		__save_and_cli(flags);
-		if ((!(charattr & 0xf000)) ^ (idx == cursor_pos)) {
-			for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
-				*dst = ~(*font_row);
-		} else {
-			for(j = 0; j < CHAR_HEIGHT; j++, font_row++, dst+=CHARS_PER_LINE)
-				*dst = *font_row;
-		}
-		__restore_flags(flags);
-		break;
-	}
-	case 8: {
-#ifdef ASM_BLITC		
-		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");
-#else		
-		const int ipl = ints_per_line;
-		unsigned int data2, data3, data4;
-		unsigned int data, rowbits;
-		register unsigned int *dst;
-		unsigned long flags;
-#endif		
-		const uint *fontm_bits = fontmask_bits;
-		
-		dst = (unsigned int *)(((unsigned long)con_fb_base) + COLOR_FBUF_OFFSET(idx));
-		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"
-#define BLITC_SPC \
-		"\n\t std	%0, [%1]" \
-		"\n\t std	%0, [%1 + %2]"
-
-			x1 = attrib >> 4;
-			x1 |= x1 << 8;
-			x1 |= x1 << 16;
-			x3 = cpl << 1;
-			
-			__asm__ __volatile__ (
-				"\n\t mov	%2, %3"
-				BLITC_SPACE
-				BLITC_SPACE
-				BLITC_SPACE
-				BLITC_SPACE
-				BLITC_SPACE
-				BLITC_SPACE
-				BLITC_SPACE
-					: : "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));
-			else
-				__asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
-			__restore_flags (flags);
-#else
-			bgmask = attrib >> 4;
-			bgmask |= bgmask << 8;
-			bgmask |= bgmask << 16;
-			
-	                for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
-        	                *dst = bgmask;
-        	                *(dst+1) = bgmask;
-                	}
-                	/* Prevent cursor spots left on the screen */
-			__save_and_cli(flags);
-			if (idx != cursor_pos) {
-	                	*dst = bgmask;
-        	        	*(dst+1) = bgmask;
-                		dst += ipl;
-	                	*dst = bgmask;
-        	        	*(dst+1) = bgmask;
-                	} else {
-	                	under_cursor [0] = bgmask;
-        	        	under_cursor [1] = bgmask;
-                		under_cursor [2] = bgmask;
-	                	under_cursor [3] = bgmask;
-        	        }
-	                __restore_flags(flags);
-#endif
-		} else /* non-space */ {
-			fgmask = attrib & 0x0f;
-			bgmask = attrib >> 4;
-			fgmask |= fgmask << 8;
-			fgmask |= fgmask << 16;
-			bgmask |= bgmask << 8;
-			bgmask |= bgmask << 16;
-
-#ifdef ASM_BLITC
-#define BLITC_INIT \
-			"\n\t ld	[%0], %%g2"
-#define BLITC_BODY(ST1,SC1,ST2,SC2)  \
-			"\n\t " #ST1 "	%%g2, " #SC1 ", %%g7"  \
-			"\n\t " #ST2 "	%%g2, " #SC2 ", %7"  \
-			"\n\t and	%%g7, 0x3c, %%g7"  \
-			"\n\t and	%7, 0x3c, %7"  \
-			"\n\t ld	[%1 + %%g7], %6"  \
-			"\n\t and	%6, %2, %%g7"  \
-			"\n\t andn	%3, %6, %6"  \
-			"\n\t or	%%g7, %6, %6"  \
-			"\n\t ld	[%1 + %7], %7"  \
-			"\n\t and	%7, %2, %%g7"  \
-			"\n\t andn	%3, %7, %7"  \
-			"\n\t or	%%g7, %7, %7"
-#define BLITC_BODYEND \
-			"\n\t sll	%3, 2, %%g7"  \
-			"\n\t srl	%3, 2, %3"  \
-			"\n\t and	%%g7, 0x3c, %%g7"  \
-			"\n\t and	%3, 0x3c, %3"  \
-			"\n\t ld	[%0 + %%g7], %4"  \
-			"\n\t and	%4, %1, %%g7"  \
-			"\n\t andn	%2, %4, %4"  \
-			"\n\t or	%%g7, %4, %4"  \
-			"\n\t ld	[%0 + %3], %3"  \
-			"\n\t and	%3, %1, %%g7"  \
-			"\n\t andn	%2, %3, %3"  \
-			"\n\t or	%%g7, %3, %3"
-#define BLITC_STOREIT \
-			"\n\t std	%6, [%5]"  \
-			"\n\t add	%5, %4, %5"  \
-			"\n\t" 
-#define BLITC_STORE \
-			"\n\t std	%%g4, [%0]"  \
-			"\n\t std	%%g2, [%0 + %1]"
-	
-			for (j = 0; j < 3; j++, font_row+=4) {
-				__asm__ __volatile__ (BLITC_INIT
-					BLITC_BODY(srl, 26, srl, 22)
-					BLITC_STOREIT
-					BLITC_BODY(srl, 18, srl, 14)
-					BLITC_STOREIT
-					BLITC_BODY(srl, 10, srl, 6)
-					BLITC_STOREIT
-					BLITC_BODY(srl, 2, sll, 2)
-					BLITC_STOREIT
-					: : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
-					    "r" (x1), "r" (x2));
-			}
-			__asm__ __volatile__ (BLITC_INIT
-				BLITC_BODY(srl, 26, srl, 22)
-				BLITC_STOREIT
-				BLITC_BODY(srl, 18, srl, 14)
-				BLITC_STOREIT
-				/* Now prepare date for the 15th line, but don't put it anywhere yet (leave it in g4,g5) */
-				BLITC_BODY(srl, 10, srl, 6)
-				: : "r" (font_row), "r" (fontm_bits), "r" (fgmask), "r" (bgmask), "r" (cpl), "r" (dst),
-				    "r" (x1), "r" (x2));
-			/* Prepare the data the bottom line (and put it into g2,g3) */
-			__asm__ __volatile__ (BLITC_BODYEND : : "r" (fontm_bits), "r" (fgmask), "r" (bgmask),
-								"r" (x3), "r" (x4));
-			__save_and_cli(flags);
-			if (idx != cursor_pos)
-				__asm__ __volatile__ (BLITC_STORE : : "r" (dst), "r" (cpl));
-			else
-				__asm__ __volatile__ (BLITC_STORE : : "r" (under_cursor), "i" (8));
-			__restore_flags (flags);
+	for (i = 0; i < FRAME_BUFFERS; i++)
+		fbinfo [i].type.fb_type = FBTYPE_NOTYPE;
+
+	if(sbus_console_probe()) {
+#ifdef CONFIG_PCI
+		pci_console_inithook();
+		return memory_start;
 #else
-	                for(j = 0; j < CHAR_HEIGHT - 2; j++, font_row++, dst += ipl) {
-        	                rowbits = *font_row;
-                	        data = fontm_bits[(rowbits>>4)&0xf];
-	                        data = (data & fgmask) | (~data & bgmask);
-        	                *dst = data;
-                	        data = fontm_bits[rowbits&0xf];
-	                        data = (data & fgmask) | (~data & bgmask);
-        	                *(dst+1) = data;
-                	}
-	                rowbits = *font_row;
-        	        data = fontm_bits[(rowbits>>4)&0xf];
-                	data = (data & fgmask) | (~data & bgmask);
-	                data2 = fontm_bits[rowbits&0xf];
-        	        data2 = (data2 & fgmask) | (~data2 & bgmask);
-                	rowbits = font_row[1];
-	                data3 = fontm_bits[(rowbits>>4)&0xf];
-        	        data3 = (data3 & fgmask) | (~data3 & bgmask);
-                	data4 = fontm_bits[rowbits&0xf];
-	                data4 = (data4 & fgmask) | (~data4 & bgmask);
-        	        
-                	/* Prevent cursor spots left on the screen */
-			__save_and_cli(flags);
-			
-			if (idx != cursor_pos) {
-	                	*dst = data;
-        	        	*(dst+1) = data2;
-                		dst += ipl;
-	                	*dst = data3;
-        	        	*(dst+1) = data4;
-                	} else {
-	                	under_cursor [0] = data;
-        	        	under_cursor [1] = data2;
-                		under_cursor [2] = data3;
-	                	under_cursor [3] = data4;
-        	        }
-                	
-	                __restore_flags(flags);
+		/* XXX We need to write PROM console fallback driver... */
+		prom_printf("Could not probe SBUS console, bailing out...\n");
+		prom_halt();
 #endif
-		}
-		break;
-	} /* case */
-	} /* switch */
-	return (0);
-}
-
-void memsetw(void * s, unsigned short c, unsigned int count)
-{
-	unsigned short * addr = (unsigned short *) s;
-
-	count /= 2;
-	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
-		while (count) {
-			count--;
-			*addr++ = c;
-		}
-		return;
-	}
-	if ((unsigned long) addr + count > video_mem_term ||
-	    (unsigned long) addr < video_mem_base) {
-	    	if ((unsigned long) addr + count <= video_mem_term ||
-	    	    (unsigned long) addr > video_mem_base) {
-			while (count) {
-				count--;
-				*addr++ = c;
-			}
-			return;
-	    	} else {
-			while (count) {
-				count--;
-				scr_writew(c, addr++);
-			}
-		}
-#define GX_SETW (*fbinfo[0].setw)(x_margin + ((xoff - (addr - last)) << 3), y_margin + CHAR_HEIGHT * yoff, c, addr - last);
-	} else if (do_accel) {
-		int yoff = (addr - (unsigned short *)video_mem_base) / video_num_columns;
-		int xoff = (addr - (unsigned short *)video_mem_base) % video_num_columns;
-		unsigned short * last = addr;
-		
-		while (count) {
-			count--;
-			if (*addr != c) {
-				if (xoff == video_num_columns) {
-					if (last != addr)
-						GX_SETW
-					xoff = 0;
-					yoff++;
-					last = addr;
-				}
-				*addr++ = c;
-				xoff++;
-			} else {
-				if (last != addr)
-					GX_SETW
-				if (xoff == video_num_columns) {
-					xoff = 0;
-					yoff++;
-				}
-				addr++;
-				xoff++;
-				last = addr;
-			}
-		}
-		if (last != addr)
-			GX_SETW
-	} else {
-		while (count) {
-			count--;
-			if (*addr != c) {
-				sun_blitc(c, (unsigned long)addr);
-				*addr++ = c;
-			} else
-				addr++;
-		}
 	}
+	return finish_console_init(memory_start);
 }
 
-void memcpyw(unsigned short *to, unsigned short *from, unsigned int count)
+#ifdef CONFIG_PCI
+extern int pci_console_probe(void);
+
+__initfunc(unsigned long pci_console_init(unsigned long memory_start))
 {
-	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) {
-		memcpy(to, from, count);
-		return;
-	}
-	if ((unsigned long) to + count > video_mem_term ||
-	    (unsigned long) to < video_mem_base) {
-	    	if ((unsigned long) to + count <= video_mem_term ||
-	    	    (unsigned long) to > video_mem_base)
-	    	    	memcpy(to, from, count);
-	    	else {
-	    		count /= 2;
-			while (count) {
-				count--;
-				scr_writew(scr_readw(from++), to++);
-			}
-		}
-#define GX_CPYW (*fbinfo[0].cpyw)(x_margin + ((xoff - (to - last)) << 3), y_margin + CHAR_HEIGHT * yoff, last, to - last);
-	} else if (do_accel) {
-		int yoff = (to - (unsigned short *)video_mem_base) / video_num_columns;
-		int xoff = (to - (unsigned short *)video_mem_base) % video_num_columns;
-		unsigned short * last = to;
-		
-		count /= 2;
-		while (count) {
-			count--;
-			if (*to != *from) {
-				if (xoff == video_num_columns) {
-					if (last != to)
-						GX_CPYW
-					xoff = 0;
-					yoff++;
-					last = to;
-				} else if (last != to && (*last & 0xff00) != (*from & 0xff00)) {
-					GX_CPYW
-					last = to;
-				}
-				*to++ = *from++;
-				xoff++;
-			} else {
-				if (last != to)
-					GX_CPYW
-				if (xoff == video_num_columns) {
-					xoff = 0;
-					yoff++;
-				}
-				to++;
-				xoff++;
-				last = to;
-				from++;
-			}
-		}
-		if (last != to)
-			GX_CPYW
-	} else {
-		count /= 2;
-		while (count) {
-			count--;
-			if (*to != *from) {
-				sun_blitc(*from, (unsigned long)to);
-				*to++ = *from++;
-			} else {
-				from++;
-				to++;
-			}
-		}
-	}
-}
+	/* Nothing to do in this case. */
+	if(serial_console)
+		return memory_start;
 
-#undef pos
-int
-sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb)
-{
-	int op;
-	int i, bytes = 0;
-	struct fbcursor f;
-	char red[2], green[2], blue[2];
-	
-	if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
-		return -EFAULT;
-	op = f.set;
-	if (op & FB_CUR_SETSHAPE){
-		if ((uint) f.size.fbx > fb->cursor.hwsize.fbx)
-			return -EINVAL;
-		if ((uint) f.size.fby > fb->cursor.hwsize.fby)
-			return -EINVAL;
-		if (f.size.fbx > 32)
-			bytes = f.size.fby << 3;
-		else
-			bytes = f.size.fby << 2;
-	}
-	if (op & FB_CUR_SETCMAP){
-		if (f.cmap.index || f.cmap.count != 2)
-			return -EINVAL;
-		if (copy_from_user (red, f.cmap.red, 2) ||
-		    copy_from_user (green, f.cmap.green, 2) ||
-		    copy_from_user (blue, f.cmap.blue, 2))
-			return -EFAULT;
-	}
-	if (op & FB_CUR_SETCMAP)
-		(*fb->setcursormap) (fb, red, green, blue);
-	if (op & FB_CUR_SETSHAPE){
-		uint u;
-		
-		fb->cursor.size = f.size;
-		memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
-		if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
-		    copy_from_user (fb->cursor.bits [1], f.image, bytes))
-			return -EFAULT;
-		if (f.size.fbx <= 32) {
-			u = ~(0xffffffff >> f.size.fbx);
-			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
-				fb->cursor.bits [0][i] &= u;
-				fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
-			}
-		} else {
-			u = ~(0xffffffff >> (f.size.fbx - 32));
-			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
-				fb->cursor.bits [0][2*i+1] &= u;
-				fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
-				fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
-			}
-		}
-		(*fb->setcurshape) (fb);
-	}
-	if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
-		if (op & FB_CUR_SETCUR)
-			fb->cursor.enable = f.enable;
-		if (op & FB_CUR_SETPOS)
-			fb->cursor.cpos = f.pos;
-		if (op & FB_CUR_SETHOT)
-			fb->cursor.chot = f.hot;
-		(*fb->setcursor) (fb);
+	if(pci_console_probe()) {
+		prom_printf("Could not probe PCI console, bailing out...\n");
+		prom_halt();
 	}
-	return 0;
-}
 
-static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+	memory_start = finish_console_init(memory_start);
 
-void
-sun_hw_hide_cursor (void)
-{
-	fbinfo[0].cursor.enable = 0;
-	(*fbinfo[0].setcursor)(&fbinfo[0]);
-	sun_hw_cursor_shown = 0;
-}
-
-void
-sun_hw_set_cursor (int xoff, int yoff)
-{
-	if (!sun_hw_cursor_shown) {
-		fbinfo[0].cursor.size.fbx = CHAR_WIDTH;
-		fbinfo[0].cursor.size.fby = CHAR_HEIGHT;
-		fbinfo[0].cursor.chot.fbx = 0;
-		fbinfo[0].cursor.chot.fby = 0;
-		fbinfo[0].cursor.enable = 1;
-		memset (fbinfo[0].cursor.bits, 0, sizeof (fbinfo[0].cursor.bits));
-		fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 2] = 0xff000000;
-		fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 2] = 0xff000000;
-		fbinfo[0].cursor.bits[0][CHAR_HEIGHT - 1] = 0xff000000;
-		fbinfo[0].cursor.bits[1][CHAR_HEIGHT - 1] = 0xff000000;
-		(*fbinfo[0].setcursormap) (&fbinfo[0], hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
-		(*fbinfo[0].setcurshape) (&fbinfo[0]);
-		sun_hw_cursor_shown = 1;
-	}
-	fbinfo[0].cursor.cpos.fbx = xoff;
-	fbinfo[0].cursor.cpos.fby = yoff;
-	(*fbinfo[0].setcursor)(&fbinfo[0]);
+	con_type_init_finish();
+	register_console(&vt_console_driver);
+
+	return memory_start;
 }
+#endif
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunfb.c linux/drivers/sbus/char/sunfb.c
--- v2.1.52/linux/drivers/sbus/char/sunfb.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/sunfb.c	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $
+/* $Id: sunfb.c,v 1.28 1997/08/22 15:55:23 jj Exp $
  * sunfb.c: Sun generic frame buffer support.
  *
  * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -42,7 +42,6 @@
 #include "fb.h"
 
 extern void set_other_palette (int);
-extern void sun_clear_fb(int);
 extern void set_cursor (int);
 
 #define FB_SETUP(err) \
@@ -197,9 +196,9 @@
  		if (fb == fbinfo) {
  			if (vt_cons[fg_console]->vc_mode == KD_TEXT)
  				return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
- 			sun_hw_cursor_shown = 0; /* Forget state of our text cursor */
+ 			sbus_hw_cursor_shown = 0; /* Forget state of our text cursor */
 		}
-		return sun_hw_scursor ((struct fbcursor *) arg, fb);
+		return sbus_hw_scursor ((struct fbcursor *) arg, fb);
 
 	case FBIOSCURPOS:
 		if (!fb->setcursor) return -EINVAL;
@@ -242,8 +241,11 @@
 		vt_cons [fb->vtconsole]->vc_mode = KD_TEXT;
 
 	/* Leaving graphics mode, turn off the cursor */
-	if (fb->mmaped)
-		sun_clear_fb (minor);
+	if (fb->mmaped) {
+		fb->clear_fb (minor);
+		if (!minor && suncons_ops.clear_margin)
+			suncons_ops.clear_margin();
+	}
 	cursor.set    = FB_CUR_SETCUR;
 	cursor.enable = 0;
 	
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v2.1.52/linux/drivers/sbus/char/sunkbd.c	Mon Jul  7 08:18:55 1997
+++ linux/drivers/sbus/char/sunkbd.c	Thu Sep  4 12:54:49 1997
@@ -1,10 +1,14 @@
 /* keyboard.c: Sun keyboard driver.
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ *
  * Added vuid event generation and /dev/kbd device for SunOS
  * compatibility - Miguel (miguel@nuclecu.unam.mx)
+ *
+ * Added PCI 8042 controller support -DaveM
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/tty.h>
@@ -16,11 +20,11 @@
 #include <linux/fcntl.h>
 #include <linux/poll.h>
 #include <linux/random.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 
 #include <asm/kbio.h>
 #include <asm/vuid_event.h>
-#include <asm/delay.h>
 #include <asm/bitops.h>
 #include <asm/oplib.h>
 #include <asm/uaccess.h>
@@ -29,6 +33,15 @@
 #include <linux/kbd_diacr.h>
 #include <linux/vt_kern.h>
 
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/pbm.h>
+#include <asm/ebus.h>
+#endif
+
+#include "sunkbd.h"
+
 #define SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
 /* Define this one if you are making a new frame buffer driver */
@@ -63,6 +76,7 @@
 extern void scrollback(int);
 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 */
 
@@ -208,7 +222,23 @@
 #define KEY_ALT         0x86
 #define KEY_L1          0x87
 
-extern void kbd_put_char(unsigned char ch);
+/* Do to kbd_init() being called before rs_init(), and kbd_init() doing:
+ *
+ *	init_bh(KEYBOARD_BH, kbd_bh);
+ *	mark_bh(KEYBOARD_BH);
+ *
+ * this might well be called before some driver has claimed interest in
+ * handling the keyboard input/output. So we need to assign an initial nop.
+ *
+ * Otherwise this would lead to the following (DaveM might want to look at):
+ *
+ *	sparc64_dtlb_refbit_catch(),
+ *	do_sparc64_fault(),
+ *	kernel NULL pointer dereference at do_sparc64_fault + 0x2c0 ;-(
+ */
+static void nop_kbd_put_char(unsigned char c) { }
+static void (*kbd_put_char)(unsigned char) = nop_kbd_put_char;
+
 static inline void send_cmd(unsigned char c)
 {
 	kbd_put_char(c);
@@ -1425,12 +1455,17 @@
 	NULL,			/* revalidate */
 };
 
-__initfunc(void keyboard_zsinit(void))
+__initfunc(void keyboard_zsinit(void (*put_char)(unsigned char)))
 {
 	int timeout = 0;
 
+	kbd_put_char = put_char;
+	if (!kbd_put_char)
+		panic("keyboard_zsinit: no put_char parameter");
+
 	/* Test out the leds */
 	sunkbd_type = 255;
+	send_cmd(SKBDCMD_RESET);
 	send_cmd(SKBDCMD_RESET);
 	while((sunkbd_type==255) && timeout < 500000) {
 		udelay(100);
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunkbd.h linux/drivers/sbus/char/sunkbd.h
--- v2.1.52/linux/drivers/sbus/char/sunkbd.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/sunkbd.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,27 @@
+/* $Id: sunkbd.h,v 1.1 1997/08/28 02:23:34 ecd Exp $
+ * sunkbd.h: Defines needed by SUN Keyboard drivers
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNKBD_H
+#define _SPARC_SUNKBD_H 1
+
+/* Keyboard defines for L1-A processing... */
+#define SUNKBD_RESET		0xff
+#define SUNKBD_L1		0x01
+#define SUNKBD_UP		0x80
+#define SUNKBD_A		0x4d
+
+struct l1a_kbd_state {
+	int kbd_id;
+	int l1_down;
+};
+
+extern struct l1a_kbd_state l1a_state;
+
+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);
+
+#endif /* !(_SPARC_SUNKBD_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c
--- v2.1.52/linux/drivers/sbus/char/sunmouse.c	Mon Jul  7 08:18:55 1997
+++ linux/drivers/sbus/char/sunmouse.c	Thu Sep  4 12:54:49 1997
@@ -1,6 +1,6 @@
 /* sunmouse.c: Sun mouse driver for the Sparc
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
  *
  * Parts based on the psaux.c driver written by:
@@ -10,6 +10,7 @@
  * Jan/5/96  Added VUID support, sigio support - miguel.
  * Mar/5/96  Added proper mouse stream support - miguel.
  * Sep/96    Allow more than one reader -miguel.
+ * Aug/97    Added PCI 8042 controller support -DaveM
  */
 
 /* The mouse is run off of one of the Zilog serial ports.  On
@@ -37,6 +38,7 @@
  * FIXME: We need to support more than one mouse.
  * */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/fcntl.h>
@@ -90,7 +92,7 @@
 
 extern void mouse_put_char(char ch);
 
-/* #define SMOUSE_DEBUG */
+#undef SMOUSE_DEBUG
 
 static void
 push_event (Firm_event *ev)
@@ -125,6 +127,9 @@
 	int next = (sunmouse.head + 1) % STREAM_SIZE;
 
 	if (next != sunmouse.tail){
+#ifdef SMOUSE_DEBUG
+		printk("P<%02x>\n", (unsigned char)c);
+#endif
 		sunmouse.queue.stream [sunmouse.head] = c;
 		sunmouse.head = next;
 	}
@@ -142,14 +147,14 @@
 /* Change the baud rate after receiving too many "bogon bytes". */
 void sun_mouse_change_baud(void)
 {
-	extern void zs_change_mouse_baud(int newbaud);
+	extern void rs_change_mouse_baud(int newbaud);
 
 	if(mouse_baud == 1200)
 		mouse_baud = 4800;
 	else
 		mouse_baud = 1200;
 
-	zs_change_mouse_baud(mouse_baud);
+	rs_change_mouse_baud(mouse_baud);
 	mouse_baud_changing = 1;
 }
 
@@ -373,9 +378,25 @@
 		char *p = buffer, *end = buffer+count;
 		
 		while (p < end && !queue_empty ()){
-			copy_to_user_ret((Firm_event *)p, get_from_queue(),
-				     sizeof(Firm_event), -EFAULT);
-			p += sizeof (Firm_event);
+#ifdef CONFIG_SPARC32_COMPAT
+			if (current->tss.flags & SPARC_FLAG_32BIT) {
+				Firm_event *q = get_from_queue();
+				
+				copy_to_user_ret((Firm_event *)p, q, 
+						 sizeof(Firm_event)-sizeof(struct timeval),
+						 -EFAULT);
+				p += sizeof(Firm_event)-sizeof(struct timeval);
+				__put_user_ret(q->time.tv_sec, (u32 *)p, -EFAULT);
+				p += sizeof(u32);
+				__put_user_ret(q->time.tv_usec, (u32 *)p, -EFAULT);
+				p += sizeof(u32);
+			} else
+#endif	
+			{	
+				copy_to_user_ret((Firm_event *)p, get_from_queue(),
+				     		 sizeof(Firm_event), -EFAULT);
+				p += sizeof (Firm_event);
+			}
 		}
 		sunmouse.ready = !queue_empty ();
 		inode->i_atime = CURRENT_TIME;
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunmouse.h linux/drivers/sbus/char/sunmouse.h
--- v2.1.52/linux/drivers/sbus/char/sunmouse.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/sunmouse.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,13 @@
+/* $Id: sunmouse.h,v 1.1 1997/08/28 02:23:38 ecd Exp $
+ * sunmouse.h: Interface to the SUN mouse driver.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SPARC_SUNMOUSE_H
+#define _SPARC_SUNMOUSE_H 1
+
+extern void sun_mouse_zsinit(void);
+extern void sun_mouse_inbyte(unsigned char);
+
+#endif /* !(_SPARC_SUNMOUSE_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.1.52/linux/drivers/sbus/char/sunserial.c	Mon Jul  7 08:18:55 1997
+++ linux/drivers/sbus/char/sunserial.c	Thu Sep  4 12:54:49 1997
@@ -1,2668 +1,134 @@
-/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $
- * serial.c: Serial port driver for the Sparc.
+/* $Id: sunserial.c,v 1.50 1997/09/03 11:54:59 ecd Exp $
+ * serial.c: Serial port driver infrastructure for the Sparc.
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
- * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  */
 
+#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
 #include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/keyboard.h>
-#include <linux/console.h>
-#include <linux/init.h>
+#include <linux/serial.h>
 
-#include <asm/io.h>
-#include <asm/irq.h>
 #include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-#include <asm/delay.h>
-#include <asm/kdebug.h>
 
 #include "sunserial.h"
 
-static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
-#define NUM_SERIAL num_serial
-#define NUM_CHANNELS (NUM_SERIAL * 2)
-
-#define KEYBOARD_LINE 0x2
-#define MOUSE_LINE    0x3
-
-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;
-int *zs_nodes;
-
-struct sun_serial *zs_soft;
-struct sun_serial *zs_chain;  /* IRQ servicing chain */
-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;
-static struct l1a_kbd_state l1a_state = { 0, 0 };
-struct sun_serial *zs_consinfo = 0;
-
-/* Keyboard defines for L1-A processing... */
-#define SUNKBD_RESET   0xff
-#define SUNKBD_L1      0x01
-#define SUNKBD_UP      0x80
-#define SUNKBD_A       0x4d
-
-extern void sunkbd_inchar(unsigned char ch, struct pt_regs *regs);
-extern void sun_mouse_inbyte(unsigned char byte);
-
-static unsigned char kgdb_regs[16] = {
-	0, 0, 0,                     /* write 0, 1, 2 */
-	(Rx8 | RxENAB),              /* write 3 */
-	(X16CLK | SB1 | PAR_EVEN),   /* write 4 */
-	(DTR | Tx8 | TxENAB),        /* write 5 */
-	0, 0, 0,                     /* write 6, 7, 8 */
-	(NV),                        /* write 9 */
-	(NRZ),                       /* write 10 */
-	(TCBR | RCBR),               /* write 11 */
-	0, 0,                        /* BRG time constant, write 12 + 13 */
-	(BRSRC | BRENAB),            /* write 14 */
-	(DCDIE)                      /* write 15 */
-};
-
-static unsigned char zscons_regs[16] = {
-	0,                           /* write 0 */
-	(EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
-	0,                           /* write 2 */
-	(Rx8 | RxENAB),              /* write 3 */
-	(X16CLK),                    /* write 4 */
-	(DTR | Tx8 | TxENAB),        /* write 5 */
-	0, 0, 0,                     /* write 6, 7, 8 */
-	(NV | MIE),                  /* write 9 */
-	(NRZ),                       /* write 10 */
-	(TCBR | RCBR),               /* write 11 */
-	0, 0,                        /* BRG time constant, write 12 + 13 */
-	(BRSRC | BRENAB),            /* write 14 */
-	(DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
-};
-
-#define ZS_CLOCK         4915200   /* Zilog input clock rate */
-
-DECLARE_TASK_QUEUE(tq_serial);
-
-struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL	1
-#define SERIAL_TYPE_CALLOUT	2
-  
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void change_speed(struct sun_serial *info);
-
-static struct tty_struct **serial_table;
-static struct termios **serial_termios;
-static struct termios **serial_termios_locked;
-
-#ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#endif
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write.  We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char tmp_buf[4096]; /* This is cheating */
-static struct semaphore tmp_buf_sem = MUTEX;
-
-static inline int serial_paranoia_check(struct sun_serial *info,
-					dev_t device, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
-	static const char *badmagic =
-		"Warning: bad magic number for serial struct (%d, %d) in %s\n";
-	static const char *badinfo =
-		"Warning: null sun_serial for (%d, %d) in %s\n";
-
-	if (!info) {
-		printk(badinfo, MAJOR(device), MINOR(device), routine);
-		return 1;
-	}
-	if (info->magic != SERIAL_MAGIC) {
-		printk(badmagic, MAJOR(device), MINOR(device), routine);
-		return 1;
-	}
-#endif
-	return 0;
-}
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 76800, 0 };
-
-/* 
- * Reading and writing Zilog8530 registers.  The delays are to make this
- * driver work on the Sun4 which needs a settling delay after each chip
- * register access, other machines handle this in hardware via auxiliary
- * flip-flops which implement the settle time we do in software.
- */
-static inline unsigned char read_zsreg(struct sun_zschannel *channel,
-				       unsigned char reg)
-{
-	unsigned char retval;
-
-	channel->control = reg;
-	udelay(5);
-	retval = channel->control;
-	udelay(5);
-	return retval;
-}
-
-static inline void write_zsreg(struct sun_zschannel *channel,
-			       unsigned char reg, unsigned char value)
-{
-	channel->control = reg;
-	udelay(5);
-	channel->control = value;
-	udelay(5);
-}
-
-static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
-{
-	unsigned long flags;
-	struct sun_zschannel *channel = info->zs_channel;
-	unsigned char stat;
-	int i;
-
-	for (i = 0; i < 1000; i++) {
-		stat = read_zsreg(channel, R1);
-		if (stat & ALL_SNT)
-			break;
-		udelay(100);
-	}
-	write_zsreg(channel, R3, 0);
-	ZS_CLEARSTAT(channel);
-	ZS_CLEARERR(channel);
-	ZS_CLEARFIFO(channel);
-
-	/* Load 'em up */
-	save_flags(flags); cli();
-	if (info->channelA)
-		write_zsreg(channel, R9, CHRA);
-	else
-		write_zsreg(channel, R9, CHRB);
-	udelay(20);	/* wait for some old sun4's */
-	write_zsreg(channel, R4, regs[R4]);
-	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
-	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
-	write_zsreg(channel, R9, regs[R9] & ~MIE);
-	write_zsreg(channel, R10, regs[R10]);
-	write_zsreg(channel, R11, regs[R11]);
-	write_zsreg(channel, R12, regs[R12]);
-	write_zsreg(channel, R13, regs[R13]);
-	write_zsreg(channel, R14, regs[R14] & ~BRENAB);
-	write_zsreg(channel, R14, regs[R14]);
-	write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
-	write_zsreg(channel, R3, regs[R3]);
-	write_zsreg(channel, R5, regs[R5]);
-	write_zsreg(channel, R15, regs[R15]);
-	write_zsreg(channel, R0, RES_EXT_INT);
-	write_zsreg(channel, R0, ERR_RES);
-	write_zsreg(channel, R1, regs[R1]);
-	write_zsreg(channel, R9, regs[R9]);
-	restore_flags(flags);
-}
-
-static inline void zs_put_char(struct sun_zschannel *channel, char ch)
-{
-	int loops = 0;
-
-	while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
-		loops++;
-		udelay(5);
-	}
-	channel->data = ch;
-	udelay(5);
-}
-
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct sun_serial *ss, int set)
-{
-	unsigned long flags;
-
-	save_flags(flags); cli();
-	if(set) {
-		ss->curregs[5] |= (RTS | DTR);
-		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
-	} else {
-		ss->curregs[5] &= ~(RTS | DTR);
-		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
-	}
-	restore_flags(flags);
-	return;
-}
-
-static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
-{
-	int brg;
-
-	if(intson) {
-		kgdb_regs[R1] = INT_ALL_Rx;
-		kgdb_regs[R9] |= MIE;
-	} else {
-		kgdb_regs[R1] = 0;
-		kgdb_regs[R9] &= ~MIE;
-	}
-	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
-	kgdb_regs[R12] = (brg & 255);
-	kgdb_regs[R13] = ((brg >> 8) & 255);
-	load_zsregs(ss, kgdb_regs);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-	unsigned long flags;
-
-	if (serial_paranoia_check(info, tty->device, "rs_stop"))
-		return;
-	
-	save_flags(flags); cli();
-	if (info->curregs[5] & TxENAB) {
-		info->curregs[5] &= ~TxENAB;
-		write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	}
-	restore_flags(flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-	unsigned long flags;
-	
-	if (serial_paranoia_check(info, tty->device, "rs_start"))
-		return;
-	
-	save_flags(flags); cli();
-	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
-		info->curregs[5] |= TxENAB;
-		write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	}
-	restore_flags(flags);
-}
-
-/* Drop into either the boot monitor or kadb upon receiving a break
- * from keyboard/console input.
- */
-static void batten_down_hatches(void)
-{
-	/* 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
-	 * first before giving up control.
-	 */
-	printk("\n");
-	flush_user_windows();
-#ifndef __sparc_v9__
-	if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
-	   (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
-		sp_enter_debugger();
-	else
-#endif
-		prom_cmdline();
-
-	/* XXX We want to notify the keyboard driver that all
-	 * XXX keys are in the up state or else weird things
-	 * XXX happen...
-	 */
-
-	return;
-}
-
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines.  All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt().  They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off.  People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible.  After you are done making modifications, it is not a bad
- * idea to do:
- * 
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-/*
- * This routine is used by the interrupt handler to schedule
- * processing in the software interrupt portion of the driver.
- */
-static _INLINE_ void rs_sched_event(struct sun_serial *info,
-				  int event)
-{
-	info->event |= 1 << event;
-	queue_task(&info->tqueue, &tq_serial);
-	mark_bh(SERIAL_BH);
-}
-
-#ifndef __sparc_v9__
-extern void breakpoint(void);  /* For the KGDB frame character */
-#endif
-
-static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
-{
-	struct tty_struct *tty = info->tty;
-	unsigned char ch, stat;
-
-	do {
-		ch = (info->zs_channel->data) & info->parity_mask;
-		udelay(5);
-
-		/* If this is the console keyboard, we need to handle
-		 * L1-A's here.
-		 */
-		if(info->cons_keyb) {
-			if(ch == SUNKBD_RESET) {
-				l1a_state.kbd_id = 1;
-				l1a_state.l1_down = 0;
-			} else if(l1a_state.kbd_id) {
-				l1a_state.kbd_id = 0;
-			} else if(ch == SUNKBD_L1) {
-				l1a_state.l1_down = 1;
-			} else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
-				l1a_state.l1_down = 0;
-			} else if(ch == SUNKBD_A && l1a_state.l1_down) {
-				/* whee... */
-				batten_down_hatches();
-				/* Continue execution... */
-				l1a_state.l1_down = 0;
-				l1a_state.kbd_id = 0;
-				return;
-			}
-			sunkbd_inchar(ch, regs);
-			return;
-		}
-		if(info->cons_mouse) {
-			sun_mouse_inbyte(ch);
-			return;
-		}
-		if(info->is_cons) {
-			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
-			}
-			/* It is a 'keyboard interrupt' ;-) */
-			wake_up(&keypress_wait);
-		}
-#ifndef __sparc_v9__
-		/* Look for kgdb 'stop' character, consult the gdb
-		 * documentation for remote target debugging and
-		 * arch/sparc/kernel/sparc-stub.c to see how all this works.
-		 */
-		if((info->kgdb_channel) && (ch =='\003')) {
-			breakpoint();
-			return;
-		}
-#endif
-		if(!tty)
-			return;
-
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-			break;
-
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = 0;
-		*tty->flip.char_buf_ptr++ = ch;
-
-		/* Check if we have another character... */
-		stat = info->zs_channel->control;
-		udelay(5);
-		if (!(stat & Rx_CH_AV))
-			break;
-
-		/* ... and see if it is clean. */
-		stat = read_zsreg(info->zs_channel, R1);
-	} while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
-
-	queue_task(&tty->flip.tqueue, &tq_timer);
-}
-
-static _INLINE_ void transmit_chars(struct sun_serial *info)
-{
-	struct tty_struct *tty = info->tty;
-
-	if (info->x_char) {
-		/* Send next char */
-		zs_put_char(info->zs_channel, info->x_char);
-		info->x_char = 0;
-		return;
-	}
-
-	if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
-		/* That's peculiar... */
-		info->zs_channel->control = RES_Tx_P;
-		udelay(5);
-		return;
-	}
-
-	/* Send char */
-	zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
-	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-	info->xmit_cnt--;
-
-	if (info->xmit_cnt < WAKEUP_CHARS)
-		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
-	if(info->xmit_cnt <= 0) {
-		info->zs_channel->control = RES_Tx_P;
-		udelay(5);
-	}
-}
-
-static _INLINE_ void status_handle(struct sun_serial *info)
+static void nop_rs_cons_hook(int chip, int out, int line)
 {
-	unsigned char status;
-
-	/* Get status from Read Register 0 */
-	status = info->zs_channel->control;
-	udelay(5);
-	/* Clear status condition... */
-	info->zs_channel->control = RES_EXT_INT;
-	udelay(5);
-
-#if 0
-	if(status & DCD) {
-		if((info->tty->termios->c_cflag & CRTSCTS) &&
-		   ((info->curregs[3] & AUTO_ENAB)==0)) {
-			info->curregs[3] |= AUTO_ENAB;
-			write_zsreg(info->zs_channel, 3, info->curregs[3]);
-		}
-	} else {
-		if((info->curregs[3] & AUTO_ENAB)) {
-			info->curregs[3] &= ~AUTO_ENAB;
-			write_zsreg(info->zs_channel, 3, info->curregs[3]);
-		}
-	}
-#endif
-	/* Whee, if this is console input and this is a
-	 * 'break asserted' status change interrupt, call
-	 * the boot prom.
-	 */
-	if((status & BRK_ABRT) && info->break_abort)
-		batten_down_hatches();
-
-	/* XXX Whee, put in a buffer somewhere, the status information
-	 * XXX whee whee whee... Where does the information go...
-	 */
-	return;
-}
-
-static _INLINE_ void special_receive(struct sun_serial *info)
-{
-	struct tty_struct *tty = info->tty;
-	unsigned char ch, stat;
-
-	stat = read_zsreg(info->zs_channel, R1);
-	if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
-		ch = info->zs_channel->data;
-		udelay(5);
-	}
-
-	if (!tty)
-		goto clear;
-
-	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-		goto done;
-
-	tty->flip.count++;
-	if(stat & PAR_ERR)
-		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
-	else if(stat & Rx_OVR)
-		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-	else if(stat & CRC_ERR)
-		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
-	queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
-	info->zs_channel->control = ERR_RES;
-	udelay(5);
-}
-
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
-	struct sun_serial *info;
-	unsigned char zs_intreg;
-	int i;
-
-	info = (struct sun_serial *)dev_id;
-	for (i = 0; i < NUM_SERIAL; i++) {
-		zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
-		zs_intreg &= STATUS_MASK;
-
-		/* NOTE: The read register 2, which holds the irq status,
-		 *       does so for both channels on each chip.  Although
-		 *       the status value itself must be read from the B
-		 *       channel and is only valid when read from channel B.
-		 *       When read from channel A, read register 2 contains
-		 *       the value written to write register 2.
-		 */
-
-		/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
-		if (zs_intreg == CHA_Rx_AVAIL) {
-			receive_chars(info, regs);
-			return;
-		}
-		if(zs_intreg == CHA_Tx_EMPTY) {
-			transmit_chars(info);
-			return;
-		}
-		if (zs_intreg == CHA_EXT_STAT) {
-			status_handle(info);
-			return;
-		}
-		if (zs_intreg == CHA_SPECIAL) {
-			special_receive(info);
-			return;
-		}
-
-		/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
-		if(zs_intreg == CHB_Rx_AVAIL) {
-			receive_chars(info->zs_next, regs);
-			return;
-		}
-		if(zs_intreg == CHB_Tx_EMPTY) {
-			transmit_chars(info->zs_next);
-			return;
-		}
-		if (zs_intreg == CHB_EXT_STAT) {
-			status_handle(info->zs_next);
-			return;
-		}
-
-		/* NOTE: The default value for the IRQ status in read register
-		 *       2 in channel B is CHB_SPECIAL, so we need to look at
-		 *       read register 3 in channel A to check if this is a
-		 *       real interrupt, or just the default value.
-		 *       Yes... broken hardware...
-		 */
-
-		zs_intreg = read_zsreg(info->zs_channel, 3);
-		if (zs_intreg & CHBRxIP) {
-			special_receive(info->zs_next);
-			return;
-		}
-		info = info->zs_next->zs_next;
-	}
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using rs_sched_event(), and they get done here.
- */
-static void do_serial_bh(void)
-{
-	run_task_queue(&tq_serial);
-}
-
-static void do_softint(void *private_)
-{
-	struct sun_serial	*info = (struct sun_serial *) private_;
-	struct tty_struct	*tty;
-	
-	tty = info->tty;
-	if (!tty)
-		return;
-
-	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		    tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-		wake_up_interruptible(&tty->write_wait);
-	}
+	printk("Oops: %s called\n", __FUNCTION__);
 }
 
-/*
- * 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_)
+static void nop_rs_kgdb_hook(int channel)
 {
-	struct sun_serial	*info = (struct sun_serial *) private_;
-	struct tty_struct	*tty;
-	
-	tty = info->tty;
-	if (!tty)
-		return;
-#ifdef SERIAL_DEBUG_OPEN
-	printk("do_serial_hangup<%p: tty-%d\n",
-		__builtin_return_address(0), info->line);
-#endif
-
-	tty_hangup(tty);
+	printk("Oops: %s called\n", __FUNCTION__);
 }
 
-
-/*
- * This subroutine is called when the RS_TIMER goes off.  It is used
- * by the serial driver to handle ports that do not have an interrupt
- * (irq=0).  This doesn't work at all for 16450's, as a sun has a Z8530.
- */
- 
-static void rs_timer(void)
+static void nop_rs_change_mouse_baud(int baud)
 {
-	printk("rs_timer called\n");
-	prom_halt();
-	return;
+	printk("Oops: %s called\n", __FUNCTION__);
 }
 
-static int startup(struct sun_serial * info)
+static int nop_rs_read_proc(char *page, char **start, off_t off, int count,
+			    int *eof, void *data)
 {
-	unsigned long flags;
-
-	if (info->flags & ZILOG_INITIALIZED)
-		return 0;
-
-	if (!info->xmit_buf) {
-		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-		if (!info->xmit_buf)
-			return -ENOMEM;
-	}
-
-	save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
-	printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
-#endif
-
-	/*
-	 * Clear the FIFO buffers and disable them
-	 * (they will be reenabled in change_speed())
-	 */
-	ZS_CLEARFIFO(info->zs_channel);
-	info->xmit_fifo_size = 1;
-
-	/*
-	 * Clear the interrupt registers.
-	 */
-	info->zs_channel->control = ERR_RES;
-	udelay(5);
-	info->zs_channel->control = RES_H_IUS;
-	udelay(5);
-
-	/*
-	 * Now, initialize the Zilog
-	 */
-	zs_rtsdtr(info, 1);
-
-	/*
-	 * Finally, enable sequencing and interrupts
-	 */
-	info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
-				(EXT_INT_ENAB | INT_ALL_Rx);
-	info->curregs[3] |= (RxENAB | Rx8);
-	/* We enable Tx interrupts as needed. */
-	info->curregs[5] |= (TxENAB | Tx8);
-	info->curregs[9] |= (NV | MIE);
-	write_zsreg(info->zs_channel, 3, info->curregs[3]);
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	write_zsreg(info->zs_channel, 9, info->curregs[9]);
-	
-	/*
-	 * And clear the interrupt registers again for luck.
-	 */
-	info->zs_channel->control = ERR_RES;
-	udelay(5);
-	info->zs_channel->control = RES_H_IUS;
-	udelay(5);
-
-	if (info->tty)
-		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
-	/*
-	 * Set up serial timers...
-	 */
-#if 0  /* Works well and stops the machine. */
-	timer_table[RS_TIMER].expires = jiffies + 2;
-	timer_active |= 1 << RS_TIMER;
-#endif
-
-	/*
-	 * and set the speed of the serial port
-	 */
-	change_speed(info);
-
-	info->flags |= ZILOG_INITIALIZED;
-	restore_flags(flags);
+	printk("Oops: %s called\n", __FUNCTION__);
 	return 0;
 }
 
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct sun_serial * info)
-{
-	unsigned long	flags;
-
-	if (!(info->flags & ZILOG_INITIALIZED))
-		return;
-
-#ifdef SERIAL_DEBUG_OPEN
-	printk("Shutting down serial port %d (irq %d)....", info->line,
-	       info->irq);
-#endif
-	
-	save_flags(flags); cli(); /* Disable interrupts */
-	
-	if (info->xmit_buf) {
-		free_page((unsigned long) info->xmit_buf);
-		info->xmit_buf = 0;
-	}
-
-	if (info->tty)
-		set_bit(TTY_IO_ERROR, &info->tty->flags);
-	
-	info->flags &= ~ZILOG_INITIALIZED;
-	restore_flags(flags);
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct sun_serial *info)
-{
-	unsigned short port;
-	unsigned cflag;
-	int	quot = 0;
-	int	i;
-	int	brg;
-
-	if (!info->tty || !info->tty->termios)
-		return;
-	cflag = info->tty->termios->c_cflag;
-	if (!(port = info->port))
-		return;
-	i = cflag & CBAUD;
-	if (cflag & CBAUDEX) {
-		i &= ~CBAUDEX;
-		if (i != 5)
-			info->tty->termios->c_cflag &= ~CBAUDEX;
-		else
-			i = 16;
-	}
-	if (i == 15) {
-		if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
-			i += 1;
-		if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
-			quot = info->custom_divisor;
-	}
-	if (quot) {
-		info->zs_baud = info->baud_base / quot;
-		info->clk_divisor = 16;
-
-		info->curregs[4] = X16CLK;
-		info->curregs[11] = TCBR | RCBR;
-		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
-		info->curregs[12] = (brg & 255);
-		info->curregs[13] = ((brg >> 8) & 255);
-		info->curregs[14] = BRSRC | BRENAB;
-		zs_rtsdtr(info, 1);
-	} else if (baud_table[i]) {
-		info->zs_baud = baud_table[i];
-		info->clk_divisor = 16;
-
-		info->curregs[4] = X16CLK;
-		info->curregs[11] = TCBR | RCBR;
-		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
-		info->curregs[12] = (brg & 255);
-		info->curregs[13] = ((brg >> 8) & 255);
-		info->curregs[14] = BRSRC | BRENAB;
-		zs_rtsdtr(info, 1);
-	} else {
-		zs_rtsdtr(info, 0);
-		return;
-	}
-
-	/* byte size and parity */
-	switch (cflag & CSIZE) {
-	case CS5:
-		info->curregs[3] &= ~(RxN_MASK);
-		info->curregs[3] |= Rx5;
-		info->curregs[5] &= ~(TxN_MASK);
-		info->curregs[5] |= Tx5;
-		info->parity_mask = 0x1f;
-		break;
-	case CS6:
-		info->curregs[3] &= ~(RxN_MASK);
-		info->curregs[3] |= Rx6;
-		info->curregs[5] &= ~(TxN_MASK);
-		info->curregs[5] |= Tx6;
-		info->parity_mask = 0x3f;
-		break;
-	case CS7:
-		info->curregs[3] &= ~(RxN_MASK);
-		info->curregs[3] |= Rx7;
-		info->curregs[5] &= ~(TxN_MASK);
-		info->curregs[5] |= Tx7;
-		info->parity_mask = 0x7f;
-		break;
-	case CS8:
-	default: /* defaults to 8 bits */
-		info->curregs[3] &= ~(RxN_MASK);
-		info->curregs[3] |= Rx8;
-		info->curregs[5] &= ~(TxN_MASK);
-		info->curregs[5] |= Tx8;
-		info->parity_mask = 0xff;
-		break;
-	}
-	info->curregs[4] &= ~(0x0c);
-	if (cflag & CSTOPB) {
-		info->curregs[4] |= SB2;
-	} else {
-		info->curregs[4] |= SB1;
-	}
-	if (cflag & PARENB) {
-		info->curregs[4] |= PAR_ENAB;
-	} else {
-		info->curregs[4] &= ~PAR_ENAB;
-	}
-	if (!(cflag & PARODD)) {
-		info->curregs[4] |= PAR_EVEN;
-	} else {
-		info->curregs[4] &= ~PAR_EVEN;
-	}
-
-	/* Load up the new values */
-	load_zsregs(info, info->curregs);
-
-	return;
-}
-
-/* This is for mouse/keyboard output.
- * XXX mouse output??? can we send it commands??? XXX
- */
-void kbd_put_char(unsigned char ch)
-{
-	struct sun_zschannel *chan = zs_kbdchan;
-	unsigned long flags;
-
-	if(!chan)
-		return;
-
-	save_flags(flags); cli();
-	zs_put_char(chan, ch);
-	restore_flags(flags);
-}
-
-void mouse_put_char(char ch)
-{
-	struct sun_zschannel *chan = zs_mousechan;
-	unsigned long flags;
-
-	if(!chan)
-		return;
-
-	save_flags(flags); cli();
-	zs_put_char(chan, ch);
-	restore_flags(flags);
-}
-
-
-/* This is for console output over ttya/ttyb */
-static void rs_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.
- */
-void putDebugChar(char kgdb_char)
-{
-	struct sun_zschannel *chan = zs_kgdbchan;
-
-	while((chan->control & Tx_BUF_EMP)==0)
-		udelay(5);
-	chan->data = kgdb_char;
-}
-
-char getDebugChar(void)
-{
-	struct sun_zschannel *chan = zs_kgdbchan;
-
-	while((chan->control & Rx_CH_AV)==0)
-		barrier();
-	return chan->data;
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void rs_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);
-
-		rs_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')
-			rs_put_char('\r');
-		rs_put_char(*s);
-	}
-
-	/* Comment this if you want to have a strict interrupt-driven output */
-	rs_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 rs_flush_chars(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-	unsigned long flags;
-
-	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
-		return;
-
-	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-	    !info->xmit_buf)
-		return;
-
-	/* Enable transmitter */
-	save_flags(flags); cli();
-	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
-	write_zsreg(info->zs_channel, 1, info->curregs[1]);
-	info->curregs[5] |= TxENAB;
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-
-	/*
-	 * Send a first (bootstrapping) character. A best solution is
-	 * to call transmit_chars() here which handles output in a
-	 * generic way. Current transmit_chars() not only transmits,
-	 * but resets interrupts also what we do not desire here.
-	 * XXX Discuss with David.
-	 */
-	zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
-	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-	info->xmit_cnt--;
-
-	restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
-		    const unsigned char *buf, int count)
-{
-	int	c, total = 0;
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-	unsigned long flags;
-
-	if (serial_paranoia_check(info, tty->device, "rs_write"))
-		return 0;
-
-	if (!info || !info->xmit_buf)
-		return 0;
-
-	save_flags(flags);
-	while (1) {
-		cli();		
-		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				   SERIAL_XMIT_SIZE - info->xmit_head));
-		if (c <= 0)
-			break;
-
-		if (from_user) {
-			down(&tmp_buf_sem);
-			copy_from_user(tmp_buf, buf, c);
-			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				       SERIAL_XMIT_SIZE - info->xmit_head));
-			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-			up(&tmp_buf_sem);
-		} else
-			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt += c;
-		restore_flags(flags);
-		buf += c;
-		count -= c;
-		total += c;
-	}
-
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
-		/* Enable transmitter */
-		info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
-		write_zsreg(info->zs_channel, 1, info->curregs[1]);
-		info->curregs[5] |= TxENAB;
-		write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	}
-#if 1
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
-		zs_put_char(info->zs_channel,
-			    info->xmit_buf[info->xmit_tail++]);
-		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt--;
-	}
-#endif
-	restore_flags(flags);
-	return total;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-	int	ret;
-				
-	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
-		return 0;
-	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-	if (ret < 0)
-		ret = 0;
-	return ret;
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
-	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
-		return 0;
-	return info->xmit_cnt;
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
-	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
-		return;
-	cli();
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-	sti();
-	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-	    tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup)(tty);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- * 
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct * tty)
-{
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
-	char	buf[64];
-	
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
-#endif
-
-	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
-		return;
-	
-	if (I_IXOFF(tty))
-		info->x_char = STOP_CHAR(tty);
-
-	/* Turn off RTS line */
-	cli();
-	info->curregs[5] &= ~RTS;
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
-}
+struct sunserial_operations rs_ops = {
+	0,
+	nop_rs_cons_hook,
+	nop_rs_kgdb_hook,
+	nop_rs_change_mouse_baud,
+	nop_rs_read_proc
+};
 
-static void rs_unthrottle(struct tty_struct * tty)
+int rs_init(void)
 {
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
-	char	buf[64];
-	
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
-#endif
+	struct rs_initfunc *init;
+	int err = -ENODEV;
 
-	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
-		return;
-	
-	if (I_IXOFF(tty)) {
-		if (info->x_char)
-			info->x_char = 0;
-		else
-			info->x_char = START_CHAR(tty);
+	init = rs_ops.rs_init;
+	while (init) {
+		err = init->rs_init();
+		init = init->next;
 	}
-
-	/* Assert RTS line */
-	cli();
-	info->curregs[5] |= RTS;
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
+	return err;
 }
 
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct sun_serial * info,
-			   struct serial_struct * retinfo)
+void rs_cons_hook(int chip, int out, int line)
 {
-	struct serial_struct tmp;
-  
-	if (!retinfo)
-		return -EFAULT;
-	memset(&tmp, 0, sizeof(tmp));
-	tmp.type = info->type;
-	tmp.line = info->line;
-	tmp.port = info->port;
-	tmp.irq = info->irq;
-	tmp.flags = info->flags;
-	tmp.baud_base = info->baud_base;
-	tmp.close_delay = info->close_delay;
-	tmp.closing_wait = info->closing_wait;
-	tmp.custom_divisor = info->custom_divisor;
-	copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
-	return 0;
+	rs_ops.rs_cons_hook(chip, out, line);
 }
 
-static int set_serial_info(struct sun_serial * info,
-			   struct serial_struct * new_info)
-{
-	struct serial_struct new_serial;
-	struct sun_serial old_info;
-	int 			retval = 0;
-
-	if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
-		return -EFAULT;
-	old_info = *info;
-
-	if (!suser()) {
-		if ((new_serial.baud_base != info->baud_base) ||
-		    (new_serial.type != info->type) ||
-		    (new_serial.close_delay != info->close_delay) ||
-		    ((new_serial.flags & ~ZILOG_USR_MASK) !=
-		     (info->flags & ~ZILOG_USR_MASK)))
-			return -EPERM;
-		info->flags = ((info->flags & ~ZILOG_USR_MASK) |
-			       (new_serial.flags & ZILOG_USR_MASK));
-		info->custom_divisor = new_serial.custom_divisor;
-		goto check_and_exit;
-	}
-
-	if (info->count > 1)
-		return -EBUSY;
-
-	/*
-	 * OK, past this point, all the error checking has been done.
-	 * At this point, we start making changes.....
-	 */
-
-	info->baud_base = new_serial.baud_base;
-	info->flags = ((info->flags & ~ZILOG_FLAGS) |
-			(new_serial.flags & ZILOG_FLAGS));
-	info->custom_divisor = new_serial.custom_divisor;
-	info->type = new_serial.type;
-	info->close_delay = new_serial.close_delay;
-	info->closing_wait = new_serial.closing_wait;
-
-check_and_exit:
-	retval = startup(info);
-	return retval;
-}
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * 	    is emptied.  On bus types like RS485, the transmitter must
- * 	    release the bus after transmitting. This must be done when
- * 	    the transmit shift register is empty, not be done when the
- * 	    transmit holding register is empty.  This functionality
- * 	    allows an RS485 driver to be written in user space. 
- */
-static int get_lsr_info(struct sun_serial * info, unsigned int *value)
+void rs_kgdb_hook(int channel)
 {
-	unsigned char status;
-
-	cli();
-	status = info->zs_channel->control;
-	sti();
-	put_user_ret(status,value, -EFAULT);
-	return 0;
+	rs_ops.rs_kgdb_hook(channel);
 }
 
-static int get_modem_info(struct sun_serial * info, unsigned int *value)
+void rs_change_mouse_baud(int baud)
 {
-	unsigned char status;
-	unsigned int result;
-
-	cli();
-	status = info->zs_channel->control;
-	sti();
-	result =  ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
-		| ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
-		| ((status  & DCD) ? TIOCM_CAR : 0)
-		| ((status  & SYNC) ? TIOCM_DSR : 0)
-		| ((status  & CTS) ? TIOCM_CTS : 0);
-	put_user_ret(result, value, -EFAULT);
-	return 0;
+	rs_ops.rs_change_mouse_baud(baud);
 }
 
-static int set_modem_info(struct sun_serial * info, unsigned int cmd,
-			  unsigned int *value)
+int rs_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
 {
-	unsigned int arg;
-
-	get_user_ret(arg, value, -EFAULT);
-	switch (cmd) {
-	case TIOCMBIS: 
-		if (arg & TIOCM_RTS)
-			info->curregs[5] |= RTS;
-		if (arg & TIOCM_DTR)
-			info->curregs[5] |= DTR;
-		break;
-	case TIOCMBIC:
-		if (arg & TIOCM_RTS)
-			info->curregs[5] &= ~RTS;
-		if (arg & TIOCM_DTR)
-			info->curregs[5] &= ~DTR;
-		break;
-	case TIOCMSET:
-		info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
-			     | ((arg & TIOCM_RTS) ? RTS : 0)
-			     | ((arg & TIOCM_DTR) ? DTR : 0));
-		break;
-	default:
-		return -EINVAL;
-	}
-	cli();
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
-	return 0;
+	return rs_ops.rs_read_proc(page, start, off, count, eof, data);
 }
 
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break(	struct sun_serial * info, int duration)
+int register_serial(struct serial_struct *req)
 {
-	if (!info->port)
-		return;
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + duration;
-	cli();
-	write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
-	schedule();
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
+	return -1;
 }
 
-static int rs_ioctl(struct tty_struct *tty, struct file * file,
-		    unsigned int cmd, unsigned long arg)
+void unregister_serial(int line)
 {
-	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-	int retval;
-
-	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
-		return -ENODEV;
-
-	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
-	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
-		if (tty->flags & (1 << TTY_IO_ERROR))
-		    return -EIO;
-	}
-	
-	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:
-			put_user_ret(C_CLOCAL(tty) ? 1 : 0,
-				     (unsigned long *) arg, -EFAULT);
-			return 0;
-		case TIOCSSOFTCAR:
-			get_user_ret(arg, (unsigned long *) arg, -EFAULT);
-			tty->termios->c_cflag =
-				((tty->termios->c_cflag & ~CLOCAL) |
-				 (arg ? CLOCAL : 0));
-			return 0;
-		case TIOCMGET:
-			return get_modem_info(info, (unsigned int *) arg);
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-			return set_modem_info(info, cmd, (unsigned int *) arg);
-		case TIOCGSERIAL:
-			return get_serial_info(info,
-					       (struct serial_struct *) arg);
-		case TIOCSSERIAL:
-			return set_serial_info(info,
-					       (struct serial_struct *) arg);
-		case TIOCSERGETLSR: /* Get line status register */
-			return get_lsr_info(info, (unsigned int *) arg);
-
-		case TIOCSERGSTRUCT:
-			copy_to_user_ret((struct sun_serial *) arg,
-				    info, sizeof(struct sun_serial), -EFAULT);
-			return 0;
-			
-		default:
-			return -ENOIOCTLCMD;
-		}
-	return 0;
 }
 
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+void
+sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void))
 {
-	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-
-	if (tty->termios->c_cflag == old_termios->c_cflag)
-		return;
+	struct rs_initfunc *rs_init;
 
-	change_speed(info);
+	*memory_start = (*memory_start + 7) & ~(7);
+	rs_init = (struct rs_initfunc *) *memory_start;
+	*memory_start += sizeof(struct rs_initfunc);
 
-	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
-		tty->hw_stopped = 0;
-		rs_start(tty);
-	}
+	rs_init->rs_init = init;
+	rs_init->next = rs_ops.rs_init;
+	rs_ops.rs_init = rs_init;
 }
 
-/*
- * ------------------------------------------------------------
- * rs_close()
- * 
- * This routine is called when the serial port gets closed.  First, we
- * wait for the last remaining data to be sent.  Then, we unlink its
- * ZILOG structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void rs_close(struct tty_struct *tty, struct file * filp)
-{
-	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-	unsigned long flags;
-
-	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
-		return;
-	
-	save_flags(flags); cli();
-	
-	if (tty_hung_up_p(filp)) {
-		restore_flags(flags);
-		return;
-	}
-	
-#ifdef SERIAL_DEBUG_OPEN
-	printk("rs_close tty-%d, count = %d\n", info->line, info->count);
-#endif
-	if ((tty->count == 1) && (info->count != 1)) {
-		/*
-		 * Uh, oh.  tty->count is 1, which means that the tty
-		 * structure will be freed.  Info->count should always
-		 * be one in these conditions.  If it's greater than
-		 * one, we've got real problems, since it means the
-		 * serial port won't be shutdown.
-		 */
-		printk("rs_close: bad serial port count; tty->count is 1, "
-		       "info->count is %d\n", info->count);
-		info->count = 1;
-	}
-	if (--info->count < 0) {
-		printk("rs_close: bad serial port count for ttys%d: %d\n",
-		       info->line, info->count);
-		info->count = 0;
-	}
-	if (info->count) {
-		restore_flags(flags);
-		return;
-	}
-	info->flags |= ZILOG_CLOSING;
-	/*
-	 * Save the termios structure, since this port may have
-	 * separate termios for callout and dialin.
-	 */
-	if (info->flags & ZILOG_NORMAL_ACTIVE)
-		info->normal_termios = *tty->termios;
-	if (info->flags & ZILOG_CALLOUT_ACTIVE)
-		info->callout_termios = *tty->termios;
-	/*
-	 * Now we wait for the transmit buffer to clear; and we notify 
-	 * the line discipline to only process XON/XOFF characters.
-	 */
-	tty->closing = 1;
-	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
-		tty_wait_until_sent(tty, info->closing_wait);
-	/*
-	 * At this point we stop accepting input.  To do this, we
-	 * disable the receive line status interrupts, and tell the
-	 * interrupt driver to stop checking the data ready bit in the
-	 * line status register.
-	 */
-	/** if (!info->iscons) ... **/
-	info->curregs[3] &= ~RxENAB;
-	write_zsreg(info->zs_channel, 3, info->curregs[3]);
-	info->curregs[1] &= ~(RxINT_MASK);
-	write_zsreg(info->zs_channel, 1, info->curregs[1]);
-	ZS_CLEARFIFO(info->zs_channel);
-
-	shutdown(info);
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-	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;
-			current->timeout = jiffies + info->close_delay;
-			schedule();
-		}
-		wake_up_interruptible(&info->open_wait);
-	}
-	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
-			 ZILOG_CLOSING);
-	wake_up_interruptible(&info->close_wait);
-#ifdef SERIAL_DEBUG_OPEN
-	printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count);
+extern int zs_probe(unsigned long *);
+#ifdef CONFIG_SAB82532
+extern int sab82532_probe(unsigned long *);
 #endif
-	restore_flags(flags);
-}
-
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void rs_hangup(struct tty_struct *tty)
-{
-	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-	
-	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
-		return;
-	
-#ifdef SERIAL_DEBUG_OPEN
-	printk("rs_hangup<%p: tty-%d, count = %d bye\n",
-		__builtin_return_address(0), info->line, info->count);
+#ifdef __sparc_v9__
+extern int ps2kbd_probe(unsigned long *);
+extern int su_probe(unsigned long *);
 #endif
 
-	rs_flush_buffer(tty);
-	shutdown(info);
-	info->event = 0;
-	info->count = 0;
-	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
-	info->tty = 0;
-	wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
-			   struct sun_serial *info)
+unsigned long
+sun_serial_setup(unsigned long memory_start)
 {
-	struct wait_queue wait = { current, NULL };
-	int		retval;
-	int		do_clocal = 0;
-	unsigned char	r0;
-
-	/*
-	 * If the device is in the middle of being closed, then block
-	 * until it's done, and then try again.
-	 */
-	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;
-#else
-		return -EAGAIN;
-#endif
-	}
-
-	/*
-	 * If this is a callout device, then just make sure the normal
-	 * device isn't being used.
-	 */
-	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
-		if (info->flags & ZILOG_NORMAL_ACTIVE)
-			return -EBUSY;
-		if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
-		    (info->flags & ZILOG_SESSION_LOCKOUT) &&
-		    (info->session != current->session))
-		    return -EBUSY;
-		if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
-		    (info->flags & ZILOG_PGRP_LOCKOUT) &&
-		    (info->pgrp != current->pgrp))
-		    return -EBUSY;
-		info->flags |= ZILOG_CALLOUT_ACTIVE;
-		return 0;
-	}
-	
-	/*
-	 * If non-blocking mode is set, or the port is not enabled,
-	 * then make the check up front and then exit.
-	 */
-	if ((filp->f_flags & O_NONBLOCK) ||
-	    (tty->flags & (1 << TTY_IO_ERROR))) {
-		if (info->flags & ZILOG_CALLOUT_ACTIVE)
-			return -EBUSY;
-		info->flags |= ZILOG_NORMAL_ACTIVE;
-		return 0;
-	}
+	/* Probe for controllers. */
+	if (zs_probe(&memory_start) == 0)
+		return memory_start;
 
-	if (info->flags & ZILOG_CALLOUT_ACTIVE) {
-		if (info->normal_termios.c_cflag & CLOCAL)
-			do_clocal = 1;
-	} else {
-		if (tty->termios->c_cflag & CLOCAL)
-			do_clocal = 1;
-	}
-	
-	/*
-	 * Block waiting for the carrier detect and the line to become
-	 * free (i.e., not in use by the callout).  While we are in
-	 * this loop, info->count is dropped by one, so that
-	 * rs_close() knows when to free things.  We restore it upon
-	 * exit, either normal or abnormal.
-	 */
-	retval = 0;
-	add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
-	printk("block_til_ready before block: ttys%d, count = %d\n",
-	       info->line, info->count);
+#ifdef CONFIG_SAB82532
+	sab82532_probe(&memory_start);
 #endif
-	cli();
-	if(!tty_hung_up_p(filp))
-		info->count--;
-	sti();
-	info->blocked_open++;
-	while (1) {
-		cli();
-		if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
-			zs_rtsdtr(info, 1);
-		sti();
-		current->state = TASK_INTERRUPTIBLE;
-		if (tty_hung_up_p(filp) ||
-		    !(info->flags & ZILOG_INITIALIZED)) {
-#ifdef SERIAL_DEBUG_OPEN
-			printk("block_til_ready hup-ed: ttys%d, count = %d\n",
-				info->line, info->count);
+#ifdef __sparc_v9__
+	if (ps2kbd_probe(&memory_start) == 0)
+		return memory_start;
+	if (su_probe(&memory_start) == 0)
+		return memory_start;
 #endif
-#ifdef SERIAL_DO_RESTART
-			if (info->flags & ZILOG_HUP_NOTIFY)
-				retval = -EAGAIN;
-			else
-				retval = -ERESTARTSYS;	
-#else
-			retval = -EAGAIN;
-#endif
-			break;
-		}
 
-		cli();
-		r0 = read_zsreg(info->zs_channel, R0);
-		sti();
-		if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
-		    !(info->flags & ZILOG_CLOSING) &&
-		    (do_clocal || (DCD & r0)))
-			break;
-		if (current->signal & ~current->blocked) {
-			retval = -ERESTARTSYS;
-			break;
-		}
-#ifdef SERIAL_DEBUG_OPEN
-		printk("block_til_ready blocking: ttys%d, count = %d\n",
-		       info->line, info->count);
-#endif
-		schedule();
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&info->open_wait, &wait);
-	if (!tty_hung_up_p(filp))
-		info->count++;
-	info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-	printk("block_til_ready after blocking: ttys%d, count = %d\n",
-	       info->line, info->count);
-#endif
-	if (retval)
-		return retval;
-	info->flags |= ZILOG_NORMAL_ACTIVE;
-	return 0;
-}	
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * enables interrupts for a serial port, linking in its ZILOG structure into
- * the IRQ chain.   It also performs the serial-specific
- * initialization for the tty structure.
- */
-int rs_open(struct tty_struct *tty, struct file * filp)
-{
-	struct sun_serial	*info;
-	int 			retval, line;
-
-	line = MINOR(tty->device) - tty->driver.minor_start;
-	/* The zilog lines for the mouse/keyboard must be
-	 * opened using their respective drivers.
-	 */
-	if ((line < 0) || (line >= NUM_CHANNELS))
-		return -ENODEV;
-	if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
-		return -ENODEV;
-	info = zs_soft + line;
-	/* Is the kgdb running over this line? */
-	if (info->kgdb_channel)
-		return -ENODEV;
-	if (serial_paranoia_check(info, tty->device, "rs_open"))
-		return -ENODEV;
-#ifdef SERIAL_DEBUG_OPEN
-	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
-	       info->count);
-#endif
-	if (info->tty != 0 && info->tty != tty) {
-		/* Never happen? */
-		printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
-		return -EBUSY;
-	}
-	info->count++;
-	tty->driver_data = info;
-	info->tty = tty;
-
-	/*
-	 * Start up serial port
-	 */
-	retval = startup(info);
-	if (retval)
-		return retval;
-
-	retval = block_til_ready(tty, filp, info);
-	if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
-		printk("rs_open returning after block_til_ready with %d\n",
-		       retval);
-#endif
-		return retval;
-	}
-
-	if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
-		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-			*tty->termios = info->normal_termios;
-		else 
-			*tty->termios = info->callout_termios;
-		change_speed(info);
-	}
-
-	info->session = current->session;
-	info->pgrp = current->pgrp;
-
-#ifdef SERIAL_DEBUG_OPEN
-	printk("rs_open ttys%d successful...", info->line);
-#endif
-	return 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-static void show_serial_version(void)
-{
-	char *revision = "$Revision: 1.43 $";
-	char *version, *p;
-
-	version = strchr(revision, ' ');
-	p = strchr(++version, ' ');
-	*p = '\0';
-	printk("Sparc Zilog8530 serial driver version %s\n", version);
-	*p = ' ';
-}
-
-/* Probe the PROM for the request zs chip number.
- *
- * Note: The Sun Voyager shows two addresses and two intr for it's
- *       Zilogs, what the second does, I don't know. It does work
- *       with using only the first number of each property.
- */
-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;
-	static int irq = 0;
-
-#if CONFIG_AP1000
-        printk("No zs chip\n");
-        return NULL;
-#endif
-
-	iospace = 0;
-	if(chip < 0 || chip >= NUM_SERIAL)
-		panic("get_zs bogon zs chip number");
-
-	if(sparc_cpu_model == sun4) {
-		/* Grrr, these have to be hardcoded aieee */
-		switch(chip) {
-		case 0:
-			paddr = 0xf1000000;
-			break;
-		case 1:
-			paddr = 0xf0000000;
-			break;
-		};
-		iospace = 0;
-		zs_nodes[chip] = 0;
-		if(!irq)
-			zilog_irq = irq = 12;
-		vaddr[0] = (unsigned long)
-				sparc_alloc_io(paddr, 0, 8,
-					       "Zilog Serial", iospace, 0);
-	} else {
-		/* Can use the prom for other machine types */
-		zsnode = prom_getchild(prom_root_node);
-		if (sparc_cpu_model == sun4d) {
-			int board, node;
-			
-			tmpnode = zsnode;
-			while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
-				board = prom_getintdefault (tmpnode, "board#", -1);
-				if (board == (chip >> 1)) {
-					node = prom_getchild(tmpnode);
-					if (node && (node = prom_searchsiblings(node, "bootbus"))) {
-						zsnode = node;
-						break;
-					}
-				}
-				tmpnode = prom_getsibling(tmpnode);
-			}
-			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)
-				zsnode = prom_getchild(tmpnode);
-		}
-		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)) {
-				/* 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));
-						panic("zilog: address property");
-					}
-				}
-				if(!irq) {
-					irq = zilog_irq = tmp_irq[0].pri;
-				} else {
-					if(tmp_irq[0].pri != irq)
-						panic("zilog: bogon irqs");
-				}
-				break;
-			}
-			zsnode = prom_getsibling(zsnode);
-			seen++;
-		}
-		if(!zsnode)
-			panic("get_zs whee chip not found");
-	}
-	if(!vaddr[0])
-		panic("get_zs whee no serial chip mappable");
-
-	return (struct sun_zslayout *)(unsigned long) vaddr[0];
-}
-
-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;
-}
-
-static inline void
-rs_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) {
-			extern void serial_finish_init (void (*)(const char *, unsigned count));
-
-			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")));
-	}
-}
-
-extern void keyboard_zsinit(void);
-extern void sun_mouse_zsinit(void);
-
-/* This is for the auto baud rate detection in the mouse driver. */
-void zs_change_mouse_baud(int newbaud)
-{
-	int channel = MOUSE_LINE;
-	int brg;
-
-	zs_soft[channel].zs_baud = newbaud;
-	brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
-			 (ZS_CLOCK / zs_soft[channel].clk_divisor));
-	write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
-	write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
-}
-
-__initfunc(unsigned long sun_serial_setup (unsigned long memory_start))
-{
-	char *p;
-	int i;
-	
-	if (sparc_cpu_model == sun4d) {
-		int node = prom_searchsiblings(prom_getchild(prom_root_node), "boards");
-		NUM_SERIAL = 0;
-		if (!node)
-			panic ("Cannot find out count of boards");
-		else
-			node = prom_getchild(node);
-		while (node && (node = prom_searchsiblings(node, "bif"))) {
-			NUM_SERIAL += 2;
-			node = prom_getsibling(node);
-		}
-	}
-	p = (char *)((memory_start + 7) & ~7);
-	zs_chips = (struct sun_zslayout **)(p);
-	i = NUM_SERIAL * sizeof (struct sun_zslayout *);
-	zs_channels = (struct sun_zschannel **)(p + i);
-	i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
-	zs_nodes = (int *)(p + i);
-	i += NUM_SERIAL * sizeof (int);
-	zs_soft = (struct sun_serial *)(p + i);
-	i += NUM_CHANNELS * sizeof (struct sun_serial);
-	zs_ttys = (struct tty_struct *)(p + i);
-	i += NUM_CHANNELS * sizeof (struct tty_struct);
-	serial_table = (struct tty_struct **)(p + i);
-	i += NUM_CHANNELS * sizeof (struct tty_struct *);
-	serial_termios = (struct termios **)(p + i);
-	i += NUM_CHANNELS * sizeof (struct termios *);
-	serial_termios_locked = (struct termios **)(p + i);
-	i += NUM_CHANNELS * sizeof (struct termios *);
-	memset (p, 0, i);
-	return (((unsigned long)p) + i + 7) & ~7;
-}
-
-__initfunc(int rs_init(void))
-{
-	int chip, channel, brg, i;
-	unsigned long flags;
-	struct sun_serial *info;
-	char dummy;
-
-#if CONFIG_AP1000
-        printk("not doing rs_init()\n");
-        return 0;
-#endif
-
-	/* Setup base handler, and timer table. */
-	init_bh(SERIAL_BH, do_serial_bh);
-	timer_table[RS_TIMER].fn = rs_timer;
-	timer_table[RS_TIMER].expires = 0;
-
-	show_serial_version();
-
-	/* Initialize the tty_driver structure */
-	/* SPARC: Not all of this is exactly right for us. */
-	
-	memset(&serial_driver, 0, sizeof(struct tty_driver));
-	serial_driver.magic = TTY_DRIVER_MAGIC;
-	serial_driver.driver_name = "serial";
-	serial_driver.name = "ttyS";
-	serial_driver.major = TTY_MAJOR;
-	serial_driver.minor_start = 64;
-	serial_driver.num = NUM_CHANNELS;
-	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;
-	serial_driver.refcount = &serial_refcount;
-	serial_driver.table = serial_table;
-	serial_driver.termios = serial_termios;
-	serial_driver.termios_locked = serial_termios_locked;
-
-	serial_driver.open = rs_open;
-	serial_driver.close = rs_close;
-	serial_driver.write = rs_write;
-	serial_driver.flush_chars = rs_flush_chars;
-	serial_driver.write_room = rs_write_room;
-	serial_driver.chars_in_buffer = rs_chars_in_buffer;
-	serial_driver.flush_buffer = rs_flush_buffer;
-	serial_driver.ioctl = rs_ioctl;
-	serial_driver.throttle = rs_throttle;
-	serial_driver.unthrottle = rs_unthrottle;
-	serial_driver.set_termios = rs_set_termios;
-	serial_driver.stop = rs_stop;
-	serial_driver.start = rs_start;
-	serial_driver.hangup = rs_hangup;
-
-	/* I'm too lazy, someone write versions of this for us. -DaveM */
-	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.
-	 */
-	callout_driver = serial_driver;
-	callout_driver.name = "cua";
-	callout_driver.major = TTYAUX_MAJOR;
-	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-
-	if (tty_register_driver(&serial_driver))
-		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;
-
-	/* 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
-		 * rs_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;
-	}
-
-	/* Initialize Hardware */
-	for(channel = 0; channel < NUM_CHANNELS; channel++) {
-
-		/* Hardware reset each chip */
-		if (!(channel & 1)) {
-			write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
-			udelay(20);	/* wait for some old sun4's */
-			dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
-		}
-
-		if(channel == KEYBOARD_LINE) {
-			zs_soft[channel].cons_keyb = 1;
-			zs_soft[channel].parity_mask = 0xff;
-			zs_kbdchan = zs_soft[channel].zs_channel;
-
-			write_zsreg(zs_soft[channel].zs_channel, R4,
-				    (PAR_EVEN | X16CLK | SB1));
-			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
-			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
-			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
-			write_zsreg(zs_soft[channel].zs_channel, R11,
-				    (TCBR | RCBR));
-			zs_soft[channel].zs_baud = 1200;
-			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
-					 ZS_CLOCK/zs_soft[channel].clk_divisor);
-			write_zsreg(zs_soft[channel].zs_channel, R12,
-				    (brg & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R13,
-				    ((brg >> 8) & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
-			/* Enable Rx/Tx, IRQs, and inform kbd driver */
-			write_zsreg(zs_soft[channel].zs_channel, R14,
-				    (BRSRC | BRENAB));
-			write_zsreg(zs_soft[channel].zs_channel, R3,
-				    (Rx8 | RxENAB));
-			write_zsreg(zs_soft[channel].zs_channel, R5,
-				    (Tx8 | TxENAB | DTR | RTS));
-
-			write_zsreg(zs_soft[channel].zs_channel, R15,
-				    (DCDIE | CTSIE | TxUIE | BRKIE));
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-
-			write_zsreg(zs_soft[channel].zs_channel, R1,
-				    (EXT_INT_ENAB | INT_ALL_Rx));
-			write_zsreg(zs_soft[channel].zs_channel, R9,
-				    (NV | MIE));
-			ZS_CLEARERR(zs_soft[channel].zs_channel);
-			ZS_CLEARFIFO(zs_soft[channel].zs_channel);
-		} else if(channel == MOUSE_LINE) {
-			zs_soft[channel].cons_mouse = 1;
-			zs_soft[channel].parity_mask = 0xff;
-			zs_mousechan = zs_soft[channel].zs_channel;
-
-			write_zsreg(zs_soft[channel].zs_channel, R4,
-				    (PAR_EVEN | X16CLK | SB1));
-			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
-			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
-			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
-			write_zsreg(zs_soft[channel].zs_channel, R11,
-				    (TCBR | RCBR));
-
-			zs_soft[channel].zs_baud = 4800;
-			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
-					 ZS_CLOCK/zs_soft[channel].clk_divisor);
-			write_zsreg(zs_soft[channel].zs_channel, R12,
-				    (brg & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R13,
-				    ((brg >> 8) & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-
-			/* Enable Rx, IRQs, and inform mouse driver */
-			write_zsreg(zs_soft[channel].zs_channel, R14,
-				    (BRSRC | BRENAB));
-			write_zsreg(zs_soft[channel].zs_channel, R3,
-				    (Rx8 | RxENAB));
-			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-
-			write_zsreg(zs_soft[channel].zs_channel, R15,
-				    (DCDIE | CTSIE | TxUIE | BRKIE));
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-
-			write_zsreg(zs_soft[channel].zs_channel, R1,
-				    (EXT_INT_ENAB | INT_ALL_Rx));
-			write_zsreg(zs_soft[channel].zs_channel, R9,
-				    (NV | MIE));
-
-			sun_mouse_zsinit();
-		} else if (zs_soft[channel].is_cons) {
-			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
-					 ZS_CLOCK/zs_soft[channel].clk_divisor);
-			zscons_regs[12] = brg & 0xff;
-			zscons_regs[13] = (brg >> 8) & 0xff;
-
-			memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
-			load_zsregs(&zs_soft[channel], zscons_regs);
-
-			ZS_CLEARERR(zs_soft[channel].zs_channel);
-			ZS_CLEARFIFO(zs_soft[channel].zs_channel);
-		} else if (zs_soft[channel].kgdb_channel) {
-			/* If this is the kgdb line, enable interrupts because
-			 * we now want to receive the 'control-c' character
-			 * from the client attached to us asynchronously.
-			 */
-			zs_soft[channel].parity_mask = 0xff;
-        		kgdb_chaninit(&zs_soft[channel], 1,
-				      zs_soft[channel].zs_baud);
-		} else {
-			zs_soft[channel].parity_mask = 0xff;
-			write_zsreg(zs_soft[channel].zs_channel, R4,
-				    (PAR_EVEN | X16CLK | SB1));
-			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
-			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
-			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
-			write_zsreg(zs_soft[channel].zs_channel, R11,
-				    (RCBR | TCBR));
-			zs_soft[channel].zs_baud = 9600;
-			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
-					 ZS_CLOCK/zs_soft[channel].clk_divisor);
-			write_zsreg(zs_soft[channel].zs_channel, R12,
-				    (brg & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R13,
-				    ((brg >> 8) & 0xff));
-			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
-			write_zsreg(zs_soft[channel].zs_channel, R14,
-				    (BRSRC | BRENAB));
-			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
-			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
-			write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
-			write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-			write_zsreg(zs_soft[channel].zs_channel, R0,
-				    RES_EXT_INT);
-		}
-	}
-
-	for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
-		info->magic = SERIAL_MAGIC;
-		info->port = (long) info->zs_channel;
-		info->line = i;
-		info->tty = 0;
-		info->irq = zilog_irq;
-		info->custom_divisor = 16;
-		info->close_delay = 50;
-		info->closing_wait = 3000;
-		info->x_char = 0;
-		info->event = 0;
-		info->count = 0;
-		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;
-		info->close_wait = 0;
-		printk("tty%02d at 0x%04x (irq = %d)", info->line, 
-		       info->port, info->irq);
-		printk(" is a Zilog8530\n");
-	}
-
-	if (request_irq(zilog_irq, rs_interrupt,
-			(SA_INTERRUPT | SA_STATIC_ALLOC),
-			"Zilog8530", zs_chain))
-		panic("Unable to attach zs intr\n");
-	restore_flags(flags);
-
-	keyboard_zsinit();
-	return 0;
-}
-
-/*
- * register_serial and unregister_serial allows for serial ports to be
- * configured at run-time, to support PCMCIA modems.
- */
-/* SPARC: Unused at this time, just here to make things link. */
-int register_serial(struct serial_struct *req)
-{
-	return -1;
-}
-
-void unregister_serial(int line)
-{
-	return;
-}
-
-/* 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
-rs_cons_hook(int chip, int out, int line)
-{
-	int channel;
-
-	if(chip)
-		panic("rs_cons_hook called with chip not zero");
-	if(line != 1 && line != 2)
-		panic("rs_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);
-	rs_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
-rs_kgdb_hook(int tty_num)
-{
-	int chip = 0;
-
-	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[tty_num].zs_channel = zs_channels[tty_num];
-	zs_kgdbchan = zs_soft[tty_num].zs_channel;
-	zs_soft[tty_num].change_needed = 0;
-	zs_soft[tty_num].clk_divisor = 16;
-	zs_soft[tty_num].zs_baud = 9600;
-	zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
-	zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
-	/* Turn on transmitter/receiver at 8-bits/char */
-        kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
-        ZS_CLEARERR(zs_kgdbchan);
-        ZS_CLEARFIFO(zs_kgdbchan);
+	prom_printf("No serial devices found, bailing out.\n");
+	prom_halt();
+	return memory_start;
 }
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/sunserial.h linux/drivers/sbus/char/sunserial.h
--- v2.1.52/linux/drivers/sbus/char/sunserial.h	Thu Jul 17 10:06:05 1997
+++ linux/drivers/sbus/char/sunserial.h	Thu Sep  4 12:54:49 1997
@@ -1,437 +1,48 @@
-/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $
- * serial.h: Definitions for the Sparc Zilog serial driver.
+/* $Id: sunserial.h,v 1.13 1997/09/03 11:55:00 ecd Exp $
+ * sunserial.h: SUN serial driver infrastructure.
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  */
-#ifndef _SPARC_SERIAL_H
-#define _SPARC_SERIAL_H
 
-/* Just one channel */
-struct sun_zschannel {
-	volatile unsigned char control;
-	volatile unsigned char pad1;
-	volatile unsigned char data;
-	volatile unsigned char pad2;
-};
+#ifndef _SPARC_SUNSERIAL_H
+#define _SPARC_SUNSERIAL_H 1
 
-/* The address space layout for each zs chip.  Yes they are
- * backwards.
- */
-struct sun_zslayout {
-	struct sun_zschannel channelB;
-	struct sun_zschannel channelA;
-};
+#include <linux/tty.h>
 
-/* We need to keep track of the keyboard state, *ahead* of what
- * the keyboard driver sees, which will be later on after the
- * interrupt via tqueue wait queues and/or base-handler processing.
- */
-struct l1a_kbd_state {
-	unsigned char kbd_id;
-	unsigned char l1_down;
+struct rs_initfunc {
+	int	(*rs_init) (void);
+	struct rs_initfunc *next;
 };
 
-#define NUM_ZSREGS    16
-
-struct serial_struct {
-	int	type;
-	int	line;
-	int	port;
-	int	irq;
-	int	flags;
-	int	xmit_fifo_size;
-	int	custom_divisor;
-	int	baud_base;
-	unsigned short	close_delay;
-	char	reserved_char[2];
-	int	hub6;
-	unsigned short	closing_wait; /* time to wait before closing */
-	unsigned short	closing_wait2; /* no longer used... */
-	int	reserved[4];
+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 *);
 };
 
 /*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output.  65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF	0
-#define ZILOG_CLOSING_WAIT_NONE	65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
-				   on the callout port */
-#define ZILOG_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK	0x0004	/* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK	0x0030
-#define ZILOG_SPD_HI	0x0010	/* Use 76800 instead of 38400 bps */
-#define ZILOG_SPD_CUST	0x0030  /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS	0x0FFF	/* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430	/* Legal flags that non-privileged
-				 * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED	0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING		0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW		0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD		0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
+ * XXX: Work in progress, don't worry this will go away in a few days. (ecd)
  * 
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-struct sun_serial {
-	struct sun_serial *zs_next;       /* For IRQ servicing chain */
-	struct sun_zschannel *zs_channel; /* Channel registers */
-	unsigned char read_reg_zero;
-
-	char soft_carrier;  /* Use soft carrier on this channel */
-	char cons_keyb;     /* Channel runs the keyboard */
-	char cons_mouse;    /* Channel runs the mouse */
-	char break_abort;   /* Is serial console in, so process brk/abrt */
-	char kgdb_channel;  /* Kgdb is running on this channel */
-	char is_cons;       /* Is this our console. */
-
-	char channelA;      /* This is channel A. */
-	char parity_mask;   /* Mask out parity bits in data register. */
-
-	/* We need to know the current clock divisor
-	 * to read the bps rate the chip has currently
-	 * loaded.
-	 */
-	unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
-	int zs_baud;
-
-	/* Current write register values */
-	unsigned char curregs[NUM_ZSREGS];
-
-	char change_needed;
-
-	int			magic;
-	int			baud_base;
-	int			port;
-	int			irq;
-	int			flags; 		/* defined in tty.h */
-	int			type; 		/* UART type */
-	struct tty_struct 	*tty;
-	int			read_status_mask;
-	int			ignore_status_mask;
-	int			timeout;
-	int			xmit_fifo_size;
-	int			custom_divisor;
-	int			x_char;	/* xon/xoff character */
-	int			close_delay;
-	unsigned short		closing_wait;
-	unsigned short		closing_wait2;
-	unsigned long		event;
-	unsigned long		last_active;
-	int			line;
-	int			count;	    /* # of fd on device */
-	int			blocked_open; /* # of blocked opens */
-	long			session; /* Session of opening process */
-	long			pgrp; /* pgrp of opening process */
-	unsigned char 		*xmit_buf;
-	int			xmit_head;
-	int			xmit_tail;
-	int			xmit_cnt;
-	struct tq_struct	tqueue;
-	struct tq_struct	tqueue_hangup;
-	struct termios		normal_termios;
-	struct termios		callout_termios;
-	struct wait_queue	*open_wait;
-	struct wait_queue	*close_wait;
-};
-
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP	0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
+ * 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);
  */
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define	FLAG	0x7e
-
-/* Write Register 0 */
-#define	R0	0		/* Register selects */
-#define	R1	1
-#define	R2	2
-#define	R3	3
-#define	R4	4
-#define	R5	5
-#define	R6	6
-#define	R7	7
-#define	R8	8
-#define	R9	9
-#define	R10	10
-#define	R11	11
-#define	R12	12
-#define	R13	13
-#define	R14	14
-#define	R15	15
-
-#define	NULLCODE	0	/* Null Code */
-#define	POINT_HIGH	0x8	/* Select upper half of registers */
-#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
-#define	SEND_ABORT	0x18	/* HDLC Abort */
-#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
-#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
-#define	ERR_RES		0x30	/* Error Reset */
-#define	RES_H_IUS	0x38	/* Reset highest IUS */
-
-#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
-#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
-#define	RES_EOM_L	0xC0	/* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
-#define	TxINT_ENAB	0x2	/* Tx Int Enable */
-#define	PAR_SPEC	0x4	/* Parity is special condition */
-
-#define	RxINT_DISAB	0	/* Rx Int Disable */
-#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
-#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
-#define	INT_ERR_Rx	0x18	/* Int on error only */
-#define RxINT_MASK	0x18
-
-#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
-#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
-#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define	RxENAB  	0x1	/* Rx Enable */
-#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
-#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
-#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
-#define	ENT_HM		0x10	/* Enter Hunt Mode */
-#define	AUTO_ENAB	0x20	/* Auto Enables */
-#define	Rx5		0x0	/* Rx 5 Bits/Character */
-#define	Rx7		0x40	/* Rx 7 Bits/Character */
-#define	Rx6		0x80	/* Rx 6 Bits/Character */
-#define	Rx8		0xc0	/* Rx 8 Bits/Character */
-#define RxN_MASK	0xc0
-
-/* Write Register 4 */
-
-#define	PAR_ENAB	0x1	/* Parity Enable */
-#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
-
-#define	SYNC_ENAB	0	/* Sync Modes Enable */
-#define	SB1		0x4	/* 1 stop bit/char */
-#define	SB15		0x8	/* 1.5 stop bits/char */
-#define	SB2		0xc	/* 2 stop bits/char */
-
-#define	MONSYNC		0	/* 8 Bit Sync character */
-#define	BISYNC		0x10	/* 16 bit sync character */
-#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
-#define	EXTSYNC		0x30	/* External Sync Mode */
-
-#define	X1CLK		0x0	/* x1 clock mode */
-#define	X16CLK		0x40	/* x16 clock mode */
-#define	X32CLK		0x80	/* x32 clock mode */
-#define	X64CLK		0xC0	/* x64 clock mode */
-
-/* Write Register 5 */
-
-#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
-#define	RTS		0x2	/* RTS */
-#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
-#define	TxENAB		0x8	/* Tx Enable */
-#define	SND_BRK		0x10	/* Send Break */
-#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
-#define	Tx7		0x20	/* Tx 7 bits/character */
-#define	Tx6		0x40	/* Tx 6 bits/character */
-#define	Tx8		0x60	/* Tx 8 bits/character */
-#define TxN_MASK	0x60
-#define	DTR		0x80	/* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define	VIS	1	/* Vector Includes Status */
-#define	NV	2	/* No Vector */
-#define	DLC	4	/* Disable Lower Chain */
-#define	MIE	8	/* Master Interrupt Enable */
-#define	STATHI	0x10	/* Status high */
-#define	NORESET	0	/* No reset on write to R9 */
-#define	CHRB	0x40	/* Reset channel B */
-#define	CHRA	0x80	/* Reset channel A */
-#define	FHWRES	0xc0	/* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define	BIT6	1	/* 6 bit/8bit sync */
-#define	LOOPMODE 2	/* SDLC Loop mode */
-#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
-#define	MARKIDLE 8	/* Mark/flag on idle */
-#define	GAOP	0x10	/* Go active on poll */
-#define	NRZ	0	/* NRZ mode */
-#define	NRZI	0x20	/* NRZI mode */
-#define	FM1	0x40	/* FM1 (transition = 1) */
-#define	FM0	0x60	/* FM0 (transition = 0) */
-#define	CRCPS	0x80	/* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define	TRxCXT	0	/* TRxC = Xtal output */
-#define	TRxCTC	1	/* TRxC = Transmit clock */
-#define	TRxCBR	2	/* TRxC = BR Generator Output */
-#define	TRxCDP	3	/* TRxC = DPLL output */
-#define	TRxCOI	4	/* TRxC O/I */
-#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
-#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
-#define	TCBR	0x10	/* Transmit clock = BR Generator output */
-#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
-#define	RCRTxCP	0	/* Receive clock = RTxC pin */
-#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
-#define	RCBR	0x40	/* Receive clock = BR Generator output */
-#define	RCDPLL	0x60	/* Receive clock = DPLL output */
-#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define	BRENAB 	1	/* Baud rate generator enable */
-#define	BRSRC	2	/* Baud rate generator source */
-#define	DTRREQ	4	/* DTR/Request function */
-#define	AUTOECHO 8	/* Auto Echo */
-#define	LOOPBAK	0x10	/* Local loopback */
-#define	SEARCH	0x20	/* Enter search mode */
-#define	RMC	0x40	/* Reset missing clock */
-#define	DISDPLL	0x60	/* Disable DPLL */
-#define	SSBR	0x80	/* Set DPLL source = BR generator */
-#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
-#define	SFMM	0xc0	/* Set FM mode */
-#define	SNRZI	0xe0	/* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define	ZCIE	2	/* Zero count IE */
-#define	DCDIE	8	/* DCD IE */
-#define	SYNCIE	0x10	/* Sync/hunt IE */
-#define	CTSIE	0x20	/* CTS IE */
-#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
-#define	BRKIE	0x80	/* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define	Rx_CH_AV	0x1	/* Rx Character Available */
-#define	ZCOUNT		0x2	/* Zero count */
-#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
-#define	DCD		0x8	/* DCD */
-#define	SYNC		0x10	/* Sync/hunt */
-#define	CTS		0x20	/* CTS */
-#define	TxEOM		0x40	/* Tx underrun */
-#define	BRK_ABRT	0x80	/* Break/Abort */
-
-/* Read Register 1 */
-#define	ALL_SNT		0x1	/* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define	RES3		0x8	/* 0/3 */
-#define	RES4		0x4	/* 0/4 */
-#define	RES5		0xc	/* 0/5 */
-#define	RES6		0x2	/* 0/6 */
-#define	RES7		0xa	/* 0/7 */
-#define	RES8		0x6	/* 0/8 */
-#define	RES18		0xe	/* 1/8 */
-#define	RES28		0x0	/* 2/8 */
-/* Special Rx Condition Interrupts */
-#define	PAR_ERR		0x10	/* Parity error */
-#define	Rx_OVR		0x20	/* Rx Overrun Error */
-#define	CRC_ERR		0x40	/* CRC/Framing Error */
-#define	END_FR		0x80	/* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-#define CHB_Tx_EMPTY	0x00
-#define CHB_EXT_STAT	0x02
-#define CHB_Rx_AVAIL	0x04
-#define CHB_SPECIAL	0x06
-#define CHA_Tx_EMPTY	0x08
-#define CHA_EXT_STAT	0x0a
-#define CHA_Rx_AVAIL	0x0c
-#define CHA_SPECIAL	0x0e
-#define STATUS_MASK	0x0e
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
-#define	CHBTxIP	0x2		/* Channel B Tx IP */
-#define	CHBRxIP	0x4		/* Channel B Rx IP */
-#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
-#define	CHATxIP	0x10		/* Channel A Tx IP */
-#define	CHARxIP	0x20		/* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10  (misc status bits) */
-#define	ONLOOP	2		/* On loop */
-#define	LOOPSEND 0x10		/* Loop sending */
-#define	CLK2MIS	0x40		/* Two clocks missing */
-#define	CLK1MIS	0x80		/* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel)    do { channel->control = ERR_RES; \
-				     udelay(5); } while(0)
-
-#define ZS_CLEARSTAT(channel)   do { channel->control = RES_EXT_INT; \
-				     udelay(5); } while(0)
 
-#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
-				     garbage = channel->data; \
-				     udelay(2); \
-				     garbage = channel->data; \
-				     udelay(2); \
-				     garbage = channel->data; \
-				     udelay(2); } while(0)
+extern struct sunserial_operations rs_ops;
+extern void sunserial_setinitfunc(unsigned long *, int (*) (void));
 
-#endif /* !(_SPARC_SERIAL_H) */
+#endif /* !(_SPARC_SUNSERIAL_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/tcx.c linux/drivers/sbus/char/tcx.c
--- v2.1.52/linux/drivers/sbus/char/tcx.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/sbus/char/tcx.c	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: tcx.c,v 1.17 1997/07/17 02:21:50 davem Exp $
+/* $Id: tcx.c,v 1.20 1997/08/22 15:55:14 jj Exp $
  * tcx.c: SUNW,tcx 24/8bit frame buffer driver
  *
  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -259,8 +259,8 @@
 {
 	struct tcx_info *tcx = &(fb->info.tcx);
 
-	if (fb->setcursor)
-		sun_hw_hide_cursor ();
+	if (fb->setcursor && fb == &fbinfo[0])
+		sbus_hw_hide_cursor ();
 	/* Reset control plane to 8bit mode if necessary */
 	if (fb->open && fb->mmaped)
 		tcx_set_control_plane (fb);
@@ -292,7 +292,7 @@
 	fb->emulations [1] = FBTYPE_SUN3COLOR;
 	fb->emulations [2] = FBTYPE_MEMCOLOR;
 	fb->switch_from_graph = tcx_switch_from_graph;
-	fb->postsetup = sun_cg_postsetup;
+	fb->postsetup = cg_postsetup;
 	
 	tcxinfo = (struct tcx_info *) &fb->info.tcx;
 	
@@ -309,7 +309,7 @@
 	tcxinfo->tec = sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_TEC_OFFSET], 0,
 		 sizeof (struct tcx_tec), "tcx_tec", fb->space, 0);
 	if (!fb->base){
-		fb->base = (unsigned long)
+		fb->base = (uint) (unsigned long)
 			sparc_alloc_io((u32)tcxinfo->tcx_offsets [TCX_RAM8BIT_OFFSET],
 				       0, fb->type.fb_size, "tcx_ram", fb->space, 0);
 	}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/weitek.c linux/drivers/sbus/char/weitek.c
--- v2.1.52/linux/drivers/sbus/char/weitek.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/sbus/char/weitek.c	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: weitek.c,v 1.14 1997/07/17 02:21:53 davem Exp $
+/* $Id: weitek.c,v 1.15 1997/07/22 06:14:11 davem Exp $
  * weitek.c: Tadpole P9100/P9000 console driver
  *
  * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- v2.1.52/linux/drivers/sbus/char/zs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/zs.c	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,2786 @@
+/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $
+ * zs.c: Zilog serial port driver for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/keyboard.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/kdebug.h>
+
+#include <asm/sbus.h>
+#ifdef __sparc_v9__
+#include <asm/fhc.h>
+#endif
+
+#include "sunserial.h"
+#include "zs.h"
+#include "sunkbd.h"
+#include "sunmouse.h"
+
+static int num_serial = 2; /* sun4/sun4c/sun4m - Two chips on board. */
+#define NUM_SERIAL num_serial
+#define NUM_CHANNELS (NUM_SERIAL * 2)
+
+#define KEYBOARD_LINE 0x2
+#define MOUSE_LINE    0x3
+
+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;
+int *zs_nodes;
+
+struct sun_serial *zs_soft;
+struct sun_serial *zs_chain;  /* IRQ servicing chain */
+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;
+
+static unsigned char kgdb_regs[16] = {
+	0, 0, 0,                     /* write 0, 1, 2 */
+	(Rx8 | RxENAB),              /* write 3 */
+	(X16CLK | SB1 | PAR_EVEN),   /* write 4 */
+	(DTR | Tx8 | TxENAB),        /* write 5 */
+	0, 0, 0,                     /* write 6, 7, 8 */
+	(NV),                        /* write 9 */
+	(NRZ),                       /* write 10 */
+	(TCBR | RCBR),               /* write 11 */
+	0, 0,                        /* BRG time constant, write 12 + 13 */
+	(BRSRC | BRENAB),            /* write 14 */
+	(DCDIE)                      /* write 15 */
+};
+
+static unsigned char zscons_regs[16] = {
+	0,                           /* write 0 */
+	(EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
+	0,                           /* write 2 */
+	(Rx8 | RxENAB),              /* write 3 */
+	(X16CLK),                    /* write 4 */
+	(DTR | Tx8 | TxENAB),        /* write 5 */
+	0, 0, 0,                     /* write 6, 7, 8 */
+	(NV | MIE),                  /* write 9 */
+	(NRZ),                       /* write 10 */
+	(TCBR | RCBR),               /* write 11 */
+	0, 0,                        /* BRG time constant, write 12 + 13 */
+	(BRSRC | BRENAB),            /* write 14 */
+	(DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
+};
+
+#define ZS_CLOCK         4915200   /* Zilog input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+  
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+int zs_init(void);
+void zs_cons_hook(int, int, int);
+void zs_kgdb_hook(int);
+
+static void change_speed(struct sun_serial *info);
+
+static struct tty_struct **serial_table;
+static struct termios **serial_termios;
+static struct termios **serial_termios_locked;
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct sun_serial *info,
+					dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+	static const char *badinfo =
+		"Warning: null sun_serial for (%d, %d) in %s\n";
+
+	if (!info) {
+		printk(badinfo, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 76800, 0 };
+
+/* 
+ * Reading and writing Zilog8530 registers.  The delays are to make this
+ * driver work on the Sun4 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ */
+static inline unsigned char read_zsreg(struct sun_zschannel *channel,
+				       unsigned char reg)
+{
+	unsigned char retval;
+
+	channel->control = reg;
+	udelay(5);
+	retval = channel->control;
+	udelay(5);
+	return retval;
+}
+
+static inline void write_zsreg(struct sun_zschannel *channel,
+			       unsigned char reg, unsigned char value)
+{
+	channel->control = reg;
+	udelay(5);
+	channel->control = value;
+	udelay(5);
+}
+
+static inline void load_zsregs(struct sun_serial *info, unsigned char *regs)
+{
+	unsigned long flags;
+	struct sun_zschannel *channel = info->zs_channel;
+	unsigned char stat;
+	int i;
+
+	for (i = 0; i < 1000; i++) {
+		stat = read_zsreg(channel, R1);
+		if (stat & ALL_SNT)
+			break;
+		udelay(100);
+	}
+	write_zsreg(channel, R3, 0);
+	ZS_CLEARSTAT(channel);
+	ZS_CLEARERR(channel);
+	ZS_CLEARFIFO(channel);
+
+	/* Load 'em up */
+	save_flags(flags); cli();
+	if (info->channelA)
+		write_zsreg(channel, R9, CHRA);
+	else
+		write_zsreg(channel, R9, CHRB);
+	udelay(20);	/* wait for some old sun4's */
+	write_zsreg(channel, R4, regs[R4]);
+	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
+	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+	write_zsreg(channel, R9, regs[R9] & ~MIE);
+	write_zsreg(channel, R10, regs[R10]);
+	write_zsreg(channel, R11, regs[R11]);
+	write_zsreg(channel, R12, regs[R12]);
+	write_zsreg(channel, R13, regs[R13]);
+	write_zsreg(channel, R14, regs[R14] & ~BRENAB);
+	write_zsreg(channel, R14, regs[R14]);
+	write_zsreg(channel, R14, (regs[R14] & ~SNRZI) | BRENAB);
+	write_zsreg(channel, R3, regs[R3]);
+	write_zsreg(channel, R5, regs[R5]);
+	write_zsreg(channel, R15, regs[R15]);
+	write_zsreg(channel, R0, RES_EXT_INT);
+	write_zsreg(channel, R0, ERR_RES);
+	write_zsreg(channel, R1, regs[R1]);
+	write_zsreg(channel, R9, regs[R9]);
+	restore_flags(flags);
+}
+
+static inline void zs_put_char(struct sun_zschannel *channel, char ch)
+{
+	int loops = 0;
+
+	while((channel->control & Tx_BUF_EMP) == 0 && loops < 10000) {
+		loops++;
+		udelay(5);
+	}
+	channel->data = ch;
+	udelay(5);
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct sun_serial *ss, int set)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	if(set) {
+		ss->curregs[5] |= (RTS | DTR);
+		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+	} else {
+		ss->curregs[5] &= ~(RTS | DTR);
+		write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+	}
+	restore_flags(flags);
+	return;
+}
+
+static inline void kgdb_chaninit(struct sun_serial *ss, int intson, int bps)
+{
+	int brg;
+
+	if(intson) {
+		kgdb_regs[R1] = INT_ALL_Rx;
+		kgdb_regs[R9] |= MIE;
+	} else {
+		kgdb_regs[R1] = 0;
+		kgdb_regs[R9] &= ~MIE;
+	}
+	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+	kgdb_regs[R12] = (brg & 255);
+	kgdb_regs[R13] = ((brg >> 8) & 255);
+	load_zsregs(ss, kgdb_regs);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_stop() and zs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void zs_stop(struct tty_struct *tty)
+{
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "zs_stop"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->curregs[5] & TxENAB) {
+		info->curregs[5] &= ~TxENAB;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	}
+	restore_flags(flags);
+}
+
+static void zs_start(struct tty_struct *tty)
+{
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "zs_start"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+		info->curregs[5] |= TxENAB;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	}
+	restore_flags(flags);
+}
+
+/* Drop into either the boot monitor or kadb upon receiving a break
+ * from keyboard/console input.
+ */
+void batten_down_hatches(void)
+{
+	/* 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
+	 * first before giving up control.
+	 */
+	printk("\n");
+	flush_user_windows();
+#ifndef __sparc_v9__
+	if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
+	   (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
+		sp_enter_debugger();
+	else
+#endif
+		prom_cmdline();
+
+	/* XXX We want to notify the keyboard driver that all
+	 * XXX keys are in the up state or else weird things
+	 * XXX happen...
+	 */
+
+	return;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * zs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: zs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * zs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void zs_sched_event(struct sun_serial *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+#ifndef __sparc_v9__
+extern void breakpoint(void);  /* For the KGDB frame character */
+#endif
+
+static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch, stat;
+
+	do {
+		ch = (info->zs_channel->data) & info->parity_mask;
+		udelay(5);
+
+		/* If this is the console keyboard, we need to handle
+		 * L1-A's here.
+		 */
+		if(info->cons_keyb) {
+			if(ch == SUNKBD_RESET) {
+				l1a_state.kbd_id = 1;
+				l1a_state.l1_down = 0;
+			} else if(l1a_state.kbd_id) {
+				l1a_state.kbd_id = 0;
+			} else if(ch == SUNKBD_L1) {
+				l1a_state.l1_down = 1;
+			} else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+				l1a_state.l1_down = 0;
+			} else if(ch == SUNKBD_A && l1a_state.l1_down) {
+				/* whee... */
+				batten_down_hatches();
+				/* Continue execution... */
+				l1a_state.l1_down = 0;
+				l1a_state.kbd_id = 0;
+				return;
+			}
+			sunkbd_inchar(ch, regs);
+			return;
+		}
+		if(info->cons_mouse) {
+			sun_mouse_inbyte(ch);
+			return;
+		}
+		if(info->is_cons) {
+			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
+			}
+			/* It is a 'keyboard interrupt' ;-) */
+			wake_up(&keypress_wait);
+		}
+#ifndef __sparc_v9__
+		/* Look for kgdb 'stop' character, consult the gdb
+		 * documentation for remote target debugging and
+		 * arch/sparc/kernel/sparc-stub.c to see how all this works.
+		 */
+		if((info->kgdb_channel) && (ch =='\003')) {
+			breakpoint();
+			return;
+		}
+#endif
+		if(!tty)
+			return;
+
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = 0;
+		*tty->flip.char_buf_ptr++ = ch;
+
+		/* Check if we have another character... */
+		stat = info->zs_channel->control;
+		udelay(5);
+		if (!(stat & Rx_CH_AV))
+			break;
+
+		/* ... and see if it is clean. */
+		stat = read_zsreg(info->zs_channel, R1);
+	} while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static _INLINE_ void transmit_chars(struct sun_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+
+	if (info->x_char) {
+		/* Send next char */
+		zs_put_char(info->zs_channel, info->x_char);
+		info->x_char = 0;
+		return;
+	}
+
+	if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+		/* That's peculiar... */
+		info->zs_channel->control = RES_Tx_P;
+		udelay(5);
+		return;
+	}
+
+	/* Send char */
+	zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt--;
+
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+	if(info->xmit_cnt <= 0) {
+		info->zs_channel->control = RES_Tx_P;
+		udelay(5);
+	}
+}
+
+static _INLINE_ void status_handle(struct sun_serial *info)
+{
+	unsigned char status;
+
+	/* Get status from Read Register 0 */
+	status = info->zs_channel->control;
+	udelay(5);
+	/* Clear status condition... */
+	info->zs_channel->control = RES_EXT_INT;
+	udelay(5);
+
+#if 0
+	if(status & DCD) {
+		if((info->tty->termios->c_cflag & CRTSCTS) &&
+		   ((info->curregs[3] & AUTO_ENAB)==0)) {
+			info->curregs[3] |= AUTO_ENAB;
+			write_zsreg(info->zs_channel, 3, info->curregs[3]);
+		}
+	} else {
+		if((info->curregs[3] & AUTO_ENAB)) {
+			info->curregs[3] &= ~AUTO_ENAB;
+			write_zsreg(info->zs_channel, 3, info->curregs[3]);
+		}
+	}
+#endif
+	/* Whee, if this is console input and this is a
+	 * 'break asserted' status change interrupt, call
+	 * the boot prom.
+	 */
+	if((status & BRK_ABRT) && info->break_abort)
+		batten_down_hatches();
+
+	/* XXX Whee, put in a buffer somewhere, the status information
+	 * XXX whee whee whee... Where does the information go...
+	 */
+	return;
+}
+
+static _INLINE_ void special_receive(struct sun_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch, stat;
+
+	stat = read_zsreg(info->zs_channel, R1);
+	if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+		ch = info->zs_channel->data;
+		udelay(5);
+	}
+
+	if (!tty)
+		goto clear;
+
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+		goto done;
+
+	tty->flip.count++;
+	if(stat & PAR_ERR)
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+	else if(stat & Rx_OVR)
+		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+	else if(stat & CRC_ERR)
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+
+done:
+	queue_task(&tty->flip.tqueue, &tq_timer);
+clear:
+	info->zs_channel->control = ERR_RES;
+	udelay(5);
+}
+
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct sun_serial *info;
+	unsigned char zs_intreg;
+	int i;
+
+	info = (struct sun_serial *)dev_id;
+	for (i = 0; i < NUM_SERIAL; i++) {
+		zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
+		zs_intreg &= STATUS_MASK;
+
+		/* NOTE: The read register 2, which holds the irq status,
+		 *       does so for both channels on each chip.  Although
+		 *       the status value itself must be read from the B
+		 *       channel and is only valid when read from channel B.
+		 *       When read from channel A, read register 2 contains
+		 *       the value written to write register 2.
+		 */
+
+		/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
+		if (zs_intreg == CHA_Rx_AVAIL) {
+			receive_chars(info, regs);
+			return;
+		}
+		if(zs_intreg == CHA_Tx_EMPTY) {
+			transmit_chars(info);
+			return;
+		}
+		if (zs_intreg == CHA_EXT_STAT) {
+			status_handle(info);
+			return;
+		}
+		if (zs_intreg == CHA_SPECIAL) {
+			special_receive(info);
+			return;
+		}
+
+		/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
+		if(zs_intreg == CHB_Rx_AVAIL) {
+			receive_chars(info->zs_next, regs);
+			return;
+		}
+		if(zs_intreg == CHB_Tx_EMPTY) {
+			transmit_chars(info->zs_next);
+			return;
+		}
+		if (zs_intreg == CHB_EXT_STAT) {
+			status_handle(info->zs_next);
+			return;
+		}
+
+		/* NOTE: The default value for the IRQ status in read register
+		 *       2 in channel B is CHB_SPECIAL, so we need to look at
+		 *       read register 3 in channel A to check if this is a
+		 *       real interrupt, or just the default value.
+		 *       Yes... broken hardware...
+		 */
+
+		zs_intreg = read_zsreg(info->zs_channel, 3);
+		if (zs_intreg & CHBRxIP) {
+			special_receive(info->zs_next);
+			return;
+		}
+		info = info->zs_next->zs_next;
+	}
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * zs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using zs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct sun_serial	*info = (struct sun_serial *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * 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() -> zs_hangup()
+ * 
+ */
+static void do_serial_hangup(void *private_)
+{
+	struct sun_serial	*info = (struct sun_serial *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("do_serial_hangup<%p: tty-%d\n",
+		__builtin_return_address(0), info->line);
+#endif
+
+	tty_hangup(tty);
+}
+
+
+/*
+ * This subroutine is called when the RS_TIMER goes off.  It is used
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0).  This doesn't work at all for 16450's, as a sun has a Z8530.
+ */
+ 
+static void zs_timer(void)
+{
+	printk("zs_timer called\n");
+	prom_halt();
+	return;
+}
+
+static int startup(struct sun_serial * info)
+{
+	unsigned long flags;
+
+	if (info->flags & ZILOG_INITIALIZED)
+		return 0;
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	ZS_CLEARFIFO(info->zs_channel);
+	info->xmit_fifo_size = 1;
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	info->zs_channel->control = ERR_RES;
+	udelay(5);
+	info->zs_channel->control = RES_H_IUS;
+	udelay(5);
+
+	/*
+	 * Now, initialize the Zilog
+	 */
+	zs_rtsdtr(info, 1);
+
+	/*
+	 * Finally, enable sequencing and interrupts
+	 */
+	info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) |
+				(EXT_INT_ENAB | INT_ALL_Rx);
+	info->curregs[3] |= (RxENAB | Rx8);
+	/* We enable Tx interrupts as needed. */
+	info->curregs[5] |= (TxENAB | Tx8);
+	info->curregs[9] |= (NV | MIE);
+	write_zsreg(info->zs_channel, 3, info->curregs[3]);
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	write_zsreg(info->zs_channel, 9, info->curregs[9]);
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	info->zs_channel->control = ERR_RES;
+	udelay(5);
+	info->zs_channel->control = RES_H_IUS;
+	udelay(5);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * Set up serial timers...
+	 */
+#if 0  /* Works well and stops the machine. */
+	timer_table[RS_TIMER].expires = jiffies + 2;
+	timer_active |= 1 << RS_TIMER;
+#endif
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+
+	info->flags |= ZILOG_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct sun_serial * info)
+{
+	unsigned long	flags;
+
+	if (!(info->flags & ZILOG_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       info->irq);
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+	
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	
+	info->flags &= ~ZILOG_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct sun_serial *info)
+{
+	unsigned short port;
+	unsigned cflag;
+	int	quot = 0;
+	int	i;
+	int	brg;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+	if (!(port = info->port))
+		return;
+	i = cflag & CBAUD;
+	if (cflag & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i != 5)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i = 16;
+	}
+	if (i == 15) {
+		if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_HI)
+			i += 1;
+		if ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)
+			quot = info->custom_divisor;
+	}
+	if (quot) {
+		info->zs_baud = info->baud_base / quot;
+		info->clk_divisor = 16;
+
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = TCBR | RCBR;
+		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+		info->curregs[12] = (brg & 255);
+		info->curregs[13] = ((brg >> 8) & 255);
+		info->curregs[14] = BRSRC | BRENAB;
+		zs_rtsdtr(info, 1);
+	} else if (baud_table[i]) {
+		info->zs_baud = baud_table[i];
+		info->clk_divisor = 16;
+
+		info->curregs[4] = X16CLK;
+		info->curregs[11] = TCBR | RCBR;
+		brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+		info->curregs[12] = (brg & 255);
+		info->curregs[13] = ((brg >> 8) & 255);
+		info->curregs[14] = BRSRC | BRENAB;
+		zs_rtsdtr(info, 1);
+	} else {
+		zs_rtsdtr(info, 0);
+		return;
+	}
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:
+		info->curregs[3] &= ~(RxN_MASK);
+		info->curregs[3] |= Rx5;
+		info->curregs[5] &= ~(TxN_MASK);
+		info->curregs[5] |= Tx5;
+		info->parity_mask = 0x1f;
+		break;
+	case CS6:
+		info->curregs[3] &= ~(RxN_MASK);
+		info->curregs[3] |= Rx6;
+		info->curregs[5] &= ~(TxN_MASK);
+		info->curregs[5] |= Tx6;
+		info->parity_mask = 0x3f;
+		break;
+	case CS7:
+		info->curregs[3] &= ~(RxN_MASK);
+		info->curregs[3] |= Rx7;
+		info->curregs[5] &= ~(TxN_MASK);
+		info->curregs[5] |= Tx7;
+		info->parity_mask = 0x7f;
+		break;
+	case CS8:
+	default: /* defaults to 8 bits */
+		info->curregs[3] &= ~(RxN_MASK);
+		info->curregs[3] |= Rx8;
+		info->curregs[5] &= ~(TxN_MASK);
+		info->curregs[5] |= Tx8;
+		info->parity_mask = 0xff;
+		break;
+	}
+	info->curregs[4] &= ~(0x0c);
+	if (cflag & CSTOPB) {
+		info->curregs[4] |= SB2;
+	} else {
+		info->curregs[4] |= SB1;
+	}
+	if (cflag & PARENB) {
+		info->curregs[4] |= PAR_ENAB;
+	} else {
+		info->curregs[4] &= ~PAR_ENAB;
+	}
+	if (!(cflag & PARODD)) {
+		info->curregs[4] |= PAR_EVEN;
+	} else {
+		info->curregs[4] &= ~PAR_EVEN;
+	}
+
+	/* Load up the new values */
+	load_zsregs(info, info->curregs);
+
+	return;
+}
+
+/* This is for mouse/keyboard output.
+ * XXX mouse output??? can we send it commands??? XXX
+ */
+static void kbd_put_char(unsigned char ch)
+{
+	struct sun_zschannel *chan = zs_kbdchan;
+	unsigned long flags;
+
+	if(!chan)
+		return;
+
+	save_flags(flags); cli();
+	zs_put_char(chan, ch);
+	restore_flags(flags);
+}
+
+void mouse_put_char(char ch)
+{
+	struct sun_zschannel *chan = zs_mousechan;
+	unsigned long flags;
+
+	if(!chan)
+		return;
+
+	save_flags(flags); cli();
+	zs_put_char(chan, ch);
+	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.
+ */
+void putDebugChar(char kgdb_char)
+{
+	struct sun_zschannel *chan = zs_kgdbchan;
+
+	while((chan->control & Tx_BUF_EMP)==0)
+		udelay(5);
+	chan->data = kgdb_char;
+}
+
+char getDebugChar(void)
+{
+	struct sun_zschannel *chan = zs_kgdbchan;
+
+	while((chan->control & Rx_CH_AV)==0)
+		barrier();
+	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;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "zs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	/* Enable transmitter */
+	save_flags(flags); cli();
+	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+	write_zsreg(info->zs_channel, 1, info->curregs[1]);
+	info->curregs[5] |= TxENAB;
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+	/*
+	 * Send a first (bootstrapping) character. A best solution is
+	 * to call transmit_chars() here which handles output in a
+	 * generic way. Current transmit_chars() not only transmits,
+	 * but resets interrupts also what we do not desire here.
+	 * XXX Discuss with David.
+	 */
+	zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt--;
+
+	restore_flags(flags);
+}
+
+static int zs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "zs_write"))
+		return 0;
+
+	if (!info || !info->xmit_buf)
+		return 0;
+
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			down(&tmp_buf_sem);
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+			up(&tmp_buf_sem);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+		/* Enable transmitter */
+		info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+		write_zsreg(info->zs_channel, 1, info->curregs[1]);
+		info->curregs[5] |= TxENAB;
+		write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	}
+#if 1
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+		zs_put_char(info->zs_channel,
+			    info->xmit_buf[info->xmit_tail++]);
+		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+	}
+#endif
+	restore_flags(flags);
+	return total;
+}
+
+static int zs_write_room(struct tty_struct *tty)
+{
+	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;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+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;
+}
+
+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();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void zs_throttle(struct tty_struct * tty)
+{
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "zs_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+
+	/* Turn off RTS line */
+	cli();
+	info->curregs[5] &= ~RTS;
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	sti();
+}
+
+static void zs_unthrottle(struct tty_struct * tty)
+{
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "zs_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+
+	/* Assert RTS line */
+	cli();
+	info->curregs[5] |= RTS;
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct sun_serial * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	copy_to_user_ret(retinfo,&tmp,sizeof(*retinfo), -EFAULT);
+	return 0;
+}
+
+static int set_serial_info(struct sun_serial * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct sun_serial old_info;
+	int 			retval = 0;
+
+	if (!new_info || copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+		return -EFAULT;
+	old_info = *info;
+
+	if (!suser()) {
+		if ((new_serial.baud_base != info->baud_base) ||
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ZILOG_USR_MASK) !=
+		     (info->flags & ~ZILOG_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+			       (new_serial.flags & ZILOG_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ZILOG_FLAGS) |
+			(new_serial.flags & ZILOG_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+	retval = startup(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct sun_serial * info, unsigned int *value)
+{
+	unsigned char status;
+
+	cli();
+	status = info->zs_channel->control;
+	sti();
+	put_user_ret(status,value, -EFAULT);
+	return 0;
+}
+
+static int get_modem_info(struct sun_serial * info, unsigned int *value)
+{
+	unsigned char status;
+	unsigned int result;
+
+	cli();
+	status = info->zs_channel->control;
+	sti();
+	result =  ((info->curregs[5] & RTS) ? TIOCM_RTS : 0)
+		| ((info->curregs[5] & DTR) ? TIOCM_DTR : 0)
+		| ((status  & DCD) ? TIOCM_CAR : 0)
+		| ((status  & SYNC) ? TIOCM_DSR : 0)
+		| ((status  & CTS) ? TIOCM_CTS : 0);
+	put_user_ret(result, value, -EFAULT);
+	return 0;
+}
+
+static int set_modem_info(struct sun_serial * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	unsigned int arg;
+
+	get_user_ret(arg, value, -EFAULT);
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS)
+			info->curregs[5] |= RTS;
+		if (arg & TIOCM_DTR)
+			info->curregs[5] |= DTR;
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			info->curregs[5] &= ~RTS;
+		if (arg & TIOCM_DTR)
+			info->curregs[5] &= ~DTR;
+		break;
+	case TIOCMSET:
+		info->curregs[5] = ((info->curregs[5] & ~(RTS | DTR))
+			     | ((arg & TIOCM_RTS) ? RTS : 0)
+			     | ((arg & TIOCM_DTR) ? DTR : 0));
+		break;
+	default:
+		return -EINVAL;
+	}
+	cli();
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	sti();
+	return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(	struct sun_serial * info, int duration)
+{
+	if (!info->port)
+		return;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+	cli();
+	write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK));
+	schedule();
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+	sti();
+}
+
+static int zs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+	int retval;
+
+	if (serial_paranoia_check(info, tty->device, "zs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	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:
+			put_user_ret(C_CLOCAL(tty) ? 1 : 0,
+				     (unsigned long *) arg, -EFAULT);
+			return 0;
+		case TIOCSSOFTCAR:
+			get_user_ret(arg, (unsigned long *) arg, -EFAULT);
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERGSTRUCT:
+			copy_to_user_ret((struct sun_serial *) arg,
+				    info, sizeof(struct sun_serial), -EFAULT);
+			return 0;
+			
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void zs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+
+	change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		zs_start(tty);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * ZILOG structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void zs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info || serial_paranoia_check(info, tty->device, "zs_close"))
+		return;
+	
+	save_flags(flags); cli();
+	
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("zs_close tty-%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("zs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("zs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ZILOG_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ZILOG_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ZILOG_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	/** if (!info->iscons) ... **/
+	info->curregs[3] &= ~RxENAB;
+	write_zsreg(info->zs_channel, 3, info->curregs[3]);
+	info->curregs[1] &= ~(RxINT_MASK);
+	write_zsreg(info->zs_channel, 1, info->curregs[1]);
+	ZS_CLEARFIFO(info->zs_channel);
+
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	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;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+			 ZILOG_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("zs_close tty-%d exiting, count = %d\n", info->line, info->count);
+#endif
+	restore_flags(flags);
+}
+
+/*
+ * zs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+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;
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("zs_hangup<%p: tty-%d, count = %d bye\n",
+		__builtin_return_address(0), info->line, info->count);
+#endif
+
+	zs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * zs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct sun_serial *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0;
+	unsigned char	r0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	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;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ZILOG_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+		    (info->flags & ZILOG_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+		    (info->flags & ZILOG_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ZILOG_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ZILOG_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ZILOG_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * zs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	cli();
+	if(!tty_hung_up_p(filp))
+		info->count--;
+	sti();
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
+			zs_rtsdtr(info, 1);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("block_til_ready hup-ed: ttys%d, count = %d\n",
+				info->line, info->count);
+#endif
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ZILOG_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+
+		cli();
+		r0 = read_zsreg(info->zs_channel, R0);
+		sti();
+		if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+		    !(info->flags & ZILOG_CLOSING) &&
+		    (do_clocal || (DCD & r0)))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ZILOG_NORMAL_ACTIVE;
+	return 0;
+}	
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int zs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct sun_serial	*info;
+	int 			retval, line;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	/* The zilog lines for the mouse/keyboard must be
+	 * opened using their respective drivers.
+	 */
+	if ((line < 0) || (line >= NUM_CHANNELS))
+		return -ENODEV;
+	if((line == KEYBOARD_LINE) || (line == MOUSE_LINE))
+		return -ENODEV;
+	info = zs_soft + line;
+	/* Is the kgdb running over this line? */
+	if (info->kgdb_channel)
+		return -ENODEV;
+	if (serial_paranoia_check(info, tty->device, "zs_open"))
+		return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("zs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	if (info->tty != 0 && info->tty != tty) {
+		/* Never happen? */
+		printk("zs_open %s%d, tty overwrite.\n", tty->driver.name, info->line);
+		return -EBUSY;
+	}
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("zs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("zs_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+	char *revision = "$Revision: 1.3 $";
+	char *version, *p;
+
+	version = strchr(revision, ' ');
+	p = strchr(++version, ' ');
+	*p = '\0';
+	printk("Sparc Zilog8530 serial driver version %s\n", version);
+	*p = ' ';
+}
+
+/* Probe the PROM for the request zs chip number.
+ *
+ * Note: The Sun Voyager shows two addresses and two intr for it's
+ *       Zilogs, what the second does, I don't know. It does work
+ *       with using only the first number of each property.  Also
+ *       we have a special version for sun4u.
+ */
+#ifdef __sparc_v9__
+static struct devid_cookie zs_dcookie;
+static unsigned long zs_irq_flags;
+static struct sun_zslayout *get_zs(int chip)
+{
+	unsigned int vaddr[2] = { 0, 0 };
+	int busnode, seen, zsnode, sun4u_ino;
+	static int irq = 0;
+
+	if(chip < 0 || chip >= NUM_SERIAL)
+		panic("get_zs bogon zs chip number");
+
+	if(central_bus)
+		busnode = central_bus->child->prom_node;
+	else
+		busnode = prom_searchsiblings(prom_getchild(prom_root_node), "sbus");
+	if(busnode == 0 || busnode == -1)
+		panic("get_zs: no zs bus to search");
+
+	zsnode = prom_getchild(busnode);
+	seen = 0;
+	while(zsnode) {
+		int slave;
+
+		zsnode = prom_searchsiblings(zsnode, "zs");
+		slave = prom_getintdefault(zsnode, "slave", -1);
+		if((slave == chip) || (seen == chip)) {
+			int 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;
+			len = prom_getproperty(zsnode, "interrupts",
+					       (char *) &sun4u_ino,
+					       (sizeof(sun4u_ino)));
+			if(!irq) {
+				irq = zilog_irq = sun4u_ino;
+
+				/* Construct dcookie. */
+				if(central_bus) {
+				  zs_dcookie.imap =
+				    &central_bus->child->fhc_regs.uregs->fhc_uart_imap;
+				  zs_dcookie.iclr =
+				    &central_bus->child->fhc_regs.uregs->fhc_uart_iclr;
+				  zs_dcookie.pil = 12;
+				  zs_dcookie.bus_cookie = NULL;
+				  zs_irq_flags =
+				    (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_FHC);
+				} else {
+				  zs_dcookie.imap = zs_dcookie.iclr = NULL;
+				  zs_dcookie.pil = -1;
+				  zs_dcookie.bus_cookie = SBus_chain;
+				  zs_irq_flags =
+				    (SA_DCOOKIE|SA_INTERRUPT|SA_STATIC_ALLOC|SA_SBUS);
+				}
+			} else if(irq != sun4u_ino) {
+				panic("zilog: bogon irqs");
+			}
+			break;
+		}
+		zsnode = prom_getsibling(zsnode);
+		seen++;
+	}
+	if(!zsnode)
+		panic("get_zs: whee chip not found");
+	if(!vaddr[0])
+		panic("get_zs: whee no serial chip mappable");
+	return (struct sun_zslayout *)(unsigned long) vaddr[0];
+}
+#else /* !(__sparc_v9__) */
+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;
+	static int irq = 0;
+
+#if CONFIG_AP1000
+        printk("No zs chip\n");
+        return NULL;
+#endif
+
+	iospace = 0;
+	if(chip < 0 || chip >= NUM_SERIAL)
+		panic("get_zs bogon zs chip number");
+
+	if(sparc_cpu_model == sun4) {
+		/* Grrr, these have to be hardcoded aieee */
+		switch(chip) {
+		case 0:
+			paddr = 0xf1000000;
+			break;
+		case 1:
+			paddr = 0xf0000000;
+			break;
+		};
+		iospace = 0;
+		zs_nodes[chip] = 0;
+		if(!irq)
+			zilog_irq = irq = 12;
+		vaddr[0] = (unsigned long)
+				sparc_alloc_io(paddr, 0, 8,
+					       "Zilog Serial", iospace, 0);
+	} else {
+		/* Can use the prom for other machine types */
+		zsnode = prom_getchild(prom_root_node);
+		if (sparc_cpu_model == sun4d) {
+			int board, node;
+			
+			tmpnode = zsnode;
+			while (tmpnode && (tmpnode = prom_searchsiblings(tmpnode, "cpu-unit"))) {
+				board = prom_getintdefault (tmpnode, "board#", -1);
+				if (board == (chip >> 1)) {
+					node = prom_getchild(tmpnode);
+					if (node && (node = prom_searchsiblings(node, "bootbus"))) {
+						zsnode = node;
+						break;
+					}
+				}
+				tmpnode = prom_getsibling(tmpnode);
+			}
+			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)
+				zsnode = prom_getchild(tmpnode);
+		}
+		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)) {
+				/* 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));
+						panic("zilog: address property");
+					}
+				}
+				if(!irq) {
+					irq = zilog_irq = tmp_irq[0].pri;
+				} else {
+					if(tmp_irq[0].pri != irq)
+						panic("zilog: bogon irqs");
+				}
+				break;
+			}
+			zsnode = prom_getsibling(zsnode);
+			seen++;
+		}
+		if(!zsnode)
+			panic("get_zs whee chip not found");
+	}
+	if(!vaddr[0])
+		panic("get_zs whee no serial chip mappable");
+
+	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)
+{
+	int channel = MOUSE_LINE;
+	int brg;
+
+	zs_soft[channel].zs_baud = newbaud;
+	brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+			 (ZS_CLOCK / zs_soft[channel].clk_divisor));
+	write_zsreg(zs_soft[channel].zs_channel, R12, (brg & 0xff));
+	write_zsreg(zs_soft[channel].zs_channel, R13, ((brg >> 8) & 0xff));
+}
+
+__initfunc(int zs_probe (unsigned long *memory_start))
+{
+	char *p;
+	int node;
+	int i;
+
+	if(sparc_cpu_model == sun4)
+		goto no_probe;
+
+	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
+			node = prom_getchild(node);
+		while (node && (node = prom_searchsiblings(node, "bif"))) {
+			NUM_SERIAL += 2;
+			node = prom_getsibling(node);
+		}
+		goto no_probe;
+	} else if (sparc_cpu_model == sun4u) {
+		node = prom_searchsiblings(node, "sbus");
+		if(node)
+			node = prom_getchild(node);
+		if(!node)
+			return -ENODEV;
+	} else {
+		node = prom_searchsiblings(node, "obio");
+		if(node)
+			node = prom_getchild(node);
+		goto no_probe;
+	}
+
+	node = prom_searchsiblings(node, "zs");
+	if (!node)
+		return -ENODEV;
+
+no_probe:
+	p = (char *)((*memory_start + 7) & ~7);
+	zs_chips = (struct sun_zslayout **)(p);
+	i = NUM_SERIAL * sizeof (struct sun_zslayout *);
+	zs_channels = (struct sun_zschannel **)(p + i);
+	i += NUM_CHANNELS * sizeof (struct sun_zschannel *);
+	zs_nodes = (int *)(p + i);
+	i += NUM_SERIAL * sizeof (int);
+	zs_soft = (struct sun_serial *)(p + i);
+	i += NUM_CHANNELS * sizeof (struct sun_serial);
+	zs_ttys = (struct tty_struct *)(p + i);
+	i += NUM_CHANNELS * sizeof (struct tty_struct);
+	serial_table = (struct tty_struct **)(p + i);
+	i += NUM_CHANNELS * sizeof (struct tty_struct *);
+	serial_termios = (struct termios **)(p + i);
+	i += NUM_CHANNELS * sizeof (struct termios *);
+	serial_termios_locked = (struct termios **)(p + i);
+	i += NUM_CHANNELS * sizeof (struct termios *);
+	memset (p, 0, i);
+	*memory_start = (((unsigned long)p) + i + 7) & ~7;
+
+	/* Fill in rs_ops struct... */
+	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;
+
+	return 0;
+}
+
+__initfunc(int zs_init(void))
+{
+	int chip, channel, brg, i;
+	unsigned long flags;
+	struct sun_serial *info;
+	char dummy;
+
+#if CONFIG_AP1000
+        printk("not doing zs_init()\n");
+        return 0;
+#endif
+
+#ifdef CONFIG_PCI
+	if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+		return 0;
+#endif
+
+	/* Setup base handler, and timer table. */
+	init_bh(SERIAL_BH, do_serial_bh);
+	timer_table[RS_TIMER].fn = zs_timer;
+	timer_table[RS_TIMER].expires = 0;
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	/* SPARC: Not all of this is exactly right for us. */
+	
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "serial";
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NUM_CHANNELS;
+	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;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = zs_open;
+	serial_driver.close = zs_close;
+	serial_driver.write = zs_write;
+	serial_driver.flush_chars = zs_flush_chars;
+	serial_driver.write_room = zs_write_room;
+	serial_driver.chars_in_buffer = zs_chars_in_buffer;
+	serial_driver.flush_buffer = zs_flush_buffer;
+	serial_driver.ioctl = zs_ioctl;
+	serial_driver.throttle = zs_throttle;
+	serial_driver.unthrottle = zs_unthrottle;
+	serial_driver.set_termios = zs_set_termios;
+	serial_driver.stop = zs_stop;
+	serial_driver.start = zs_start;
+	serial_driver.hangup = zs_hangup;
+
+	/* I'm too lazy, someone write versions of this for us. -DaveM */
+	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.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&serial_driver))
+		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;
+
+	/* 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;
+	}
+
+	/* Initialize Hardware */
+	for(channel = 0; channel < NUM_CHANNELS; channel++) {
+
+		/* Hardware reset each chip */
+		if (!(channel & 1)) {
+			write_zsreg(zs_soft[channel].zs_channel, R9, FHWRES);
+			udelay(20);	/* wait for some old sun4's */
+			dummy = read_zsreg(zs_soft[channel].zs_channel, R0);
+		}
+
+		if(channel == KEYBOARD_LINE) {
+			zs_soft[channel].cons_keyb = 1;
+			zs_soft[channel].parity_mask = 0xff;
+			zs_kbdchan = zs_soft[channel].zs_channel;
+
+			write_zsreg(zs_soft[channel].zs_channel, R4,
+				    (PAR_EVEN | X16CLK | SB1));
+			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+			write_zsreg(zs_soft[channel].zs_channel, R11,
+				    (TCBR | RCBR));
+			zs_soft[channel].zs_baud = 1200;
+			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+					 ZS_CLOCK/zs_soft[channel].clk_divisor);
+			write_zsreg(zs_soft[channel].zs_channel, R12,
+				    (brg & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R13,
+				    ((brg >> 8) & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+			/* Enable Rx/Tx, IRQs, and inform kbd driver */
+			write_zsreg(zs_soft[channel].zs_channel, R14,
+				    (BRSRC | BRENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R3,
+				    (Rx8 | RxENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R5,
+				    (Tx8 | TxENAB | DTR | RTS));
+
+			write_zsreg(zs_soft[channel].zs_channel, R15,
+				    (DCDIE | CTSIE | TxUIE | BRKIE));
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+
+			write_zsreg(zs_soft[channel].zs_channel, R1,
+				    (EXT_INT_ENAB | INT_ALL_Rx));
+			write_zsreg(zs_soft[channel].zs_channel, R9,
+				    (NV | MIE));
+			ZS_CLEARERR(zs_soft[channel].zs_channel);
+			ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+		} else if(channel == MOUSE_LINE) {
+			zs_soft[channel].cons_mouse = 1;
+			zs_soft[channel].parity_mask = 0xff;
+			zs_mousechan = zs_soft[channel].zs_channel;
+
+			write_zsreg(zs_soft[channel].zs_channel, R4,
+				    (PAR_EVEN | X16CLK | SB1));
+			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+			write_zsreg(zs_soft[channel].zs_channel, R11,
+				    (TCBR | RCBR));
+
+			zs_soft[channel].zs_baud = 4800;
+			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+					 ZS_CLOCK/zs_soft[channel].clk_divisor);
+			write_zsreg(zs_soft[channel].zs_channel, R12,
+				    (brg & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R13,
+				    ((brg >> 8) & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+
+			/* Enable Rx, IRQs, and inform mouse driver */
+			write_zsreg(zs_soft[channel].zs_channel, R14,
+				    (BRSRC | BRENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R3,
+				    (Rx8 | RxENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+
+			write_zsreg(zs_soft[channel].zs_channel, R15,
+				    (DCDIE | CTSIE | TxUIE | BRKIE));
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+
+			write_zsreg(zs_soft[channel].zs_channel, R1,
+				    (EXT_INT_ENAB | INT_ALL_Rx));
+			write_zsreg(zs_soft[channel].zs_channel, R9,
+				    (NV | MIE));
+
+			sun_mouse_zsinit();
+		} else if (zs_soft[channel].is_cons) {
+			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+					 ZS_CLOCK/zs_soft[channel].clk_divisor);
+			zscons_regs[12] = brg & 0xff;
+			zscons_regs[13] = (brg >> 8) & 0xff;
+
+			memcpy(zs_soft[channel].curregs, zscons_regs, sizeof(zscons_regs));
+			load_zsregs(&zs_soft[channel], zscons_regs);
+
+			ZS_CLEARERR(zs_soft[channel].zs_channel);
+			ZS_CLEARFIFO(zs_soft[channel].zs_channel);
+		} else if (zs_soft[channel].kgdb_channel) {
+			/* If this is the kgdb line, enable interrupts because
+			 * we now want to receive the 'control-c' character
+			 * from the client attached to us asynchronously.
+			 */
+			zs_soft[channel].parity_mask = 0xff;
+        		kgdb_chaninit(&zs_soft[channel], 1,
+				      zs_soft[channel].zs_baud);
+		} else {
+			zs_soft[channel].parity_mask = 0xff;
+			write_zsreg(zs_soft[channel].zs_channel, R4,
+				    (PAR_EVEN | X16CLK | SB1));
+			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+			write_zsreg(zs_soft[channel].zs_channel, R9, NV);
+			write_zsreg(zs_soft[channel].zs_channel, R10, NRZ);
+			write_zsreg(zs_soft[channel].zs_channel, R11,
+				    (RCBR | TCBR));
+			zs_soft[channel].zs_baud = 9600;
+			brg = BPS_TO_BRG(zs_soft[channel].zs_baud,
+					 ZS_CLOCK/zs_soft[channel].clk_divisor);
+			write_zsreg(zs_soft[channel].zs_channel, R12,
+				    (brg & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R13,
+				    ((brg >> 8) & 0xff));
+			write_zsreg(zs_soft[channel].zs_channel, R14, BRSRC);
+			write_zsreg(zs_soft[channel].zs_channel, R14,
+				    (BRSRC | BRENAB));
+			write_zsreg(zs_soft[channel].zs_channel, R3, Rx8);
+			write_zsreg(zs_soft[channel].zs_channel, R5, Tx8);
+			write_zsreg(zs_soft[channel].zs_channel, R15, DCDIE);
+			write_zsreg(zs_soft[channel].zs_channel, R9, NV | MIE);
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+			write_zsreg(zs_soft[channel].zs_channel, R0,
+				    RES_EXT_INT);
+		}
+	}
+
+	for (info = zs_chain, i=0; info; info = info->zs_next, i++) {
+		info->magic = SERIAL_MAGIC;
+		info->port = (long) info->zs_channel;
+		info->line = i;
+		info->tty = 0;
+		info->irq = zilog_irq;
+		info->custom_divisor = 16;
+		info->close_delay = 50;
+		info->closing_wait = 3000;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		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;
+		info->close_wait = 0;
+		printk("tty%02d at 0x%04x (irq = %d)", info->line, 
+		       info->port, info->irq);
+		printk(" is a Zilog8530\n");
+	}
+
+#ifndef __sparc_v9__
+	if (request_irq(zilog_irq, zs_interrupt,
+			(SA_INTERRUPT | SA_STATIC_ALLOC),
+			"Zilog8530", zs_chain))
+		panic("Unable to attach zs intr\n");
+#else
+	zs_dcookie.real_dev_id = zs_chain;
+	if (request_irq(zilog_irq, zs_interrupt,
+			zs_irq_flags, "Zilog8530", &zs_dcookie))
+		panic("Unable to attach zs intr\n");
+#endif
+	restore_flags(flags);
+
+	keyboard_zsinit(kbd_put_char);
+	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)
+{
+	int chip = 0;
+
+	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[tty_num].zs_channel = zs_channels[tty_num];
+	zs_kgdbchan = zs_soft[tty_num].zs_channel;
+	zs_soft[tty_num].change_needed = 0;
+	zs_soft[tty_num].clk_divisor = 16;
+	zs_soft[tty_num].zs_baud = 9600;
+	zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
+	zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+	/* Turn on transmitter/receiver at 8-bits/char */
+        kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+        ZS_CLEARERR(zs_kgdbchan);
+        ZS_CLEARFIFO(zs_kgdbchan);
+}
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/char/zs.h linux/drivers/sbus/char/zs.h
--- v2.1.52/linux/drivers/sbus/char/zs.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/zs.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,428 @@
+/* $Id: zs.h,v 1.1 1997/08/28 02:23:45 ecd Exp $
+ * zs.h: Definitions for the Sparc Zilog serial driver.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ */
+#ifndef _SPARC_SERIAL_H
+#define _SPARC_SERIAL_H
+
+/* Just one channel */
+struct sun_zschannel {
+	volatile unsigned char control;
+	volatile unsigned char pad1;
+	volatile unsigned char data;
+	volatile unsigned char pad2;
+};
+
+/* The address space layout for each zs chip.  Yes they are
+ * backwards.
+ */
+struct sun_zslayout {
+	struct sun_zschannel channelB;
+	struct sun_zschannel channelA;
+};
+
+#define NUM_ZSREGS    16
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF	0
+#define ZILOG_CLOSING_WAIT_NONE	65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
+				   on the callout port */
+#define ZILOG_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK	0x0030
+#define ZILOG_SPD_HI	0x0010	/* Use 76800 instead of 38400 bps */
+#define ZILOG_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS	0x0FFF	/* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING		0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct sun_serial {
+	struct sun_serial *zs_next;       /* For IRQ servicing chain */
+	struct sun_zschannel *zs_channel; /* Channel registers */
+	unsigned char read_reg_zero;
+
+	char soft_carrier;  /* Use soft carrier on this channel */
+	char cons_keyb;     /* Channel runs the keyboard */
+	char cons_mouse;    /* Channel runs the mouse */
+	char break_abort;   /* Is serial console in, so process brk/abrt */
+	char kgdb_channel;  /* Kgdb is running on this channel */
+	char is_cons;       /* Is this our console. */
+
+	char channelA;      /* This is channel A. */
+	char parity_mask;   /* Mask out parity bits in data register. */
+
+	/* We need to know the current clock divisor
+	 * to read the bps rate the chip has currently
+	 * loaded.
+	 */
+	unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
+	int zs_baud;
+
+	/* Current write register values */
+	unsigned char curregs[NUM_ZSREGS];
+
+	char change_needed;
+
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			type; 		/* UART type */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+#define RxINT_MASK	0x18
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENAB  	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxN_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENAB	0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxN_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENAB 	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	ZCIE	2	/* Zero count IE */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC		0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY	0x00
+#define CHB_EXT_STAT	0x02
+#define CHB_Rx_AVAIL	0x04
+#define CHB_SPECIAL	0x06
+#define CHA_Tx_EMPTY	0x08
+#define CHA_EXT_STAT	0x0a
+#define CHA_Rx_AVAIL	0x0c
+#define CHA_SPECIAL	0x0e
+#define STATUS_MASK	0x0e
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    do { channel->control = ERR_RES; \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARSTAT(channel)   do { channel->control = RES_EXT_INT; \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
+				     garbage = channel->data; \
+				     udelay(2); \
+				     garbage = channel->data; \
+				     udelay(2); \
+				     garbage = channel->data; \
+				     udelay(2); } while(0)
+
+#endif /* !(_SPARC_SERIAL_H) */
diff -u --recursive --new-file v2.1.52/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.1.52/linux/drivers/sbus/sbus.c	Mon Aug  4 16:25:38 1997
+++ linux/drivers/sbus/sbus.c	Thu Sep  4 12:54:49 1997
@@ -68,8 +68,14 @@
 	   sparc_cpu_model == sun4m ||
 	   sparc_cpu_model == sun4u) {
 		/* Ahh, we can determine the slot and offset */
-		sbus_dev->slot = sbus_dev_slot(base);
-		sbus_dev->offset = sbus_dev_offset(base);
+		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 {
+			sbus_dev->slot = sbus_dev_slot(base);
+			sbus_dev->offset = sbus_dev_offset(base);
+		}
 	} else {   /* Grrr, gotta do calculations to fix things up */
 		sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
 		sbus_dev->offset = base;
@@ -108,6 +114,18 @@
 			sbus_dev->num_irqs = 0;
 		} else {
 			sbus_dev->num_irqs = 1;
+			if(sbus_dev->irqs[0].pri < 0x20) {
+				int old_irq = sbus_dev->irqs[0].pri;
+
+				/* Need to do special SLOT fixups in this case. */
+#if 0 /* DEBUGGING */
+				printk("SBUS[%x:%lx]: INO fixup from [%x] to [%x]\n",
+				       sbus_dev->slot, sbus_dev->offset,
+				       old_irq, old_irq + (sbus_dev->slot * 8));
+#endif
+				sbus_dev->irqs[0].pri =
+					(old_irq + (sbus_dev->slot * 8));
+			}
 		}
 	} else {
 		len = prom_getproperty(nd, "intr", (void *)sbus_dev->irqs,
@@ -212,6 +230,8 @@
 	return memory_start;
 }
 
+/* #define E3000_DEBUG */
+
 __initfunc(unsigned long
 sbus_init(unsigned long memory_start, unsigned long memory_end))
 {
@@ -221,6 +241,9 @@
 	struct linux_sbus_device *this_dev;
 	int num_sbus = 0;  /* How many did we find? */
 
+#ifdef E3000_DEBUG
+	prom_printf("sbus_init: Radek, record following output for me. -DaveM\n");
+#endif
 	memory_start = ((memory_start + 7) & (~7));
 
 	topnd = prom_getchild(prom_root_node);
@@ -228,11 +251,15 @@
 	/* Finding the first sbus is a special case... */
 	iommund = 0;
 	if(sparc_cpu_model == sun4u) {
-		/* IOMMU "hides" inside SBUS/SYSIO node. */
-		iommund = nd = prom_searchsiblings(topnd, "sbus");
+		nd = prom_searchsiblings(topnd, "sbus");
 		if(nd == 0) {
+#ifdef CONFIG_PCI
+			printk("SBUS: No SBUS's found.\n");
+			return sun_console_init(memory_start);
+#else
 			prom_printf("YEEE, UltraSparc sbus not found\n");
 			prom_halt();
+#endif
 		}
 	} else if(sparc_cpu_model == sun4d) {
 		if((iommund = prom_searchsiblings(topnd, "io-unit")) == 0 ||
@@ -249,6 +276,9 @@
 		}
 	}
 
+#ifdef E3000_DEBUG
+	prom_printf("sbus_init: 1st sbus node(%x)\n", nd);
+#endif
 	/* Ok, we've found the first one, allocate first SBus struct
 	 * and place in chain.
 	 */
@@ -257,16 +287,34 @@
 	sbus->next = 0;
 	this_sbus=nd;
 
-	/* Have IOMMU will travel. XXX grrr - this should be per sbus... */
-	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(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);
 	}
 
 	/* Loop until we find no more SBUS's */
 	while(this_sbus) {
+		/* IOMMU hides inside SBUS/SYSIO prom node on Ultra. */
+#ifdef E3000_DEBUG
+		prom_printf("sbus%d: [ii()", num_sbus);
+#endif
+		if(sparc_cpu_model == sun4u)
+			memory_start = iommu_init(this_sbus,
+						  memory_start, memory_end,
+						  sbus);
+
+#ifdef E3000_DEBUG
+		prom_printf("1");
+#endif
 		printk("sbus%d: ", num_sbus);
 		sbus_clock = prom_getint(this_sbus, "clock-frequency");
 		if(sbus_clock==-1) sbus_clock = (25*1000*1000);
@@ -279,9 +327,15 @@
 		strcpy(sbus->prom_name, lbuf);
 		sbus->clock_freq = sbus_clock;
 		
+#ifdef E3000_DEBUG
+		prom_printf("psri()");
+#endif
 		prom_sbus_ranges_init (iommund, sbus);
 
 		sbus_devs = prom_getchild(this_sbus);
+#ifdef E3000_DEBUG
+		prom_printf("chld(%x)", sbus_devs);
+#endif
 
 		sbus->devices = (struct linux_sbus_device *) memory_start;
 		memory_start += sizeof(struct linux_sbus_device);
@@ -289,6 +343,9 @@
 		this_dev = sbus->devices;
 		this_dev->next = 0;
 
+#ifdef E3000_DEBUG
+		prom_printf("fsd()");
+#endif
 		fill_sbus_device(sbus_devs, this_dev);
 		this_dev->my_bus = sbus;
 
@@ -298,8 +355,14 @@
 			this_dev->child = (struct linux_sbus_device *) memory_start;
 			memory_start += sizeof(struct linux_sbus_device);
 			/* Fill it */
+#ifdef E3000_DEBUG
+			prom_printf("fsd(chld)");
+#endif
 			fill_sbus_device(prom_getchild(sbus_devs), this_dev->child);
 			this_dev->child->my_bus = sbus;
+#ifdef E3000_DEBUG
+			prom_printf("sdcs()");
+#endif
 			memory_start = sbus_do_child_siblings(memory_start,
 							      prom_getchild(sbus_devs),
 							      this_dev->child,
@@ -308,6 +371,9 @@
 			this_dev->child = 0;
 		}
 
+#ifdef E3000_DEBUG
+		prom_printf("2");
+#endif
 		while((sbus_devs = prom_getsibling(sbus_devs)) != 0) {
 			/* Allocate device node */
 			this_dev->next = (struct linux_sbus_device *) memory_start;
@@ -316,6 +382,9 @@
 			this_dev->next=0;
 
 			/* Fill it */
+#ifdef E3000_DEBUG
+			prom_printf("fsd()");
+#endif
 			fill_sbus_device(sbus_devs, this_dev);
 			this_dev->my_bus = sbus;
 
@@ -327,9 +396,15 @@
 				memory_start += sizeof(struct linux_sbus_device);
 
 				/* Fill it */
+#ifdef E3000_DEBUG
+				prom_printf("fsd()");
+#endif
 				fill_sbus_device(prom_getchild(sbus_devs),
 						 this_dev->child);
 				this_dev->child->my_bus = sbus;
+#ifdef E3000_DEBUG
+				prom_printf("sdcs()");
+#endif
 				memory_start = sbus_do_child_siblings(
 						     memory_start,
 						     prom_getchild(sbus_devs),
@@ -340,14 +415,26 @@
 			}
 		}
 
+#ifdef E3000_DEBUG
+		prom_printf("di()");
+#endif
 		memory_start = dvma_init(sbus, memory_start);
 
 		num_sbus++;
+#ifdef E3000_DEBUG
+		prom_printf("3, off to next sbus\n");
+#endif
 		if(sparc_cpu_model == sun4u) {
 			this_sbus = prom_getsibling(this_sbus);
+#ifdef E3000_DEBUG
+			prom_printf("sbus_init: sibling(%x), ", this_sbus);
+#endif
 			if(!this_sbus)
 				break;
 			this_sbus = prom_searchsiblings(this_sbus, "sbus");
+#ifdef E3000_DEBUG
+			prom_printf("next sbus node(%x),", this_sbus);
+#endif
 		} else if(sparc_cpu_model == sun4d) {
 			iommund = prom_getsibling(iommund);
 			if(!iommund) break;
@@ -360,6 +447,9 @@
 			this_sbus = prom_searchsiblings(this_sbus, "sbus");
 		}
 		if(this_sbus) {
+#ifdef E3000_DEBUG
+			prom_printf(" scanning another sbus\n");
+#endif
 			sbus->next = (struct linux_sbus *) memory_start;
 			memory_start += sizeof(struct linux_sbus);
 			sbus = sbus->next;
@@ -368,7 +458,13 @@
 			break;
 		}
 	} /* while(this_sbus) */
+#ifdef E3000_DEBUG
+	prom_printf("sbus_init: No more sbus's, calling sun_console_init()\n");
+#endif
 	memory_start = sun_console_init(memory_start); /* whee... */
+#ifdef E3000_DEBUG
+	prom_printf("sbus_init: back from sun_console_init()\n");
+#endif
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
 #endif
@@ -388,8 +484,10 @@
 #ifdef __sparc_v9__
 	if (sparc_cpu_model == sun4u) {
 		extern void sun4u_start_timers(void);
+		extern void clock_probe(void);
 
 		sun4u_start_timers();
+		clock_probe();
 	}
 #endif
 	return memory_start;
diff -u --recursive --new-file v2.1.52/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.52/linux/fs/dcache.c	Wed Sep  3 20:52:43 1997
+++ linux/fs/dcache.c	Thu Sep  4 11:23:53 1997
@@ -365,21 +365,19 @@
 	if (!dentry->d_inode)
 		printk("VFS: moving negative dcache entry\n");
 
-	/* Switch the hashes.. */
-	oldhead = dentry->d_hash.prev;
+	/* Move the dentry to the target hash queue */
 	list_del(&dentry->d_hash);
 	list_add(&dentry->d_hash, &target->d_hash);
+
+	/* Unhash the target: dput() will then get rid of it */
 	list_del(&target->d_hash);
-	list_add(&target->d_hash, oldhead);
+	INIT_LIST_HEAD(&target->d_hash);
 
 	/* Switch the parents and the names.. */
 	switch(dentry->d_parent, target->d_parent);
 	switch(dentry->d_name.name, target->d_name.name);
 	switch(dentry->d_name.len, target->d_name.len);
 	switch(dentry->d_name.hash, target->d_name.hash);
-
-	/* Mark the (now overwritten) target deleted. */
-	d_delete(target);
 }
 
 /*
diff -u --recursive --new-file v2.1.52/linux/fs/select.c linux/fs/select.c
--- v2.1.52/linux/fs/select.c	Wed Sep  3 20:52:43 1997
+++ linux/fs/select.c	Wed Sep  3 20:49:40 1997
@@ -141,25 +141,16 @@
 #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
 #define POLLEX_SET (POLLPRI)
 
-static int do_select(int n, fd_set_buffer *fds)
+static int do_select(int n, fd_set_buffer *fds, poll_table *wait)
 {
 	int retval;
-	poll_table wait_table, *wait;
-	struct poll_table_entry *entry;
 	int i;
 
 	retval = max_select_fd(n, fds);
 	if (retval < 0)
 		goto out;
 	n = retval;
-	retval = -ENOMEM;
-	entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-	if (!entry)
-		goto out;
 	retval = 0;
-	wait_table.nr = 0;
-	wait_table.entry = entry;
-	wait = &wait_table;
 	for (;;) {
 		struct file ** fd = current->files->fd;
 		current->state = TASK_INTERRUPTIBLE;
@@ -197,8 +188,6 @@
 			break;
 		schedule();
 	}
-	free_wait(&wait_table);
-	free_page((unsigned long) entry);
 	current->state = TASK_RUNNING;
 out:
 	return retval;
@@ -286,51 +275,60 @@
  */
 asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
 {
-	int error = -EINVAL;
+	int error;
 	fd_set_buffer *fds;
 	unsigned long timeout;
+	poll_table wait_table, *wait;
 
 	lock_kernel();
-	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
-	if (!fds)
-		goto out;
-	if (n < 0)
-		goto out;
-	if (n > KFDS_NR)
-		n = KFDS_NR;
-	if ((error = get_fd_set(n, inp, &fds->in)) ||
-	    (error = get_fd_set(n, outp, &fds->out)) ||
-	    (error = get_fd_set(n, exp, &fds->ex))) goto out;
 	timeout = ~0UL;
 	if (tvp) {
-		error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
+		error = -EFAULT;
+		if (!access_ok(VERIFY_WRITE, tvp, sizeof(*tvp)))
+			goto out_nowait;
+		error = __get_user(timeout, &tvp->tv_usec);
 		if (error)
-			goto out;
-		__get_user(timeout, &tvp->tv_usec);
+			goto out_nowait;
 		timeout = ROUND_UP(timeout,(1000000/HZ));
 		{
 			unsigned long tmp;
-			__get_user(tmp, &tvp->tv_sec);
+			error = __get_user(tmp, &tvp->tv_sec);
+			if (error)
+				goto out_nowait;
 			timeout += tmp * (unsigned long) HZ;
 		}
 		if (timeout)
 			timeout += jiffies + 1;
 	}
+	error = -ENOMEM;
+	wait = NULL;
+	current->timeout = timeout;
+	if (timeout) {
+		struct poll_table_entry *entry;
+
+		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+		if (!entry)
+			goto out_nowait;
+		wait_table.nr = 0;
+		wait_table.entry = entry;
+		wait = &wait_table;
+	}
+	fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL);
+	if (!fds)
+		goto out_nofds;
+	error = -EINVAL;
+	if (n < 0)
+		goto out;
+	if (n > KFDS_NR)
+		n = KFDS_NR;
+	if ((error = get_fd_set(n, inp, &fds->in)) ||
+	    (error = get_fd_set(n, outp, &fds->out)) ||
+	    (error = get_fd_set(n, exp, &fds->ex))) goto out;
 	zero_fd_set(n, &fds->res_in);
 	zero_fd_set(n, &fds->res_out);
 	zero_fd_set(n, &fds->res_ex);
-	current->timeout = timeout;
-	error = do_select(n, fds);
-	timeout = current->timeout - jiffies - 1;
+	error = do_select(n, fds, wait);
 	current->timeout = 0;
-	if ((long) timeout < 0)
-		timeout = 0;
-	if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
-		__put_user(timeout/HZ, &tvp->tv_sec);
-		timeout %= HZ;
-		timeout *= (1000000/HZ);
-		__put_user(timeout, &tvp->tv_usec);
-	}
 	if (error < 0)
 		goto out;
 	if (!error) {
@@ -344,6 +342,12 @@
 	set_fd_set(n, exp, &fds->res_ex);
 out:
 	free_page((unsigned long) fds);
+out_nofds:
+	if (wait) {
+		free_wait(&wait_table);
+		free_page((unsigned long) wait->entry);
+	}
+out_nowait:
 	unlock_kernel();
 	return error;
 }
@@ -392,43 +396,43 @@
 {
 	int i, count, fdcount, err;
 	struct pollfd * fds, *fds1;
-	poll_table wait_table;
-	struct poll_table_entry *entry;
+	poll_table wait_table, *wait;
 
 	lock_kernel();
+	if (timeout < 0)
+		timeout = 0x7fffffff;
+	else if (timeout)
+		timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
 	err = -ENOMEM;
-	entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
-	if (!entry)
-		goto out;
+
+	wait = NULL;
+	if (timeout) {
+		struct poll_table_entry *entry;
+		entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+		if (!entry)
+			goto out;
+		wait_table.nr = 0;
+		wait_table.entry = entry;
+		wait = &wait_table;
+	}
 	fds = (struct pollfd *) kmalloc(nfds*sizeof(struct pollfd), GFP_KERNEL);
 	if (!fds) {
-		free_page((unsigned long) entry);
 		goto out;
 	}
 
 	err = -EFAULT;
 	if (copy_from_user(fds, ufds, nfds*sizeof(struct pollfd))) {
-		free_page((unsigned long)entry);
 		kfree(fds);
 		goto out;
 	}
 
-	if (timeout < 0)
-		timeout = 0x7fffffff;
-	else if (timeout)
-		timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
 	current->timeout = timeout;
 
 	count = 0;
-	wait_table.nr = 0;
-	wait_table.entry = entry;
 
-	fdcount = do_poll(nfds, fds, timeout ? &wait_table : NULL);
+	fdcount = do_poll(nfds, fds, wait);
 	current->timeout = 0;
 
-	free_wait(&wait_table);
-	free_page((unsigned long) entry);
-
 	/* OK, now copy the revents fields back to user space. */
 	fds1 = fds;
 	for(i=0; i < (int)nfds; i++, ufds++, fds++) {
@@ -440,6 +444,10 @@
 	else
 		err = fdcount;
 out:
+	if (wait) {
+		free_wait(&wait_table);
+		free_page((unsigned long) wait->entry);
+	}
 	unlock_kernel();
 	return err;
 }
diff -u --recursive --new-file v2.1.52/linux/include/asm-m68k/elf.h linux/include/asm-m68k/elf.h
--- v2.1.52/linux/include/asm-m68k/elf.h	Sat May 24 09:10:24 1997
+++ linux/include/asm-m68k/elf.h	Thu Sep  4 12:54:49 1997
@@ -27,13 +27,20 @@
 #define ELF_DATA	ELFDATA2MSB;
 #define ELF_ARCH	EM_68K
 
-	/* For SVR4/m68k the function pointer to be registered with
-	   `atexit' is passed in %a1.  Although my copy of the ABI has
-	   no such statement, it is actually used on ASV.  */
+/* For SVR4/m68k the function pointer to be registered with `atexit' is
+   passed in %a1.  Although my copy of the ABI has no such statement, it
+   is actually used on ASV.  */
 #define ELF_PLAT_INIT(_r)	_r->a1 = 0
 
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
+
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   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_CORE_COPY_REGS(pr_reg, regs)				\
 	/* Bleech. */							\
diff -u --recursive --new-file v2.1.52/linux/include/asm-m68k/namei.h linux/include/asm-m68k/namei.h
--- v2.1.52/linux/include/asm-m68k/namei.h	Mon Jun 16 16:36:00 1997
+++ linux/include/asm-m68k/namei.h	Thu Sep  4 12:54:49 1997
@@ -12,7 +12,7 @@
  * Look at asm-sparc/namei.h for details.
  */
 
-#define __prefix_namei(retrieve_mode, name, base, buf, res_dir, res_inode, \
-		       last_name, last_entry, last_error) 1
+#define __prefix_lookup_dentry(name, follow_link) \
+        do {} while (0)
 
 #endif
diff -u --recursive --new-file v2.1.52/linux/include/asm-mips/elf.h linux/include/asm-mips/elf.h
--- v2.1.52/linux/include/asm-mips/elf.h	Thu Jun 26 12:33:39 1997
+++ linux/include/asm-mips/elf.h	Thu Sep  4 12:54:49 1997
@@ -40,4 +40,11 @@
  */
 #define ELF_PLAT_INIT(_r)	_r->regs[2] = 0;
 
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   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)
+
 #endif /* __ASM_MIPS_ELF_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-mips/namei.h linux/include/asm-mips/namei.h
--- v2.1.52/linux/include/asm-mips/namei.h	Thu Jul 17 10:06:08 1997
+++ linux/include/asm-mips/namei.h	Thu Sep  4 12:54:49 1997
@@ -8,50 +8,36 @@
 
 #include <linux/config.h>
 
-#ifdef CONFIG_BINFMT_IRIX
-
 /* Only one at this time. */
 #define IRIX32_EMUL "usr/gnemul/irix/"
 
-#if 0 /* XXX FIXME */
-
-extern int __namei(int, const char *, struct inode *, char *, struct inode **,
-		   struct inode **, struct qstr *, struct dentry **, int *);
-
-static __inline__ int
-__prefix_namei(int retrieve_mode, const char * name, struct inode * base,
-	       char * buf, struct inode ** res_dir, struct inode ** res_inode,
-	       struct qstr * last_name, struct dentry ** last_entry,
-	       int * last_error)
+static inline struct dentry *
+__mips_lookup_dentry(const char *name, int follow_link)
 {
 	int error;
+	struct dentry *base;
 
 	if (current->personality != PER_IRIX32)
-		return -EINVAL;
+		return ERR_PTR(-ENOENT);
 
-	while (*name == '/')
-		name++;
-
-	atomic_inc(&current->fs->root->i_count);
-	error = __namei(NAM_FOLLOW_LINK, IRIX32_EMUL, current->fs->root,
-			buf, NULL, &base, NULL, NULL, NULL);
-	if (error)
-		return error;
-
-	error = __namei(retrieve_mode, name, base, buf, res_dir, res_inode,
-			last_name, last_entry, last_error);
-	if (error)
-		return error;
-
-	return 0;
+	base = lookup_dentry (IRIX32_EMUL,
+			dget (current->fs->root), 1);
+			
+	if (IS_ERR (base)) return base;
+	
+	return lookup_dentry (name, base, follow_link);
 }
 
-#endif /* XXX FIXME */
+#ifdef CONFIG_BINFMT_IRIX
+
+#define __prefix_lookup_dentry(name, follow_link)				\
+	dentry = __mips_lookup_dentry (name, follow_link);			\
+	if (!IS_ERR (dentry)) return dentry;
 
 #else /* !defined(CONFIG_BINFMT_IRIX) */
 
-#define __prefix_namei(retrieve_mode, name, base, buf, res_dir, res_inode, \
-		       last_name, last_entry, last_error) 1
+#define __prefix_lookup_dentry(name, follow_link) \
+        do {} while (0)
 
 #endif /* !defined(CONFIG_BINFMT_IRIX) */
 
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/dma.h linux/include/asm-ppc/dma.h
--- v2.1.52/linux/include/asm-ppc/dma.h	Mon Aug 18 18:19:46 1997
+++ linux/include/asm-ppc/dma.h	Thu Sep  4 12:54:49 1997
@@ -23,13 +23,15 @@
 #ifndef _ASM_DMA_H
 #define _ASM_DMA_H
 
+#ifndef MAX_DMA_CHANNELS
 #define MAX_DMA_CHANNELS	8
+#endif
 
 /* The maximum address that we can perform a DMA transfer to on this platform */
 /* Doesn't really apply... */
 #define MAX_DMA_ADDRESS      0xFFFFFFFF
 
-#ifdef CONFIG_PREP
+#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
 #include <asm/io.h>		/* need byte IO */
 
 
@@ -57,7 +59,8 @@
  *  - page registers for 5-7 don't use data bit 0, represent 128K pages
  *  - page registers for 0-3 use bit 0, represent 64K pages
  *
- * DMA transfers are limited to the lower 16MB of _physical_ memory.  
+ * On PReP, DMA transfers are limited to the lower 16MB of _physical_ memory.  
+ * On CHRP, the W83C553F (and VLSI Tollgate?) support full 32 bit addressing.
  * Note that addresses loaded into registers must be _physical_ addresses,
  * not logical addresses (which may differ if paging is active).
  *
@@ -325,12 +328,14 @@
 	return (dmanr<=3)? count : (count<<1);
 }
 
-#else /* CONFIG_PREP */
+#endif /* CONFIG_PREP || CONFIG_CHRP */
+
+#ifdef CONFIG_PMAC
 
 #define DMA_MODE_READ	1
 #define DMA_MODE_WRITE	2
 
-#endif /* CONFIG_PREP */
+#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.52/linux/include/asm-ppc/elf.h linux/include/asm-ppc/elf.h
--- v2.1.52/linux/include/asm-ppc/elf.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/elf.h	Thu Sep  4 12:54:49 1997
@@ -24,6 +24,13 @@
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
 
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   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)
+
 typedef unsigned long elf_greg_t;
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/hardirq.h linux/include/asm-ppc/hardirq.h
--- v2.1.52/linux/include/asm-ppc/hardirq.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/hardirq.h	Thu Sep  4 12:54:49 1997
@@ -14,10 +14,45 @@
 
 #define synchronize_irq()	do { } while (0)
 
-#else
+#else /* __SMP__ */
 
-#error Somebody should do SMP support for PPC...
+extern unsigned char global_irq_holder;
+extern spinlock_t global_irq_lock;
+extern atomic_t global_irq_count;
 
+#define release_irqlock(cpu)				\
+do {	if(global_irq_holder == (unsigned char) cpu) {	\
+		global_irq_holder = NO_PROC_ID;		\
+		spin_unlock(&global_irq_lock);		\
+	}						\
+} while(0)
+
+/* Ordering of the counter bumps is _deadly_ important. */
+#define hardirq_enter(cpu) \
+	do { ++local_irq_count[cpu]; atomic_inc(&global_irq_count); } while(0)
+
+#define hardirq_exit(cpu) \
+	do { atomic_dec(&global_irq_count); --local_irq_count[cpu]; } while(0)
+
+#define hardirq_trylock(cpu)					\
+({	unsigned long flags; int ret = 1;			\
+	__save_flags(flags);					\
+	__cli();						\
+	if(atomic_add_return(1, &global_irq_count) != 1 ||	\
+	   *(((unsigned char *)(&global_irq_lock)))) {		\
+		atomic_dec(&global_irq_count);			\
+		__restore_flags(flags);				\
+		ret = 0;					\
+	} else {						\
+		++local_irq_count[cpu];				\
+		__sti();					\
+	}							\
+	ret;							\
+})
+
+#define hardirq_endlock(cpu) do { __cli(); hardirq_exit(cpu); __sti(); } while(0)
+
+extern void synchronize_irq(void);
 #endif /* __SMP__ */
 
 #endif /* __ASM_HARDIRQ_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/hydra.h linux/include/asm-ppc/hydra.h
--- v2.1.52/linux/include/asm-ppc/hydra.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/hydra.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,105 @@
+/*
+ *  linux/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
+ *	Apple Computer, Inc.
+ *
+ *	© Copyright 1995 Apple Computer, Inc. All rights reserved.
+ *
+ *  It's available online from http://chrp.apple.com/MacTech.pdf.
+ *  You can obtain paper copies of this book from computer bookstores or by
+ *  writing Morgan Kaufmann Publishers, Inc., 340 Pine Street, Sixth Floor, San
+ *  Francisco, CA 94104. Reference ISBN 1-55860-393-X.
+ *
+ *  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_HYDRA_H
+#define _ASMPPC_HYDRA_H
+
+#ifdef __KERNEL__
+
+struct Hydra {
+    /* DBDMA Controller Register Space */
+    char Pad1[0x30];
+    u_int CachePD;
+    u_int IDs;
+    u_int Feature_Control;
+    char Pad2[0x7fc4];
+    /* DBDMA Channel Register Space */
+    char SCSI_DMA[0x100];
+    char Pad3[0x300];
+    char SCCA_Tx_DMA[0x100];
+    char SCCA_Rx_DMA[0x100];
+    char SCCB_Tx_DMA[0x100];
+    char SCCB_Rx_DMA[0x100];
+    char Pad4[0x7800];
+    /* Device Register Space */
+    char SCSI[0x1000];
+    char ADB[0x1000];
+    char SCC_Legacy[0x1000];
+    char SCC[0x1000];
+    char Pad9[0x2000];
+    char VIA[0x2000];
+    char Pad10[0x28000];
+    char OpenPIC[0x40000];
+};
+
+extern volatile struct Hydra *Hydra;
+
+
+    /*
+     *  Feature Control Register
+     */
+
+#define HYDRA_FC_SCC_CELL_EN	0x00000001	/* Enable SCC Clock */
+#define HYDRA_FC_SCSI_CELL_EN	0x00000002	/* Enable SCSI Clock */
+#define HYDRA_FC_SCCA_ENABLE	0x00000004	/* Enable SCC A Lines */
+#define HYDRA_FC_SCCB_ENABLE	0x00000008	/* Enable SCC B Lines */
+#define HYDRA_FC_ARB_BYPASS	0x00000010	/* Bypass Internal Arbiter */
+#define HYDRA_FC_RESET_SCC	0x00000020	/* Reset SCC */
+#define HYDRA_FC_MPIC_ENABLE	0x00000040	/* Enable OpenPIC */
+#define HYDRA_FC_SLOW_SCC_PCLK	0x00000080	/* 1=15.6672, 0=25 MHz */
+#define HYDRA_FC_MPIC_IS_MASTER	0x00000100	/* OpenPIC Master Mode */
+
+
+    /*
+     *  OpenPIC Interrupt Sources
+     */
+
+#define HYDRA_INT_SIO		0
+#define HYDRA_INT_SCSI_DMA	1
+#define HYDRA_INT_SCCA_TX_DMA	2
+#define HYDRA_INT_SCCA_RX_DMA	3
+#define HYDRA_INT_SCCB_TX_DMA	4
+#define HYDRA_INT_SCCB_RX_DMA	5
+#define HYDRA_INT_SCSI		6
+#define HYDRA_INT_SCCA		7
+#define HYDRA_INT_SCCB		8
+#define HYDRA_INT_VIA		9
+#define HYDRA_INT_ADB		10
+#define HYDRA_INT_ADB_NMI	11
+#define HYDRA_INT_EXT1		12
+#define HYDRA_INT_EXT2		13
+#define HYDRA_INT_EXT3		14
+#define HYDRA_INT_EXT4		15
+#define HYDRA_INT_EXT5		16
+#define HYDRA_INT_EXT6		17
+#define HYDRA_INT_EXT7		18
+#define HYDRA_INT_SPARE		19
+
+#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.52/linux/include/asm-ppc/ide.h linux/include/asm-ppc/ide.h
--- v2.1.52/linux/include/asm-ppc/ide.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/ide.h	Thu Sep  4 12:54:49 1997
@@ -21,10 +21,12 @@
 
 #define ide_sti()	sti()
 
-#ifdef CONFIG_PREP
-
 typedef unsigned short 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);
 
+#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
 static __inline__ int ide_default_irq(ide_ioreg_t base)
 {
 	switch (base) {
@@ -49,18 +51,6 @@
 	}
 }
 
-static __inline__ void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
-{
-	ide_ioreg_t port = base;
-	int i = 8;
-
-	while (i--)
-		*p++ = port++;
-	*p++ = base + 0x206;
-	if (irq != NULL)
-		*irq = 0;
-}
-
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
 	struct {
@@ -89,7 +79,8 @@
 
 #define ide_fix_driveid(id)		do {} while (0)
 
-#endif
+#endif /* CONFIG_CHRP || CONFIG_PREP */
+
 
 #ifdef CONFIG_PMAC
 
@@ -107,7 +98,6 @@
 	return index;
 }
 
-void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq);
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/io.h linux/include/asm-ppc/io.h
--- v2.1.52/linux/include/asm-ppc/io.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/io.h	Thu Sep  4 12:54:49 1997
@@ -18,17 +18,18 @@
 
 extern unsigned long io_base;
 #define SLOW_DOWN_IO
-
 #define _IO_BASE io_base
+
+extern unsigned long pci_dram_offset;
 #undef PCI_DRAM_OFFSET
-#define PCI_DRAM_OFFSET  _IO_BASE
+#define PCI_DRAM_OFFSET  pci_dram_offset
 
 #define readb(addr) (*(volatile unsigned char *) (addr))
 #define readw(addr) ld_le16((volatile unsigned short *)(addr))
-#define readl(addr) ld_le32(addr)
+#define readl(addr) ld_le32((volatile unsigned *)addr)
 #define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
 #define writew(b,addr) st_le16((volatile unsigned short *)(addr),(b))
-#define writel(b,addr) st_le32((addr),(b))
+#define writel(b,addr) st_le32((volatile unsigned *)(addr),(b))
 
 #define insb(port, buf, ns)	_insb((unsigned char *)((port)+_IO_BASE), (buf), (ns))
 #define outsb(port, buf, ns)	_outsb((unsigned char *)((port)+_IO_BASE), (buf), (ns))
@@ -48,7 +49,7 @@
 #define outb_p(val, port)	out_8((unsigned char *)((port)+_IO_BASE), (val))
 #define inw_p(port)		in_le16((unsigned short *)((port)+_IO_BASE))
 #define outw_p(val, port)	out_le16((unsigned short *)((port)+_IO_BASE), (val))
-#define inl_p(port)		in_le32(((unsigned *)port))
+#define inl_p(port)		in_le32(((unsigned *)(port)+_IO_BASE))
 #define outl_p(val, port)	out_le32((unsigned *)((port)+_IO_BASE), (val))
 
 extern void _insb(volatile unsigned char *port, void *buf, int ns);
@@ -85,6 +86,7 @@
  * I/O devices etc.
  */
 extern void *ioremap(unsigned long address, unsigned long size);
+extern void iounmap(unsigned long *addr);
 
 /*
  * Change virtual addresses to physical addresses and vv, for
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/irq.h linux/include/asm-ppc/irq.h
--- v2.1.52/linux/include/asm-ppc/irq.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/irq.h	Thu Sep  4 12:54:49 1997
@@ -3,7 +3,19 @@
 
 #include <linux/config.h>
 
-#define NR_IRQS 32
+/*
+ * 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
+ * -- Cort
+ */
+#define NR_IRQS			(NUM_8259_INTERRUPTS+NUM_OPENPIC_INTERRUPTS)
+
+#define NUM_8259_INTERRUPTS	16
+#define NUM_OPENPIC_INTERRUPTS	20
+#define is_8259_irq(n)		((n) < NUM_8259_INTERRUPTS)
+#define openpic_to_irq(n)	((n)+NUM_8259_INTERRUPTS)
+#define irq_to_openpic(n)	((n)-NUM_8259_INTERRUPTS)
+#define IRQ_8259_CASCADE	NUM_8259_INTERRUPTS
 
 extern void disable_irq(unsigned int);
 extern void enable_irq(unsigned int);
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/namei.h linux/include/asm-ppc/namei.h
--- v2.1.52/linux/include/asm-ppc/namei.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/namei.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.2 1997/07/31 07:10:55 paulus Exp $
+/* $Id: namei.h,v 1.3 1997/08/29 15:52:22 jj Exp $
  * linux/include/asm-ppc/namei.h
  * Adapted from linux/include/asm-alpha/namei.h
  *
@@ -8,15 +8,12 @@
 #ifndef __PPC_NAMEI_H
 #define __PPC_NAMEI_H
 
-/* These dummy routines maybe changed to something useful
+/* This dummy routine maybe changed to something useful
  * for /usr/gnemul/ emulation stuff.
  * Look at asm-sparc/namei.h for details.
  */
 
-#define translate_namei(pathname, base, follow_links, res_inode)	\
-	do { } while (0)
-
-#define translate_open_namei(pathname, flag, mode, res_inode, base) \
-	do { } while (0)
+#define __prefix_lookup_dentry(name, follow_link) \
+	do {} while (0)
 
 #endif /* __PPC_NAMEI_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/page.h linux/include/asm-ppc/page.h
--- v2.1.52/linux/include/asm-ppc/page.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/page.h	Thu Sep  4 12:54:49 1997
@@ -13,11 +13,11 @@
 /*
  * 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 the io areas
+ * than their virtual address.  On the pmac and chrp the io areas
  * are mapped 1-1 virtual/physical.
  * -- Cort
  */
-#ifdef CONFIG_PREP
+#if defined(CONFIG_PREP) || defined(CONFIG_CHRP)
 #define KERNELBASE	0x90000000
 #endif
 #ifdef CONFIG_PMAC
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/pnp.h linux/include/asm-ppc/pnp.h
--- v2.1.52/linux/include/asm-ppc/pnp.h	Wed Dec 18 00:54:09 1996
+++ linux/include/asm-ppc/pnp.h	Thu Sep  4 12:54:49 1997
@@ -17,10 +17,11 @@
 #ifndef _PNP_
 #define _PNP_
 
+#ifndef __ASSEMBLY__
 #define MAX_MEM_REGISTERS 9
 #define MAX_IO_PORTS 20
 #define MAX_IRQS 7
-#define MAX_DMA_CHANNELS 7
+/*#define MAX_DMA_CHANNELS 7*/
 
 /* Interrupt controllers */
 
@@ -638,4 +639,5 @@
 
   } PnP_TAG_PACKET;
 
+#endif /* __ASSEMBLY__ */
 #endif  /* ndef _PNP_ */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- v2.1.52/linux/include/asm-ppc/processor.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/processor.h	Thu Sep  4 12:54:49 1997
@@ -2,6 +2,7 @@
 #define __ASM_PPC_PROCESSOR_H
 
 #include <asm/ptrace.h>
+#include <asm/residual.h>
 
 /* Bit encodings for Machine State Register (MSR) */
 #define MSR_POW		(1<<18)		/* Enable Power Management */
@@ -51,11 +52,29 @@
 #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 */
 
 #ifndef __ASSEMBLY__
+extern int _machine;
+
+/* if we're a prep machine */
+#define is_prep (_machine & (_MACH_Motorola|_MACH_IBM))
 /*
- * PowerPC machine specifics
+ * 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;
+}
+
 struct task_struct;
 void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp);
 void release_thread(struct task_struct *);
@@ -69,14 +88,10 @@
 #define MCA_bus__is_a_macro /* for versions in ksyms.c */
 
 /*
- * Write Protection works right in supervisor mode on the PowerPC
- */
-#define wp_works_ok 1
-#define wp_works_ok__is_a_macro /* for versions in ksyms.c */
-
-/*
- * User space process size: 2GB. This is hardcoded into a few places,
- * so don't change it unless you know what you are doing.
+ * this is the minimum allowable io space due to the location
+ * of the io areas on prep (first one at 0x80000000) but
+ * as soon as I get around to remapping the io areas with the BATs
+ * to match the mac we can raise this. -- Cort
  */
 #define TASK_SIZE	(0x80000000UL)
 
@@ -129,17 +144,17 @@
 int ll_printk(const char *, ...);
 void ll_puts(const char *);
 
-extern int _machine;
-#endif /* ndef ASSEMBLY*/
-
-#define _MACH_Motorola 1 /* motorola prep */
-#define _MACH_IBM      2 /* ibm prep */
-#define _MACH_Pmac     4 /* pmac or pmac clone */
-#define _MACH_chrp     8 /* chrp machine */
-
-#define is_prep ((_machine == _MACH_Motorola)||(_machine == _MACH_IBM))
-
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
+
+#endif /* ndef ASSEMBLY*/
+
   
 #endif /* __ASM_PPC_PROCESSOR_H */
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/residual.h linux/include/asm-ppc/residual.h
--- v2.1.52/linux/include/asm-ppc/residual.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/residual.h	Thu Sep  4 12:54:49 1997
@@ -13,6 +13,8 @@
 #ifndef _RESIDUAL_
 #define _RESIDUAL_
 
+#ifndef __ASSEMBLY__
+
 #define MAX_CPUS 32                     /* These should be set to the maximum */
 #define MAX_MEMS 64                     /* number possible for this system.   */
 #define MAX_DEVICES 256                 /* Changing these will change the     */
@@ -313,5 +315,7 @@
 
 
 extern RESIDUAL res;
+void print_residual_device_info(void);
+#endif /* __ASSEMBLY__ */
 #endif  /* ndef _RESIDUAL_ */
 
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/scatterlist.h linux/include/asm-ppc/scatterlist.h
--- v2.1.52/linux/include/asm-ppc/scatterlist.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/scatterlist.h	Thu Sep  4 12:54:49 1997
@@ -24,4 +24,9 @@
 #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.52/linux/include/asm-ppc/smp.h linux/include/asm-ppc/smp.h
--- v2.1.52/linux/include/asm-ppc/smp.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/smp.h	Thu Sep  4 12:54:49 1997
@@ -6,21 +6,54 @@
 #ifndef _PPC_SMP_H
 #define _PPC_SMP_H
 
+#include <linux/kernel.h> /* for panic */
+
 #ifdef __SMP__
 
 #ifndef __ASSEMBLY__
 
-extern struct prom_cpuinfo linux_cpus[NCPUS];
-
-/* Per processor PPC parameters we need. */
+extern unsigned long cpu_present_map;
 
+/* per processor PPC parameters we need. */
 struct cpuinfo_PPC {
-	unsigned long udelay_val; /* that's it */
+	unsigned long udelay_val;
 };
 
 extern struct cpuinfo_PPC cpu_data[NR_CPUS];
+
+struct klock_info {
+	unsigned char kernel_flag;
+	unsigned char akp;
+};
+
+extern struct klock_info klock_info;
+
+#define KLOCK_HELD       0xff
+#define KLOCK_CLEAR      0x00
+
+#define PROC_CHANGE_PENALTY     20
+
+extern __volatile__ int cpu_number_map[NR_CPUS];
+extern __volatile__ int cpu_logical_map[NR_CPUS];
+extern unsigned long smp_proc_in_lock[NR_CPUS];
+
+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");
+
+	return cpuid;
+}
+
+#define smp_processor_id() hard_smp_processor_id()
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* !(__SMP__) */
+
+#define NO_PROC_ID               0xFF            /* No processor magic marker */
 
 #endif /* !(_PPC_SMP_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/smp_lock.h linux/include/asm-ppc/smp_lock.h
--- v2.1.52/linux/include/asm-ppc/smp_lock.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/smp_lock.h	Thu Sep  4 12:54:49 1997
@@ -1,6 +1,7 @@
 #ifndef __PPC_SMPLOCK_H
 #define __PPC_SMPLOCK_H
 
+#include <linux/kernel.h> /* for panic */
 #ifndef __SMP__
 
 #define lock_kernel()		do { } while (0)
@@ -10,7 +11,49 @@
 
 #else
 
-#error need to defined lock_kernel and unlock_kernel, etc.
+/* Release global kernel lock and global interrupt lock */
+#define release_kernel_lock(task, cpu, depth)		\
+do {							\
+	if((depth = (task)->lock_depth) != 0) {		\
+		__cli();				\
+		(task)->lock_depth = 0;			\
+		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)
+
+/* 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 __inline__ void lock_kernel(void)
+{
+	panic("lock_kernel()\n");
+}
+
+/* Release kernel global lock. */
+extern __inline__ void unlock_kernel(void)
+{
+	panic("unlock_kernel()\n");
+}
+
 
 #endif /* __SMP__ */
 #endif /* __PPC_SMPLOCK_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/softirq.h linux/include/asm-ppc/softirq.h
--- v2.1.52/linux/include/asm-ppc/softirq.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/softirq.h	Thu Sep  4 12:54:49 1997
@@ -8,7 +8,17 @@
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 
+/*
+ * The locking mechanism for base handlers, to prevent re-entrancy,
+ * is entirely private to an implementation, it should not be
+ * referenced at all outside of this file.
+ */
 #define get_active_bhs()	(bh_mask & bh_active)
+
+#ifndef __SMP__
+
+extern int __ppc_bh_counter;
+
 #define clear_active_bhs(x)	atomic_clear_mask((x),&bh_active)
 
 extern inline void init_bh(int nr, void (*routine)(void))
@@ -45,15 +55,6 @@
 		bh_mask |= 1 << nr;
 }
 
-#ifndef __SMP__
-
-/*
- * The locking mechanism for base handlers, to prevent re-entrancy,
- * is entirely private to an implementation, it should not be
- * referenced at all outside of this file.
- */
-
-extern int __ppc_bh_counter;
 
 extern inline void start_bh_atomic(void)
 {
@@ -71,8 +72,80 @@
 #define softirq_trylock()	(__ppc_bh_counter? 0: ((__ppc_bh_counter=1),1))
 #define softirq_endlock()	(__ppc_bh_counter = 0)
 
-#else
-#error Nein, wir haben noch kein SMP
-#endif
+#else /* __SMP__ */
+
+extern atomic_t __sparc_bh_counter;
+
+#define start_bh_atomic() \
+	do { atomic_inc(&__sparc_bh_counter); synchronize_irq(); } while(0)
+
+#define end_bh_atomic()		atomic_dec(&__sparc_bh_counter)
+
+#include <asm/spinlock.h>
+
+extern spinlock_t global_bh_lock;
+
+#define init_bh(nr, routine)				\
+do {	unsigned long flags;				\
+	int ent = nr;					\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	bh_base[ent] = routine;				\
+	bh_mask_count[ent] = 0;				\
+	bh_mask |= 1 << ent;				\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#define remove_bh(nr)					\
+do {	unsigned long flags;				\
+	int ent = nr;					\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	bh_base[ent] = NULL;				\
+	bh_mask &= ~(1 << ent);				\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#define mark_bh(nr)					\
+do {	unsigned long flags;				\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	bh_active |= (1 << nr);				\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#define disable_bh(nr)					\
+do {	unsigned long flags;				\
+	int ent = nr;					\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	bh_mask &= ~(1 << ent);				\
+	bh_mask_count[ent]++;				\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#define enable_bh(nr)					\
+do {	unsigned long flags;				\
+	int ent = nr;					\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	if (!--bh_mask_count[ent])			\
+		bh_mask |= 1 << ent;			\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#define softirq_trylock()					\
+({								\
+	int ret = 1;						\
+	if(atomic_add_return(1, &__sparc_bh_counter) != 1) {	\
+		atomic_dec(&__sparc_bh_counter);		\
+		ret = 0;					\
+	}							\
+	ret;							\
+})
+#define softirq_endlock()	atomic_dec(&__sparc_bh_counter)
+#define clear_active_bhs(mask)				\
+do {	unsigned long flags;				\
+	spin_lock_irqsave(&global_bh_lock, flags);	\
+	bh_active &= ~(mask);				\
+	spin_unlock_irqrestore(&global_bh_lock, flags);	\
+} while(0)
+
+#endif /* __SMP__ */
 
 #endif
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/spinlock.h linux/include/asm-ppc/spinlock.h
--- v2.1.52/linux/include/asm-ppc/spinlock.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-ppc/spinlock.h	Thu Sep  4 12:54:49 1997
@@ -3,7 +3,7 @@
 
 #ifndef __SMP__
 
-typedef struct { int blah; } spinlock_t;
+typedef struct { } spinlock_t;
 #define SPIN_LOCK_UNLOCKED { }
 
 #define spin_lock_init(lock)	do { } while(0)
@@ -28,7 +28,7 @@
  * irq-safe write-lock, but readers can get non-irqsafe
  * read-locks.
  */
-typedef struct { int fred; } rwlock_t;
+typedef struct { } rwlock_t;
 #define RW_LOCK_UNLOCKED { }
 
 #define read_lock(lock)		do { } while(0)
@@ -49,7 +49,7 @@
 #define write_unlock_irqrestore(lock, flags) \
 	restore_flags(flags)
 
-#else
+#else /* __SMP__ */
 
 /* Simple spin lock operations.  There are two variants, one clears IRQ's
  * on the local processor, one does not.
@@ -57,22 +57,48 @@
  * We make no fairness assumptions. They have a cost.
  */
 
-typedef struct {
-	volatile unsigned int lock;
-	unsigned long previous;
-} spinlock_t;
+struct _spinlock_debug {
+	volatile unsigned long lock;
+	volatile unsigned long owner_pc;
+};
+
+typedef struct _spinlock_debug spinlock_t;
 
 #define SPIN_LOCK_UNLOCKED { 0, 0 }
 
-#define spin_unlock(lock)	((lock)->lock = 0)
+#define SPIN_LOCK_UNLOCKED	{ 0, 0 }
+#define spin_lock_init(lp)	do { (lp)->owner_pc = 0; (lp)->lock = 0; } while(0)
+#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;
-	__label__ l1;
 
-l1:	__asm__ __volatile__(
+	__asm__ __volatile__(
 		"	mtctr %2\n"
 		"1:	lwarx %0,0,%3\n"
 		"	andi. %1,%0,1\n\t"
@@ -83,12 +109,13 @@
 		: "=r" (tmp), "=r" (val)
 		: "r" (stuck), "r" (lock)
 		: "ctr");
-	if (!val) {
-		printk("spinlock stuck at %p (%lx)\n", &&l1, lock->previous);
-	} else
-		lock->previous = (unsigned long) &&l1;
+	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_lock_irq(lock) \
@@ -102,6 +129,59 @@
 
 #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;
+
+#define RW_LOCK_UNLOCKED { 0, 0 }
+
+extern void _read_lock(rwlock_t *rw);
+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)
+
 
 #endif /* SMP */
 #endif /* __ASM_SPINLOCK_H */
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v2.1.52/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.1.52/linux/include/asm-ppc/system.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/system.h	Thu Sep  4 12:54:49 1997
@@ -8,6 +8,8 @@
 
 #define __save_flags(flags)	({\
 	__asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); })
+#define __save_and_cli(flags)	({__save_flags(flags);__cli();})
+
 extern __inline__ void __restore_flags(unsigned long flags)
 {
         extern unsigned lost_interrupts;
@@ -80,27 +82,29 @@
 extern void dump_regs(struct pt_regs *);
 
 #ifndef __SMP__
+
 #define cli()	__cli()
 #define sti()	__sti()
 #define save_flags(flags)	__save_flags(flags)
 #define restore_flags(flags)	__restore_flags(flags)
 
-#else
-#error need global cli/sti etc. defined for SMP
-#endif
+#else /* __SMP__ */
 
-#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+extern void __global_cli(void);
+extern void __global_sti(void);
+extern unsigned long __global_save_flags(void);
+extern void __global_restore_flags(unsigned long);
+#define cli() __global_cli()
+#define sti() __global_sti()
+#define save_flags(x) ((x)=__global_save_flags())
+#define restore_flags(x) __global_restore_flags(x)
 
-/* this guy lives in arch/ppc/kernel */
-extern inline unsigned long *xchg_u32(void *m, unsigned long val);
+#endif /* !__SMP__ */
+
+#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
-/*
- *  these guys don't exist.
- *  someone should create them.
- *              -- Cort
- */
 extern void *xchg_u64(void *ptr, unsigned long val);
-extern int xchg_u8(char *m, char val);
+extern void *xchg_u32(void *m, unsigned long val);
 
 /*
  * This function doesn't exist, so you'll get a linker error
@@ -112,6 +116,9 @@
  */
 extern void __xchg_called_with_bad_pointer(void);
 
+#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+#define tas(ptr) (xchg((ptr),1))
+
 static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
 	switch (size) {
@@ -124,13 +131,6 @@
 	return x;
 
 
-}
-
-
-
-extern inline int tas(char * m)
-{
-	return xchg_u8(m,1);
 }
 
 extern inline void * xchg_ptr(void * m, void * val)
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc/elf.h linux/include/asm-sparc/elf.h
--- v2.1.52/linux/include/asm-sparc/elf.h	Sat Nov  9 12:16:59 1996
+++ linux/include/asm-sparc/elf.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.5 1996/08/08 00:06:13 ecd Exp $ */
+/* $Id: elf.h,v 1.7 1997/08/29 17:04:34 richard Exp $ */
 #ifndef __ASMSPARC_ELF_H
 #define __ASMSPARC_ELF_H
 
@@ -29,6 +29,13 @@
 
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	4096
+
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   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         (TASK_UNMAPPED_BASE + 0x1000000)
 
 
 #endif /* !(__ASMSPARC_ELF_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h
--- v2.1.52/linux/include/asm-sparc/namei.h	Thu Jul 17 10:06:08 1997
+++ linux/include/asm-sparc/namei.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.6 1997/07/17 02:24:25 davem Exp $
+/* $Id: namei.h,v 1.7 1997/08/29 15:52:27 jj Exp $
  * linux/include/asm-sparc/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -11,41 +11,31 @@
 #define SPARC_BSD_EMUL "usr/gnemul/sunos/"
 #define SPARC_SOL_EMUL "usr/gnemul/solaris/"
 
-#if 0 /* XXX FIXME */
-
-extern int __namei(int, const char *, struct inode *, char *, struct inode **,
-		   struct inode **, struct qstr *, struct dentry **, int *);
-
-static __inline__ int
-__prefix_namei(int retrieve_mode, const char * name, struct inode * base,
-	       char * buf, struct inode ** res_dir, struct inode ** res_inode,
-	       struct qstr * last_name, struct dentry ** last_entry,
-	       int * last_error)
+static inline struct dentry *
+__sparc_lookup_dentry(const char *name, int follow_link)
 {
 	int error;
+	struct dentry *base;
 
-	if (!(current->personality & (PER_BSD|PER_SVR4)))
-		return -ENOENT;
-
-	while (*name == '/')
-		name++;
-
-	atomic_inc(&current->fs->root->i_count);
-	error = __namei(NAM_FOLLOW_LINK,
-		        current->personality & PER_BSD ?
-		        SPARC_BSD_EMUL : SPARC_SOL_EMUL, current->fs->root,
-		        buf, NULL, &base, NULL, NULL, NULL);
-	if (error)
-		return error;
-
-	error = __namei(retrieve_mode, name, base, buf, res_dir, res_inode,
-			last_name, last_entry, last_error);
-	if (error)
-		return error;
-
-	return 0;
+	switch (current->personality) {
+	case PER_BSD:
+	case PER_SVR4:
+		break;
+	default:
+		return ERR_PTR(-ENOENT);
+	}
+
+	base = lookup_dentry ((current->personality == PER_BSD) ?
+			SPARC_BSD_EMUL : SPARC_SOL_EMUL,
+			dget (current->fs->root), 1);
+			
+	if (IS_ERR (base)) return base;
+	
+	return lookup_dentry (name, base, follow_link);
 }
 
-#endif /* XXX FIXME */
+#define __prefix_lookup_dentry(name, follow_link)				\
+	dentry = __sparc_lookup_dentry (name, follow_link);			\
+	if (!IS_ERR (dentry)) return dentry;
 
 #endif /* __SPARC_NAMEI_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/ebus.h linux/include/asm-sparc64/ebus.h
--- v2.1.52/linux/include/asm-sparc64/ebus.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/ebus.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: ebus.h,v 1.1 1997/08/12 04:13:12 ecd Exp $
+/* $Id: ebus.h,v 1.2 1997/08/17 22:40:07 ecd Exp $
  * ebus.h: PCI to Ebus pseudo driver software state.
  *
  * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -14,9 +14,9 @@
 	struct linux_ebus		*parent;
 	int				 prom_node;
 	char				 prom_name[64];
-	struct linux_prom_registers	 regs[PROMREG_MAX];
-	int				 num_registers;
-	struct linux_prom_irqs		 irqs[PROMINTR_MAX];
+	unsigned long			 base_address[PROMREG_MAX];
+	int				 num_addrs;
+	unsigned int			 irqs[PROMINTR_MAX];
 	int				 num_irqs;
 };
 	
@@ -24,18 +24,16 @@
 	struct linux_ebus		*next;
 	struct linux_ebus_device	*devices;
 	struct linux_pbm_info		*parent;
+	struct pci_dev			*self;
 	int				 prom_node;
 	char				 prom_name[64];
-	struct linux_prom_ranges	 ebus_ranges[PROMREG_MAX];
+	struct linux_prom_ebus_ranges	 ebus_ranges[PROMREG_MAX];
 	int				 num_ebus_ranges;
 };
 
 extern struct linux_ebus		*ebus_chain;
 
 extern unsigned long ebus_init(unsigned long, unsigned long);
-extern void prom_apply_ebus_ranges(struct linux_ebus *ebus,
-				   struct linux_prom_registers *regs,
-				   int nregs);
 
 #define for_each_ebus(bus)						\
         for((bus) = ebus_chain; (bus); (bus) = (bus)->next)
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- v2.1.52/linux/include/asm-sparc64/elf.h	Thu Jun 26 12:33:40 1997
+++ linux/include/asm-sparc64/elf.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.7 1997/06/14 21:28:07 davem Exp $ */
+/* $Id: elf.h,v 1.8 1997/08/21 18:09:07 richard Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
@@ -38,5 +38,14 @@
 
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	8192
+
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   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.  */
+
+#ifndef ELF_ET_DYN_BASE
+#define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)
+#endif
 
 #endif /* !(__ASM_SPARC64_ELF_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/fbio.h linux/include/asm-sparc64/fbio.h
--- v2.1.52/linux/include/asm-sparc64/fbio.h	Thu Jul 17 10:06:08 1997
+++ linux/include/asm-sparc64/fbio.h	Thu Sep  4 12:54:49 1997
@@ -36,6 +36,9 @@
 /* Does not seem to be listed in the Sun file either */
 #define FBTYPE_CREATOR          22
 
+#define FBTYPE_PCI_GENERIC	1000
+#define FBTYPE_PCI_MACH64	1001
+
 /* fbio ioctls */
 /* Returned by FBIOGTYPE */
 struct  fbtype {
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/ide.h linux/include/asm-sparc64/ide.h
--- v2.1.52/linux/include/asm-sparc64/ide.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/ide.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,263 @@
+/* $Id: ide.h,v 1.4 1997/08/30 16:29:29 davem Exp $
+ * ide.h: Ultra/PCI specific IDE glue.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC64_IDE_H
+#define _SPARC64_IDE_H
+
+#ifdef __KERNEL__
+
+typedef unsigned long ide_ioreg_t;
+
+#undef  MAX_HWIFS
+#define MAX_HWIFS	2
+
+extern int sparc64_ide_ports_known;
+extern ide_ioreg_t sparc64_ide_regbase[MAX_HWIFS];
+extern ide_ioreg_t sparc64_idedma_regbase; /* one for both channels */
+extern unsigned int sparc64_ide_irq;
+
+extern void sparc64_ide_probe(void);
+
+#define	ide_sti()	sti()
+
+static __inline__ int ide_default_irq(ide_ioreg_t base)
+{
+	if(sparc64_ide_ports_known == 0)
+		sparc64_ide_probe();
+	return sparc64_ide_irq;
+}
+
+static __inline__ ide_ioreg_t ide_default_io_base(int index)
+{
+	if(sparc64_ide_ports_known == 0)
+		sparc64_ide_probe();
+	return sparc64_ide_regbase[index];
+}
+
+static __inline__ void ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+	ide_ioreg_t port = base;
+	int i = 9;
+
+	while(i--)
+		*p++ = port++;
+	if(base == sparc64_ide_regbase[0])
+		*p = sparc64_idedma_regbase + 0x2;
+	else
+		*p = sparc64_idedma_regbase + 0xa;
+	if(irq != NULL)
+		*irq = sparc64_ide_irq;
+}
+
+typedef union {
+	unsigned int		all	: 8;	/* all of the bits together */
+	struct {
+		unsigned int	bit7	: 1;
+		unsigned int	lba	: 1;
+		unsigned int	bit5	: 1;
+		unsigned int	unit	: 1;
+		unsigned int	head	: 4;
+	} b;
+} select_t;
+
+static __inline__ int ide_request_irq(unsigned int irq,
+				      void (*handler)(int, void *, struct pt_regs *),
+				      unsigned long flags, const char *name, void *devid)
+{
+	return request_irq(irq, handler, SA_SHIRQ, name, devid);
+}
+
+static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
+{
+	free_irq(irq, dev_id);
+}
+
+static __inline__ int ide_check_region(ide_ioreg_t base, unsigned int size)
+{
+	return check_region(base, size);
+}
+
+static __inline__ void ide_request_region(ide_ioreg_t base, unsigned int size,
+					  const char *name)
+{
+	request_region(base, size, name);
+}
+
+static __inline__ void ide_release_region(ide_ioreg_t base, unsigned int size)
+{
+	release_region(base, size);
+}
+
+#undef  SUPPORT_SLOW_DATA_PORTS
+#define SUPPORT_SLOW_DATA_PORTS 0
+
+#undef  SUPPORT_VLB_SYNC
+#define SUPPORT_VLB_SYNC 0
+
+#undef  HD_DATA
+#define HD_DATA ((ide_ioreg_t)0)
+
+static __inline__ int ide_ack_intr(ide_ioreg_t status_port, ide_ioreg_t irq_port)
+{
+	unsigned char stat = inb(irq_port);
+
+	if(stat & 0x4) {
+		outb((inb(irq_port) & 0x60) | 4, irq_port);
+		return 1;
+	}
+	return 0;
+}
+
+/* From m68k code... */
+
+#define insl(data_reg, buffer, wcount) insw(data_reg, buffer, (wcount)<<1)
+#define outsl(data_reg, buffer, wcount) outsw(data_reg, buffer, (wcount)<<1)
+
+#define insw(port, buf, nr) ide_insw((port), (buf), (nr))
+#define outsw(port, buf, nr) ide_outsw((port), (buf), (nr))
+
+static __inline__ void ide_insw(unsigned long port,
+				void *dst,
+				unsigned long count)
+{
+	volatile unsigned short *data_port;
+	u16 *ps = dst;
+	u32 *pi;
+
+	data_port = (volatile unsigned short *)port;
+
+	if(((u64)ps) & 0x2) {
+		*ps++ = *data_port;
+		count--;
+	}
+	pi = (u32 *)ps;
+	while(count >= 2) {
+		u32 w;
+
+		w  = (*data_port) << 16;
+		w |= (*data_port);
+		*pi++ = w;
+		count -= 2;
+	}
+	ps = (u16 *)pi;
+	if(count)
+		*ps = *data_port;
+}
+
+static __inline__ void ide_outsw(unsigned long port,
+				 const void *src,
+				 unsigned long count)
+{
+	volatile unsigned short *data_port;
+	const u16 *ps = src;
+	const u32 *pi;
+
+	data_port = (volatile unsigned short *)port;
+
+	if(((u64)src) & 0x2) {
+		*data_port = *ps++;
+		count--;
+	}
+	pi = (const u32 *)ps;
+	while(count >= 2) {
+		u32 w;
+
+		w = *pi++;
+		*data_port = (w >> 16);
+		*data_port = w;
+		count -= 2;
+	}
+	ps = (const u16 *)pi;
+	if(count)
+		*data_port = *ps;
+}
+
+#define T_CHAR          (0x0000)        /* char:  don't touch  */
+#define T_SHORT         (0x4000)        /* short: 12 -> 21     */
+#define T_INT           (0x8000)        /* int:   1234 -> 4321 */
+#define T_TEXT          (0xc000)        /* text:  12 -> 21     */
+
+#define T_MASK_TYPE     (0xc000)
+#define T_MASK_COUNT    (0x3fff)
+
+#define D_CHAR(cnt)     (T_CHAR  | (cnt))
+#define D_SHORT(cnt)    (T_SHORT | (cnt))
+#define D_INT(cnt)      (T_INT   | (cnt))
+#define D_TEXT(cnt)     (T_TEXT  | (cnt))
+
+static u_short driveid_types[] = {
+	D_SHORT(10),	/* config - vendor2 */
+	D_TEXT(20),	/* serial_no */
+	D_SHORT(3),	/* buf_type - ecc_bytes */
+	D_TEXT(48),	/* fw_rev - model */
+	D_CHAR(2),	/* max_multsect - vendor3 */
+	D_SHORT(1),	/* dword_io */
+	D_CHAR(2),	/* vendor4 - capability */
+	D_SHORT(1),	/* reserved50 */
+	D_CHAR(4),	/* vendor5 - tDMA */
+	D_SHORT(4),	/* field_valid - cur_sectors */
+	D_INT(1),	/* cur_capacity */
+	D_CHAR(2),	/* multsect - multsect_valid */
+	D_INT(1),	/* lba_capacity */
+	D_SHORT(194)	/* dma_1word - reservedyy */
+};
+
+#define num_driveid_types       (sizeof(driveid_types)/sizeof(*driveid_types))
+
+static __inline__ void ide_fix_driveid(struct hd_driveid *id)
+{
+	u_char *p = (u_char *)id;
+	int i, j, cnt;
+	u_char t;
+
+	for (i = 0; i < num_driveid_types; i++) {
+		cnt = driveid_types[i] & T_MASK_COUNT;
+		switch (driveid_types[i] & T_MASK_TYPE) {
+		case T_CHAR:
+			p += cnt;
+			break;
+		case T_SHORT:
+			for (j = 0; j < cnt; j++) {
+				t = p[0];
+				p[0] = p[1];
+				p[1] = t;
+				p += 2;
+			}
+			break;
+		case T_INT:
+			for (j = 0; j < cnt; j++) {
+				t = p[0];
+				p[0] = p[3];
+				p[3] = t;
+				t = p[1];
+				p[1] = p[2];
+				p[2] = t;
+				p += 4;
+			}
+			break;
+		case T_TEXT:
+			for (j = 0; j < cnt; j += 2) {
+				t = p[0];
+				p[0] = p[1];
+				p[1] = t;
+				p += 2;
+			}
+			break;
+		};
+	}
+}
+
+static __inline__ void ide_release_lock (int *ide_lock)
+{
+}
+
+static __inline__ void ide_get_lock (int *ide_lock, void (*handler)(int, void *, struct pt_regs *), void *data)
+{
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _SPARC64_IDE_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- v2.1.52/linux/include/asm-sparc64/io.h	Mon Apr 14 16:28:22 1997
+++ linux/include/asm-sparc64/io.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.10 1997/04/10 05:13:29 davem Exp $ */
+/* $Id: io.h,v 1.12 1997/08/19 03:11:52 davem Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -7,83 +7,122 @@
 
 #include <asm/page.h>      /* IO address mapping routines need this */
 #include <asm/system.h>
+#include <asm/asi.h>
 
-extern __inline__ unsigned long inb_local(unsigned long addr)
-{
-	return 0;
-}
+/* PC crapola... */
+#define __SLOW_DOWN_IO	do { } while (0)
+#define SLOW_DOWN_IO	do { } while (0)
 
-extern __inline__ void outb_local(unsigned char b, unsigned long addr)
+extern __inline__ unsigned long virt_to_phys(volatile void *addr)
 {
-	return;
+	return ((((unsigned long)addr) - PAGE_OFFSET) | 0x80000000UL);
 }
 
-extern __inline__ unsigned long inb(unsigned long addr)
+extern __inline__ void *phys_to_virt(unsigned long addr)
 {
-	return 0;
+	return ((void *)((addr & ~0x80000000) + PAGE_OFFSET));
 }
 
-extern __inline__ unsigned long inw(unsigned long addr)
-{
-	return 0;
-}
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
 
-extern __inline__ unsigned long inl(unsigned long addr)
+extern __inline__ unsigned int inb(unsigned long addr)
 {
-	return 0;
-}
+	unsigned int ret;
 
-extern __inline__ void outb(unsigned char b, unsigned long addr)
-{
-	return;
-}
+	__asm__ __volatile__("lduba [%1] %2, %0"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_PL));
 
-extern __inline__ void outw(unsigned short b, unsigned long addr)
-{
-	return;
+	return ret;
 }
 
-extern __inline__ void outl(unsigned int b, unsigned long addr)
+extern __inline__ unsigned int inw(unsigned long addr)
 {
-	return;
-}
+	unsigned int ret;
 
-/*
- * Memory functions
- */
-extern __inline__ unsigned long readb(unsigned long addr)
-{
-	return 0;
-}
+	__asm__ __volatile__("lduha [%1] %2, %0"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_PL));
 
-extern __inline__ unsigned long readw(unsigned long addr)
-{
-	return 0;
+	return ret;
 }
 
-extern __inline__ unsigned long readl(unsigned long addr)
+extern __inline__ unsigned int inl(unsigned long addr)
 {
-	return 0;
+	unsigned int ret;
+
+	__asm__ __volatile__("lduwa [%1] %2, %0"
+			     : "=r" (ret)
+			     : "r" (addr), "i" (ASI_PL));
+
+	return ret;
 }
 
-extern __inline__ void writeb(unsigned short b, unsigned long addr)
+extern __inline__ void outb(unsigned char b, unsigned long addr)
 {
-	return;
+	__asm__ __volatile__("stba %0, [%1] %2"
+			     : /* no outputs */
+			     : "r" (b), "r" (addr), "i" (ASI_PL));
 }
 
-extern __inline__ void writew(unsigned short b, unsigned long addr)
+extern __inline__ void outw(unsigned short w, unsigned long addr)
 {
-	return;
+	__asm__ __volatile__("stha %0, [%1] %2"
+			     : /* no outputs */
+			     : "r" (w), "r" (addr), "i" (ASI_PL));
 }
 
-extern __inline__ void writel(unsigned int b, unsigned long addr)
+extern __inline__ void outl(unsigned int l, unsigned long addr)
 {
-	return;
+	__asm__ __volatile__("stwa %0, [%1] %2"
+			     : /* no outputs */
+			     : "r" (l), "r" (addr), "i" (ASI_PL));
 }
 
 #define inb_p inb
 #define outb_p outb
 
+extern void outsb(unsigned long addr, const void *src, unsigned long count);
+extern void outsw(unsigned long addr, const void *src, unsigned long count);
+extern void outsl(unsigned long addr, const void *src, unsigned long count);
+extern void insb(unsigned long addr, void *dst, unsigned long count);
+extern void insw(unsigned long addr, void *dst, unsigned long count);
+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))
+
+/* Memcpy to/from I/O space is just a regular memory operation on Ultra as well. */
+
+#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
+
+static inline int check_signature(unsigned long io_addr,
+				  const unsigned char *signature,
+				  int length)
+{
+	int retval = 0;
+	do {
+		if (readb(io_addr++) != *signature++)
+			goto out;
+	} while (--length);
+	retval = 1;
+out:
+	return retval;
+}
+
 extern void sparc_ultra_mapioaddr   (unsigned long physaddr, unsigned long virt_addr,
 				     int bus, int rdonly);
 extern void sparc_ultra_unmapioaddr (unsigned long virt_addr);
@@ -102,8 +141,5 @@
 extern void *sparc_alloc_io (u32 pa, void *va, int sz, char *name, u32 io, int rdonly);
 extern void sparc_free_io (void *va, int sz);
 extern void *sparc_dvma_malloc (int sz, char *name, __u32 *dvma_addr);
-
-#define virt_to_phys(x) __pa((unsigned long)(x))
-#define phys_to_virt(x) __va((unsigned long)(x))
 
 #endif /* !(__SPARC64_IO_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/keyboard.h linux/include/asm-sparc64/keyboard.h
--- v2.1.52/linux/include/asm-sparc64/keyboard.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/keyboard.h	Thu Sep  4 12:54:49 1997
@@ -0,0 +1,43 @@
+/* $Id: keyboard.h,v 1.1 1997/09/04 05:50:39 ecd Exp $
+ * linux/include/asm-sparc64/keyboard.h
+ *
+ * Created Aug 29 1997 by Eddie C. Dost (ecd@skynet.be)
+ */
+
+/*
+ *  This file contains the Ultra/PCI architecture specific keyboard definitions
+ */
+
+#ifndef _SPARC64_KEYBOARD_H
+#define _SPARC64_KEYBOARD_H 1
+
+#ifdef __KERNEL__
+
+#define KEYBOARD_IRQ			1
+#define DISABLE_KBD_DURING_INTERRUPTS	0
+
+extern int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode);
+extern int pcikbd_getkeycode(unsigned int scancode);
+extern int pcikbd_pretranslate(unsigned char scancode, char raw_mode);
+extern int pcikbd_translate(unsigned char scancode, unsigned char *keycode,
+			    char raw_mode);
+extern char pcikbd_unexpected_up(unsigned char keycode);
+extern void pcikbd_leds(unsigned char leds);
+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 SYSRQ_KEY 0x54
+
+#endif /* __KERNEL__ */
+
+#endif /* !(_SPARC64_KEYBOARD_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/linux_logo.h linux/include/asm-sparc64/linux_logo.h
--- v2.1.52/linux/include/asm-sparc64/linux_logo.h	Tue May 13 22:41:18 1997
+++ linux/include/asm-sparc64/linux_logo.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.1 1997/04/16 17:51:37 jj Exp $
+/* $Id: linux_logo.h,v 1.3 1997/08/25 07:50:35 jj Exp $
  * include/asm-sparc64/linux_logo.h: This is a linux logo
  *                                   to be displayed on boot.
  *
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h
--- v2.1.52/linux/include/asm-sparc64/namei.h	Thu Jul 17 10:06:08 1997
+++ linux/include/asm-sparc64/namei.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.5 1997/07/17 02:24:28 davem Exp $
+/* $Id: namei.h,v 1.7 1997/09/04 15:46:31 jj Exp $
  * linux/include/asm-sparc64/namei.h
  *
  * Routines to handle famous /usr/gnemul/s*.
@@ -11,40 +11,40 @@
 #define SPARC_BSD_EMUL "usr/gnemul/sunos/"
 #define SPARC_SOL_EMUL "usr/gnemul/solaris/"
 
-#if 0 /* XXX FIXME */
-extern int __namei(int, const char *, struct inode *, char *, struct inode **,
-		   struct inode **, struct qstr *, struct dentry **, int *);
-
-static inline int
-__prefix_namei(int retrieve_mode, const char * name, struct inode * base,
-	       char * buf, struct inode ** res_dir, struct inode ** res_inode,
-	       struct qstr * last_name, struct dentry ** last_entry,
-	       int * last_error)
+static inline struct dentry *
+__sparc64_lookup_dentry(const char *name, int follow_link)
 {
 	int error;
+	struct dentry *base;
 
-	if (!(current->personality & (PER_BSD|PER_SVR4)))
-		return -ENOENT;
-
-	while (*name == '/')
-		name++;
-
-	atomic_inc(&current->fs->root->i_count);
-	error = __namei(NAM_FOLLOW_LINK,
-		        current->personality & PER_BSD ?
-		        SPARC_BSD_EMUL : SPARC_SOL_EMUL, current->fs->root,
-		        buf, NULL, &base, NULL, NULL, NULL);
-	if (error)
-		return error;
-
-	error = __namei(retrieve_mode, name, base, buf, res_dir, res_inode,
-			last_name, last_entry, last_error);
-	if (error)
-		return error;
-
-	return 0;
+	switch (current->personality) {
+	case PER_BSD:
+	case PER_SVR4:
+		break;
+	default:
+		return ERR_PTR(-ENOENT);
+	}
+
+	base = lookup_dentry ((current->personality == PER_BSD) ?
+			SPARC_BSD_EMUL : SPARC_SOL_EMUL,
+			dget (current->fs->root), 1);
+			
+	if (IS_ERR (base)) return base;
+	
+	base = lookup_dentry (name, base, follow_link);
+	
+	if (IS_ERR (base)) return base;
+	
+	if (!base->d_inode) {
+		dput(base);
+		return ERR_PTR(-ENOENT);
+	}
+	
+	return base;
 }
 
-#endif /* XXX FIXME */
+#define __prefix_lookup_dentry(name, follow_link)				\
+	dentry = __sparc64_lookup_dentry (name, follow_link);			\
+	if (!IS_ERR (dentry)) return dentry;
 
 #endif /* __SPARC64_NAMEI_H */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/openprom.h linux/include/asm-sparc64/openprom.h
--- v2.1.52/linux/include/asm-sparc64/openprom.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/openprom.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: openprom.h,v 1.5 1997/08/15 06:44:51 davem Exp $ */
+/* $Id: openprom.h,v 1.6 1997/08/17 22:40:09 ecd Exp $ */
 #ifndef __SPARC64_OPENPROM_H
 #define __SPARC64_OPENPROM_H
 
@@ -230,6 +230,17 @@
 
 	unsigned int size_hi;
 	unsigned int size_lo;
+};
+
+struct linux_prom_ebus_ranges {
+	unsigned int child_phys_hi;
+	unsigned int child_phys_lo;
+
+	unsigned int parent_phys_hi;
+	unsigned int parent_phys_mid;
+	unsigned int parent_phys_lo;
+
+	unsigned int size;
 };
 
 #endif /* !(__ASSEMBLY__) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/pbm.h linux/include/asm-sparc64/pbm.h
--- v2.1.52/linux/include/asm-sparc64/pbm.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/pbm.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: pbm.h,v 1.4 1997/08/15 06:44:52 davem Exp $
+/* $Id: pbm.h,v 1.7 1997/08/25 06:01:14 davem Exp $
  * pbm.h: U2P PCI bus module pseudo driver software state.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -13,12 +13,28 @@
 #include <asm/psycho.h>
 #include <asm/oplib.h>
 
+struct linux_pbm_info;
+
+/* This is what we use to determine what the PROM has assigned so
+ * far, so that we can perform assignments for addresses which
+ * were not taken care of by OBP.  See psycho.c for details.
+ * Per-PBM these are ordered by start address.
+ */
+struct pci_vma {
+	struct pci_vma			*next;
+	struct linux_pbm_info		*pbm;
+	unsigned int			start;
+	unsigned int			end;
+	unsigned int			base_reg;
+	unsigned int			_pad;
+};
+
 struct linux_psycho;
 
 struct linux_pbm_info {
 	struct linux_psycho		*parent;
-	unsigned long			*pbm_IO;
-	unsigned long			*pbm_mem;
+	struct pci_vma			*IO_assignments;
+	struct pci_vma			*MEM_assignments;
 	int				prom_node;
 	char				prom_name[64];
 	struct linux_prom_pci_ranges	pbm_ranges[PROMREG_MAX];
@@ -41,6 +57,45 @@
 	struct linux_pbm_info		pbm_B;
 };
 
+/* PCI devices which are not bridges have this placed in their pci_dev
+ * sysdata member.  This makes OBP aware PCI device drivers easier to
+ * code.
+ */
+struct pcidev_cookie {
+	struct linux_pbm_info		*pbm;
+	int				prom_node;
+};
+
 extern struct linux_psycho *psycho_root;
+
+/* 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,
+ * fhc, and dcookies.
+ */
+#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_IGN		0x000007c0	/* PSYCHO "Int Group Number"           */
+#define PCI_IRQ_INO		0x0000003f	/* PSYCHO INO                          */
+
+#define PCI_IRQ_P(__irq)	(((__irq) & PCI_IRQ_IDENT) != 0)
+
+extern __inline__ unsigned int pci_irq_encode(unsigned long imap_off,
+					      unsigned long psycho_instance,
+					      unsigned long ign,
+					      unsigned long ino)
+{
+	unsigned int irq;
+
+	irq  = PCI_IRQ_IDENT;
+	irq |= ((imap_off << PCI_IRQ_IMAP_OFF_SHFT) & PCI_IRQ_IMAP_OFF);
+	irq |= ((psycho_instance << PCI_IRQ_BUSNO_SHFT) & PCI_IRQ_BUSNO);
+	irq |= ((ign << 6) & PCI_IRQ_IGN);
+	irq |= (ino & PCI_IRQ_INO);
+
+	return irq;
+}
 
 #endif /* !(__SPARC64_PBM_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.1.52/linux/include/asm-sparc64/processor.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc64/processor.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.32 1997/07/01 21:59:38 davem Exp $
+/* $Id: processor.h,v 1.33 1997/08/19 14:18:36 jj Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -55,10 +55,13 @@
 
 #endif /* !(__ASSEMBLY__) */
 
-#define SPARC_FLAG_KTHREAD      0x1    /* task is a kernel thread */
-#define SPARC_FLAG_UNALIGNED    0x2    /* is allowed to do unaligned accesses */
-#define SPARC_FLAG_NEWSIGNALS   0x4    /* task wants new-style signals */
-#define SPARC_FLAG_32BIT        0x8    /* task is older 32-bit binary */
+#define SPARC_FLAG_USEDFPUL	0x01    /* Used f0-f31 */
+#define SPARC_FLAG_USEDFPUU	0x02    /* Used f32-f62 */
+#define SPARC_FLAG_USEDFPU	0x04    /* If ever FEF bit was set while TSTATE_PEF */
+#define SPARC_FLAG_KTHREAD      0x10    /* task is a kernel thread */
+#define SPARC_FLAG_UNALIGNED    0x20    /* is allowed to do unaligned accesses */
+#define SPARC_FLAG_NEWSIGNALS   0x40    /* task wants new-style signals */
+#define SPARC_FLAG_32BIT        0x80    /* task is older 32-bit binary */
 
 #define INIT_MMAP { &init_mm, 0xfffff80000000000, 0xfffff80001000000, \
 		    PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
@@ -89,7 +92,7 @@
 /* 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|TSTATE_PEF); \
+	regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE); \
 	regs->tpc = ((pc & (~3)) - 4); \
 	regs->tnpc = regs->tpc + 4; \
 	regs->y = 0; \
@@ -125,7 +128,7 @@
 	pc &= 0x00000000ffffffffUL; \
 	sp &= 0x00000000ffffffffUL; \
 \
-	regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM|TSTATE_PEF); \
+	regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM); \
 	regs->tpc = ((pc & (~3)) - 4); \
 	regs->tnpc = regs->tpc + 4; \
 	regs->y = 0; \
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/sab82532.h linux/include/asm-sparc64/sab82532.h
--- v2.1.52/linux/include/asm-sparc64/sab82532.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/sab82532.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: sab82532.h,v 1.2 1997/08/12 04:13:15 ecd Exp $
+/* $Id: sab82532.h,v 1.3 1997/09/03 11:55:04 ecd Exp $
  * sab82532.h: Register Definitions for the Siemens SAB82532 DUSCC
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
@@ -122,11 +122,10 @@
 #define NR_PORTS			 2
 
 union sab82532_irq_status {
-	unsigned long			 stat;
+	unsigned short			 stat;
 	struct {
 		unsigned char		 isr0;
 		unsigned char		 isr1;
-		unsigned char		 ddsr;
 	} sreg;
 };
 
@@ -142,6 +141,7 @@
 	int				 ignore_status_mask;
 	int				 timeout;
 	int				 xmit_fifo_size;
+	int				 recv_fifo_size;
 	int				 custom_divisor;
 	int				 quot;
 	int				 x_char;
@@ -154,6 +154,9 @@
 	unsigned char			 interrupt_mask1;
 	unsigned char			 pvr_dtr_bit;
 	unsigned char			 pvr_dsr_bit;
+	unsigned char			 dcd;
+	unsigned char			 cts;
+	unsigned char			 dsr;
 	unsigned long			 event;
 	unsigned long			 last_active;
 	int				 line;
@@ -338,22 +341,23 @@
 
 /* Interrupt Mask Register 0 (IMR0) */
 #define SAB82532_IMR0_TCD		0x80
+#define SAB82532_IMR0_TIME		0x40
 #define SAB82532_IMR0_PERR		0x20
-#define SAB82532_IMR0_SCD		0x10
+#define SAB82532_IMR0_FERR		0x10
 #define SAB82532_IMR0_PLLA		0x08
 #define SAB82532_IMR0_CDSC		0x04
 #define SAB82532_IMR0_RFO		0x02
 #define SAB82532_IMR0_RPF		0x01
-#define SAB82532_IMR0_OR_ME_IN		0x40
 
 /* Interrupt Mask Register 1 (IMR1) */
+#define SAB82532_IMR1_BRK		0x80
+#define SAB82532_IMR1_BRKT		0x40
 #define SAB82532_IMR1_ALLS		0x20
-#define SAB82532_IMR1_XDU		0x10
+#define SAB82532_IMR1_XOFF		0x10
 #define SAB82532_IMR1_TIN		0x08
 #define SAB82532_IMR1_CSC		0x04
-#define SAB82532_IMR1_XMR		0x02
+#define SAB82532_IMR1_XON		0x02
 #define SAB82532_IMR1_XPR		0x01
-#define SAB82532_IMR1_OR_ME_IN		0xc0
 
 /* Port Interrupt Status Register (PIS) */
 #define SAB82532_PIS_SYNC_B		0x08
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/sigcontext.h linux/include/asm-sparc64/sigcontext.h
--- v2.1.52/linux/include/asm-sparc64/sigcontext.h	Mon Jul  7 08:18:56 1997
+++ linux/include/asm-sparc64/sigcontext.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: sigcontext.h,v 1.8 1997/06/20 11:54:41 davem Exp $ */
+/* $Id: sigcontext.h,v 1.9 1997/08/19 14:18:38 jj Exp $ */
 #ifndef __SPARC64_SIGCONTEXT_H
 #define __SPARC64_SIGCONTEXT_H
 
@@ -31,7 +31,6 @@
 	int sigc_oswins;       /* outstanding windows */
 
 	/* stack ptrs for each regwin buf */
-	/* XXX 32-bit ptrs pinhead... */
 	unsigned sigc_spbuf[SUNOS_MAXWIN];
 
 	/* Windows to restore after signal */
@@ -75,11 +74,7 @@
 	unsigned   int si_float_regs [64];
 	unsigned   long si_fsr;
 	unsigned   long si_gsr;
-	unsigned   int si_fpqdepth;
-	struct {
-		unsigned int *insn_addr;
-		unsigned int insn;
-	} si_fpqueue [16];
+	unsigned   long si_fprs;
 } __siginfo_fpu_t;
 
 #endif /* !(__ASSEMBLY__) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/svr4.h linux/include/asm-sparc64/svr4.h
--- v2.1.52/linux/include/asm-sparc64/svr4.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/svr4.h	Thu Sep  4 12:54:49 1997
@@ -43,15 +43,13 @@
 #define SVR4_MAXWIN 31
 
 typedef struct {
-	uint rwin_lo[8];
-	uint rwin_in[8];
+	u32 rwin_lo[8];
+	u32 rwin_in[8];
 } svr4_rwindow_t;
 
 typedef struct {
 	int            count;
-
-	/* XXX 32-bit pointers on 64-bit kernel... fixme XXX */
-	int            *winptr [SVR4_MAXWIN]; /* pointer to the windows */
+	u32            winptr [SVR4_MAXWIN]; /* pointer to the windows */
 
 	svr4_rwindow_t win[SVR4_MAXWIN];      /* the windows */
 } svr4_gwindows_t;
@@ -59,28 +57,23 @@
 typedef int svr4_gregset_t[SVR4_NREGS];
 
 typedef struct {
-	double   fpu_regs[32];
-	void     *fp_q;
-	unsigned fp_fsr;
+	u64   	 fpu_regs[32];
+	u32	 fp_q;
+	u32      fp_fsr;
 	u_char   fp_nqel;
 	u_char   fp_nqsize;
 	u_char   inuse;		/* if fpu is in use */
 } svr4_fregset_t;
 
 typedef struct {
-	uint    id;		/* if this holds "xrs" string => ptr is valid */
-
-	/* XXX what is caddr_t on sparc64?? XXX */
-	caddr_t ptr;
+	u32    id;		/* if this holds "xrs" string => ptr is valid */
+	u32    ptr;
 } svr4_xrs_t;
 
 /* Machine dependant context */
 typedef struct {
 	svr4_gregset_t   greg;	/* registers 0..19 (see top) */
-
-	/* XXX 32-bit pointers again... fixme XXX */
-	svr4_gwindows_t  *gwin;	/* may point to register windows */
-
+	u32		 gwin;	/* may point to register windows */
 	svr4_fregset_t   freg;	/* floating point registers */
 	svr4_xrs_t       xrs;	/* mhm? */
 	int              pad[19];
@@ -101,15 +94,12 @@
 
 /* Context used by getcontext and setcontext */
 typedef struct svr4_ucontext_t {
-	u_int                flags; /* context flags, indicate what is loaded */
-
-	/* XXX 32-bit pointer... fixme XXX */
-	struct svr4_ucontext *link;
-
-	svr4_sigset_t        sigmask;
-	svr4_stack_t         stack;
-	svr4_mcontext_t      mcontext;
-	int                  pad[23];
+	u32		flags; /* context flags, indicate what is loaded */
+	u32		link;
+	svr4_sigset_t	sigmask;
+	svr4_stack_t	stack;
+	svr4_mcontext_t	mcontext;
+	int		pad[23];
 } svr4_ucontext_t;                          
 
 /* windows hold the windows as they were at signal time,
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/sysio.h linux/include/asm-sparc64/sysio.h
--- v2.1.52/linux/include/asm-sparc64/sysio.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/sysio.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: sysio.h,v 1.6 1997/08/15 06:44:53 davem Exp $
+/* $Id: sysio.h,v 1.7 1997/08/18 03:47:26 davem Exp $
  * sysio.h: UltraSparc sun5 specific SBUS definitions.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -280,7 +280,7 @@
 #define SYSIO_SBAFSR_MID	0x000003e000000000 /* MID causing the error        */
 #define SYSIO_SBAFSR_RESV3	0x0000001fffffffff /* Reserved                     */
 
-/* SYSIO SBUS Slot Configuration Register */
+/* SYSIO SBUS Slot Configuration Register(s) */
 #define SYSIO_SBSCFG_RESV1	0xfffffffff8000000 /* Reserved                     */
 #define SYSIO_SBSCFG_SADDR	0x0000000007ff0000 /* Segment Address (PA[40:30])  */
 #define SYSIO_SBSCFG_CP		0x0000000000008000 /* Bypasses are cacheable       */
@@ -326,14 +326,115 @@
 /* SYSIO Interrupt Retry Timer register. */
 #define SYSIO_IRETRY_LIMIT	0x000000ff	/* The retry interval.		   */
 
-/* SYSIO Interrupt State registers. XXX fields to be documented later */
+/* SYSIO Interrupt State registers. */
+#define SYSIO_ISTATE_IDLE	0x0 /* No interrupt received or pending */
+#define SYSIO_ISTATE_TRANSMIT	0x1 /* Received, but IRQ not dispatched */
+#define SYSIO_ISTATE_ILLEGAL	0x2 /* Impossible state                 */
+#define SYSIO_ISTATE_PENDING	0x3 /* Received and dispatched          */
+
+/* Two ways to get at the right bits, your choice... note that level
+ * zero is illegal.  For slots 0 --> 3 the formula for the bit range
+ * in the register is:
+ *
+ *	LSB	((SBUS_SLOT X 16) + (SBUS_LEVEL X 2))
+ *	MSB	((SBUS_SLOT X 16) + (SBUS_LEVEL X 2)) + 1
+ *
+ * Thus the following macro.
+ */
+#define SYSIO_SBUS_ISTATE(regval, slot, level)	\
+	(((regval) >> (((slot) * 16) + ((level) * 2))) & 0x3)
 
-/* SYSIO Counter register. XXX fields to be documented later */
+#define SYSIO_SBUS_ISTATE_S0L1	0x000000000000000c /* Slot 0, level 1 */
+#define SYSIO_SBUS_ISTATE_S0L2	0x0000000000000030 /* Slot 0, level 2 */
+#define SYSIO_SBUS_ISTATE_S0L3	0x00000000000000c0 /* Slot 0, level 3 */
+#define SYSIO_SBUS_ISTATE_S0L4	0x0000000000000300 /* Slot 0, level 4 */
+#define SYSIO_SBUS_ISTATE_S0L5	0x0000000000000c00 /* Slot 0, level 5 */
+#define SYSIO_SBUS_ISTATE_S0L6	0x0000000000003000 /* Slot 0, level 6 */
+#define SYSIO_SBUS_ISTATE_S0L7	0x000000000000c000 /* Slot 0, level 7 */
+#define SYSIO_SBUS_ISTATE_S1L1	0x00000000000c0000 /* Slot 1, level 1 */
+#define SYSIO_SBUS_ISTATE_S1L2	0x0000000000300000 /* Slot 1, level 2 */
+#define SYSIO_SBUS_ISTATE_S1L3	0x0000000000c00000 /* Slot 1, level 3 */
+#define SYSIO_SBUS_ISTATE_S1L4	0x0000000003000000 /* Slot 1, level 4 */
+#define SYSIO_SBUS_ISTATE_S1L5	0x000000000c000000 /* Slot 1, level 5 */
+#define SYSIO_SBUS_ISTATE_S1L6	0x0000000030000000 /* Slot 1, level 6 */
+#define SYSIO_SBUS_ISTATE_S1L7	0x00000000c0000000 /* Slot 1, level 7 */
+#define SYSIO_SBUS_ISTATE_S2L1	0x0000000c00000000 /* Slot 2, level 1 */
+#define SYSIO_SBUS_ISTATE_S2L2	0x0000003000000000 /* Slot 2, level 2 */
+#define SYSIO_SBUS_ISTATE_S2L3	0x000000c000000000 /* Slot 2, level 3 */
+#define SYSIO_SBUS_ISTATE_S2L4	0x0000030000000000 /* Slot 2, level 4 */
+#define SYSIO_SBUS_ISTATE_S2L5	0x00000c0000000000 /* Slot 2, level 5 */
+#define SYSIO_SBUS_ISTATE_S2L6	0x0000300000000000 /* Slot 2, level 6 */
+#define SYSIO_SBUS_ISTATE_S2L7	0x0000c00000000000 /* Slot 2, level 7 */
+#define SYSIO_SBUS_ISTATE_S3L1	0x000c000000000000 /* Slot 3, level 1 */
+#define SYSIO_SBUS_ISTATE_S3L2	0x0030000000000000 /* Slot 3, level 2 */
+#define SYSIO_SBUS_ISTATE_S3L3	0x00c0000000000000 /* Slot 3, level 3 */
+#define SYSIO_SBUS_ISTATE_S3L4	0x0300000000000000 /* Slot 3, level 4 */
+#define SYSIO_SBUS_ISTATE_S3L5	0x0c00000000000000 /* Slot 3, level 5 */
+#define SYSIO_SBUS_ISTATE_S3L6	0x3000000000000000 /* Slot 3, level 6 */
+#define SYSIO_SBUS_ISTATE_S3L7	0xc000000000000000 /* Slot 3, level 7 */
 
-/* SYSIO Limit register. XXX fields to be documented later */
+/* For OBIO devices things are a bit different, you just have to know what
+ * you are looking for.
+ */
+#define SYSIO_OBIO_ISTATE_SCSI	0x0000000000000003 /* Scsi		*/
+#define SYSIO_OBIO_ISTATE_ETH	0x000000000000000c /* Ethernet		*/
+#define SYSIO_OBIO_ISTATE_PP	0x0000000000000030 /* Parallel Port	*/
+#define SYSIO_OBIO_ISTATE_AUDIO	0x00000000000000c0 /* Sun Audio		*/
+#define SYSIO_OBIO_ISTATE_PFAIL	0x0000000000000300 /* Power Fail	*/
+#define SYSIO_OBIO_ISTATE_KBMS	0x0000000000000c00 /* kbd/mouse/serial	*/
+#define SYSIO_OBIO_ISTATE_FLPY	0x0000000000003000 /* Floppy Controller	*/
+#define SYSIO_OBIO_ISTATE_SPHW	0x000000000000c000 /* Spare HW		*/
+#define SYSIO_OBIO_ISTATE_KBD	0x0000000000030000 /* Keyboard		*/
+#define SYSIO_OBIO_ISTATE_MS	0x00000000000c0000 /* Mouse		*/
+#define SYSIO_OBIO_ISTATE_SER	0x0000000000300000 /* Serial		*/
+#define SYSIO_OBIO_ISTATE_TIM0	0x0000000000c00000 /* Timer 0		*/
+#define SYSIO_OBIO_ISTATE_TIM1	0x0000000003000000 /* Timer 1		*/
+#define SYSIO_OBIO_ISTATE_UE	0x000000000c000000 /* Uncorrectable Err	*/
+#define SYSIO_OBIO_ISTATE_CE	0x0000000030000000 /* Correctable Err	*/
+#define SYSIO_OBIO_ISTATE_SERR	0x00000000c0000000 /* SBUS Err		*/
+#define SYSIO_OBIO_ISTATE_PMGMT	0x0000000300000000 /* Power Management	*/
+#define SYSIO_OBIO_ISTATE_RSVI	0x0000000400000000 /* Reserved Int	*/
+#define SYSIO_OBIO_ISTATE_EUPA	0x0000000800000000 /* Expansion UPA (creator) */
+#define SYSIO_OBIO_ISTATE_RESV	0xfffffff000000000 /* Reserved...	*/
 
-/* SYSIO Performance Monitor Control register. XXX fields to be documented later */
+/* SYSIO Counter and Limit registers are documented in timer.h as these
+ * are generic SUN4U things.
+ */
 
-/* SYSIO Performance Monitor Counter register. XXX fields to be documented later */
+/* SYSIO Performance Monitor Control register. */
+#define SYSIO_PCNTRL_CLR1	0x0000000000008000 /* Clear SEL1 counter              */
+#define SYSIO_PCNTRL_SEL1_SDR	0x0000000000000000 /* SEL1: Streaming DVMA reads      */
+#define SYSIO_PCNTRL_SEL1_SDW	0x0000000000000100 /* SEL1: Streaming DVMA writes     */
+#define SYSIO_PCNTRL_SEL1_CDR	0x0000000000000200 /* SEL1: Consistent DVMA reads     */
+#define SYSIO_PCNTRL_SEL1_CDW	0x0000000000000300 /* SEL1: Consistent DVMA writes    */
+#define SYSIO_PCNTRL_SEL1_TMISS	0x0000000000000400 /* SEL1: IOMMU TLB misses          */
+#define SYSIO_PCNTRL_SEL1_SMISS	0x0000000000000500 /* SEL1: Streaming Buffer misses   */
+#define SYSIO_PCNTRL_SEL1_SDC	0x0000000000000600 /* SEL1: SBUS dvma cycles          */
+#define SYSIO_PCNTRL_SEL1_DB	0x0000000000000700 /* SEL1: DVMA bytes transferred    */
+#define SYSIO_PCNTRL_SEL1_IRQ	0x0000000000000800 /* SEL1: Interrupts                */
+#define SYSIO_PCNTRL_SEL1_UIN	0x0000000000000900 /* SEL1: UPA IRQ NACK's            */
+#define SYSIO_PCNTRL_SEL1_PRD	0x0000000000000a00 /* SEL1: PIO reads                 */
+#define SYSIO_PCNTRL_SEL1_PWR	0x0000000000000b00 /* SEL1: PIO writes                */
+#define SYSIO_PCNTRL_SEL1_SRR	0x0000000000000c00 /* SEL1: SBUS reruns               */
+#define SYSIO_PCNTRL_SEL1_SPIO	0x0000000000000d00 /* SEL1: SYSIO PIO cycles          */
+#define SYSIO_PCNTRL_CLR0	0x0000000000000080 /* Clear SEL0 counter              */
+#define SYSIO_PCNTRL_SEL0_SDR	0x0000000000000000 /* SEL0: Streaming DVMA reads      */
+#define SYSIO_PCNTRL_SEL0_SDW	0x0000000000000001 /* SEL0: Streaming DVMA writes     */
+#define SYSIO_PCNTRL_SEL0_CDR	0x0000000000000002 /* SEL0: Consistent DVMA reads     */
+#define SYSIO_PCNTRL_SEL0_CDW	0x0000000000000003 /* SEL0: Consistent DVMA writes    */
+#define SYSIO_PCNTRL_SEL0_TMISS	0x0000000000000004 /* SEL0: IOMMU TLB misses          */
+#define SYSIO_PCNTRL_SEL0_SMISS	0x0000000000000005 /* SEL0: Streaming Buffer misses   */
+#define SYSIO_PCNTRL_SEL0_SDC	0x0000000000000006 /* SEL0: SBUS dvma cycles          */
+#define SYSIO_PCNTRL_SEL0_DB	0x0000000000000007 /* SEL0: DVMA bytes transferred    */
+#define SYSIO_PCNTRL_SEL0_IRQ	0x0000000000000008 /* SEL0: Interrupts                */
+#define SYSIO_PCNTRL_SEL0_UIN	0x0000000000000009 /* SEL0: UPA IRQ NACK's            */
+#define SYSIO_PCNTRL_SEL0_PRD	0x000000000000000a /* SEL0: PIO reads                 */
+#define SYSIO_PCNTRL_SEL0_PWR	0x000000000000000b /* SEL0: PIO writes                */
+#define SYSIO_PCNTRL_SEL0_SRR	0x000000000000000c /* SEL0: SBUS reruns               */
+#define SYSIO_PCNTRL_SEL0_SPIO	0x000000000000000d /* SEL0: SYSIO PIO cycles          */
+
+/* SYSIO Performance Monitor Counter register. */
+#define SYSIO_PCOUNT_CNT0	0xffffffff00000000 /* Counter zero  */
+#define SYSIO_PCOUNT_CNT1	0x00000000ffffffff /* Counter one  */
 
 #endif /* !(__SPARC64_SYSIO_H) */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/system.h linux/include/asm-sparc64/system.h
--- v2.1.52/linux/include/asm-sparc64/system.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/system.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: system.h,v 1.35 1997/08/07 03:53:00 davem Exp $ */
+/* $Id: system.h,v 1.36 1997/08/19 14:18:33 jj Exp $ */
 #ifndef __SPARC64_SYSTEM_H
 #define __SPARC64_SYSTEM_H
 
@@ -143,7 +143,7 @@
 #define switch_to(prev, next)							\
 do {	__label__ switch_continue;						\
 	register unsigned long task_pc asm("o7");				\
-	(prev)->tss.kregs->fprs = 0;						\
+	(prev)->tss.kregs->tstate &= ~TSTATE_PEF;				\
 	task_pc = ((unsigned long) &&switch_continue) - 0x8;			\
 	(next)->mm->cpu_vm_mask |= (1UL << smp_processor_id());			\
 	__asm__ __volatile__(							\
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/termbits.h linux/include/asm-sparc64/termbits.h
--- v2.1.52/linux/include/asm-sparc64/termbits.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/termbits.h	Thu Sep  4 12:54:49 1997
@@ -159,7 +159,6 @@
 #define  B307200  0x00001007
 #define  B614400  0x00001008
 #define  B921600  0x00001009
-#define  B1843200 0x0000100a
 #define CIBAUD	  0x100f0000  /* input baud rate (not used) */
 #define CMSPAR    0x40000000  /* mark or space (stick) parity */
 #define CRTSCTS	  0x80000000  /* flow control */
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/ttable.h linux/include/asm-sparc64/ttable.h
--- v2.1.52/linux/include/asm-sparc64/ttable.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/ttable.h	Thu Sep  4 12:54:49 1997
@@ -1,7 +1,9 @@
-/* $Id: ttable.h,v 1.2 1997/08/09 09:03:36 davem Exp $ */
+/* $Id: ttable.h,v 1.3 1997/08/29 15:52:35 jj Exp $ */
 #ifndef _SPARC64_TTABLE_H
 #define _SPARC64_TTABLE_H
 
+#include <linux/config.h>
+
 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop;
 
 /* We need a "cleaned" instruction... */
@@ -87,17 +89,28 @@
 	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
 	ba,pt	%xcc, rtrap;				\
 	 clr	%l6;
+	 
+#define INDIRECT_SOLARIS_SYSCALL(num)			\
+	sethi	%hi(109f), %g7;				\
+	ba,pt	%xcc, etrap;				\
+109:	 or	%g7, %lo(109b), %g7;			\
+	ba,pt	%xcc, tl0_solaris + 0xc;		\
+	 mov	num, %g1;				\
+	nop;nop;nop;
 
 #define SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sunos_sys_table)
 #define	LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table32)
 #define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64)
 #define GETCC_TRAP TRAP(getcc)
 #define SETCC_TRAP TRAP(setcc)
+#ifdef CONFIG_SOLARIS_EMUL
+#define SOLARIS_SYSCALL_TRAP TRAP(solaris_sparc_syscall)
+#else
+#define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall)
+#endif
 /* FIXME: Write these actually */	
 #define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall)
-#define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall)
 #define BREAKPOINT_TRAP TRAP(breakpoint_trap)
-#define INDIRECT_SOLARIS_SYSCALL(tlvl) TRAP_ARG(indirect_syscall, tlvl)
 
 #define TRAP_IRQ(routine, level)			\
 	rdpr	%pil, %g2;				\
diff -u --recursive --new-file v2.1.52/linux/include/asm-sparc64/uaccess.h linux/include/asm-sparc64/uaccess.h
--- v2.1.52/linux/include/asm-sparc64/uaccess.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/uaccess.h	Thu Sep  4 12:54:49 1997
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.21 1997/07/31 07:37:25 davem Exp $ */
+/* $Id: uaccess.h,v 1.22 1997/08/19 15:25:35 jj Exp $ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
@@ -81,7 +81,7 @@
 
 struct exception_table_entry
 {
-        unsigned long insn, fixup;
+        unsigned insn, fixup;
 };
 
 /* Returns 0 if exception not found and fixup otherwise.  */
@@ -155,8 +155,8 @@
 	" mov	%3, %0\n\n\t"						\
 	".previous\n\t"							\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b, 3b\n\t"						\
+	".align	4\n\t"							\
+	".word	1b, 3b\n\t"						\
 	".previous\n\n\t"						\
        : "=r" (ret) : "r" (x), "r" (__m(addr)),				\
 	 "i" (-EFAULT), "i" (ASI_S))
@@ -167,8 +167,8 @@
 	"/* Put user asm ret, inline. */\n"				\
 "1:\t"	"st"#size "a %1, [%2] %3\n\n\t"					\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b, __ret_efault\n\n\t"					\
+	".align	4\n\t"							\
+	".word	1b, __ret_efault\n\n\t"					\
 	".previous\n\n\t"						\
        : "=r" (foo) : "r" (x), "r" (__m(addr)), "i" (ASI_S));		\
 else									\
@@ -182,8 +182,8 @@
 	" restore %%g0, %3, %%o0\n\n\t"					\
 	".previous\n\t"							\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b, 3b\n\n\t"						\
+	".align	4\n\t"							\
+	".word	1b, 3b\n\n\t"						\
 	".previous\n\n\t"						\
        : "=r" (foo) : "r" (x), "r" (__m(addr)),				\
          "i" (ret), "i" (ASI_S))
@@ -225,8 +225,8 @@
 	" mov	%3, %0\n\n\t"						\
 	".previous\n\t"							\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b, 3b\n\n\t"						\
+	".align	4\n\t"							\
+	".word	1b, 3b\n\n\t"						\
 	".previous\n\t"							\
        : "=r" (ret), "=r" (x) : "r" (__m(addr)),			\
 	 "i" (-EFAULT), "i" (ASI_S))
@@ -237,8 +237,8 @@
 	"/* Get user asm ret, inline. */\n"				\
 "1:\t"	"ld"#size "a [%1] %2, %0\n\n\t"					\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b,__ret_efault\n\n\t"					\
+	".align	4\n\t"							\
+	".word	1b,__ret_efault\n\n\t"					\
 	".previous\n\t"							\
        : "=r" (x) : "r" (__m(addr)), "i" (ASI_S));			\
 else									\
@@ -252,8 +252,8 @@
 	" restore %%g0, %3, %%o0\n\n\t"					\
 	".previous\n\t"							\
 	".section __ex_table,#alloc\n\t"				\
-	".align	8\n\t"							\
-	".xword	1b, 3b\n\n\t"						\
+	".align	4\n\t"							\
+	".word	1b, 3b\n\n\t"						\
 	".previous\n\t"							\
        : "=r" (x) : "r" (__m(addr)), "i" (retval), "i" (ASI_S))
 
diff -u --recursive --new-file v2.1.52/linux/include/linux/arcdevice.h linux/include/linux/arcdevice.h
--- v2.1.52/linux/include/linux/arcdevice.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/arcdevice.h	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,329 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  NET  is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Definitions for the ARCnet handlers.
+ *
+ * Version:	@(#)arcdevice.h	1.0	31/07/97
+ *
+ * Authors:	Avery Pennarun <apenwarr@bond.net>
+ *              David Woodhouse <dwmw2@cam.ac.uk>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ */
+#ifndef _LINUX_ARCDEVICE_H
+#define _LINUX_ARCDEVICE_H
+
+#include <linux/config.h>
+#include <linux/if_arcnet.h>
+
+#ifdef __KERNEL__
+
+
+#define ARC_20020     1
+#define ARC_RIM_I     2
+#define ARC_90xx      3
+#define ARC_90xx_IO   4
+
+#define MAX_ARCNET_DEVS 8
+
+
+/* The card sends the reconfiguration signal when it loses the connection to
+ * the rest of its network. It is a 'Hello, is anybody there?' cry.  This
+ * usually happens when a new computer on the network is powered on or when
+ * the cable is broken.
+ *
+ * Define DETECT_RECONFIGS if you want to detect network reconfigurations.
+ * Recons may be a real nuisance on a larger ARCnet network; if you are a
+ * network administrator you probably would like to count them.
+ * Reconfigurations will be recorded in stats.tx_carrier_errors (the last
+ * field of the /proc/net/dev file).
+ *
+ * Define SHOW_RECONFIGS if you really want to see a log message whenever
+ * a RECON occurs.
+ */
+#define DETECT_RECONFIGS
+#undef SHOW_RECONFIGS
+
+/* RECON_THRESHOLD is the maximum number of RECON messages to receive within
+ * one minute before printing a "cabling problem" warning.  You must have
+ * DETECT_RECONFIGS enabled if you want to use this.  The default value
+ * should be fine.
+ *
+ * After that, a "cabling restored" message will be printed on the next IRQ
+ * if no RECON messages have been received for 10 seconds.
+ *
+ * Do not define RECON_THRESHOLD at all if you want to disable this feature.
+ */
+#define RECON_THRESHOLD 30
+
+/* Define this to the minimum "timeout" value.  If a transmit takes longer
+ * than TX_TIMEOUT jiffies, Linux will abort the TX and retry.  On a large
+ * network, or one with heavy network traffic, this timeout may need to be
+ * increased.  The larger it is, though, the longer it will be between
+ * necessary transmits - don't set this too large.
+ */
+#define TX_TIMEOUT 20
+
+
+/* New debugging bitflags: each option can be enabled individually.
+ *
+ * These can be set while the driver is running by typing:
+ *	ifconfig arc0 down metric 1xxx HOSTNAME
+ *		where 1xxx is 1000 + the debug level you want
+ *		and HOSTNAME is your hostname/ip address
+ * and then resetting your routes.
+ *
+ * An ioctl() should be used for this instead, someday.
+ *
+ * Note: only debug flags included in the ARCNET_DEBUG_MAX define will
+ *   actually be available.  GCC will (at least, GCC 2.7.0 will) notice
+ *   lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
+ *   them out.
+ */
+#define D_NORMAL	1	/* important operational info		*/
+#define D_EXTRA		2	/* useful, but non-vital information	*/
+#define	D_INIT		4	/* show init/probe messages		*/
+#define D_INIT_REASONS	8	/* show reasons for discarding probes	*/
+/* debug levels below give LOTS of output during normal operation! */
+#define D_DURING	16	/* trace operations (including irq's)	*/
+#define D_TX		32	/* show tx packets			*/
+#define D_RX		64	/* show rx packets			*/
+#define D_SKB		128	/* show skb's				*/
+
+#ifndef ARCNET_DEBUG_MAX
+#define ARCNET_DEBUG_MAX (~0)		/* enable ALL debug messages	 */
+#endif
+
+#ifndef ARCNET_DEBUG
+#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
+#endif
+extern int arcnet_debug;
+
+/* macros to simplify debug checking */
+#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
+#define BUGMSG2(x,msg,args...) BUGLVL(x) printk(msg, ## args)
+#define BUGMSG(x,msg,args...) BUGMSG2(x,"%s%6s: " msg, \
+            x==D_NORMAL	? KERN_WARNING : \
+      x<=D_INIT_REASONS	? KERN_INFO    : KERN_DEBUG , \
+	dev->name , ## args)
+
+
+#define SETMASK AINTMASK(lp->intmask)
+
+	/* Time needed to resetthe card - in jiffies.  This works on my SMC
+	 * PC100.  I can't find a reference that tells me just how long I
+	 * should wait.
+	 */
+#define RESETtime (HZ * 3 / 10)		/* reset */
+
+	/* these are the max/min lengths of packet data. (including
+	 * ClientData header)
+	 * note: packet sizes 250, 251, 252 are impossible (God knows why)
+	 *  so exception packets become necessary.
+	 *
+	 * These numbers are compared with the length of the full packet,
+	 * including ClientData header.
+	 */
+#define MTU	253	/* normal packet max size */
+#define MinTU	257	/* extended packet min size */
+#define XMTU	508	/* extended packet max size */
+
+	/* status/interrupt mask bit fields */
+#define TXFREEflag	0x01            /* transmitter available */
+#define TXACKflag       0x02            /* transmitted msg. ackd */
+#define RECONflag       0x04            /* system reconfigured */
+#define TESTflag        0x08            /* test flag */
+#define RESETflag       0x10            /* power-on-reset */
+#define RES1flag        0x20            /* reserved - usually set by jumper */
+#define RES2flag        0x40            /* reserved - usually set by jumper */
+#define NORXflag        0x80            /* receiver inhibited */
+
+       /* Flags used for IO-mapped memory operations */
+#define AUTOINCflag     0x40    /* Increase location with each access */
+#define IOMAPflag       0x02    /* (for 90xx) Use IO mapped memory, not mmap */
+#define ENABLE16flag    0x80    /* (for 90xx) Enable 16-bit mode */
+
+       /* in the command register, the following bits have these meanings:
+        *                0-2     command
+        *                3-4     page number (for enable rcv/xmt command)
+        *                 7      receive broadcasts
+        */
+#define NOTXcmd         0x01            /* disable transmitter */
+#define NORXcmd         0x02            /* disable receiver */
+#define TXcmd           0x03            /* enable transmitter */
+#define RXcmd           0x04            /* enable receiver */
+#define CONFIGcmd       0x05            /* define configuration */
+#define CFLAGScmd       0x06            /* clear flags */
+#define TESTcmd         0x07            /* load test flags */
+
+       /* flags for "clear flags" command */
+#define RESETclear      0x08            /* power-on-reset */
+#define CONFIGclear     0x10            /* system reconfigured */
+
+	/* flags for "load test flags" command */
+#define TESTload        0x08            /* test flag (diagnostic) */
+
+	/* byte deposited into first address of buffers on reset */
+#define TESTvalue       0321		 /* that's octal for 0xD1 :) */
+
+	/* for "enable receiver" command */
+#define RXbcasts        0x80            /* receive broadcasts */
+
+	/* flags for "define configuration" command */
+#define NORMALconf      0x00            /* 1-249 byte packets */
+#define EXTconf         0x08            /* 250-504 byte packets */
+
+	/* Starts receiving packets into recbuf.
+	 */
+#define EnableReceiver()	ACOMMAND(RXcmd|(recbuf<<3)|RXbcasts)
+
+
+
+#define JIFFER(time) for (delayval=jiffies+time; jiffies<delayval;) ;
+
+	/* a complete ARCnet packet */
+union ArcPacket
+{
+	struct archdr hardheader;	/* the hardware header */
+	u_char raw[512];		/* raw packet info, incl ClientData */
+};
+
+
+
+	/* the "client data" header - RFC1201 information
+	 * notice that this screws up if it's not an even number of bytes
+	 * <sigh>
+	 */
+struct ClientData
+{
+	/* data that's NOT part of real packet - we MUST get rid of it before
+	 * actually sending!!
+	 */
+	u_char  saddr,		/* Source address - needed for IPX */
+		daddr;		/* Destination address */
+
+	/* data that IS part of real packet */
+	u_char	protocol_id,	/* ARC_P_IP, ARC_P_ARP, etc */
+		split_flag;	/* for use with split packets */
+	u_short	sequence;	/* sequence number */
+};
+#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4)
+
+
+	/* the "client data" header - RFC1051 information
+	 * this also screws up if it's not an even number of bytes
+	 * <sigh again>
+	 */
+struct S_ClientData
+{
+	/* data that's NOT part of real packet - we MUST get rid of it before
+	 * actually sending!!
+	 */
+	u_char  saddr,		/* Source address - needed for IPX */
+		daddr,		/* Destination address */
+		junk;		/* padding to make an even length */
+
+	/* data that IS part of real packet */
+	u_char	protocol_id;	/* ARC_P_IP, ARC_P_ARP, etc */
+};
+#define S_EXTRA_CLIENTDATA (sizeof(struct S_ClientData)-1)
+
+
+
+/* "Incoming" is information needed for each address that could be sending
+ * to us.  Mostly for partially-received split packets.
+ */
+struct Incoming
+{
+	struct sk_buff *skb;		/* packet data buffer             */
+	unsigned char lastpacket,	/* number of last packet (from 1) */
+		      numpackets;	/* number of packets in split     */
+	u_short sequence;		/* sequence number of assembly	  */
+};
+
+struct Outgoing
+{
+	struct sk_buff *skb;		/* buffer from upper levels */
+	struct ClientData *hdr;		/* clientdata of last packet */
+	u_char *data;			/* pointer to data in packet */
+	short length,			/* bytes total */
+	      dataleft,			/* bytes left */
+	      segnum,			/* segment being sent */
+	      numsegs,			/* number of segments */
+	      seglen;			/* length of segment */
+};
+
+
+
+struct arcnet_local {
+  struct net_device_stats stats;
+  u_short sequence;	/* sequence number (incs with each packet) */
+  u_short aborted_seq;
+  u_char stationid,	/* our 8-bit station address */
+    recbuf,		/* receive buffer # (0 or 1) */
+    txbuf,		/* transmit buffer # (2 or 3) */
+    txready,	/* buffer where a packet is ready to send */
+    config,         /* current value of CONFIG register */
+    timeout,        /* Extended timeout for COM20020 */
+    backplane,      /* Backplane flag for COM20020 */     
+    setup,          /* Contents of setup register */
+    intmask;	/* current value of INTMASK register */
+  short intx,		/* in TX routine? */
+    in_txhandler,	/* in TX_IRQ handler? */
+    sending,		/* transmit in progress? */
+    lastload_dest,	/* can last loaded packet be acked? */
+    lasttrans_dest;	/* can last TX'd packet be acked? */
+  
+#if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
+  time_t first_recon,	/* time of "first" RECON message to count */
+    last_recon;	/* time of most recent RECON */
+  int num_recons,		/* number of RECONs between first and last. */
+    network_down;	/* do we think the network is down? */
+#endif
+  
+  struct timer_list timer; /* the timer interrupt struct */
+  struct Incoming incoming[256];	/* one from each address */
+  struct Outgoing outgoing; /* packet currently being sent */
+  
+  int card_type;
+  char *card_type_str;
+  
+  void (*inthandler) (struct device *dev);
+  int (*arcnet_reset) (struct device *dev, int reset_delay);
+  void (*asetmask) (struct device *dev, u_char mask);
+  void (*acommand) (struct device *dev, u_char command);
+  u_char (*astatus) (struct device *dev);
+  void (*en_dis_able_TX) (struct device *dev, int enable); 
+  void (*prepare_tx)(struct device *dev,u_char *hdr,int hdrlen,
+		     char *data,int length,int daddr,int exceptA, int offset);
+  void (*openclose_device)(int open);
+  
+  
+  struct device *adev;	/* RFC1201 protocol device */
+  
+  /* These are last to ensure that the chipset drivers don't depend on the
+   * CONFIG_ARCNET_ETH and CONFIG_ARCNET_1051 options. 
+   */
+  
+#ifdef CONFIG_ARCNET_ETH
+  struct device *edev;	/* Ethernet-Encap device */
+#endif
+  
+#ifdef CONFIG_ARCNET_1051
+  struct device *sdev;	/* RFC1051 protocol device */
+#endif
+
+
+};
+
+
+#endif  /* __KERNEL__ */
+#endif	/* _LINUX_ARCDEVICE_H */
+
+
diff -u --recursive --new-file v2.1.52/linux/include/linux/if.h linux/include/linux/if.h
--- v2.1.52/linux/include/linux/if.h	Mon Jun 30 15:23:57 1997
+++ linux/include/linux/if.h	Thu Sep  4 13:25:28 1997
@@ -39,6 +39,9 @@
 
 #define IFF_MULTICAST	0x1000		/* Supports multicast		*/
 
+#define IFF_PORTSEL	0x2000          /* can set media type		*/
+#define IFF_AUTOMEDIA	0x4000		/* auto media select active	*/
+
 /*
  * 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.52/linux/include/linux/if_arcnet.h linux/include/linux/if_arcnet.h
--- v2.1.52/linux/include/linux/if_arcnet.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/if_arcnet.h	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,64 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Global definitions for the ARCnet interface.
+ *
+ * Version:	@(#)if_arcnet.h	1.0	07/08/97
+ *
+ * Author:	David Woodhouse <dwmw2@cam.ac.uk>
+ *		Avery Pennarun <apenwarr@bond.net>	
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+ 
+#ifndef _LINUX_IF_ARCNET_H
+#define _LINUX_IF_ARCNET_H
+
+
+/*
+ *	These are the defined ARCnet Protocol ID's.
+ */
+
+	/* RFC1201 Protocol ID's */
+#define ARC_P_IP	212		/* 0xD4 */
+#define ARC_P_ARP	213		/* 0xD5 */
+#define ARC_P_RARP	214		/* 0xD6 */
+#define ARC_P_IPX	250		/* 0xFA */
+#define ARC_P_NOVELL_EC	236		/* 0xEC */
+
+	/* Old RFC1051 Protocol ID's */
+#define ARC_P_IP_RFC1051 240		/* 0xF0 */
+#define ARC_P_ARP_RFC1051 241		/* 0xF1 */
+
+	/* MS LanMan/WfWg protocol */
+#define ARC_P_ETHER	0xE8
+
+	/* Unsupported/indirectly supported protocols */
+#define ARC_P_DATAPOINT_BOOT	0	/* very old Datapoint equipment */
+#define ARC_P_DATAPOINT_MOUNT	1
+#define ARC_P_POWERLAN_BEACON	8	/* Probably ATA-Netbios related */
+#define ARC_P_POWERLAN_BEACON2	243
+#define ARC_P_LANSOFT	251		/* 0xFB - what is this? */
+#define ARC_P_ATALK	0xDD
+
+
+/*
+ *	This is an ARCnet frame header.
+ */
+ 
+struct archdr                       /* was struct HardHeader */
+{
+	u_char	source,		/* source ARCnet - filled in automagically */
+		destination,	/* destination ARCnet - 0 for broadcast    */
+		offset1,	/* offset of ClientData (256-byte packets) */
+		offset2;	/* offset of ClientData (512-byte packets) */
+
+};
+
+#endif	/* _LINUX_IF_ARCNET_H */
+
diff -u --recursive --new-file v2.1.52/linux/include/linux/if_pppvar.h linux/include/linux/if_pppvar.h
--- v2.1.52/linux/include/linux/if_pppvar.h	Wed Dec 18 02:34:31 1996
+++ linux/include/linux/if_pppvar.h	Thu Sep  4 13:25:28 1997
@@ -1,4 +1,4 @@
-/*	$Id: if_pppvar.h,v 1.3 1996/09/01 07:45:23 davem Exp $	*/
+/*	$Id: if_pppvar.h,v 1.4 1997/09/03 11:55:06 ecd Exp $	*/
 /*
  * if_pppvar.h - private structures and declarations for PPP.
  *
@@ -72,7 +72,7 @@
         __s32		count;		/* Count of characters in bufr	*/
         __s32		head;		/* index to head of list	*/
         __s32		tail;		/* index to tail of list	*/
-        __u32		locked;		/* Buffer is being sent		*/
+        unsigned long	locked;		/* Buffer is being sent		*/
         __s32		type;		/* Type of the buffer		*/
 					/* =0, device read buffer	*/
 					/* =1, device write buffer	*/
@@ -93,7 +93,7 @@
 	__s32		magic;		/* magic value for structure	*/
 
 	/* Bitmapped flag fields. */
-	__u32		inuse;		/* are we allocated?		*/
+	unsigned long	inuse;		/* are we allocated?		*/
 	__u8		escape;		/* 0x20 if prev char was PPP_ESC*/
 	__u8		toss;		/* toss this frame		*/
 
diff -u --recursive --new-file v2.1.52/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.52/linux/include/linux/netdevice.h	Mon Jul  7 16:02:45 1997
+++ linux/include/linux/netdevice.h	Thu Sep  4 13:25:28 1997
@@ -130,8 +130,21 @@
 	
 };
 
+/* Media selection options. */
+enum {
+        IF_PORT_UNKNOWN = 0,
+        IF_PORT_10BASE2,
+        IF_PORT_10BASET,
+        IF_PORT_AUI,
+        IF_PORT_100BASET,
+        IF_PORT_100BASETX,
+        IF_PORT_100BASEFX
+};
+
 #ifdef __KERNEL__
 
+extern const char *if_port_text[];
+
 #include <linux/skbuff.h>
 
 /*
@@ -163,7 +176,7 @@
 	unsigned long		mem_end;	/* shared mem end	*/
 	unsigned long		mem_start;	/* shared mem start	*/
 	unsigned long		base_addr;	/* device I/O address	*/
-	unsigned char		irq;		/* device IRQ number	*/
+	unsigned int		irq;		/* device IRQ number	*/
 	
 	/* Low-level status flags. */
 	volatile unsigned char	start,		/* start an operation	*/
diff -u --recursive --new-file v2.1.52/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v2.1.52/linux/include/linux/sockios.h	Thu Dec 12 06:54:20 1996
+++ linux/include/linux/sockios.h	Thu Sep  4 13:25:28 1997
@@ -54,6 +54,9 @@
 #define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
 #define SIOCDELMULTI	0x8932
 #define SIOGIFINDEX	0x8933		/* name -> if_index mapping	*/
+#define SIOGIFNAME	0x8934		/* if_index -> name mapping	*/
+#define SIOCGIFCOUNT	0x8935		/* get number of interfaces	*/
+#define SIOCDIFADDR	0x8936		/* delete PA address		*/
 
 #define SIOCGIFBR	0x8940		/* Bridging support		*/
 #define SIOCSIFBR	0x8941		/* Set bridging options 	*/
diff -u --recursive --new-file v2.1.52/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.52/linux/include/linux/sysctl.h	Thu Jun 26 12:33:40 1997
+++ linux/include/linux/sysctl.h	Thu Sep  4 13:25:28 1997
@@ -172,8 +172,9 @@
 	NET_IPV4_IGMP_TIMER_SCALE,
 	NET_IPV4_IGMP_AGE_THRESHOLD,
 	NET_TCP_SYNCOOKIES,
-	NET_TCP_ALWAYS_SYNCOOKIE,
 	NET_TCP_STDURG,
+	NET_TCP_SYN_TAILDROP,
+	NET_TCP_MAX_SYN_BACKLOG,
 };
 
 
diff -u --recursive --new-file v2.1.52/linux/include/linux/trdevice.h linux/include/linux/trdevice.h
--- v2.1.52/linux/include/linux/trdevice.h	Thu Jun 12 15:29:21 1997
+++ linux/include/linux/trdevice.h	Thu Sep  4 13:25:28 1997
@@ -33,7 +33,7 @@
 				   void *saddr, unsigned len);
 extern int		tr_rebuild_header(struct sk_buff *skb);
 extern unsigned short	tr_type_trans(struct sk_buff *skb, struct device *dev);
-
+extern void             tr_reformat(struct sk_buff *, unsigned int);
 extern struct device    * init_trdev(struct device *, int);
 
 #endif
diff -u --recursive --new-file v2.1.52/linux/include/linux/udp.h linux/include/linux/udp.h
--- v2.1.52/linux/include/linux/udp.h	Wed Dec  1 04:44:15 1993
+++ linux/include/linux/udp.h	Thu Sep  4 13:25:28 1997
@@ -19,10 +19,10 @@
 
 
 struct udphdr {
-  unsigned short	source;
-  unsigned short	dest;
-  unsigned short	len;
-  unsigned short	check;
+  u16	source;
+  u16	dest;
+  u16	len;
+  u16	check;
 };
 
 
diff -u --recursive --new-file v2.1.52/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.1.52/linux/include/net/addrconf.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/addrconf.h	Thu Sep  4 13:25:28 1997
@@ -52,6 +52,7 @@
 						void * data);
 
 extern int			addrconf_add_ifaddr(void *arg);
+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);
diff -u --recursive --new-file v2.1.52/linux/include/net/inet_common.h linux/include/net/inet_common.h
--- v2.1.52/linux/include/net/inet_common.h	Tue Mar  4 10:25:26 1997
+++ linux/include/net/inet_common.h	Thu Sep  4 14:56:26 1997
@@ -1,6 +1,8 @@
 #ifndef _INET_COMMON_H
 #define _INET_COMMON_H
 
+#include <linux/config.h>
+
 extern struct proto_ops		inet_stream_ops;
 extern struct proto_ops		inet_dgram_ops;
 
diff -u --recursive --new-file v2.1.52/linux/include/net/netlink.h linux/include/net/netlink.h
--- v2.1.52/linux/include/net/netlink.h	Thu Mar 27 14:40:11 1997
+++ linux/include/net/netlink.h	Thu Sep  4 13:25:28 1997
@@ -2,14 +2,7 @@
 #define __NET_NETLINK_H
 
 #define NET_MAJOR 36		/* Major 18 is reserved for networking 						*/
-/* so....                                ^^ is this 36? */
-/* and the things below... */
-#define MAX_LINKS 16		/* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved 	*/
-				/* 4-7 are psi0-psi3  8 is arpd 9 is ppp */
-				/* 10 is for IPSEC <John Ioannidis> */
-				/* 11 IPv6 route updates		*/
-				/* 12 is for firewall trapout	*/
-
+#define MAX_LINKS 32
 #define MAX_QBYTES 32768	/* Maximum bytes in the queue 							*/
 
 #include <linux/netlink.h>
@@ -23,7 +16,7 @@
 /*
  *	skb should fit one page. This choice is good for headerless malloc.
  */
-#define NLMSG_GOODSIZE (PAGE_SIZE - ((sizeof(struct sk_buff)+0xF)&~0xF))
+#define NLMSG_GOODSIZE (PAGE_SIZE - ((sizeof(struct sk_buff)+0xF)&~0xF)-32)
 
 #define NLMSG_RECOVERY_TIMEO	(HZ/2)		/* If deleivery was failed,
 						   retry after */
@@ -62,13 +55,11 @@
 #define NETLINK_SKIP		1	/* Reserved for ENskip  			*/
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
 #define NETLINK_FIREWALL	3	/* Firewalling hook				*/
-#define NETLINK_PSI		4	/* PSI devices - 4 to 7 */
-#define NETLINK_ARPD		8
-#define NETLINK_IPSEC		10	/* IPSEC */
-#define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
-#define NETLINK_IP6_FW		13
-/* Wouldn't this suffice instead of the confusion at the top of
-   this file?  i.e. 3 is firewall or ppp... */
-/* #define MAX_LINKS		16 */
-
+#define NETLINK_FREE		4	/* PSI devices - 4 to 7 (obsolete)		*/
+#define NETLINK_ARPD		8	/* ARP daemon for big switched networks		*/
+#define NETLINK_IPSEC		10	/* IPSEC  (JI)					*/
+#define NETLINK_ROUTE6		11	/* Af_inet6 route communication channel		*/
+#define NETLINK_IP6_FW		13	/* IPv6 firewall trap outs			*/
+#define NETLINK_DNRT		14	/* DECnet routing messages			*/
+#define NETLINK_TAPBASE		16	/* 16->31 are the ethertap devices 		*/
 #endif
diff -u --recursive --new-file v2.1.52/linux/include/net/snmp.h linux/include/net/snmp.h
--- v2.1.52/linux/include/net/snmp.h	Sun Nov  3 01:04:42 1996
+++ linux/include/net/snmp.h	Thu Sep  4 14:56:40 1997
@@ -19,6 +19,8 @@
 #ifndef _SNMP_H
 #define _SNMP_H
  
+#include <linux/config.h>
+
 /*
  *	We use all unsigned longs. Linux will soon be so reliable that even these
  *	will rapidly get too small 8-). Seriously consider the IpInReceives count
@@ -122,6 +124,5 @@
  	unsigned long	UdpInErrors;
  	unsigned long	UdpOutDatagrams;
 };
- 
  	
 #endif
diff -u --recursive --new-file v2.1.52/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.52/linux/include/net/sock.h	Mon Jul  7 16:02:47 1997
+++ linux/include/net/sock.h	Thu Sep  4 14:56:54 1997
@@ -23,6 +23,7 @@
  *	Pauline Middelink	:	identd support
  *		Alan Cox	:	Eliminate low level recv/recvfrom
  *		David S. Miller	:	New socket lookup architecture.
+ *              Steve Whitehouse:       Default routines for sock_ops
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -88,7 +89,7 @@
 	char *			name;
 	int  			locks;
 	struct unix_address	*addr;
-	struct inode *		inode;
+	struct dentry *		dentry;
 	struct semaphore	readsem;
 	struct sock *		other;
 	struct sock **		list;
@@ -281,6 +282,8 @@
 
 	struct open_request	*syn_wait_queue;
 	struct open_request	**syn_wait_last;
+	int syn_backlog;
+
 	struct tcp_func		*af_specific;
 };
 
@@ -430,8 +433,8 @@
 						   'timed out' */
 	unsigned char		protocol;
 	volatile unsigned char	state;
-	unsigned char		ack_backlog;
-	unsigned char		max_ack_backlog;
+	unsigned short		ack_backlog;
+	unsigned short		max_ack_backlog;
 	unsigned char		priority;
 	unsigned char		debug;
 	int			rcvbuf;
@@ -734,7 +737,7 @@
 	return a;
 }
 
-extern struct sock *		sk_alloc(int priority);
+extern struct sock *		sk_alloc(int family, int priority);
 extern void			sk_free(struct sock *sk);
 extern void			destroy_sock(struct sock *sk);
 
@@ -762,12 +765,42 @@
 						     int noblock,
 						     int *errcode);
 
-extern int 			sock_no_fcntl(struct socket *, unsigned int, unsigned long);
+/*
+ * Functions to fill in entries in struct proto_ops when a protocol
+ * does not implement a particular function.
+ */
+extern int                      sock_no_dup(struct socket *, struct socket *);
+extern int                      sock_no_release(struct socket *, 
+						struct socket *);
+extern int                      sock_no_bind(struct socket *, 
+					     struct sockaddr *, int);
+extern int                      sock_no_connect(struct socket *,
+						struct sockaddr *, int, int);
+extern int                      sock_no_socketpair(struct socket *,
+						   struct socket *);
+extern int                      sock_no_accept(struct socket *,
+					       struct socket *, int);
+extern int                      sock_no_getname(struct socket *,
+						struct sockaddr *, int *, int);
+extern unsigned int             sock_no_poll(struct socket *,
+					     poll_table *);
+extern int                      sock_no_ioctl(struct socket *, unsigned int,
+					      unsigned long);
+extern int			sock_no_listen(struct socket *, int);
+extern int                      sock_no_shutdown(struct socket *, int);
 extern int			sock_no_getsockopt(struct socket *, int , int,
 						   char *, int *);
 extern int			sock_no_setsockopt(struct socket *, int, int,
 						   char *, int);
-extern int			sock_no_listen(struct socket *, int);
+extern int 			sock_no_fcntl(struct socket *, 
+					      unsigned int, unsigned long);
+extern int                      sock_no_sendmsg(struct socket *,
+						struct msghdr *, int,
+						struct scm_cookie *);
+extern int                      sock_no_recvmsg(struct socket *,
+						struct msghdr *, int,
+						struct scm_cookie *);
+
 /*
  *	Default socket callbacks and setup code
  */
@@ -775,6 +808,7 @@
 extern void sock_def_callback1(struct sock *);
 extern void sock_def_callback2(struct sock *, int);
 extern void sock_def_callback3(struct sock *);
+extern void sock_def_destruct(struct sock *);
 
 /* Initialise core socket variables */
 extern void sock_init_data(struct socket *sock, struct sock *sk);
diff -u --recursive --new-file v2.1.52/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.52/linux/include/net/tcp.h	Thu Aug 14 20:49:17 1997
+++ linux/include/net/tcp.h	Thu Sep  4 13:25:28 1997
@@ -243,23 +243,25 @@
 };
 #endif
 
+/* this structure is too big */
 struct open_request {
-	struct open_request	*dl_next;
-	struct open_request	**dl_pprev;
+	struct open_request	*dl_next; /* Must be first member! */
 	__u32			rcv_isn;
 	__u32			snt_isn;
 	__u16			rmt_port;
 	__u16			mss;
-	__u8			snd_wscale;
-	__u8			rcv_wscale;
-	char			sack_ok;
-	char			tstamp_ok;
-	char			wscale_ok;
+	__u8			retrans;
+	__u8			__pad;
+	unsigned snd_wscale : 4, 
+		rcv_wscale : 4, 
+		sack_ok : 1,
+		tstamp_ok : 1,
+		wscale_ok : 1;
+	/* The following two fields can be easily recomputed I think -AK */
 	__u32			window_clamp;	/* window clamp at creation time */
 	__u32			rcv_wnd;	/* rcv_wnd offered first time */
 	__u32			ts_recent;
 	unsigned long		expires;
-	int			retrans;
 	struct or_calltable	*class;
 	struct sock		*sk;
 	union {
@@ -330,6 +332,13 @@
 
 	void			(*send_reset)		(struct sk_buff *skb);
 
+	struct open_request *   (*search_open_req)	(struct tcp_opt *, void *, 
+							 struct tcphdr *,
+							 struct open_request **);
+
+	struct sock *		(*cookie_check)		(struct sock *, struct sk_buff *,
+							 void *);
+
 	int sockaddr_len;
 };
 
@@ -447,6 +456,7 @@
 extern void tcp_write_xmit(struct sock *);
 extern void tcp_time_wait(struct sock *);
 extern void tcp_do_retransmit(struct sock *, int);
+extern void tcp_simple_retransmit(struct sock *);
 
 /* tcp_output.c */
 
@@ -474,6 +484,9 @@
 extern void tcp_delack_timer(unsigned long);
 extern void tcp_probe_timer(unsigned long);
 
+extern struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, 
+				  void *);
+
 
 /*
  *	TCP slow timer
@@ -670,21 +683,59 @@
 	(*window_clamp) = min(65535<<(*rcv_wscale),*window_clamp);
 }
 
-extern __inline__ void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req)
+#define SYNQ_DEBUG 1
+
+extern __inline__ void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req, struct open_request *prev)
 {
-	if(req->dl_next)
-		req->dl_next->dl_pprev = req->dl_pprev;
-	else
-		tp->syn_wait_last = req->dl_pprev;
-	*req->dl_pprev = req->dl_next;
+#ifdef SYNQ_DEBUG
+	if (prev->dl_next != req) {
+		printk(KERN_DEBUG "synq_unlink: bad prev ptr: %p\n",prev);
+		return;
+	}
+#endif
+	if(!req->dl_next) {
+#ifdef SYNQ_DEBUG
+		if (tp->syn_wait_last != (void*) req)
+			printk(KERN_DEBUG "synq_unlink: bad last ptr %p,%p\n",
+			       req,tp->syn_wait_last);
+#endif
+		tp->syn_wait_last = (struct open_request **)prev;
+	}
+	prev->dl_next = req->dl_next;
 }
 
 extern __inline__ void tcp_synq_queue(struct tcp_opt *tp, struct open_request *req)
-{
+{ 
+#ifdef SYNQ_DEBUG
+	if (*tp->syn_wait_last != NULL)
+	    printk("synq_queue: last ptr doesn't point to last req.\n"); 
+#endif
 	req->dl_next = NULL;
-	req->dl_pprev = tp->syn_wait_last;
-	*tp->syn_wait_last = req;
+	*tp->syn_wait_last = req; 
 	tp->syn_wait_last = &req->dl_next;
+}
+
+extern __inline__ void tcp_synq_init(struct tcp_opt *tp)
+{
+	tp->syn_wait_queue = NULL;
+	tp->syn_wait_last = &tp->syn_wait_queue;
+}
+
+extern __inline__ struct open_request *tcp_synq_unlink_tail(struct tcp_opt *tp)
+{
+	struct open_request *head = tp->syn_wait_queue;
+#ifdef SYNQ_DEBUG
+	if (!head) {
+		printk(KERN_DEBUG "tail drop on empty queue? - bug\n"); 
+		return NULL;
+	}
+#endif
+	printk(KERN_DEBUG "synq tail drop with expire=%ld\n", 
+	       head->expires-jiffies);
+	if (head->dl_next == NULL)
+		tp->syn_wait_last = &tp->syn_wait_queue;
+	tp->syn_wait_queue = head->dl_next;
+	return head;
 }
 
 extern void __tcp_inc_slow_timer(struct tcp_sl_timer *slt);
diff -u --recursive --new-file v2.1.52/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.52/linux/net/802/tr.c	Tue May 13 22:41:20 1997
+++ linux/net/802/tr.c	Thu Sep  4 13:25:28 1997
@@ -34,7 +34,7 @@
 #include <linux/init.h>
 #include <net/arp.h>
 
-static void tr_source_route(struct trh_hdr *trh, struct device *dev);
+static void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh, struct device *dev);
 static void tr_add_rif_info(struct trh_hdr *trh, struct device *dev);
 static void rif_check_expire(unsigned long dummy);
 
@@ -114,7 +114,7 @@
 	if(daddr) 
 	{
 		memcpy(trh->daddr,daddr,dev->addr_len);
-		tr_source_route(trh,dev);
+		tr_source_route(skb,trh,dev);
 		return(dev->hard_header_len);
 	}
 	return -dev->hard_header_len;
@@ -146,7 +146,7 @@
 	}
 	else 
 	{	
-		tr_source_route(trh,dev); 
+		tr_source_route(skb,trh,dev); 
 		return 0;
 	}
 }
@@ -187,15 +187,46 @@
 }
 
 /*
- *	We try to do source routing... 
+ *      Reformat the headers to make a "standard" frame. This is done
+ *      in-place in the sk_buff. 
  */
 
-static void tr_source_route(struct trh_hdr *trh,struct device *dev) 
+void tr_reformat(struct sk_buff *skb, unsigned int hdr_len)
 {
+	struct trllc *llc = (struct trllc *)(skb->data+hdr_len);
+	struct device *dev = skb->dev;
+	unsigned char *olddata = skb->data;
+	int slack;
 
-	int i;
+	if (llc->dsap == 0xAA && llc->ssap == 0xAA)
+	{
+		slack = sizeof(struct trh_hdr) - hdr_len;
+		skb_push(skb, slack);
+		memmove(skb->data, olddata, hdr_len);
+		memset(skb->data+hdr_len, 0, slack);
+	}
+	else
+	{
+		struct trllc *local_llc;
+		slack = sizeof(struct trh_hdr) - hdr_len + sizeof(struct trllc);
+		skb_push(skb, slack);
+		memmove(skb->data, olddata, hdr_len);
+		memset(skb->data+hdr_len, 0, slack);
+		local_llc = (struct trllc *)(skb->data+dev->hard_header_len);
+		local_llc->ethertype = htons(ETH_P_TR_802_2);
+       	}
+}
+
+/*
+ *	We try to do source routing... 
+ */
+
+static void tr_source_route(struct sk_buff *skb,struct trh_hdr *trh,struct device *dev) 
+{
+	int i, slack;
 	unsigned int hash;
 	rif_cache entry;
+	unsigned char *olddata;
 
 	/*
 	 *	Broadcasts are single route as stated in RFC 1042 
@@ -252,9 +283,20 @@
 			trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK)  
 				       | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
 			trh->saddr[0]|=TR_RII;
+#if TR_SR_DEBUG
 			printk("no entry in rif table found - broadcasting frame\n");
+#endif
 		}
 	}
+
+	/* Compress the RIF here so we don't have to do it in the driver(s) */
+	if (!(trh->saddr[0] & 0x80))
+		slack = 18;
+	else 
+		slack = 18 - ((ntohs(trh->rcf) & TR_RCF_LEN_MASK)>>8);
+	olddata = skb->data;
+	skb_pull(skb, slack);
+	memmove(skb->data, olddata, sizeof(struct trh_hdr) - slack);
 }
 
 /*
diff -u --recursive --new-file v2.1.52/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.52/linux/net/appletalk/ddp.c	Thu Jul 17 10:06:09 1997
+++ linux/net/appletalk/ddp.c	Thu Sep  4 13:25:28 1997
@@ -956,7 +956,7 @@
 static int atalk_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
-	sk=sk_alloc(GFP_KERNEL);
+	sk=sk_alloc(AF_APPLETALK, GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
@@ -985,15 +985,6 @@
 }
 
 /*
- *	Copy a socket. No work needed.
- */
-
-static int atalk_dup(struct socket *newsock,struct socket *oldsock)
-{
-	return(atalk_create(newsock,SOCK_DGRAM));
-}
-
-/*
  *	Free a socket. No work needed
  */
 
@@ -1147,15 +1138,6 @@
  *	Not relevant
  */
 
-static int atalk_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return(-EOPNOTSUPP);
-}
-
-/*
- *	Not relevant
- */
-
 static int atalk_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	if(newsock->sk) {
@@ -1994,7 +1976,9 @@
 		case SIOCGIFCONF:
 		case SIOCADDMULTI:
 		case SIOCDELMULTI:
-
+		case SIOCGIFCOUNT:
+		case SIOGIFINDEX:
+		case SIOGIFNAME:
 			return(dev_ioctl(cmd,(void *) arg));
 
 		case SIOCSIFMETRIC:
@@ -2021,11 +2005,11 @@
 static struct proto_ops atalk_dgram_ops = {
 	AF_APPLETALK,
 
-	atalk_dup,
+	sock_no_dup,
 	atalk_release,
 	atalk_bind,
 	atalk_connect,
-	atalk_socketpair,
+	sock_no_socketpair,
 	atalk_accept,
 	atalk_getname,
 	datagram_poll,
diff -u --recursive --new-file v2.1.52/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.52/linux/net/ax25/af_ax25.c	Mon Jul  7 08:19:59 1997
+++ linux/net/ax25/af_ax25.c	Thu Sep  4 13:25:28 1997
@@ -828,7 +828,7 @@
 			return -ESOCKTNOSUPPORT;
 	}
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
@@ -854,7 +854,7 @@
 	struct sock *sk;
 	ax25_cb *ax25;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
@@ -919,16 +919,6 @@
 	return sk;
 }
 
-static int ax25_dup(struct socket *newsock, struct socket *oldsock)
-{
-	struct sock *sk = oldsock->sk;
-
-	if (sk == NULL || newsock == NULL)
-		return -EINVAL;
-
-	return ax25_create(newsock, sk->protocol);
-}
-
 static int ax25_release(struct socket *sock, struct socket *peer)
 {
 	struct sock *sk = sock->sk;
@@ -1204,10 +1194,6 @@
 	return 0;
 }
 
-static int ax25_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return -EOPNOTSUPP;
-}
 
 static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
 {
@@ -1707,11 +1693,11 @@
 static struct proto_ops ax25_proto_ops = {
 	AF_AX25,
 
-	ax25_dup,
+	sock_no_dup,
 	ax25_release,
 	ax25_bind,
 	ax25_connect,
-	ax25_socketpair,
+	sock_no_socketpair,
 	ax25_accept,
 	ax25_getname,
 	datagram_poll,
diff -u --recursive --new-file v2.1.52/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.52/linux/net/core/dev.c	Mon Aug  4 16:25:41 1997
+++ linux/net/core/dev.c	Thu Sep  4 13:25:28 1997
@@ -90,6 +90,16 @@
 extern int plip_init(void);
 #endif
 
+const char *if_port_text[] = {
+  "unknown",
+  "BNC",
+  "10baseT",
+  "AUI",
+  "100baseT",
+  "100baseTX",
+  "100baseFX"
+};
+
 /*
  *	The list of devices, that are able to output.
  */
@@ -954,6 +964,53 @@
 
 
 /*
+ *	Count the installed interfaces (SIOCGIFCOUNT)
+ */
+
+static int dev_ifcount(unsigned int *arg)
+{
+	struct device *dev;
+	int err;
+	unsigned int count = 0;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) 
+		count++;
+
+	err = copy_to_user(arg, &count, sizeof(unsigned int));
+	if (err)
+		return -EFAULT; 
+	return 0;
+}
+
+/*
+ *	Map an interface index to its name (SIOGIFNAME)
+ */
+
+static int dev_ifname(struct ifreq *arg)
+{
+	struct device *dev;
+	struct ifreq ifr;
+	int err;
+
+	/*
+	 *	Fetch the caller's info block. 
+	 */
+	
+	err = copy_from_user(&ifr, arg, sizeof(struct ifreq));
+	if (err)
+		return -EFAULT;
+
+	dev = dev_get_by_index(ifr.ifr_ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	strcpy(ifr.ifr_name, dev->name);
+
+	err = copy_to_user(&ifr, arg, sizeof(struct ifreq));
+	return (err)?-EFAULT:0;
+}
+
+/*
  *	Perform a SIOCGIFCONF call. This structure will change
  *	size eventually, and there is nothing I can do about it.
  *	Thus we will need a 'compatibility mode'.
@@ -965,7 +1022,7 @@
 	struct ifreq ifr;
 	struct device *dev;
 	char *pos;
-	int len;
+	unsigned int len;
 	int err;
 
 	/*
@@ -1262,8 +1319,8 @@
 				 */
 				 
 				dev->flags = (ifr.ifr_flags & (
-					IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
-					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+					IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_PORTSEL |
+					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_AUTOMEDIA |
 					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
 					| IFF_MULTICAST)) | (dev->flags & IFF_UP);
 				/*
@@ -1476,6 +1533,10 @@
 		case SIOCGIFCONF:
 			(void) dev_ifconf((char *) arg);
 			return 0;
+		case SIOCGIFCOUNT:
+			return dev_ifcount((unsigned int *) arg);
+		case SIOGIFNAME:
+			return dev_ifname((struct ifreq *)arg);
 
 		/*
 		 *	Ioctl calls that can be done by all.
@@ -1554,6 +1615,7 @@
 extern int sm_init(void);
 extern int baycom_init(void);
 extern int lapbeth_init(void);
+extern void arcnet_init(void);
 
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_dev = {
@@ -1630,6 +1692,9 @@
 #endif
 #if defined(CONFIG_PLIP)
 	plip_init();
+#endif
+#if defined(CONFIG_ARCNET)
+	arcnet_init();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
diff -u --recursive --new-file v2.1.52/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v2.1.52/linux/net/core/net_alias.c	Wed Apr 23 19:01:29 1997
+++ linux/net/core/net_alias.c	Thu Sep  4 13:25:28 1997
@@ -216,6 +216,17 @@
 }
 
 
+/* 
+ * 2 options for multicast:
+ *    1) fake it for aliases.
+ *    2) allow aliases and actual device to set it.
+ * current choice: option 1
+ */
+static void net_alias_setmulticast(struct device *dev)
+{
+}
+
+
 /*
  *	Hard_start_xmit() should not be called.
  *	ignore ... but shout!.
@@ -269,6 +280,8 @@
 	dev->type = main_dev->type;
 	dev->open = net_alias_open;
 	dev->stop = net_alias_close;
+	if (main_dev->set_multicast_list)
+	  dev->set_multicast_list = net_alias_setmulticast;
 	dev->hard_header_len = main_dev->hard_header_len;
 	memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
 	memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
diff -u --recursive --new-file v2.1.52/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.52/linux/net/core/sock.c	Mon Jun 16 16:36:01 1997
+++ linux/net/core/sock.c	Thu Sep  4 13:25:28 1997
@@ -71,8 +71,10 @@
  *		Alan Cox	: 	Generic socket allocation to make hooks
  *					easier (suggested by Craig Metz).
  *		Michael Pall	:	SO_ERROR returns positive errno again
- *             Steve Whitehouse:       Added default destructor to free
- *                                     protocol private data.
+ *              Steve Whitehouse:       Added default destructor to free
+ *                                      protocol private data.
+ *              Steve Whitehouse:       Added various other default routines
+ *                                      common to several socket families.
  *
  * To Fix:
  *
@@ -458,12 +460,15 @@
  *	usage.
  */
  
-struct sock *sk_alloc(int priority)
+struct sock *sk_alloc(int family, int priority)
 {
 	struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
 
-	if(sk)
+	if(sk) {
 		memset(sk, 0, sizeof(struct sock));
+		sk->family = family;
+	}
+
 	return sk;
 }
 
@@ -802,13 +807,83 @@
 }
 
 /*
- *	Support routines for general vectors
+ * Set of default routines for initialising struct proto_ops when
+ * the protocol does not support a particular function. In certain
+ * cases where it makes no sense for a protocol to have a "do nothing"
+ * function, some default processing is provided.
  */
 
-/*
- *	Socket with no special fcntl calls.
- */ 
- 
+int sock_no_dup(struct socket *newsock, struct socket *oldsock)
+{
+	struct sock *sk = oldsock->sk;
+
+	return net_families[sk->family]->create(newsock, sk->protocol);
+}
+
+int sock_no_release(struct socket *sock, struct socket *peersock)
+{
+	return 0;
+}
+
+int sock_no_bind(struct socket *sock, struct sockaddr *saddr, int len)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_connect(struct socket *sock, struct sockaddr *saddr, 
+		    int len, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_getname(struct socket *sock, struct sockaddr *saddr, 
+		    int *len, int peer)
+{
+	return -EOPNOTSUPP;
+}
+
+unsigned int sock_no_poll(struct socket *sock, poll_table *pt)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_listen(struct socket *sock, int backlog)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_shutdown(struct socket *sock, int how)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_setsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int optlen)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_getsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int *optlen)
+{
+	return -EOPNOTSUPP;
+}
+
 int sock_no_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
@@ -832,26 +907,19 @@
 	}
 }
 
-/*
- *	Default socket getsockopt / setsockopt
- */
- 
-int sock_no_setsockopt(struct socket *sock, int level, int optname,
-		    char *optval, int optlen)
+int sock_no_sendmsg(struct socket *sock, struct msghdr *m, int flags,
+		    struct scm_cookie *scm)
 {
 	return -EOPNOTSUPP;
 }
 
-int sock_no_getsockopt(struct socket *sock, int level, int optname,
-		    char *optval, int *optlen)
+int sock_no_recvmsg(struct socket *sock, struct msghdr *m, int flags,
+		    struct scm_cookie *scm)
 {
 	return -EOPNOTSUPP;
 }
 
-int sock_no_listen(struct socket *sock, int backlog)
-{
-	return -EOPNOTSUPP;
-}
+
 
 /*
  *	Default Socket Callbacks
@@ -903,6 +971,7 @@
 	sk->state 	= 	TCP_CLOSE;
 	sk->zapped	=	1;
 	sk->socket	=	sock;
+
 	if(sock)
 	{
 		sk->type	=	sock->type;
diff -u --recursive --new-file v2.1.52/linux/net/decnet/README linux/net/decnet/README
--- v2.1.52/linux/net/decnet/README	Sat May 18 01:15:09 1996
+++ linux/net/decnet/README	Thu Sep  4 13:25:28 1997
@@ -1,6 +1,13 @@
-Yes.. it's being worked on.
+                       Linux DECnet Project
+                      ======================
 
-If you want to get involved email me <Alan.Cox@linux.org> and I'll put you
-in touch with the people doing the work.
+For information on the Linux DECnet Project and the latest progress,
+look at the project home page:
 
-Alan
+http://eeshack3.swan.ac.uk/~gw7rrm/DECnet/index.html
+
+To contribute either mail <SteveW@ACM.org> or post on one of the Linux
+mailing lists (either linux-net or netdev).
+
+Steve Whitehouse <SteveW@ACM.org>
+http://eeshack3.swan.ac.uk/~gw7rrm
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.52/linux/net/ipv4/af_inet.c	Mon Jun 16 16:36:01 1997
+++ linux/net/ipv4/af_inet.c	Thu Sep  4 13:27:03 1997
@@ -294,12 +294,6 @@
 		return -EAGAIN;
 
 	/* We might as well re use these. */ 
-	/*
-	 * note that the backlog is "unsigned char", so truncate it
-	 * somewhere. We might as well truncate it to what everybody
-	 * else does..
-	 * Now truncate to 128 not 5. 
-	 */
 	if ((unsigned) backlog == 0)	/* BSDism */
 		backlog = 1;
 	if ((unsigned) backlog > SOMAXCONN)
@@ -328,7 +322,7 @@
 	struct proto *prot;
 
 	sock->state = SS_UNCONNECTED;
-	sk = sk_alloc(GFP_KERNEL);
+	sk = sk_alloc(AF_INET, GFP_KERNEL);
 	if (sk == NULL) 
 		goto do_oom;
 
@@ -439,15 +433,6 @@
 
 
 /*
- *	Duplicate a socket.
- */
- 
-static int inet_dup(struct socket *newsock, struct socket *oldsock)
-{
-	return inet_create(newsock, oldsock->sk->protocol);
-}
-
-/*
  *	The peer socket should always be NULL (or else). When we call this
  *	function we are destroying the object and from then on nobody
  *	should refer to it.
@@ -924,6 +909,8 @@
 		case SIOCSIFSLAVE:
 		case SIOCGIFSLAVE:
 		case SIOGIFINDEX:
+		case SIOGIFNAME:
+		case SIOCGIFCOUNT:
 			return(dev_ioctl(cmd,(void *) arg));
 
 		case SIOCGIFBR:
@@ -973,11 +960,11 @@
 struct proto_ops inet_stream_ops = {
 	AF_INET,
 
-	inet_dup,
+	sock_no_dup,
 	inet_release,
 	inet_bind,
 	inet_stream_connect,
-	NULL,
+	sock_no_socketpair,
 	inet_accept,
 	inet_getname, 
 	inet_poll,
@@ -994,12 +981,12 @@
 struct proto_ops inet_dgram_ops = {
 	AF_INET,
 
-	inet_dup,
+	sock_no_dup,
 	inet_release,
 	inet_bind,
 	inet_dgram_connect,
-	NULL,
-	NULL,
+	sock_no_socketpair,
+	sock_no_accept,
 	inet_getname, 
 	datagram_poll,
 	inet_ioctl,
@@ -1017,7 +1004,6 @@
 	inet_create
 };
 
-extern unsigned long seq_offset;
 
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_INET_RARP
@@ -1084,8 +1070,6 @@
 	 */
    
   	(void) sock_register(&inet_family_ops);
-
-  	seq_offset = CURRENT_TIME*250;
 
 	/*
 	 *	Add all the protocols. 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.52/linux/net/ipv4/icmp.c	Thu May 15 16:48:06 1997
+++ linux/net/ipv4/icmp.c	Thu Sep  4 13:25:28 1997
@@ -38,7 +38,9 @@
  *					path MTU bug.
  *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are
  *					valid (RFC 1812).
- *
+ *		Andi Kleen	:	Check all packet lengths properly
+ *					and moved all kfree_skb() up to
+ *					icmp_rcv.
  *
  * RFC1122 (Host Requirements -- Comm. Layer) Status:
  * (boy, are there a lot of rules for ICMP)
@@ -690,14 +692,15 @@
 	
 	/*
 	 *	Incomplete header ?
+	 * 	Only checks for the IP header, there should be an
+	 *	additional check for longer headers in upper levels.
 	 */
-	 
-	if(skb->len<sizeof(struct iphdr)+8)
-	{
-		kfree_skb(skb, FREE_READ);
+
+	if(len<sizeof(struct iphdr)) {
+		icmp_statistics.IcmpInErrors++;
 		return;
 	}
-	
+		
 	iph = (struct iphdr *) (icmph + 1);
 	dp = (unsigned char*)iph;
 	
@@ -712,29 +715,27 @@
 			case ICMP_PORT_UNREACH:
 				break;
 			case ICMP_FRAG_NEEDED:
-				if (ipv4_config.no_pmtu_disc)
-					printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
+				if (ipv4_config.no_pmtu_disc) {
+					if (net_ratelimit())
+						printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
 					       in_ntoa(iph->daddr));
-				else {
+				} else {
 					unsigned short new_mtu;
 					new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
-					if (!new_mtu) {
-						kfree_skb(skb, FREE_READ);
+					if (!new_mtu) 
 						return;
-					}
 					icmph->un.frag.mtu = htons(new_mtu);
 				}
 				break;
 			case ICMP_SR_FAILED:
-				printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+				if (net_ratelimit())
+					printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
 				break;
 			default:
 				break;
 		}
-		if (icmph->code>NR_ICMP_UNREACH) {
-			kfree_skb(skb, FREE_READ);
+		if (icmph->code>NR_ICMP_UNREACH) 
 			return;
-		}
 	}
 	
 	/*
@@ -754,11 +755,13 @@
 	 
 	if(__ip_chk_addr(iph->daddr)==IS_BROADCAST)
 	{
-		printk("%s sent an invalid ICMP error to a broadcast.\n",
-			in_ntoa(skb->nh.iph->saddr));
-		kfree_skb(skb, FREE_READ);
+		if (net_ratelimit())
+			printk("%s sent an invalid ICMP error to a broadcast.\n",
+			       in_ntoa(skb->nh.iph->saddr));
+		return; 
 	}
 
+
 	/*
 	 *	Deliver ICMP message to raw sockets. Pretty useless feature?
 	 */
@@ -794,12 +797,10 @@
 		/* appropriate protocol layer (MUST), as per 3.2.2. */
 
 		if (iph->protocol == ipprot->protocol && ipprot->err_handler)
-			ipprot->err_handler(skb, dp);
+ 			ipprot->err_handler(skb, dp);
 
 		ipprot = nextip;
   	}
-
-	kfree_skb(skb, FREE_READ);
 }
 
 
@@ -812,6 +813,11 @@
 	struct iphdr *iph;
 	unsigned long ip;
 
+	if (len < sizeof(struct iphdr)) {
+		icmp_statistics.IcmpInErrors++;
+		return; 
+	}
+		
 	/*
 	 *	Get the copied header of the packet that caused the redirect
 	 */
@@ -819,7 +825,6 @@
 	iph = (struct iphdr *) (icmph + 1);
 	ip = iph->daddr;
 
-
 	switch(icmph->code & 7) {
 		case ICMP_REDIR_NET:
 		case ICMP_REDIR_NETTOS:
@@ -835,11 +840,6 @@
 		default:
 			break;
   	}
-  	/*
-  	 *	Discard the original packet
-  	 */
-  	 
-  	kfree_skb(skb, FREE_READ);
 }
 
 /*
@@ -862,7 +862,6 @@
 	icmp_param.data_len=len;
 	icmp_reply(&icmp_param, skb);
 #endif
-	kfree_skb(skb, FREE_READ);
 }
 
 /*
@@ -885,7 +884,6 @@
 	 
 	if(len<12) {
 		icmp_statistics.IcmpInErrors++;
-		kfree_skb(skb, FREE_READ);
 		return;
 	}
 	
@@ -903,7 +901,6 @@
 	icmp_param.data_ptr=&times;
 	icmp_param.data_len=12;
 	icmp_reply(&icmp_param, skb);
-	kfree_skb(skb,FREE_READ);
 }
 
 
@@ -940,13 +937,14 @@
 	struct device *dev = skb->dev;
 
 	if (!ipv4_config.addrmask_agent ||
+	    len < 4 ||
 	    ZERONET(rt->rt_src) ||
 	    rt->rt_src_dev != rt->u.dst.dev ||
 	    !(rt->rt_flags&RTCF_DIRECTSRC) ||
 	    (rt->rt_flags&RTF_GATEWAY) ||
 	    !(dev->ip_flags&IFF_IP_ADDR_OK) ||
 	    !(dev->ip_flags&IFF_IP_MASK_OK)) {
-		kfree_skb(skb, FREE_READ);
+		icmp_statistics.IcmpInErrors++;
 		return;
 	}
 
@@ -956,7 +954,6 @@
 	icmp_param.data_ptr=&dev->pa_mask;
 	icmp_param.data_len=4;
 	icmp_reply(&icmp_param, skb);
-	kfree_skb(skb, FREE_READ);
 }
 
 /*
@@ -976,20 +973,19 @@
 	    (rt->rt_flags&RTF_GATEWAY) ||
 	    !(dev->ip_flags&IFF_IP_ADDR_OK) ||
 	    !(dev->ip_flags&IFF_IP_MASK_OK)) {
-		kfree_skb(skb, FREE_READ);
+		icmp_statistics.IcmpInErrors++;
 		return;
 	}
 
 	mask = *(u32*)&icmph[1];
-	if (mask != dev->pa_mask)
+	if (mask != dev->pa_mask && net_ratelimit())
 		printk(KERN_INFO "Wrong address mask %08lX from %08lX/%s\n",
 		       ntohl(mask), ntohl(rt->rt_src), dev->name);
-	kfree_skb(skb, FREE_READ);
 }
 
 static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
-	kfree_skb(skb, FREE_READ);
+	return; 
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -1062,38 +1058,21 @@
 	struct rtable *rt = (struct rtable*)skb->dst;
 
 	icmp_statistics.IcmpInMsgs++;
-	
-	if(len < sizeof(struct icmphdr))
-	{
-		icmp_statistics.IcmpInErrors++;
-		printk(KERN_INFO "ICMP: runt packet\n");
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
- 	
-  	/*
-	 *	Validate the packet
-  	 */
-	
-	if (ip_compute_csum((unsigned char *) icmph, len)) {
-		icmp_statistics.IcmpInErrors++;
-		printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(skb->nh.iph->saddr));
-		kfree_skb(skb, FREE_READ);
-		return(0);
-	}
-	
+
 	/*
 	 *	18 is the highest 'known' ICMP type. Anything else is a mystery
 	 *
 	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently discarded.
 	 */
-	 
-	if (icmph->type > NR_ICMP_TYPES) {
-		icmp_statistics.IcmpInErrors++;		/* Is this right - or do we ignore ? */
-		kfree_skb(skb,FREE_READ);
-		return(0);
+	if(len < sizeof(struct icmphdr) ||
+	   ip_compute_csum((unsigned char *) icmph, len) ||
+	   icmph->type > NR_ICMP_TYPES)
+	{
+		icmp_statistics.IcmpInErrors++;
+		kfree_skb(skb, FREE_READ);
+		return 0;
 	}
-	
+	 
 	/*
 	 *	Parse the ICMP message 
 	 */
@@ -1117,6 +1096,7 @@
 	len -= sizeof(struct icmphdr);
 	(*icmp_pointers[icmph->type].input)++;
 	(icmp_pointers[icmph->type].handler)(icmph, skb, len);
+	kfree_skb(skb, FREE_READ); 
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.52/linux/net/ipv4/ip_fragment.c	Mon Aug  4 16:25:41 1997
+++ linux/net/ipv4/ip_fragment.c	Thu Sep  4 15:42:20 1997
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.23 1997/05/31 12:36:35 freitag Exp $
+ * Version:	$Id: ip_fragment.c,v 1.25 1997/08/17 05:56:07 freitag Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -313,8 +313,8 @@
 	len = qp->ihlen + qp->len;
 	
 	if(len>65535) {
-		printk(KERN_INFO "Oversized IP packet from %s.\n",
-		       in_ntoa(qp->iph->saddr));
+		printk(KERN_INFO "Oversized IP packet from %I.\n", 
+		       &qp->iph->saddr);
 		ip_statistics.IpReasmFails++;
 		ip_free(qp);
 		return NULL;
@@ -360,7 +360,6 @@
 
 	skb->pkt_type = qp->fragments->skb->pkt_type;
 	skb->protocol = qp->fragments->skb->protocol;
-
 	/* We glued together all fragments, so remove the queue entry. */
 	ip_free(qp);
 
@@ -437,8 +436,8 @@
 	
 	/* Attempt to construct an oversize packet. */
 	if(ntohs(iph->tot_len)+(int)offset>65535) {
-		printk(KERN_INFO "Oversized packet received from %s\n",
-		       in_ntoa(iph->saddr));
+		printk(KERN_INFO "Oversized packet received from %I\n",
+		       &iph->saddr);
 		frag_kfree_skb(skb, FREE_READ);
 		ip_statistics.IpReasmFails++;
 		return NULL;
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.52/linux/net/ipv4/ip_output.c	Tue May 13 22:41:23 1997
+++ linux/net/ipv4/ip_output.c	Thu Sep  4 13:25:28 1997
@@ -344,7 +344,7 @@
 {
 	struct sock *sk = skb->sk;
 	struct rtable *rt = (struct rtable*)skb->dst;
-	struct device *dev = rt->u.dst.dev;
+	struct device *dev;
 	unsigned int tot_len;
 	struct iphdr *iph = skb->nh.iph;
 
@@ -358,6 +358,11 @@
 	iph->tot_len = htons(tot_len);
 	iph->id = htons(ip_id_count++);
 
+	if (rt->u.dst.obsolete)
+		goto check_route;
+after_check_route:
+	dev = rt->u.dst.dev;
+
 	if (call_out_firewall(PF_INET, dev, iph, NULL,&skb) < FW_ACCEPT) {
 		kfree_skb(skb, FREE_WRITE);
 		return;
@@ -419,18 +424,38 @@
 	skb->dst->output(skb);
 	return;
 
+check_route:
+	/* Ugly... ugly... but what can I do?
+
+	   Essentially it is "ip_reroute_output" function. --ANK
+	 */
+	{
+		struct rtable *nrt;
+		if (ip_route_output(&nrt, rt->key.dst, rt->key.src,
+				    rt->key.tos, NULL)) {
+			kfree_skb(skb, 0);
+			return;
+		}
+		skb->dst = &nrt->u.dst;
+		ip_rt_put(rt);
+		rt = nrt;
+	}
+	goto after_check_route;
+	
 fragment:
 	if ((iph->frag_off & htons(IP_DF)))
 	{
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-			  htonl(dev->mtu));
+			  htonl(rt->u.dst.pmtu));
 			  
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
 	
 	ip_fragment(skb, 1, skb->dst->output);
+
+
 }
 
 
@@ -446,7 +471,8 @@
  *	field in the last fragment it sends... actually it also helps
  * 	the reassemblers, they can put most packets in at the head of
  *	the fragment queue, and they know the total size in advance. This
- *	last feature will measurable improve the Linux fragment handler.
+ *	last feature will measurably improve the Linux fragment handler one
+ *	day.
  *
  *	The callback has five args, an arbitrary pointer (copy of frag),
  *	the source IP address (may depend on the routing table), the 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.52/linux/net/ipv4/ip_sockglue.c	Thu May 15 16:48:06 1997
+++ linux/net/ipv4/ip_sockglue.c	Thu Sep  4 13:25:28 1997
@@ -261,7 +261,16 @@
 				return -EINVAL;
 			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
 				return -EPERM;
-			sk->ip_tos=val;
+			if (sk->ip_tos != val) {
+				start_bh_atomic(); 
+				sk->ip_tos=val;
+				sk->priority = rt_tos2priority(val);
+				if (sk->dst_cache) {
+					dst_release(sk->dst_cache); 
+					sk->dst_cache = NULL;
+				}
+				end_bh_atomic();
+			}
 			sk->priority = rt_tos2priority(val);
 			return 0;
 		case IP_TTL:
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.52/linux/net/ipv4/ipip.c	Mon Aug  4 16:25:41 1997
+++ linux/net/ipv4/ipip.c	Thu Sep  4 13:25:28 1997
@@ -65,8 +65,7 @@
 	/*
 	 *	Discard the original IP header
 	 */
-
-	skb->mac.raw = skb->data;	 
+	 
 	skb_pull(skb, skb->h.raw - skb->nh.raw);
 	
 	/*
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.52/linux/net/ipv4/proc.c	Mon Aug  4 16:25:41 1997
+++ linux/net/ipv4/proc.c	Thu Sep  4 13:25:28 1997
@@ -23,6 +23,8 @@
  *		Alan Cox	:	Handle dead sockets properly.
  *	Gerhard Koerting	:	Show both timers
  *		Alan Cox	:	Allow inode to be NULL (kernel socket)
+ *	Andi Kleen		:	Add support for open_requests and 
+ *					split functions for more readibility.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -47,6 +49,82 @@
 #include <net/sock.h>
 #include <net/raw.h>
 
+/* Format a single open_request into tmpbuf. */
+static inline void get__openreq(struct sock *sk, struct open_request *req, 
+				char *tmpbuf, 
+				int i)
+{
+	/* FIXME: I'm not sure if the timer fields are correct. */
+	sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu",
+		i,
+		(long unsigned int)req->af.v4_req.loc_addr,
+		ntohs(sk->dummy_th.source),
+		(long unsigned int)req->af.v4_req.rmt_addr,
+		req->rmt_port,
+		TCP_SYN_RECV,
+		0,0, /* use sizeof(struct open_request) here? */
+		0, (unsigned long)(req->expires - jiffies), /* ??? */
+		req->retrans,
+		sk->socket ? sk->socket->inode->i_uid : 0,
+		0,      /* ??? */
+		sk->socket ? sk->socket->inode->i_ino:0);
+}
+
+/* Format a single socket into tmpbuf. */
+static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format)
+{
+	unsigned long  dest, src;
+	unsigned short destp, srcp;
+	int timer_active, timer_active1, timer_active2;
+	unsigned long timer_expires;
+	struct tcp_opt *tp = &sp->tp_pinfo.af_tcp;
+
+	dest  = sp->daddr;
+	src   = sp->saddr;
+	destp = sp->dummy_th.dest;
+	srcp  = sp->dummy_th.source;
+	
+	/* FIXME: The fact that retransmit_timer occurs as a field
+	 * in two different parts of the socket structure is,
+	 * to say the least, confusing. This code now uses the
+	 * right retransmit_timer variable, but I'm not sure
+	 * the rest of the timer stuff is still correct.
+	 * In particular I'm not sure what the timeout value
+	 * is suppose to reflect (as opposed to tm->when). -- erics
+	 */
+	
+	destp = ntohs(destp);
+	srcp  = ntohs(srcp);
+	timer_active1 = del_timer(&tp->retransmit_timer);
+	timer_active2 = del_timer(&sp->timer);
+	if (!timer_active1) tp->retransmit_timer.expires=0;
+	if (!timer_active2) sp->timer.expires=0;
+	timer_active=0;
+	timer_expires=(unsigned)-1;
+	if (timer_active1 && tp->retransmit_timer.expires < timer_expires) {
+		timer_active=timer_active1;
+		timer_expires=tp->retransmit_timer.expires;
+	}
+	if (timer_active2 && sp->timer.expires < timer_expires) {
+		timer_active=timer_active2;
+		timer_expires=sp->timer.expires;
+		}
+	sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+		i, src, srcp, dest, destp, sp->state, 
+		format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc), 
+		format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+				timer_active, timer_expires-jiffies,
+		tp->retransmits,
+		sp->socket ? sp->socket->inode->i_uid:0,
+		timer_active?sp->timeout:0,
+		sp->socket ? sp->socket->inode->i_ino:0);
+	
+	if (timer_active1) add_timer(&tp->retransmit_timer);
+	if (timer_active2) add_timer(&sp->timer);	
+}
+
 /*
  * Get__netinfo returns the length of that string.
  *
@@ -57,12 +135,7 @@
 static int
 get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
 {
-	struct sock *sp;
-	struct tcp_opt *tp;
-	int timer_active, timer_active1, timer_active2;
-	unsigned long timer_expires;
-	unsigned long  dest, src;
-	unsigned short destp, srcp;
+	struct sock *sp, *next;
 	int len=0, i = 0;
 	off_t pos=0;
 	off_t begin;
@@ -78,68 +151,46 @@
  *	at the wrong moment (eg a syn recv socket getting a reset), or
  *	a memory timer destroy. Instead of playing with timers we just
  *	concede defeat and do a start_bh_atomic().
+ * 	Why not just use lock_sock()? As far as I can see all timer routines
+ *	check for sock_readers before doing anything. -AK
+ *      [Disabled for now again, because it hard-locked my machine, and there
+ *	 is an theoretical situation then, where an user could prevent
+ *	 sockets from being destroyed by constantly reading /proc/net/tcp.]
  */
-	SOCKHASH_LOCK();
+	SOCKHASH_LOCK(); 
 	sp = pro->sklist_next;
 	while(sp != (struct sock *)pro) {
+		if (format == 0 && sp->state == TCP_LISTEN) {
+			struct open_request *req;
+
+			for (req = sp->tp_pinfo.af_tcp.syn_wait_queue; req;
+			     i++, req = req->dl_next) {
+				pos += 128;
+				if (pos < offset) 
+					continue;
+				get__openreq(sp, req, tmpbuf, i); 
+				len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+				if(len >= length)
+					break; 
+			}
+		}
+		
 		pos += 128;
 		if (pos < offset)
 			goto next;
-
-		tp = &(sp->tp_pinfo.af_tcp);
-		dest  = sp->daddr;
-		src   = sp->saddr;
-		destp = sp->dummy_th.dest;
-		srcp  = sp->dummy_th.source;
-
-		/* FIXME: The fact that retransmit_timer occurs as a field
-		 * in two different parts of the socket structure is,
-	 	 * to say the least, confusing. This code now uses the
-		 * right retransmit_timer variable, but I'm not sure
-		 * the rest of the timer stuff is still correct.
-		 * In particular I'm not sure what the timeout value
-		 * is suppose to reflect (as opposed to tm->when). -- erics
-		 */
-
-		/* Since we are Little Endian we need to swap the bytes :-( */
-		destp = ntohs(destp);
-		srcp  = ntohs(srcp);
-		timer_active1 = del_timer(&tp->retransmit_timer);
-		timer_active2 = del_timer(&sp->timer);
-		if (!timer_active1) tp->retransmit_timer.expires=0;
-		if (!timer_active2) sp->timer.expires=0;
-		timer_active=0;
-		timer_expires=(unsigned)-1;
-		if (timer_active1 && tp->retransmit_timer.expires < timer_expires) {
-			timer_active=timer_active1;
-			timer_expires=tp->retransmit_timer.expires;
-		}
-		if (timer_active2 && sp->timer.expires < timer_expires) {
-			timer_active=timer_active2;
-			timer_expires=sp->timer.expires;
-		}
-		sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
-			" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
-			i, src, srcp, dest, destp, sp->state, 
-			format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc), 
-			format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
-			timer_active, timer_expires-jiffies,
-			tp->retransmits,
-			sp->socket ? sp->socket->inode->i_uid:0,
-			timer_active?sp->timeout:0,
-			sp->socket ? sp->socket->inode->i_ino:0);
-
-		if (timer_active1) add_timer(&tp->retransmit_timer);
-		if (timer_active2) add_timer(&sp->timer);
+		
+		get__sock(sp, tmpbuf, i, format);
+		
 		len += sprintf(buffer+len, "%-127s\n", tmpbuf);
 		if(len >= length)
 			break;
 	next:
-		sp = sp->sklist_next;
+		next = sp->sklist_next;
+		sp = next;
 		i++;
 	}
 	SOCKHASH_UNLOCK();
-
+	
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/protocol.c linux/net/ipv4/protocol.c
--- v2.1.52/linux/net/ipv4/protocol.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/protocol.c	Thu Sep  4 13:27:19 1997
@@ -77,7 +77,6 @@
 	"TCP"			/* name			*/
 };
 
-
 static struct inet_protocol udp_protocol = 
 {
 	udp_rcv,		/* UDP handler		*/
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.1.52/linux/net/ipv4/rarp.c	Tue May 13 22:41:23 1997
+++ linux/net/ipv4/rarp.c	Thu Sep  4 13:25:28 1997
@@ -96,7 +96,7 @@
 	NULL
 };
 
-static initflag = 1;
+static int initflag = 1;
 
 
 /*
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/syncookies.c linux/net/ipv4/syncookies.c
--- v2.1.52/linux/net/ipv4/syncookies.c	Thu Jun 26 12:33:41 1997
+++ linux/net/ipv4/syncookies.c	Thu Sep  4 13:25:28 1997
@@ -9,7 +9,7 @@
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  * 
- *  $Id: syncookies.c,v 1.1 1997/06/06 20:37:56 freitag Exp $
+ *  $Id: syncookies.c,v 1.2 1997/08/22 19:15:08 freitag Exp $
  *
  *  Missing: IPv6 support. 
  *           Some counter so that the Administrator can see when the machine
@@ -149,6 +149,7 @@
 	struct open_request *req; 
 	int mss; 
 	struct rtable *rt; 
+	__u8 rcv_wscale;
 
 	if (!sysctl_tcp_syncookies)
 		return sk;
@@ -210,7 +211,8 @@
 	req->window_clamp = rt->u.dst.window;  
 	tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
 				  &req->rcv_wnd, &req->window_clamp, 
-				  0, &req->rcv_wscale);
+				  0, &rcv_wscale);
+	req->rcv_wscale = rcv_wscale; 
 
 	return get_cookie_sock(sk, skb, req, &rt->u.dst);
 }
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.52/linux/net/ipv4/sysctl_net_ipv4.c	Thu Jun 26 12:33:41 1997
+++ linux/net/ipv4/sysctl_net_ipv4.c	Thu Sep  4 13:25:28 1997
@@ -62,6 +62,10 @@
 extern int sysctl_tcp_syncookies;
 extern int sysctl_tcp_syn_retries;
 extern int sysctl_tcp_stdurg; 
+extern int sysctl_tcp_syn_taildrop; 
+extern int sysctl_max_syn_backlog; 
+
+int tcp_retr1_max = 255; 
 
 extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
 				void *buffer, size_t *lenp);
@@ -184,7 +188,8 @@
 	 &sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL, 
 	 &proc_dointvec},
 	{NET_IPV4_TCP_RETRIES1, "tcp_retries1",
-	 &sysctl_tcp_retries1, sizeof(int), 0644, NULL, &proc_dointvec},
+	 &sysctl_tcp_retries1, sizeof(int), 0644, NULL, &proc_dointvec_minmax, 
+	 &sysctl_intvec, NULL, NULL, &tcp_retr1_max},
 	{NET_IPV4_TCP_RETRIES2, "tcp_retries2",
 	 &sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_IPV4_TCP_MAX_DELAY_ACKS, "tcp_max_delay_acks",
@@ -208,6 +213,10 @@
 	 &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
 #endif
 	{NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg,
+	 sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_TCP_SYN_TAILDROP, "tcp_syn_taildrop", &sysctl_tcp_syn_taildrop,
+	 sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_TCP_MAX_SYN_BACKLOG, "tcp_max_syn_backlog", &sysctl_max_syn_backlog,
 	 sizeof(int), 0644, NULL, &proc_dointvec},
 	{0}
 };
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.52/linux/net/ipv4/tcp.c	Mon Aug  4 16:25:41 1997
+++ linux/net/ipv4/tcp.c	Thu Sep  4 13:25:28 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.67 1997/07/20 12:46:07 freitag Exp $
+ * Version:	$Id: tcp.c,v 1.70 1997/09/01 03:14:28 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -268,7 +268,8 @@
  *
  * Urgent Pointer (4.2.2.4)
  * **MUST point urgent pointer to last byte of urgent data (not right
- *     after). (doesn't, to be like BSD)
+ *     after). (doesn't, to be like BSD. That's configurable, but defaults
+ *	to off)
  *   MUST inform application layer asynchronously of incoming urgent
  *     data. (does)
  *   MUST provide application with means of determining the amount of
@@ -282,7 +283,8 @@
  *   MUST ignore unsupported options (does)
  *
  * Maximum Segment Size Option (4.2.2.6)
- *   MUST implement both sending and receiving MSS. (does)
+ *   MUST implement both sending and receiving MSS. (does, but currently
+ *	only uses the smaller of both of them)
  *   SHOULD send an MSS with every SYN where receive MSS != 536 (MAY send
  *     it always). (does, even when MSS == 536, which is legal)
  *   MUST assume MSS == 536 if no MSS received at connection setup (does)
@@ -296,7 +298,8 @@
  * Initial Sequence Number Selection (4.2.2.8)
  *   MUST use the RFC 793 clock selection mechanism.  (doesn't, but it's
  *     OK: RFC 793 specifies a 250KHz clock, while we use 1MHz, which is
- *     necessary for 10Mbps networks - and harder than BSD to spoof!)
+ *     necessary for 10Mbps networks - and harder than BSD to spoof!
+ *     With syncookies we doesn't)
  *
  * Simultaneous Open Attempts (4.2.2.10)
  *   MUST support simultaneous open attempts (does)
@@ -359,8 +362,8 @@
  *   MAY provide keep-alives. (does)
  *   MUST make keep-alives configurable on a per-connection basis. (does)
  *   MUST default to no keep-alives. (does)
- * **MUST make keep-alive interval configurable. (doesn't)
- * **MUST make default keep-alive interval > 2 hours. (doesn't)
+ *   MUST make keep-alive interval configurable. (does)
+ *   MUST make default keep-alive interval > 2 hours. (does)
  *   MUST NOT interpret failure to ACK keep-alive packet as dead
  *     connection. (doesn't)
  *   SHOULD send keep-alive with no data. (does)
@@ -384,15 +387,16 @@
  *     Unreachables (0, 1, 5), Time Exceededs and Parameter
  *     Problems. (doesn't)
  *   SHOULD report soft Destination Unreachables etc. to the
- *     application. (does)
+ *     application. (does, but may drop them in the ICMP error handler
+ *	during an accept())
  *   SHOULD abort connection upon receipt of hard Destination Unreachable
- *     messages (2, 3, 4). (does)
+ *     messages (2, 3, 4). (does, but see above)
  *
  * Remote Address Validation (4.2.3.10)
  *   MUST reject as an error OPEN for invalid remote IP address. (does)
  *   MUST ignore SYN with invalid source address. (does)
  *   MUST silently discard incoming SYN for broadcast/multicast
- *     address. (does)
+ *     address. (I'm not sure if it does. Someone should check this.)
  *
  * Asynchronous Reports (4.2.4.1)
  * MUST provide mechanism for reporting soft errors to application
@@ -402,6 +406,7 @@
  *   MUST allow application layer to set Type of Service. (does IP_TOS)
  *
  * (Whew. -- MS 950903)
+ * (Updated by AK, but not complete yet.)
  **/
 
 #include <linux/types.h>
@@ -416,7 +421,6 @@
 
 int sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT;
 
-unsigned long seq_offset;
 struct tcp_mib	tcp_statistics;
 
 kmem_cache_t *tcp_openreq_cachep;
@@ -426,17 +430,20 @@
  *	the socket locked or with interrupts disabled
  */
 
-static struct open_request *tcp_find_established(struct tcp_opt *tp)
+static struct open_request *tcp_find_established(struct tcp_opt *tp, 
+						 struct open_request **prevp)
 {
 	struct open_request *req = tp->syn_wait_queue;
-
+	struct open_request *prev = (struct open_request *)&tp->syn_wait_queue; 
 	while(req) {
 		if (req->sk && 
 		    (req->sk->state == TCP_ESTABLISHED ||
 		     req->sk->state >= TCP_FIN_WAIT1))
 			break;
+		prev = req; 
 		req = req->dl_next;
 	}
+	*prevp = prev; 
 	return req;
 }
 
@@ -466,8 +473,7 @@
 		tcp_openreq_free(iter);
 	}
 
-	tp->syn_wait_queue = NULL;
-	tp->syn_wait_last = &tp->syn_wait_queue;
+	tcp_synq_init(tp);
 }
 
 /*
@@ -566,10 +572,10 @@
  */
 static unsigned int tcp_listen_poll(struct sock *sk, poll_table *wait)
 {
-	struct open_request *req;
+	struct open_request *req, *dummy;
 
 	lock_sock(sk);
-	req = tcp_find_established(&sk->tp_pinfo.af_tcp);
+	req = tcp_find_established(&sk->tp_pinfo.af_tcp, &dummy);
 	release_sock(sk);
 	if (req)
 		return POLLIN | POLLRDNORM;
@@ -1021,7 +1027,10 @@
 			sk->urg_data = URG_READ;
 			
 		if(len>0)
+		{
 			err = memcpy_toiovec(msg->msg_iov, &c, 1);
+			msg->msg_flags|=MSG_OOB;
+		}
 		else
 			msg->msg_flags|=MSG_TRUNC;
 			
@@ -1415,13 +1424,9 @@
 
 static inline int closing(struct sock * sk)
 {
-	switch (sk->state) {
-		case TCP_FIN_WAIT1:
-		case TCP_CLOSING:
-		case TCP_LAST_ACK:
-			return 1;
-	};
-	return 0;
+	return ((1 << sk->state) & ((1 << TCP_FIN_WAIT1)|
+				    (1 << TCP_CLOSING)|
+				    (1 << TCP_LAST_ACK)));
 }
 
 
@@ -1501,7 +1506,7 @@
 static struct open_request * wait_for_connect(struct sock * sk)
 {
 	struct wait_queue wait = { current, NULL };
-	struct open_request *req = NULL;
+	struct open_request *req = NULL, *dummy;
 
 	add_wait_queue(sk->sleep, &wait);
 	for (;;) {
@@ -1509,8 +1514,8 @@
 		release_sock(sk);
 		schedule();
 		lock_sock(sk);
-		req = tcp_find_established(&(sk->tp_pinfo.af_tcp));
-		if (req)
+		req = tcp_find_established(&(sk->tp_pinfo.af_tcp), &dummy);
+		if (req) 
 			break;
 		if (current->signal & ~current->blocked)
 			break;
@@ -1528,7 +1533,7 @@
 struct sock *tcp_accept(struct sock *sk, int flags)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	struct open_request *req;
+	struct open_request *req, *prev;
 	struct sock *newsk = NULL;
 	int error;
 
@@ -1541,13 +1546,18 @@
 
 	lock_sock(sk);
 
-	req = tcp_find_established(tp);
+	req = tcp_find_established(tp, &prev);
 	if (req) {
 got_new_connect:
-		tcp_synq_unlink(tp, req);
+		tcp_synq_unlink(tp, req, prev);
 		newsk = req->sk;
 		tcp_openreq_free(req);
 		sk->ack_backlog--;
+		/* FIXME: need to check here if socket has already
+		 * an soft_err or err set.
+		 * We have two options here then: reply (this behaviour matches
+		 * Solaris) or return the error to the application (old Linux)
+		 */
 		error = 0;
 out:
 		release_sock(sk);
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.52/linux/net/ipv4/tcp_input.c	Thu Jul 17 10:06:09 1997
+++ linux/net/ipv4/tcp_input.c	Thu Sep  4 13:25:28 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.54 1997/07/10 11:19:39 freitag Exp $
+ * Version:	$Id: tcp_input.c,v 1.56 1997/08/31 08:24:54 freitag Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -39,6 +39,8 @@
  *		David S. Miller	:	Don't allow zero congestion window.
  *		Eric Schenk	:	Fix retransmitter so that it sends
  *					next packet on ack of previous packet.
+ *		Andi Kleen	:	Moved open_request checking here
+ *					and process RSTs for open_requests.
  */
 
 #include <linux/config.h>
@@ -1319,7 +1321,7 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int queued = 0;
 	u32 flg;
-	
+
 	/*
 	 *	Header prediction.
 	 *	The code follows the one in the famous 
@@ -1388,7 +1390,6 @@
 				tcp_send_delayed_ack(sk, HZ/2);
 			else
 				tcp_send_ack(sk);
-
 			return 0;
 		}
 	}
@@ -1402,21 +1403,20 @@
 			}
 			tcp_send_ack(sk);
 			kfree_skb(skb, FREE_READ);
-			return 0;
+			return 0; 
 		}
 	}
 
 	if(th->syn && skb->seq != sk->syn_seq) {
-		printk(KERN_DEBUG "syn in established state\n");
+		SOCK_DEBUG(sk, "syn in established state\n");
 		tcp_reset(sk, skb);
-		kfree_skb(skb, FREE_READ);
 		return 1;
 	}
 	
 	if(th->rst) {
 		tcp_reset(sk,skb);
 		kfree_skb(skb, FREE_READ);
-		return 0;
+		return 0; 
 	}
 	
 	if(th->ack)
@@ -1443,9 +1443,88 @@
 
 	if (!queued)
 		kfree_skb(skb, FREE_READ);
+
 	return 0;
 }
 
+/* Shared between IPv4 and IPv6 now. */
+struct sock *
+tcp_check_req(struct sock *sk, struct sk_buff *skb, void *opt)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct open_request *dummy, *req; 
+
+	/*	assumption: the socket is not in use.
+	 *	as we checked the user count on tcp_rcv and we're
+	 *	running from a soft interrupt.
+	 */
+	req = tp->af_specific->search_open_req(tp, (void *)skb->nh.raw, skb->h.th, 
+					       &dummy); 
+	if (req) {
+		if (req->sk) {
+			/*	socket already created but not
+			 *	yet accepted()...
+			 */
+			sk = req->sk;
+		} else {
+			u32 flg; 
+
+			/* Check for syn retransmission */
+			flg = *(((u32 *)skb->h.th) + 3);
+
+			flg &= __constant_htonl(0x00170000); 
+			if ((flg == __constant_htonl(0x00020000)) &&
+			    (!after(skb->seq, req->rcv_isn))) {
+				/*	retransmited syn.
+				 */
+				req->class->rtx_syn_ack(sk, req); 
+				return NULL;
+			}
+		      
+			/* In theory the packet could be for a cookie, but
+			 * TIME_WAIT should guard us against this. 
+			 * XXX: Nevertheless check for cookies?
+			 */ 
+			if (skb->ack_seq != req->snt_isn+1) {
+				tp->af_specific->send_reset(skb);
+				return NULL; 
+			}
+
+			sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
+			tcp_dec_slow_timer(TCP_SLT_SYNACK);
+			if (sk == NULL)
+				return NULL;
+
+			req->expires = 0UL;
+			req->sk = sk;
+		}
+	} 
+#ifdef CONFIG_SYNCOOKIES
+	else {
+		sk = tp->af_specific->cookie_check(sk, skb, opt); 
+		if (sk == NULL)
+			return NULL; 
+	}
+#endif
+	skb_orphan(skb); 
+	skb_set_owner_r(skb, sk);
+	return sk; 
+}
+
+
+static void tcp_rst_req(struct tcp_opt *tp, struct sk_buff *skb)
+{
+	struct open_request *req, *prev;
+
+	req = tp->af_specific->search_open_req(tp,skb->nh.iph,skb->h.th,&prev);
+	if (!req)
+		return;
+	/* Sequence number check required by RFC793 */
+	if (before(skb->seq, req->snt_isn) || after(skb->seq, req->snt_isn+1))
+		return;
+	tcp_synq_unlink(tp, req, prev);
+}
+
 /*
  *	This function implements the receiving procedure of RFC 793.
  *	It's called from both tcp_v4_rcv and tcp_v6_rcv and should be
@@ -1461,14 +1540,16 @@
 	/* state == CLOSED, hash lookup always fails, so no worries. -DaveM */
 	switch (sk->state) {
 	case TCP_LISTEN:
-		if (th->rst)			
+		if (th->rst) {
+			tcp_rst_req(tp, skb);  
 			goto discard;
+		}
 
 		/* These use the socket TOS.. 
 		 * might want to be the received TOS 
 		 */
 		if(th->ack)  
-			return 1; /* send reset */
+			return 1; 
 		
 		if(th->syn) {
 			if(tp->af_specific->conn_request(sk, skb, opt, 0) < 0)
@@ -1490,7 +1571,7 @@
 			 * against this problem. So, we drop the data
 			 * in the interest of security over speed.
 			 */
-			return 0;
+			goto discard;
 		}
 		
 		goto discard;
@@ -1635,7 +1716,8 @@
 
 			if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0)
 				return 1;
-			return 0;
+
+			goto discard;
 		}
 
 		break;
@@ -1794,10 +1876,10 @@
 	tcp_data_snd_check(sk);
 	tcp_ack_snd_check(sk);
 
-	if (queued)
-		return 0;
+	if (!queued) { 
 discard:
-	kfree_skb(skb, FREE_READ);
+		kfree_skb(skb, FREE_READ);
+	}
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.52/linux/net/ipv4/tcp_ipv4.c	Thu Aug 14 20:49:18 1997
+++ linux/net/ipv4/tcp_ipv4.c	Thu Sep  4 15:42:20 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.52 1997/07/23 15:19:10 freitag Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.61 1997/09/02 09:46:55 freitag Exp $
  *
  *		IPv4 specific functions
  *
@@ -33,6 +33,13 @@
  *		Andi Kleen :		Add support for syncookies and fixed
  *					some bugs: ip options weren't passed to
  *					the TCP layer, missed a check for an ACK bit.
+ *		Andi Kleen :		Implemented fast path mtu discovery.
+ *	     				Fixed many serious bugs in the
+ *					open_request handling and moved
+ *					most of it into the af independent code.
+ *					Added tail drop and some other bugfixes.
+ *					Added new listen sematics (ifdefed by
+ *					NEW_LISTEN for now)
  */
 
 #include <linux/config.h>
@@ -53,6 +60,9 @@
 extern int sysctl_tcp_window_scaling;
 extern int sysctl_tcp_syncookies;
 
+/* Define this to check TCP sequence numbers in ICMP packets. */
+#define ICMP_PARANOIA 1
+
 static void tcp_v4_send_reset(struct sk_buff *skb);
 
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
@@ -517,9 +527,11 @@
 	}
 
 	if (!tcp_unique_address(rt->rt_src, sk->num, rt->rt_dst,
-				usin->sin_port))
+				usin->sin_port)) {
+		ip_rt_put(rt);
 		return -EADDRNOTAVAIL;
-  
+	}
+
 	lock_sock(sk);
 	sk->dst_cache = &rt->u.dst;
 	sk->daddr = rt->rt_dst;
@@ -673,6 +685,76 @@
 	return retval;
 }
 
+
+/*
+ * Do a linear search in the socket open_request list. 
+ * This should be replaced with a global hash table.
+ */
+static struct open_request *tcp_v4_search_req(struct tcp_opt *tp, 
+				      void *header,
+				      struct tcphdr *th,
+				      struct open_request **prevp)
+{
+	struct iphdr *iph = header;
+	struct open_request *req, *prev;  
+	__u16 rport = th->source; 
+
+	/*	assumption: the socket is not in use.
+	 *	as we checked the user count on tcp_rcv and we're
+	 *	running from a soft interrupt.
+	 */
+	prev = (struct open_request *) (&tp->syn_wait_queue); 
+	for (req = prev->dl_next; req; req = req->dl_next) {
+		if (req->af.v4_req.rmt_addr == iph->saddr &&
+		    req->af.v4_req.loc_addr == iph->daddr &&
+		    req->rmt_port == rport) {
+			*prevp = prev; 
+			return req; 
+		}
+		prev = req; 
+	}
+	return NULL; 
+}
+
+
+/* 
+ * This routine does path mtu discovery as defined in RFC1197.
+ */
+static inline void do_pmtu_discovery(struct sock *sk,
+				    struct iphdr *ip,
+				    struct tcphdr *th)
+{
+	int new_mtu; 
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+	/* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
+	 * send out by Linux are always <576bytes so they should go through
+	 * unfragmented).
+	 */
+	if (sk->state == TCP_LISTEN)
+		return; 
+
+	/* We don't check in the destentry if pmtu discovery is forbidden
+	 * on this route. We just assume that no packet_to_big packets
+	 * are send back when pmtu discovery is not active.
+     	 * There is a small race when the user changes this flag in the
+	 * route, but I think that's acceptable.
+	 */
+	if (sk->ip_pmtudisc != IP_PMTUDISC_DONT && sk->dst_cache) {
+		new_mtu = sk->dst_cache->pmtu - 
+			(ip->ihl<<2) - tp->tcp_header_len; 
+		if (new_mtu < sk->mss && new_mtu > 0) {
+			sk->mss = new_mtu;
+			/* Resend the TCP packet because it's  
+			 * clear that the old packet has been
+			 * dropped. This is the new "fast" path mtu
+			 * discovery.
+			 */
+			tcp_simple_retransmit(sk);
+		}
+	}
+}
+
 /*
  * This routine is called by the ICMP module when it gets some
  * sort of error condition.  If err < 0 then the socket should
@@ -685,61 +767,125 @@
 void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
 {
 	struct iphdr *iph = (struct iphdr*)dp;
-	struct tcphdr *th = (struct tcphdr*)(dp+(iph->ihl<<2));
+	struct tcphdr *th; 
 	struct tcp_opt *tp;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 	struct sock *sk;
+	__u32 seq; 
 
-	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source);
-
-	if (sk == NULL)
+#if 0
+	/* check wrong - icmp.c should pass in len */
+	if (skb->len < 8+(iph->ihl << 2)+sizeof(struct tcphdr)) {
+		icmp_statistics.IcmpInErrors++;
 		return;
+	}
+#endif
+
+	th = (struct tcphdr*)(dp+(iph->ihl<<2));
+
+	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source);
+	if (sk == NULL) {
+		icmp_statistics.IcmpInErrors++;
+		return; 
+	}
 
+	/* pointless, because we have no way to retry when sk is locked.
+	   But the socket should be really locked here for better interaction
+	   with the socket layer. This needs to be solved for SMP
+	   (I would prefer an "ICMP backlog"). */
+	/* lock_sock(sk); */ 
 	tp = &sk->tp_pinfo.af_tcp;
-	if (type == ICMP_SOURCE_QUENCH) {
+
+	seq = ntohl(th->seq);
+
+#ifdef ICMP_PARANOIA
+	if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) {
+		if (net_ratelimit()) 
+			printk(KERN_DEBUG "icmp packet outside the tcp window:"
+					  " s:%d %u,%u,%u\n",
+			       (int)sk->state, seq, tp->snd_una, tp->snd_nxt); 
+		goto out; 
+	}
+#endif
+
+	switch (type) {
+	case ICMP_SOURCE_QUENCH:
 		tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
 		tp->snd_cwnd = tp->snd_ssthresh;
 		tp->high_seq = tp->snd_nxt;
-		return;
-	}
-
-	if (type == ICMP_PARAMETERPROB) {
+		goto out;
+	case ICMP_PARAMETERPROB:
 		sk->err=EPROTO;
 		sk->error_report(sk);
-	}
-
-	/* FIXME: What about the IP layer options size here? */
-	/* FIXME: add a timeout here, to cope with broken devices that
-		  drop all DF=1 packets. Do some more sanity checking 
-		  here to prevent DOS attacks?
-		  This code should kick the tcp_output routine to
-		  retransmit a packet immediately because we know that
-		  the last packet has been dropped. -AK */
-	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-		if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
-			int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - tp->tcp_header_len;
-			if (new_mtu < sk->mss && new_mtu > 0) {
-				sk->mss = new_mtu;
-			}
+		break; 
+	case ICMP_DEST_UNREACH:
+		if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
+			do_pmtu_discovery(sk, iph, th); 
+			goto out; 
 		}
-		return;
+		break; 
 	}
 
 	/* If we've already connected we will keep trying
 	 * until we time out, or the user gives up.
 	 */
-	if (code <= NR_ICMP_UNREACH) {
-		if(icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+	if (code <= NR_ICMP_UNREACH) { 
+		int fatal = 0; 
+
+		if (sk->state == TCP_LISTEN) {
+			struct open_request *req, *prev;
+	
+			/* Prevent race conditions with accept()
+			 * icmp is unreliable. 
+			 * This is the easiest solution for now - for
+			 * very big servers it might prove inadequate.
+			 */
+			if (sk->sock_readers) {
+				/* XXX: add a counter here to profile this. 
+				 * If too many ICMPs get dropped on busy
+				 * servers this needs to be solved differently.
+				 */
+				goto out;
+			}
+ 
+			req = tcp_v4_search_req(tp, iph, th, &prev); 
+			if (!req)
+				goto out;
+#ifdef ICMP_PARANOIA
+			if (seq != req->snt_isn) {
+				if (net_ratelimit())
+					printk(KERN_DEBUG "icmp packet for openreq "
+					       "with wrong seq number:%d:%d\n",
+					       seq, req->snt_isn);
+				goto out;
+			}
+#endif
+ 			if (req->sk) {	/* not yet accept()ed */
+				sk = req->sk;
+			} else {
+				tcp_synq_unlink(tp, req, prev);
+				tcp_openreq_free(req);
+				fatal = 1; 
+			}
+		} else if (sk->state == TCP_SYN_SENT 
+			   || sk->state == TCP_SYN_RECV)
+			fatal = 1; 
+		
+		if(icmp_err_convert[code].fatal || fatal) {
 			sk->err = icmp_err_convert[code].errno;
-			if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+			if (fatal) {
 				tcp_statistics.TcpAttemptFails++;
-				tcp_set_state(sk,TCP_CLOSE);
+				if (sk->state != TCP_LISTEN)
+					tcp_set_state(sk,TCP_CLOSE);
 				sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
 			}
 		} else	/* Only an error on timeout */
 			sk->err_soft = icmp_err_convert[code].errno;
 	}
+
+out:
+	/* release_sock(sk); */
 }
 
 /* This routine computes an IPv4 TCP checksum. */
@@ -872,16 +1018,18 @@
 	th->dest = req->rmt_port;
 	skb->seq = req->snt_isn;
 	skb->end_seq = skb->seq + 1;
-	th->seq = ntohl(skb->seq);
+	th->seq = htonl(skb->seq);
 	th->ack_seq = htonl(req->rcv_isn + 1);
-	if (req->rcv_wnd == 0) {
+	if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
+		__u8 rcv_wscale; 
 		/* Set this up on the first call only */
 		req->window_clamp = skb->dst->window;
 		tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
 			&req->rcv_wnd,
 			&req->window_clamp,
 			req->wscale_ok,
-			&req->rcv_wscale);
+			&rcv_wscale);
+		req->rcv_wscale = rcv_wscale; 
 	}
 	th->window = htons(req->rcv_wnd);
 
@@ -912,11 +1060,34 @@
 			sizeof(struct ip_options) + req->af.v4_req.opt->optlen);
 }
 
+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+	static unsigned long warntime;
+	
+	if (jiffies - warntime > HZ*60) {
+		warntime = jiffies;
+		printk(KERN_INFO 
+		       "possible SYN flooding on port %d. Sending cookies.\n",  
+		       ntohs(skb->h.th->dest));
+	}
+}
+
+int sysctl_max_syn_backlog = 1024; 
+int sysctl_tcp_syn_taildrop = 1;
+
 struct or_calltable or_ipv4 = {
 	tcp_v4_send_synack,
 	tcp_v4_or_free
 };
 
+#ifdef NEW_LISTEN
+#define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */
+#define BACKLOGMAX(sk) sysctl_max_syn_backlog
+#else
+#define BACKLOG(sk) ((sk)->ack_backlog)
+#define BACKLOGMAX(sk) ((sk)->max_ack_backlog)
+#endif
+
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, 
 						__u32 isn)
 {
@@ -936,35 +1107,33 @@
 	if (sk->dead) 
 		goto dead; 
 
-	if (sk->ack_backlog >= sk->max_ack_backlog) {
+	/* XXX: Check against a global syn pool counter. */
+	if (BACKLOG(sk) > BACKLOGMAX(sk)) {
 #ifdef CONFIG_SYN_COOKIES
 		if (sysctl_tcp_syncookies) {
-			static unsigned long warntime;
-
-			if (jiffies - warntime > HZ*60) {
-				warntime = jiffies;
-				printk(KERN_INFO 
-				       "possible SYN flooding on port %d. Sending cookies.\n", ntohs(skb->h.th->dest));
-			}
+			syn_flood_warning(skb);
 			want_cookie = 1; 
 		} else 
 #endif
-		{
-			SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog,
-				   sk->max_ack_backlog);
+		if (sysctl_tcp_syn_taildrop) {
+			struct open_request *req;
+
+			req = tcp_synq_unlink_tail(&sk->tp_pinfo.af_tcp);
+			tcp_openreq_free(req);
 			tcp_statistics.TcpAttemptFails++;
-			goto exit;
+		} else {
+			goto error;
 		}
 	} else { 
 		if (isn == 0)
 			isn = tcp_v4_init_sequence(sk, skb);
-		sk->ack_backlog++;
+		BACKLOG(sk)++;
 	}
 
 	req = tcp_openreq_alloc();
 	if (req == NULL) {
-		tcp_statistics.TcpAttemptFails++;
-		goto exit;
+		if (!want_cookie) BACKLOG(sk)--;
+		goto error;
 	}
 
 	req->rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
@@ -972,7 +1141,7 @@
 	req->rcv_isn = skb->seq;
  	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
-	tcp_parse_options(th,&tp, want_cookie);
+	tcp_parse_options(th,&tp,want_cookie);
 	if (tp.saw_tstamp)
 		req->ts_recent = tp.rcv_tsval;
 	req->mss = tp.in_mss;
@@ -1023,15 +1192,16 @@
 	}
 
 	sk->data_ready(sk, 0);
-
 exit:
-	kfree_skb(skb, FREE_READ);
 	return 0;
 
 dead:
 	SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
 	tcp_statistics.TcpAttemptFails++;
 	return -ENOTCONN;
+error:
+	tcp_statistics.TcpAttemptFails++;
+	goto exit;
 }
 
 struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
@@ -1042,13 +1212,16 @@
 	struct sock *newsk;
 	int snd_mss;
 
-	newsk = sk_alloc(GFP_ATOMIC);
-	if (newsk == NULL) {
-		if (dst) 
-			dst_release(dst);
-		return NULL;
-	}
-
+#ifdef NEW_LISTEN
+	if (sk->ack_backlog > sk->max_ack_backlog)
+		goto exit; /* head drop */
+#endif
+	newsk = sk_alloc(AF_INET, GFP_ATOMIC);
+	if (!newsk) 
+		goto exit;
+#ifdef NEW_LISTEN
+	sk->ack_backlog++;
+#endif
 	memcpy(newsk, sk, sizeof(*newsk));
 
 	/* Or else we die! -DaveM */
@@ -1132,7 +1305,7 @@
 				    newsk->opt && newsk->opt->srr ? 
 				    newsk->opt->faddr : newsk->daddr,
 				    newsk->saddr, newsk->ip_tos, NULL)) {
-			kfree(newsk);
+			sk_free(newsk);
 			return NULL;
 		}
 	        dst = &rt->u.dst;
@@ -1179,73 +1352,11 @@
 	tcp_v4_hash(newsk);
 	add_to_prot_sklist(newsk);
 	return newsk;
-}
-
-static inline struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	struct open_request *req = tp->syn_wait_queue;
-
-	/*	assumption: the socket is not in use.
-	 *	as we checked the user count on tcp_rcv and we're
-	 *	running from a soft interrupt.
-	 */
-	if(!req) {
-#ifdef CONFIG_SYN_COOKIES
-		goto checkcookie; 
-#else
-		return sk;
-#endif
-	}
 
-	while(req) {
-		if (req->af.v4_req.rmt_addr == skb->nh.iph->saddr &&
-		    req->af.v4_req.loc_addr == skb->nh.iph->daddr &&
-		    req->rmt_port == skb->h.th->source) {
-			u32 flg;
-
-			if (req->sk) {
-				/*	socket already created but not
-				 *	yet accepted()...
-				 */
-				sk = req->sk;
-				goto ende;
-			}
-
-			/* Check for syn retransmission */
-			flg = *(((u32 *)skb->h.th) + 3);
-			flg &= __constant_htonl(0x001f0000);
-			if ((flg == __constant_htonl(0x00020000)) &&
-			    (!after(skb->seq, req->rcv_isn))) {
-				/*	retransmited syn
-				 *	FIXME: must send an ack
-				 */
-				return NULL;
-			}
-
-			if (!skb->h.th->ack)
-				return sk; 
-
-			sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
-			tcp_dec_slow_timer(TCP_SLT_SYNACK);
-			if (sk == NULL)
-				return NULL;
-
-			req->expires = 0UL;
-			req->sk = sk;
-			goto ende;
-		}
-		req = req->dl_next;
-	}
-
-#ifdef CONFIG_SYN_COOKIES
-checkcookie:       
-	sk = cookie_v4_check(sk, skb, opt);
-#endif
-ende:	skb_orphan(skb);
-	if (sk)
-		skb_set_owner_r(skb, sk);
-	return sk;
+exit:
+	if (dst) 
+		dst_release(dst);
+	return NULL;
 }
 
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
@@ -1256,47 +1367,49 @@
 	 *	socket locking is here for SMP purposes as backlog rcv
 	 *	is currently called with bh processing disabled.
 	 */
-	lock_sock(sk);
-
-	if (sk->state == TCP_ESTABLISHED)
-	{
+	lock_sock(sk); 
+	
+	if (sk->state == TCP_ESTABLISHED) { /* Fast path */
 		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
 			goto reset;
-		goto ok;
-	}
-
-	/*
-	 * We check packets with only the SYN bit set against the
-	 * open_request queue too: This increases connection latency a bit,
-	 * but is required to detect retransmitted SYNs.
-	 *
-	 * The ACK/SYN bit check is probably not needed here because
-	 * it is checked later again (we play save now).
-	 */
-	if (sk->state == TCP_LISTEN && (skb->h.th->ack || skb->h.th->syn)) {
-	   	struct sock *nsk;
+	} else {
+		/* Check for embryonic sockets (open_requests)
+		 * We check packets with only the SYN bit set
+		 * against the open_request queue too: This
+		 * increases connection latency a bit, but is
+		 * required to detect retransmitted SYNs.  
+		 */
+		/* FIXME: need to check for multicast syns
+		 * here to satisfy RFC1122 4.2.3.10, p. 104:
+		 * discard bcast/mcast SYN. I'm not sure if
+		 * they're filtered out at the IP layer (I
+		 * think not) 
+		 */
+		if (sk->state == TCP_LISTEN && 
+		    ((u32 *)skb->h.th)[3] & __constant_htonl(0x00120000)) {
+			struct sock *nsk;
+			
+			/* Find possible connection requests. */
+			nsk = tcp_check_req(sk, skb, &(IPCB(skb)->opt));
+			if (nsk == NULL)
+				goto discard;
+			
+			release_sock(sk);
+			lock_sock(nsk); 
+			sk = nsk; 
+		}
 
-	   	/* Find possible connection requests. */
-	   	nsk = tcp_v4_check_req(sk, skb, &(IPCB(skb)->opt));
-	  	if (nsk == NULL)
-			goto discard_it;
-	    
-	   	release_sock(sk);
-	 	lock_sock(nsk);
-		sk = nsk;
+		if (tcp_rcv_state_process(sk, skb, skb->h.th, 
+					  &(IPCB(skb)->opt), skb->len))
+			goto reset; 
 	}
-
-	if (tcp_rcv_state_process(sk, skb, skb->h.th, &(IPCB(skb)->opt), skb->len) == 0)
-		goto ok;
+	release_sock(sk); 
+	return 0;
 
 reset:
 	tcp_v4_send_reset(skb);
-
-discard_it:
-	/* Discard frame. */
-	kfree_skb(skb, FREE_READ);
-
-ok:
+discard:
+	kfree_skb(skb, FREE_READ); 
 	release_sock(sk);
 	return 0;
 }
@@ -1327,8 +1440,8 @@
 	case CHECKSUM_HW:
 		if (tcp_v4_check(th,len,saddr,daddr,skb->csum)) {
 			struct iphdr * iph = skb->nh.iph;
-			printk(KERN_DEBUG "TCPv4 bad checksum from %08x:%04x to %08x:%04x, len=%d/%d/%d\n",
-			       saddr, ntohs(th->source), daddr,
+			printk(KERN_DEBUG "TCPv4 bad checksum from %I:%04x to %I:%04x, len=%d/%d/%d\n",
+			       &saddr, ntohs(th->source), &daddr,
 			       ntohs(th->dest), len, skb->len, ntohs(iph->tot_len));
 					goto discard_it;
 		}
@@ -1435,6 +1548,12 @@
 	ip_getsockopt,
 	v4_addr2sockaddr,
 	tcp_v4_send_reset,
+	tcp_v4_search_req,
+#ifdef CONFIG_SYNCOOKIES
+	cookie_v4_check,
+#else
+	NULL,
+#endif
 	sizeof(struct sockaddr_in)
 };
 
@@ -1461,6 +1580,7 @@
 	tp->snd_wscale = 0;
 	tp->sacks = 0;
 	tp->saw_tstamp = 0;
+	tp->syn_backlog = 0;
 
 	/*
 	 * See draft-stevens-tcpca-spec-01 for discussion of the
@@ -1484,8 +1604,7 @@
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	/* Init SYN queue. */
-	tp->syn_wait_queue = NULL;
-	tp->syn_wait_last = &tp->syn_wait_queue;
+	tcp_synq_init(tp);
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific;
 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.52/linux/net/ipv4/tcp_output.c	Tue May 13 22:41:24 1997
+++ linux/net/ipv4/tcp_output.c	Thu Sep  4 13:25:28 1997
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.43 1997/04/27 19:24:43 schenk Exp $
+ * Version:	$Id: tcp_output.c,v 1.46 1997/08/24 16:22:28 freitag Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -306,11 +306,13 @@
 		tp->packets_out--;
 		return -1;
 	} else {
+#if 0
 		/* If tcp_fragment succeded then
 		 * the send head is the resulting
 		 * fragment
 		 */
 		tp->send_head = skb->next;
+#endif
 	}
 	return 0;
 }
@@ -365,6 +367,7 @@
 		if (size - (th->doff << 2) > sk->mss) {
 			if (tcp_wrxmit_frag(sk, skb, size))
 				break;
+			size = skb->len - (((unsigned char*)th) - skb->data);
 		}
 
 		tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
@@ -620,11 +623,31 @@
 	return 0;
 }
 
+/* Do a simple retransmit without using the backoff mechanisms in
+ * tcp_timer. This is used to speed up path mtu recovery. Note that
+ * these simple retransmit aren't counted in the usual tcp retransmit
+ * backoff counters. 
+ * The socket is already locked here.
+ */ 
+void tcp_simple_retransmit(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+	/* Clear delay ack timer. */
+ 	tcp_clear_xmit_timer(sk, TIME_DACK);
+ 
+ 	tp->retrans_head = NULL; 
+ 	/* Don't muck with the congestion window here. */
+ 	tp->dup_acks = 0;
+ 	tp->high_seq = tp->snd_nxt;
+ 	/* FIXME: make the current rtt sample invalid */
+ 	tcp_do_retransmit(sk, 0); 
+}
 
 /*
  *	A socket has timed out on its send queue and wants to do a
  *	little retransmitting.
- *	retransmit_head can be different from the head of the write_queue
+ *	retrans_head can be different from the head of the write_queue
  *	if we are doing fast retransmit.
  */
 
diff -u --recursive --new-file v2.1.52/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.52/linux/net/ipv4/tcp_timer.c	Mon Jun 16 16:36:02 1997
+++ linux/net/ipv4/tcp_timer.c	Thu Sep  4 13:25:28 1997
@@ -447,6 +447,7 @@
 			
 			/* TCP_LISTEN is implied. */
 			if (!sk->sock_readers && tp->syn_wait_queue) {
+				struct open_request *prev = (struct open_request *)(&tp->syn_wait_queue);
 				struct open_request *req = tp->syn_wait_queue;
 				do {
 					struct open_request *conn;
@@ -454,13 +455,15 @@
 					conn = req;
 					req = req->dl_next;
 
-					if (conn->sk)
-						continue;
+					if (conn->sk) {
+						prev = conn; 
+						continue; 
+					}
 
 					if ((long)(now - conn->expires) <= 0)
 						break;
 
-					tcp_synq_unlink(tp, conn);
+					tcp_synq_unlink(tp, conn, prev);
 					if (conn->retrans >= sysctl_tcp_retries1) {
 #ifdef TCP_DEBUG
 						printk(KERN_DEBUG "syn_recv: "
@@ -475,6 +478,7 @@
 							break;
 					} else {
 						__u32 timeo;
+						struct open_request *op; 
 
 						(*conn->class->rtx_syn_ack)(sk, conn);
 
@@ -487,8 +491,12 @@
 							     << conn->retrans),
 							    120*HZ);
 						conn->expires = now + timeo;
+						op = prev->dl_next; 
 						tcp_synq_queue(tp, conn);
+						if (op != prev->dl_next)
+							prev = prev->dl_next;
 					}
+					/* old prev still valid here */
 				} while (req);
 			}
 			sk = sk->next;
diff -u --recursive --new-file v2.1.52/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.52/linux/net/ipv6/addrconf.c	Thu May 15 16:48:06 1997
+++ linux/net/ipv6/addrconf.c	Thu Sep  4 13:25:29 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.20 1997/05/07 09:40:04 davem Exp $
+ *	$Id: addrconf.c,v 1.21 1997/08/09 03:44:24 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -627,6 +627,39 @@
 	else
 		ip6_rt_addr_add(&ifp->addr, dev);
 
+	return 0;
+}
+
+int addrconf_del_ifaddr(void *arg)
+{
+	struct in6_ifreq ireq;
+	struct inet6_ifaddr *ifp;
+	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)
+		return -EINVAL;
+
+	if ((idev = ipv6_get_idev(dev)) == NULL)
+		return -EINVAL;
+
+	scope = ipv6_addr_scope(&ireq.ifr6_addr);
+
+	for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+	  if (ifp->scope == scope && 
+	    (!memcmp(&ireq.ifr6_addr, &ifp->addr, sizeof(struct in6_addr)))) {
+	    ipv6_del_addr(ifp);
+	    break;
+	  }
+	}
+	
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.52/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.52/linux/net/ipv6/af_inet6.c	Mon Jun 16 16:36:02 1997
+++ linux/net/ipv6/af_inet6.c	Thu Sep  4 13:25:29 1997
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.19 1997/06/02 14:40:40 alan Exp $
+ *	$Id: af_inet6.c,v 1.21 1997/08/20 11:25:00 alan Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -71,7 +71,7 @@
 	struct sock *sk;
 	struct proto *prot;
 
-	sk = sk_alloc(GFP_KERNEL);
+	sk = sk_alloc(AF_INET6, GFP_KERNEL);
 	if (sk == NULL) 
 		goto do_oom;
 
@@ -167,10 +167,6 @@
 	return -ENOBUFS;
 }
 
-static int inet6_dup(struct socket *newsock, struct socket *oldsock)
-{
-	return(inet6_create(newsock, oldsock->sk->protocol));
-}
 
 /* bind for INET6 API */
 static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
@@ -249,11 +245,6 @@
 	return inet_release(sock, peer);
 }
 
-static int inet6_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return(-EOPNOTSUPP);
-}
-
 /*
  *	This does both peername and sockname.
  */
@@ -364,11 +355,14 @@
 	case SIOCSIFSLAVE:
 	case SIOCGIFSLAVE:
 	case SIOGIFINDEX:
-
+	case SIOGIFNAME:
+	case SIOCGIFCOUNT:
 		return(dev_ioctl(cmd,(void *) arg));		
 		
 	case SIOCSIFADDR:
 		return addrconf_add_ifaddr((void *) arg);
+	case SIOCDIFADDR:
+		return addrconf_del_ifaddr((void *) arg);
 	case SIOCSIFDSTADDR:
 		return addrconf_set_dstaddr((void *) arg);
 	default:
@@ -387,11 +381,11 @@
 struct proto_ops inet6_stream_ops = {
 	AF_INET6,
 
-	inet6_dup,
+	sock_no_dup,
 	inet6_release,
 	inet6_bind,
 	inet_stream_connect,		/* ok		*/
-	inet6_socketpair,		/* a do nothing	*/
+	sock_no_socketpair,		/* a do nothing	*/
 	inet_accept,			/* ok		*/
 	inet6_getname, 
 	inet_poll,			/* ok		*/
@@ -408,11 +402,11 @@
 struct proto_ops inet6_dgram_ops = {
 	AF_INET6,
 
-	inet6_dup,
+	sock_no_dup,
 	inet6_release,
 	inet6_bind,
 	inet_dgram_connect,		/* ok		*/
-	inet6_socketpair,		/* a do nothing	*/
+	sock_no_socketpair,		/* a do nothing	*/
 	inet_accept,			/* ok		*/
 	inet6_getname, 
 	datagram_poll,			/* ok		*/
diff -u --recursive --new-file v2.1.52/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.52/linux/net/ipv6/tcp_ipv6.c	Mon Aug  4 16:25:41 1997
+++ linux/net/ipv6/tcp_ipv6.c	Thu Sep  4 13:25:29 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.35 1997/07/23 15:18:04 freitag Exp $
+ *	$Id: tcp_ipv6.c,v 1.37 1997/08/22 19:15:40 freitag Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -536,6 +536,7 @@
 	return retval;
 }
 
+/* XXX: this functions needs to be updated like tcp_v4_err. */
 void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
 		struct in6_addr *saddr, struct in6_addr *daddr,
 		struct inet6_protocol *protocol)
@@ -553,7 +554,7 @@
 
 	np = &sk->net_pinfo.af_inet6;
 
-	if (type == ICMPV6_PKT_TOOBIG) {
+	if (type == ICMPV6_PKT_TOOBIG && sk->state != TCP_LISTEN) {
 		/* icmp should have updated the destination cache entry */
 
 		dst_check(&np->dst, np->dst_cookie);
@@ -579,11 +580,12 @@
 		else
 			sk->mtu = np->dst->pmtu;
 
+		release_sock(sk);
 		return;
 	}
 
+	/* FIXME: This is wrong. Need to check for open_requests here. */
 	opening = (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV);
-	
 	if (icmpv6_err_convert(type, code, &err) || opening) {
 		sk->err = err;
 
@@ -657,13 +659,15 @@
         }
 
 	if (req->rcv_wnd == 0) {
+		__u8 rcv_wscale;
 		/* Set this up on the first call only */
 		req->window_clamp = 0; /* FIXME: should be in dst cache */
 		tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
 			&req->rcv_wnd,
 			&req->window_clamp,
 			req->wscale_ok,
-			&req->rcv_wscale);
+			&rcv_wscale);
+		req->rcv_wscale = rcv_wscale; 
 	}
 	th->window = htons(req->rcv_wnd);
 
@@ -764,7 +768,6 @@
 	sk->data_ready(sk, 0);
 
 exit:
-	kfree_skb(skb, FREE_READ);
 	return 0;
 }
 
@@ -814,7 +817,7 @@
 		return newsk;
 	}
 
-	newsk = sk_alloc(GFP_ATOMIC);
+	newsk = sk_alloc(AF_INET6, GFP_ATOMIC);
 	if (newsk == NULL) {
 	        if (dst)
 		    dst_release(dst);
@@ -1021,58 +1024,30 @@
 	tcp_statistics.TcpOutSegs++;
 }
 
-struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	struct open_request *req = tp->syn_wait_queue;
+static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
+					      void *header, 
+					      struct tcphdr *th,
+					      struct open_request **prevp)
+{
+	struct ipv6hdr *ip6h = header; 
+	struct open_request *req, *prev; 
+	__u16 rport = th->source; 
 
 	/*	assumption: the socket is not in use.
 	 *	as we checked the user count on tcp_rcv and we're
 	 *	running from a soft interrupt.
 	 */
-	if (!req)
-		return sk;
-
-	while(req) {
-		if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr) &&
-		    !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr) &&
-		    req->rmt_port == skb->h.th->source) {
-			u32 flg;
-
-			if (req->sk) {
-				printk(KERN_DEBUG "BUG: syn_recv:"
-				       "socket exists\n");
-				break;
-			}
-
-			/* Check for syn retransmission */
-			flg = *(((u32 *)skb->h.th) + 3);
-			flg &= __constant_htonl(0x001f0000);
-
-			if ((flg == __constant_htonl(0x00020000)) &&
-			    (!after(skb->seq, req->rcv_isn))) {
-				/*	retransmited syn
-				 *	FIXME: must send an ack
-				 */
-				return NULL;
-			}
-
-			skb_orphan(skb);
-			sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
-
-			tcp_dec_slow_timer(TCP_SLT_SYNACK);
-
-			if (sk == NULL)
-				return NULL;
-
-			skb_set_owner_r(skb, sk);
-			req->expires = 0UL;
-			req->sk = sk;
-			break;
+	prev = (struct open_request *) (&tp->syn_wait_queue); 
+	for (req = prev->dl_next; req; req = req->dl_next) {
+		if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &ip6h->saddr) &&
+		    !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &ip6h->daddr) &&
+		    req->rmt_port == rport) {
+			*prevp = prev; 
+			return req; 
 		}
-		req = req->dl_next;
+		prev = req; 
 	}
-	return sk;
+	return NULL; 
 }
 
 int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
@@ -1149,10 +1124,11 @@
 	/*
 	 *	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);
+		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 		if (after(skb->seq, tp->rcv_nxt) ||
 		    after(skb->ack_seq, tp->snd_una)) {
@@ -1168,18 +1144,19 @@
 
 	skb_set_owner_r(skb, sk);
 
+	/* I don't understand why lock_sock()/release_sock() is not  
+	 * called here. IPv4 does this. It looks like a bug to me. -AK
+	 */
 	if (sk->state == TCP_ESTABLISHED) {
 		if (tcp_rcv_established(sk, skb, th, len))
 			goto no_tcp_socket;
 		return 0;
 	}
 
-	if (sk->state == TCP_LISTEN) {
-		/*
-		 *	find possible connection requests
-		 */
-		sk = tcp_v6_check_req(sk, skb);
 
+	if (sk->state == TCP_LISTEN && 
+	    ((u32 *)th)[3] & __constant_htonl(0x00120000)) {
+		sk = tcp_check_req(sk, skb, opt); 
 		if (sk == NULL)
 			goto discard_it;
 	}
@@ -1308,6 +1285,12 @@
 	sin6->sin6_port	= sk->dummy_th.dest;
 }
 
+static struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb,
+				    void *opt)
+{
+	return sk;  /* dummy */
+}
+
 static struct tcp_func ipv6_specific = {
 	tcp_v6_build_header,
 	tcp_v6_xmit,
@@ -1320,6 +1303,8 @@
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
 	tcp_v6_reply_reset,
+	tcp_v6_search_req,
+	/* not implemented yet: */ cookie_v6_check,
 	sizeof(struct sockaddr_in6)
 };
 
@@ -1339,6 +1324,8 @@
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
 	tcp_v6_reply_reset,
+	tcp_v6_search_req,
+	cookie_v6_check, /* not implemented yet. */
 	sizeof(struct sockaddr_in6)
 };
 
@@ -1360,11 +1347,20 @@
 	tp->rcv_wnd = 0;
 	tp->in_mss = 536;
 	/* tp->rcv_wnd = 8192; */
+	tp->tstamp_ok = 0;
+	tp->sack_ok = 0;
+	tp->wscale_ok = 0;
+	tp->snd_wscale = 0;
+	tp->sacks = 0;
+	tp->saw_tstamp = 0;
+	tp->syn_backlog = 0;
 
 	/* start with only sending one packet at a time. */
 	tp->snd_cwnd = 1;
 	tp->snd_ssthresh = 0x7fffffff;
 
+
+
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
 
@@ -1384,8 +1380,7 @@
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
 	/* Init SYN queue. */
-	tp->syn_wait_queue = NULL;
-	tp->syn_wait_last = &tp->syn_wait_queue;
+	tcp_synq_init(tp);
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 
diff -u --recursive --new-file v2.1.52/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.52/linux/net/ipx/af_ipx.c	Mon Jun 16 16:36:02 1997
+++ linux/net/ipx/af_ipx.c	Thu Sep  4 13:25:29 1997
@@ -1743,7 +1743,7 @@
 static int ipx_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
-	sk=sk_alloc(GFP_KERNEL);
+	sk=sk_alloc(AF_IPX, GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
@@ -1776,11 +1776,6 @@
 	return(0);
 }
 
-static int ipx_dup(struct socket *newsock,struct socket *oldsock)
-{
-	return(ipx_create(newsock,SOCK_DGRAM));
-}
-
 static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc)
 {
 	unsigned short	socketNum = intrfc->if_sknum;
@@ -1933,11 +1928,6 @@
 	return 0;
 }
 
-static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return(-EOPNOTSUPP);
-}
-
 static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	if(newsock->sk) {
@@ -2283,11 +2273,11 @@
 static struct proto_ops ipx_dgram_ops = {
 	AF_IPX,
 
-	ipx_dup,
+	sock_no_dup,
 	ipx_release,
 	ipx_bind,
 	ipx_connect,
-	ipx_socketpair,
+	sock_no_socketpair,
 	ipx_accept,
 	ipx_getname,
 	datagram_poll,
diff -u --recursive --new-file v2.1.52/linux/net/netlink.c linux/net/netlink.c
--- v2.1.52/linux/net/netlink.c	Thu Jul 17 10:06:09 1997
+++ linux/net/netlink.c	Thu Sep  4 13:25:29 1997
@@ -37,8 +37,8 @@
 static int rdq_size[MAX_LINKS];
 static struct wait_queue *read_space_wait[MAX_LINKS];
 
-static unsigned active_map = 0;
-static unsigned open_map = 0;
+static unsigned long active_map = 0;
+static unsigned long open_map = 0;
 
 /*
  *	Device operations
diff -u --recursive --new-file v2.1.52/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.52/linux/net/netrom/af_netrom.c	Mon Jul  7 08:19:59 1997
+++ linux/net/netrom/af_netrom.c	Thu Sep  4 13:25:29 1997
@@ -98,7 +98,7 @@
 	struct sock *sk;
 	nr_cb *nr;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_NETROM, GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
@@ -526,16 +526,6 @@
 	return sk;
 }
 
-static int nr_dup(struct socket *newsock, struct socket *oldsock)
-{
-	struct sock *sk = oldsock->sk;
-
-	if (sk == NULL || newsock == NULL)
-		return -EINVAL;
-
-	return nr_create(newsock, sk->protocol);
-}
-
 static int nr_release(struct socket *sock, struct socket *peer)
 {
 	struct sock *sk = sock->sk;
@@ -728,11 +718,6 @@
 	return 0;
 }
 
-static int nr_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return -EOPNOTSUPP;
-}
-
 static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	struct sock *sk;
@@ -1211,11 +1196,11 @@
 static struct proto_ops nr_proto_ops = {
 	AF_NETROM,
 
-	nr_dup,
+	sock_no_dup,
 	nr_release,
 	nr_bind,
 	nr_connect,
-	nr_socketpair,
+	sock_no_socketpair,
 	nr_accept,
 	nr_getname,
 	datagram_poll,
diff -u --recursive --new-file v2.1.52/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.52/linux/net/netsyms.c	Mon Jun 16 16:36:02 1997
+++ linux/net/netsyms.c	Thu Sep  4 13:25:29 1997
@@ -96,10 +96,22 @@
 EXPORT_SYMBOL(sock_wake_async);
 EXPORT_SYMBOL(sock_alloc_send_skb);
 EXPORT_SYMBOL(sock_init_data);
-EXPORT_SYMBOL(sock_no_fcntl);
+EXPORT_SYMBOL(sock_no_dup);
+EXPORT_SYMBOL(sock_no_release);
+EXPORT_SYMBOL(sock_no_bind);
+EXPORT_SYMBOL(sock_no_connect);
+EXPORT_SYMBOL(sock_no_socketpair);
+EXPORT_SYMBOL(sock_no_accept);
+EXPORT_SYMBOL(sock_no_getname);
+EXPORT_SYMBOL(sock_no_poll);
+EXPORT_SYMBOL(sock_no_ioctl);
 EXPORT_SYMBOL(sock_no_listen);
+EXPORT_SYMBOL(sock_no_shutdown);
 EXPORT_SYMBOL(sock_no_getsockopt);
 EXPORT_SYMBOL(sock_no_setsockopt);
+EXPORT_SYMBOL(sock_no_fcntl);
+EXPORT_SYMBOL(sock_no_sendmsg);
+EXPORT_SYMBOL(sock_no_recvmsg);
 EXPORT_SYMBOL(sock_rfree);
 EXPORT_SYMBOL(sock_wfree);
 EXPORT_SYMBOL(skb_recv_datagram);
@@ -218,6 +230,7 @@
 EXPORT_SYMBOL(tcp_getsockopt);
 EXPORT_SYMBOL(tcp_recvmsg);
 EXPORT_SYMBOL(tcp_send_synack);
+EXPORT_SYMBOL(tcp_check_req);
 EXPORT_SYMBOL(sock_wmalloc);
 EXPORT_SYMBOL(tcp_reset_xmit_timer);
 EXPORT_SYMBOL(tcp_parse_options);
@@ -266,6 +279,7 @@
 EXPORT_SYMBOL(unregister_trdev);
 EXPORT_SYMBOL(init_trdev);
 EXPORT_SYMBOL(tr_freedev);
+EXPORT_SYMBOL(tr_reformat);
 #endif
                   
 #ifdef CONFIG_NET_ALIAS
diff -u --recursive --new-file v2.1.52/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.52/linux/net/rose/af_rose.c	Mon Jul  7 08:20:00 1997
+++ linux/net/rose/af_rose.c	Thu Sep  4 13:25:29 1997
@@ -149,7 +149,7 @@
 	struct sock *sk;
 	rose_cb *rose;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_ROSE, GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
@@ -613,16 +613,6 @@
 	return sk;
 }
 
-static int rose_dup(struct socket *newsock, struct socket *oldsock)
-{
-	struct sock *sk = oldsock->sk;
-
-	if (sk == NULL || newsock == NULL)
-		return -EINVAL;
-
-	return rose_create(newsock, sk->protocol);
-}
-
 static int rose_release(struct socket *sock, struct socket *peer)
 {
 	struct sock *sk = sock->sk;
@@ -816,11 +806,6 @@
 	return 0;
 }
 
-static int rose_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return -EOPNOTSUPP;
-}
-
 static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	struct sock *sk;
@@ -1332,11 +1317,11 @@
 static struct proto_ops rose_proto_ops = {
 	AF_ROSE,
 
-	rose_dup,
+	sock_no_dup,
 	rose_release,
 	rose_bind,
 	rose_connect,
-	rose_socketpair,
+	sock_no_socketpair,
 	rose_accept,
 	rose_getname,
 	datagram_poll,
diff -u --recursive --new-file v2.1.52/linux/net/socket.c linux/net/socket.c
--- v2.1.52/linux/net/socket.c	Mon Aug 18 18:19:47 1997
+++ linux/net/socket.c	Thu Sep  4 13:25:29 1997
@@ -321,9 +321,6 @@
 	int err;
 	struct scm_cookie scm;
 
-	if (!sock->ops->sendmsg)
-		return -EOPNOTSUPP;
-
 	err = scm_send(sock, msg, &scm);
 	if (err < 0)
 		return err;
@@ -339,9 +336,6 @@
 {
 	struct scm_cookie scm;
 
-	if (!sock->ops->recvmsg)
-		return -EOPNOTSUPP;
-
 	memset(&scm, 0, sizeof(scm));
 
 	size = sock->ops->recvmsg(sock, msg, size, flags, &scm);
@@ -374,7 +368,6 @@
 		      char *ubuf, unsigned long size)
 {
 	struct socket *sock;
-	int err;
 	struct iovec iov;
 	struct msghdr msg;
 
@@ -382,9 +375,7 @@
   
 	if (size==0)		/* Match SYS5 behaviour */
 		return 0;
-	/* FIXME: I think this can be removed now. */
-	if ((err=verify_area(VERIFY_WRITE,ubuf,size))<0)
-	  	return err;
+
 	msg.msg_name=NULL;
 	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
@@ -408,7 +399,6 @@
 		       const char *ubuf, unsigned long size)
 {
 	struct socket *sock;
-	int err;
 	struct msghdr msg;
 	struct iovec iov;
 	
@@ -417,10 +407,6 @@
 	if(size==0)		/* Match SYS5 behaviour */
 		return 0;
 
-	/* FIXME: I think this can be removed now */
-	if ((err=verify_area(VERIFY_READ,ubuf,size))<0)
-	  	return err;
-	
 	msg.msg_name=NULL;
 	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
@@ -480,9 +466,7 @@
 	 *	We can't return errors to poll, so it's either yes or no. 
 	 */
 
-	if (sock->ops->poll)
-		return sock->ops->poll(sock, wait);
-	return 0;
+	return sock->ops->poll(sock, wait);
 }
 
 
@@ -617,7 +601,10 @@
  */
   
 	if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
-	     type != SOCK_SEQPACKET && type != SOCK_RAW &&
+	     type != SOCK_SEQPACKET && type != SOCK_RAW && type != SOCK_RDM &&
+#ifdef CONFIG_XTP
+		type != SOCK_WEB  &&
+#endif
 	     type != SOCK_PACKET) || protocol < 0)
 			return -EINVAL;
 
@@ -634,7 +621,7 @@
 					   closest posix thing */
 	}
 
-	sock->type = type;
+	sock->type   = type;
 
 	if ((i = net_families[family]->create(sock, protocol)) < 0) 
 	{
@@ -694,13 +681,6 @@
 	sock1 = sockfd_lookup(fd1, &err);
 	if (!sock1)
 		goto out;
-	err = -EOPNOTSUPP;
-	if (!sock1->ops->socketpair) 
-	{
-		sys_close(fd1);
-		goto out;
-	}
-
 	/*
 	 *	Now grab another socket and try to connect the two together. 
 	 */
@@ -1304,7 +1284,7 @@
 	struct socket *sock;
 
 	sock = socki_lookup (filp->f_dentry->d_inode);
-	if (sock && sock->ops && sock->ops->fcntl)
+	if (sock && sock->ops)
 		return sock->ops->fcntl(sock, cmd, arg);
 	return(-EINVAL);
 }
@@ -1410,6 +1390,9 @@
  
 int sock_register(struct net_proto_family *ops)
 {
+	if (ops->family < 0 || ops->family >= NPROTO)
+		return -1;
+
 	net_families[ops->family]=ops;
 	return 0;
 }
@@ -1422,6 +1405,9 @@
  
 int sock_unregister(int family)
 {
+	if (family < 0 || family >= NPROTO)
+		return -1;
+
 	net_families[family]=NULL;
 	return 0;
 }
diff -u --recursive --new-file v2.1.52/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.52/linux/net/unix/af_unix.c	Thu Jul 17 10:06:09 1997
+++ linux/net/unix/af_unix.c	Thu Sep  4 13:25:29 1997
@@ -24,6 +24,8 @@
  *		Alan Cox	:	Started proper garbage collector
  *		Heiko EiBfeldt	:	Missing verify_area check
  *		Alan Cox	:	Started POSIXisms
+ *		Andreas Schwab	:	Replace inode by dentry for proper
+ *					reference counting
  *
  * Known differences from reference BSD that was tested:
  *
@@ -229,7 +231,9 @@
 
 	for (s=unix_socket_table[i->i_ino & 0xF]; s; s=s->next)
 	{
-		if(s->protinfo.af_unix.inode==i)
+		struct dentry *dentry = s->protinfo.af_unix.dentry;
+
+		if(dentry && dentry->d_inode == i)
 		{
 			unix_lock(s);
 			return(s);
@@ -291,10 +295,10 @@
 		}
 	}
 	
-	if(sk->protinfo.af_unix.inode!=NULL)
+	if(sk->protinfo.af_unix.dentry!=NULL)
 	{
-		iput(sk->protinfo.af_unix.inode);
-		sk->protinfo.af_unix.inode=NULL;
+		dput(sk->protinfo.af_unix.dentry);
+		sk->protinfo.af_unix.dentry=NULL;
 	}
 	
 	if(!unix_unlock(sk) && atomic_read(&sk->wmem_alloc) == 0)
@@ -355,7 +359,7 @@
 		default:
 			return -ESOCKTNOSUPPORT;
 	}
-	sk = sk_alloc(GFP_KERNEL);
+	sk = sk_alloc(AF_UNIX, GFP_KERNEL);
 	if (!sk)
 		return -ENOMEM;
 
@@ -363,7 +367,7 @@
 
 	sk->destruct = unix_destruct_addr;
 	sk->protinfo.af_unix.family=AF_UNIX;
-	sk->protinfo.af_unix.inode=NULL;
+	sk->protinfo.af_unix.dentry=NULL;
 	sk->sock_readers=1;			/* Us */
 	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
 	sk->mtu=4096;
@@ -372,11 +376,6 @@
 	return 0;
 }
 
-static int unix_dup(struct socket *newsock, struct socket *oldsock)
-{
-	return unix_create(newsock, 0);
-}
-
 static int unix_release(struct socket *sock, struct socket *peer)
 {
 	unix_socket *sk = sock->sk;
@@ -427,7 +426,7 @@
 	addr = kmalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);
 	if (!addr)
 		return -ENOBUFS;
-	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.dentry)
 	{
 		kfree(addr);
 		return -EINVAL;
@@ -494,12 +493,11 @@
 	struct sock *sk = sock->sk;
 	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
 	struct dentry * dentry;
-	struct inode * inode = NULL;
 	int err;
 	unsigned hash;
 	struct unix_address *addr;
 	
-	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode ||
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.dentry ||
 	    sunaddr->sun_family != AF_UNIX)
 		return -EINVAL;
 
@@ -516,7 +514,7 @@
 
 	/* We slept; recheck ... */
 
-	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.dentry)
 	{
 		kfree(addr);
 		return -EINVAL;		/* Already bound */
@@ -549,16 +547,9 @@
 	
 
 	dentry = do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
-	err = PTR_ERR(dentry);
-	if (!IS_ERR(dentry)) {
-		inode = dentry->d_inode;
-		inode->i_count++;	/* HATEFUL - we should use the dentry */
-		dput(dentry);
-		err = 0;
-	}
-
-	if(err<0)
+	if (IS_ERR(dentry))
 	{
+		err = PTR_ERR(dentry);
 		unix_release_addr(addr);
 		sk->protinfo.af_unix.addr = NULL;
 		if (err==-EEXIST)
@@ -567,8 +558,8 @@
 			return err;
 	}
 	unix_remove_socket(sk);
-	sk->protinfo.af_unix.list = &unix_socket_table[inode->i_ino & 0xF];
-	sk->protinfo.af_unix.inode = inode;
+	sk->protinfo.af_unix.list = &unix_socket_table[dentry->d_inode->i_ino & 0xF];
+	sk->protinfo.af_unix.dentry = dentry;
 	unix_insert_socket(sk);
 
 	return 0;
@@ -800,11 +791,8 @@
 		atomic_inc(&sk->protinfo.af_unix.addr->refcnt);
 		newsk->protinfo.af_unix.addr=sk->protinfo.af_unix.addr;
 	}
-	if (sk->protinfo.af_unix.inode)
-	{
-		sk->protinfo.af_unix.inode->i_count++;	/* Should use dentry */
-		newsk->protinfo.af_unix.inode=sk->protinfo.af_unix.inode;
-	}
+	if (sk->protinfo.af_unix.dentry)
+		newsk->protinfo.af_unix.dentry=dget(sk->protinfo.af_unix.dentry);
 		
 	for (;;)
 	{
@@ -1215,8 +1203,15 @@
 			if (copied >= target)
 				break;
 
+			/*
+			 *	POSIX 1003.1g mandates this order.
+			 */
+			 
 			if (sk->err) 
+			{
+				up(&sk->protinfo.af_unix.readsem);
 				return sock_error(sk);
+			}
 
 			if (sk->shutdown & RCV_SHUTDOWN)
 				break;
@@ -1426,7 +1421,7 @@
 struct proto_ops unix_stream_ops = {
 	AF_UNIX,
 	
-	unix_dup,
+	sock_no_dup,
 	unix_release,
 	unix_bind,
 	unix_stream_connect,
@@ -1447,12 +1442,12 @@
 struct proto_ops unix_dgram_ops = {
 	AF_UNIX,
 	
-	unix_dup,
+	sock_no_dup,
 	unix_release,
 	unix_bind,
 	unix_dgram_connect,
 	unix_socketpair,
-	NULL,
+	sock_no_accept,
 	unix_getname,
 	datagram_poll,
 	unix_ioctl,
diff -u --recursive --new-file v2.1.52/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.52/linux/net/x25/af_x25.c	Mon Jul  7 08:20:00 1997
+++ linux/net/x25/af_x25.c	Thu Sep  4 13:25:29 1997
@@ -423,7 +423,7 @@
 	struct sock *sk;
 	x25_cb *x25;
 
-	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_X25, GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) {
@@ -523,16 +523,6 @@
 	return sk;
 }
 
-static int x25_dup(struct socket *newsock, struct socket *oldsock)
-{
-	struct sock *sk = oldsock->sk;
-
-	if (sk == NULL || newsock == NULL)
-		return -EINVAL;
-
-	return x25_create(newsock, sk->protocol);
-}
-
 static int x25_release(struct socket *sock, struct socket *peer)
 {
 	struct sock *sk = sock->sk;
@@ -682,11 +672,6 @@
 	return 0;
 }
 	
-static int x25_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return -EOPNOTSUPP;
-}
-
 static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	struct sock *sk;
@@ -1254,11 +1239,11 @@
 static struct proto_ops x25_proto_ops = {
 	AF_X25,
 
-	x25_dup,
+	sock_no_dup,
 	x25_release,
 	x25_bind,
 	x25_connect,
-	x25_socketpair,
+	sock_no_socketpair,
 	x25_accept,
 	x25_getname,
 	datagram_poll,
