diff -u --recursive --new-file v2.1.28/linux/CREDITS linux/CREDITS
--- v2.1.28/linux/CREDITS	Thu Feb  6 04:57:24 1997
+++ linux/CREDITS	Wed Mar  5 17:04:29 1997
@@ -667,6 +667,7 @@
 N: Jakub Jelinek
 E: jj@sunsite.mff.cuni.cz
 W: http://sunsite.mff.cuni.cz/~jj
+P: 1024/0F7623C5 53 95 71 3C EB 73 99 97  02 49 40 47 F9 19 68 20
 D: Sparc hacker, SILO, mc
 D: Maintain sunsite.mff.cuni.cz
 S: Na Orechovce 7
diff -u --recursive --new-file v2.1.28/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.28/linux/Documentation/Configure.help	Tue Mar  4 10:25:22 1997
+++ linux/Documentation/Configure.help	Tue Mar  4 13:22:49 1997
@@ -1774,10 +1774,11 @@
 
 use normal IO
 CONFIG_SCSI_NCR53C8XX_IOMAPPED
-  Warning! Under linux/Alpha only normal io has been currently tested.
   This option allows you to force the driver to use normal IO.
   Memory mapped IO has less latency than normal IO and works for most 
   Intel-based hardware.
+  Under Linux/Alpha only normal IO is currently supported by the driver 
+  and so, this option has no effect.
   The normal answer therefore is N.
 
 not allow targets to disconnect
@@ -1809,6 +1810,21 @@
   that can be queued to a device, when tagged command queuing is
   possible.  The default value is 4. Minimum is 2, maximum is 12.  The
   normal answer therefore is the default one.
+
+assume boards are SYMBIOS compatible
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+  This option allows you to enable some features depending on GPIO wiring.
+  These general purpose input/output pins can be used for vendor specific 
+  features or implementation of the standard SYMBIOS features.
+  Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 
+  bit as a flag indicating singled-ended/differential interface.
+  If all the boards of your system are genuine SYMBIOS boards or use
+  BIOS and drivers from SYMBIOS, you would want to enable this option,
+  obviously at your own risks.
+  The driver behaves correctly on my system with this option enabled.
+  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev 0x12).
+  This option must be set to N if your system has at least one 53C8XX based  
+  scsi board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F).
 
 IBMMCA SCSI support
 CONFIG_SCSI_IBMMCA
diff -u --recursive --new-file v2.1.28/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.1.28/linux/Documentation/devices.tex	Sat Dec 21 00:40:58 1996
+++ linux/Documentation/devices.tex	Tue Mar  4 10:07:24 1997
@@ -34,15 +34,19 @@
 \newcommand{\link}[4]{{\file #1} \> {\file #2} \> #3 \> #4 \\}
 \newcommand{\vlink}[4]{{\file #1} \> {\em #2 \/} \> #3 \> #4 \\}
 \newcommand{\node}[3]{{\file #1} \> #2 \> #3 \\}
+\newcommand{\tum}{$''$}
 \newenvironment{nodelist}%
  {\begin{tabbing}%
 {\file /dev/crambamboli} \= {\file /proc/self/fd/99} \= symbolicxxx \=
 foo \kill}%
  {\end{tabbing}}
 %
+% If you reformat this document, *please* make sure this information
+% gets included!
+%
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: December 20, 1996}
+\date{Last revised: March 3, 1997}
 \maketitle
 %
 \noindent
@@ -153,7 +157,6 @@
 \major{34}{}{char }{Z8530 HDLC driver}
 \major{  }{}{block}{Fourth IDE hard disk/CD-ROM interface}
 \major{35}{}{char }{tclmidi MIDI driver}
-\major{  }{}{block}{Modular RAM disk}
 \major{36}{}{char }{Netlink support}
 \major{  }{}{block}{MCA ESDI hard disk}
 \major{37}{}{char }{IDE tape}
@@ -189,7 +192,14 @@
 \major{65}{}{char }{Sundance ``plink'' Transputer boards}
 \major{66}{}{char }{YARC PowerPC PCI coprocessor card}
 \major{67}{}{char }{Coda network filesystem}
-\major{68}{--119}{}{Unallocated}
+\major{68}{}{char }{CAPI 2.0 interface}
+\major{69}{}{char }{MA16 numeric accelerator card}
+\major{70}{}{char }{SpellCaster Protocol Services Interface}
+\major{71}{}{char }{Computone IntelliPort II serial card}
+\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices}
+\major{73}{}{char }{Computone IntelliPort II serial card -- control devices}
+\major{74}{}{char }{SCI bridge}
+\major{75}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -198,7 +208,6 @@
 
 \section{Minor numbers}
 
-
 \begin{devicelist}
 \major{0}{}{}{Unnamed devices (e.g. non-device mounts)}
 	\minor{0}{}{reserved as null device number}
@@ -258,37 +267,38 @@
 \\
 \major{}{}{}{To specify format, add to the autodetect device number}
 	\minor{  0}{/dev/fd?}{Autodetect format}
-	\minor{  4}{/dev/fd?d360}{5.25" \num{4}{360}K in a \num{4}{360}K drive\1}
-	\minor{ 20}{/dev/fd?h360}{5.25" \num{4}{360}K in a 1200K drive\1}
-	\minor{ 48}{/dev/fd?h410}{5.25" \num{4}{410}K in a 1200K drive}
-	\minor{ 64}{/dev/fd?h420}{5.25" \num{4}{420}K in a 1200K drive}
-	\minor{ 24}{/dev/fd?h720}{5.25" \num{4}{720}K in a 1200K drive}
-	\minor{ 80}{/dev/fd?h880}{5.25" \num{4}{880}K in a 1200K drive\1}
-	\minor{  8}{/dev/fd?h1200}{5.25" 1200K in a 1200K drive\1}
-	\minor{ 40}{/dev/fd?h1440}{5.25" 1440K in a 1200K drive\1}
-	\minor{ 56}{/dev/fd?h1476}{5.25" 1476K in a 1200K drive}
-	\minor{ 72}{/dev/fd?h1494}{5.25" 1494K in a 1200K drive}
-	\minor{ 92}{/dev/fd?h1600}{5.25" 1600K in a 1200K drive\1}
 	\minor{}{}{}
-	\minor{ 12}{/dev/fd?u360}{3.5" \num{4}{360}K Double Density}
-	\minor{ 16}{/dev/fd?u720}{3.5" \num{4}{720}K Double Density\1}
-	\minor{120}{/dev/fd?u800}{3.5" \num{4}{800}K Double Density\2}
-	\minor{ 52}{/dev/fd?u820}{3.5" \num{4}{820}K Double Density}
-	\minor{ 68}{/dev/fd?u830}{3.5" \num{4}{830}K Double Density}
-	\minor{ 84}{/dev/fd?u1040}{3.5" 1040K Double Density\1}
-	\minor{ 88}{/dev/fd?u1120}{3.5" 1120K Double Density\1}
-	\minor{ 28}{/dev/fd?u1440}{3.5" 1440K High Density\1}
-	\minor{124}{/dev/fd?u1600}{3.5" 1600K High Density\1}
-	\minor{ 44}{/dev/fd?u1680}{3.5" 1680K High Density\3}
-	\minor{ 60}{/dev/fd?u1722}{3.5" 1722K High Density}
-	\minor{ 76}{/dev/fd?u1743}{3.5" 1743K High Density}
-	\minor{ 96}{/dev/fd?u1760}{3.5" 1760K High Density}
-	\minor{116}{/dev/fd?u1840}{3.5" 1840K High Density\3}
-	\minor{100}{/dev/fd?u1920}{3.5" 1920K High Density\1}
-	\minor{ 32}{/dev/fd?u2880}{3.5" 2880K Extra Density\1}
-	\minor{104}{/dev/fd?u3200}{3.5" 3200K Extra Density}
-	\minor{108}{/dev/fd?u3520}{3.5" 3520K Extra Density}
-	\minor{112}{/dev/fd?u3840}{3.5" 3840K Extra Density\1}
+	\minor{  4}{/dev/fd?d360}{5.25\tum\ \num{4}{360}K in a \num{4}{360}K drive\1}
+	\minor{ 20}{/dev/fd?h360}{5.25\tum\ \num{4}{360}K in a 1200K drive\1}
+	\minor{ 48}{/dev/fd?h410}{5.25\tum\ \num{4}{410}K in a 1200K drive}
+	\minor{ 64}{/dev/fd?h420}{5.25\tum\ \num{4}{420}K in a 1200K drive}
+	\minor{ 24}{/dev/fd?h720}{5.25\tum\ \num{4}{720}K in a 1200K drive}
+	\minor{ 80}{/dev/fd?h880}{5.25\tum\ \num{4}{880}K in a 1200K drive\1}
+	\minor{  8}{/dev/fd?h1200}{5.25\tum\ 1200K in a 1200K drive\1}
+	\minor{ 40}{/dev/fd?h1440}{5.25\tum\ 1440K in a 1200K drive\1}
+	\minor{ 56}{/dev/fd?h1476}{5.25\tum\ 1476K in a 1200K drive}
+	\minor{ 72}{/dev/fd?h1494}{5.25\tum\ 1494K in a 1200K drive}
+	\minor{ 92}{/dev/fd?h1600}{5.25\tum\ 1600K in a 1200K drive\1}
+	\minor{}{}{}
+	\minor{ 12}{/dev/fd?u360}{3.5\tum\ \num{4}{360}K Double Density}
+	\minor{ 16}{/dev/fd?u720}{3.5\tum\ \num{4}{720}K Double Density\1}
+	\minor{120}{/dev/fd?u800}{3.5\tum\ \num{4}{800}K Double Density\2}
+	\minor{ 52}{/dev/fd?u820}{3.5\tum\ \num{4}{820}K Double Density}
+	\minor{ 68}{/dev/fd?u830}{3.5\tum\ \num{4}{830}K Double Density}
+	\minor{ 84}{/dev/fd?u1040}{3.5\tum\ 1040K Double Density\1}
+	\minor{ 88}{/dev/fd?u1120}{3.5\tum\ 1120K Double Density\1}
+	\minor{ 28}{/dev/fd?u1440}{3.5\tum\ 1440K High Density\1}
+	\minor{124}{/dev/fd?u1600}{3.5\tum\ 1600K High Density\1}
+	\minor{ 44}{/dev/fd?u1680}{3.5\tum\ 1680K High Density\3}
+	\minor{ 60}{/dev/fd?u1722}{3.5\tum\ 1722K High Density}
+	\minor{ 76}{/dev/fd?u1743}{3.5\tum\ 1743K High Density}
+	\minor{ 96}{/dev/fd?u1760}{3.5\tum\ 1760K High Density}
+	\minor{116}{/dev/fd?u1840}{3.5\tum\ 1840K High Density\3}
+	\minor{100}{/dev/fd?u1920}{3.5\tum\ 1920K High Density\1}
+	\minor{ 32}{/dev/fd?u2880}{3.5\tum\ 2880K Extra Density\1}
+	\minor{104}{/dev/fd?u3200}{3.5\tum\ 3200K Extra Density}
+	\minor{108}{/dev/fd?u3520}{3.5\tum\ 3520K Extra Density}
+	\minor{112}{/dev/fd?u3840}{3.5\tum\ 3840K Extra Density\1}
 	\minor{}{}{}
 	\minor{36}{/dev/fd?CompaQ}{Compaq 2880K drive; probably obsolete}
 \\
@@ -298,11 +308,12 @@
 \end{devicelist}
 
 NOTE: The letter in the device name ({\file d}, {\file q}, {\file h}
-or {\file u}) signifies the type of drive supported: 5.25" Double
-Density ({\file d}), 5.25" Quad Density ({\file q}), 5.25" High
-Density ({\file h}) or 3.5" (any type, {\file u}).  The capital
-letters {\file D}, {\file H}, or {\file E} for the 3.5" models have
-been deprecated, since the drive type is insignificant for these devices.
+or {\file u}) signifies the type of drive supported: 5.25\tum\ Double
+Density ({\file d}), 5.25\tum\ Quad Density ({\file q}), 5.25\tum\
+High Density ({\file h}) or 3.5\tum\ (any type, {\file u}).  The
+capital letters {\file D}, {\file H}, or {\file E} for the 3.5\tum\
+models have been deprecated, since the drive type is insignificant for
+these devices.
 
 \begin{devicelist}
 \major{3}{}{char}{Pseudo-TTY slaves}
@@ -324,9 +335,10 @@
 \end{devicelist}
 
 \noindent
-For Linux/i386, partitions 1-4 are the primary partitions, partitions
-5 and up are logical partitions.  Other versions of Linux use
-partitioning schemes appropriate to their respective architectures.
+For MS-DOS style partition tables (typically used by Linux/i386),
+partitions 1-4 are the primary partitions, partitions 5 and up are
+logical partitions.  For other partitioning schemes, the meaning of
+the numbers vary.
 
 \begin{devicelist}
 \major{ 4}{}{char }{TTY devices}
@@ -481,6 +493,7 @@
 	\minor{139}{/dev/openprom}{SPARC OpenBoot PROM}
 	\minor{140}{/dev/relay8}{Berkshire Products Octal relay card}
 	\minor{141}{/dev/relay16}{Berkshire Products ISO-16 relay card}
+	\minor{142}{/dev/msr}{x86 model specific registers}
 \end{devicelist}
 
 \begin{devicelist}
@@ -913,15 +926,8 @@
 	\minor{129}{/dev/smpte1}{Second MIDI port, SMPTE timed}
 	\minor{130}{/dev/smpte2}{Third MIDI port, SMPTE timed}
 	\minor{131}{/dev/smpte3}{Fourth MIDI port, SMPTE timed}
-\\
-\major{  }{}{block}{Modular RAM disk}
 \end{devicelist}
 
-\noindent
-This device number is provided for older kernels which did not have
-the modular RAM disk in the standard distribution.  See major number
-1.  This assignment will be removed when the 2.0 kernel is released.
-
 \begin{devicelist}
 \major{36}{}{char }{Netlink support}
 	\minor{0}{/dev/route}{Routing, device updates (kernel to user)}
@@ -1213,7 +1219,98 @@
 See {\em http://www.coda.cs.cmu.edu\/} for information about Coda.
 
 \begin{devicelist}
-\major{68}{--119}{}{Unallocated}
+\major{68}{}{char }{CAPI 2.0 interface}
+	\minor{0}{/dev/capi20}{Control device}
+	\minor{1}{/dev/capi20.00}{First CAPI 2.0 application}
+	\minor{2}{/dev/capi20.01}{Second CAPI 2.0 application}
+	\minordots
+	\minor{20}{/dev/capi20.19}{19th CAPI 2.0 application}
+\end{devicelist}
+
+\noindent
+ISDN CAPI 2.0 driver for use with CAPI 2.0 applications; currently
+supports the AVM B1 card.
+
+\begin{devicelist}
+\major{69}{}{char }{MA16 numeric accelerator card}
+	\minor{0}{/dev/ma16}{Board memory access}
+\end{devicelist}
+
+\begin{devicelist}
+\major{70}{}{char }{SpellCaster Protocol Services Interface}
+	\minor{0}{/dev/apscfg}{Configuration interface}
+	\minor{1}{/dev/apsauth}{Authentication interface}
+	\minor{2}{/dev/apslog}{Logging interface}
+	\minor{3}{/dev/apsdbg}{Debugging interface}
+	\minor{64}{/dev/apsisdn}{ISDN command interface}
+	\minor{65}{/dev/apsasync}{Async command interface}
+	\minor{128}{/dev/apsmon}{Monitor interface}
+\end{devicelist}
+
+\begin{devicelist}
+\major{71}{}{char }{Computone IntelliPort II serial card}
+	\minor{0}{/dev/ttyF0}{IntelliPort II board 0, port 0}
+	\minor{1}{/dev/ttyF1}{IntelliPort II board 0, port 1}
+	\minordots
+	\minor{63}{/dev/ttyF63}{IntelliPort II board 0, port 63}
+	\minor{64}{/dev/ttyF64}{IntelliPort II board 1, port 0}
+	\minor{65}{/dev/ttyF65}{IntelliPort II board 1, port 1}
+	\minordots
+	\minor{127}{/dev/ttyF127}{IntelliPort II board 1, port 63}
+	\minor{128}{/dev/ttyF128}{IntelliPort II board 2, port 0}
+	\minor{129}{/dev/ttyF129}{IntelliPort II board 2, port 1}
+	\minordots
+	\minor{191}{/dev/ttyF191}{IntelliPort II board 2, port 63}
+	\minor{192}{/dev/ttyF192}{IntelliPort II board 3, port 0}
+	\minor{193}{/dev/ttyF193}{IntelliPort II board 3, port 1}
+	\minordots
+	\minor{255}{/dev/ttyF255}{IntelliPort II board 3, port 63}
+\end{devicelist}
+
+\begin{devicelist}
+\major{72}{}{char }{Computone IntelliPort II serial card -- alternate devices}
+	\minor{0}{/dev/cuf0}{Callout device corresponding to {\file ttyF0}}
+	\minor{1}{/dev/cuf1}{Callout device corresponding to {\file ttyF1}}
+	\minordots
+	\minor{63}{/dev/cuf63}{Callout device corresponding to {\file ttyF63}}
+	\minor{64}{/dev/cuf64}{Callout device corresponding to {\file ttyF64}}
+	\minor{65}{/dev/cuf65}{Callout device corresponding to {\file ttyF65}}
+	\minordots
+	\minor{127}{/dev/cuf127}{Callout device corresponding to {\file ttyF127}}
+	\minor{128}{/dev/cuf128}{Callout device corresponding to {\file ttyF128}}
+	\minor{129}{/dev/cuf129}{Callout device corresponding to {\file ttyF129}}
+	\minordots
+	\minor{191}{/dev/cuf191}{Callout device corresponding to {\file ttyF191}}
+	\minor{192}{/dev/cuf192}{Callout device corresponding to {\file ttyF192}}
+	\minor{193}{/dev/cuf193}{Callout device corresponding to {\file ttyF193}}
+	\minordots
+	\minor{255}{/dev/cuf255}{Callout device corresponding to {\file ttyF255}}
+\end{devicelist}
+
+\begin{devicelist}
+\major{73}{}{char }{Computone IntelliPort II serial card -- control devices}
+	\minor{0}{/dev/ip2ipl0}{Loadware device for board 0}
+	\minor{1}{/dev/ip2stat0}{Status device for board 0}
+	\minor{4}{/dev/ip2ipl1}{Loadware device for board 1}
+	\minor{5}{/dev/ip2stat1}{Status device for board 1}
+	\minor{8}{/dev/ip2ipl2}{Loadware device for board 2}
+	\minor{9}{/dev/ip2stat2}{Status device for board 2}
+	\minor{12}{/dev/ip2ipl3}{Loadware device for board 3}
+	\minor{13}{/dev/ip2stat3}{Status device for board 3}
+\end{devicelist}
+
+\begin{devicelist}
+\major{74}{}{char }{SCI bridge}
+	\minor{0}{/dev/SCI/0}{SCI device 0}
+	\minor{1}{/dev/SCI/1}{SCI device 1}
+	\minordots
+\end{devicelist}
+
+\noindent
+Currently for Dolphin Interconnect Solutions' PCI-SCI bridge.
+
+\begin{devicelist}
+\major{75}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1317,5 +1414,108 @@
 \node{/dev/gpmdata}{socket}{{\file gpm} mouse multiplexer}
 \end{nodelist}
 
-\end{document}
+\section{Terminal devices}
 
+Terminal, or TTY devices are a special class of character devices.  A
+terminal device is any device that could act as a controlling terminal
+for a session; this includes virtual consoles, serial ports, and
+pseudoterminals (PTYs).
+
+All terminal devices share a common set of capabilities known as line
+diciplines; these include the common terminal line dicipline as well
+as SLIP and PPP modes.
+
+All terminal devices are named similarly; this section explains the
+naming and use of the various types of TTYs.  Note that the naming
+conventions include several historical warts; some of these are
+Linux-specific, some were inherited from other systems, and some
+reflect Linux outgrowing a borrowed convention.
+
+A hash mark ($\#$) in a device name is used here to indicate a decimal
+number without leading zeroes.
+
+\subsection{Virtual consoles and the console device}
+
+Virtual consoles are full-screen terminal displays on the system video
+monitor.  Virtual consoles are named {\file /dev/tty$\#$}, with
+numbering starting at {\file /dev/tty1}; {\file /dev/tty0} is the
+current virtual console.  {\file /dev/tty0} is the device that should
+be used to access the system video card on those architectures for
+which the frame buffer devices ({\file /dev/fb*}) do not exist
+(including the x86).  Do not use {\file /dev/console} for this
+purpose.
+
+The {\em console device\/}, {\file /dev/console}, is the device to
+which system messages should be sent, and on which logins should be
+permitted in single-user mode.  {\file /dev/console} should be a
+symbolic link to either {\file /dev/tty0}, a specific virtual console
+such as {\file /dev/tty1}, or to a serial port primary ({\file tty})
+device, depending on the configuration of the system.
+
+\subsection{Serial ports}
+
+Serial ports are RS-232 serial ports and any device which simulates
+one, either in hardware (such as internal modems) or in software (such
+as the ISDN driver.)  Under Linux, each serial ports has two device
+names, the primary or callin device and the alternate or callout one.
+Each kind of device is indicated by a different letter.  For any
+letter $X$, the names of the devices are {\file /dev/tty${X\#}$} and
+{\file /dev/cu${x\#}$}, respectively; for historical reasons, {\file
+/dev/ttyS$\#$} and {\file /dev/ttyC$\#$} correspond to {\file
+/dev/cua$\#$} and {\file /dev/cub$\#$}.  In the future, it should be
+expected that multiple letters will be used; all letters will be upper
+case for the {\file tty} device and lower case for the {\file cu}
+device.
+
+The alternate devices provide for kernel-based exclusion and somewhat
+different defaults than the primary devices.  Their main purpose is to
+allow the use of serial ports with programs with no inherent or broken
+support for serial ports.  For programs with proper knowledge of
+serial port operation, their use should probably be avoided.
+
+Arbitration of serial ports is provided by the use of lock files with
+the names {\file /var/lock/LCK..tty${X\#}$}.  The contents of the lock
+file should be the PID of the locking process as an ASCII number.
+
+It is common practice to install links such as {\file /dev/modem\/}
+which point to serial ports.  In order to ensure proper locking in the
+presence of these links, it is recommended that software chase
+symlinks and lock all possible names; additionally, it is recommended
+that a lock file be installed with the corresponding alternate
+device.  In order to avoid deadlocks, it is recommended that the locks
+are acquired in the following order, and released in the reverse:
+\begin{itemize}
+\item{The symbolic link name, if any ({\file /var/lock/LCK..modem})}
+\item{The {\file tty} name ({\file /var/lock/LCK..ttyS2})}
+\item{The alternate device name ({\file /var/lock/LCK..cua2})}
+\end{itemize}
+In the case of nested symbolic links, the lock files should be
+installed in the order the symlinks are resolved.
+
+Under no circumstances should an application hold a lock while waiting
+for another to be released.  In addition, applications which attempt
+to create lock files for the corresponding alternate device names
+should take into account the possibility of being used on a non-serial
+port TTY, for which no alternate device would exist.
+
+\subsection{Pseudoterminals (PTYs)}
+
+Pseudoterminals, or PTYs, are used to create login sessions or provide
+other capabilities requiring a TTY line dicipline (including SLIP or
+PPP capability) to arbitrary data-generation processes.  Each PTY has
+a {\em master\/} side, named {\file /dev/pty[p-za-e][0-9a-f]\/}, and a
+{\em slave\/} side, named {\file /dev/tty[p-za-e][0-9a-f]\/}.  The
+kernel arbitrates the use of PTYs by allowing each master side to be
+opened only once.
+
+Once the master side has been opened, the corresponding slave device
+can be used in the same manner as any TTY device.  The master and
+slave devices are connected by the kernel, generating the equivalent
+of a bidirectional pipe with TTY capabilities.
+
+The entire namespace {\file /dev/[pt]ty[p-za-o][0-9a-z]\/} should be
+considered reserved for the future use of PTYs.  Should more than 936
+PTYs ever become a necessity, we will likely adopt a System V-like
+scheme by which PTYs use a subdirectory of {\file /dev}.
+
+\end{document}
diff -u --recursive --new-file v2.1.28/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.28/linux/Documentation/devices.txt	Sat Dec 21 00:40:58 1996
+++ linux/Documentation/devices.txt	Tue Mar  4 10:07:24 1997
@@ -2,7 +2,7 @@
 
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		   Last revised: December 20, 1996
+		     Last revised: March 3, 1997
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he got busy with other things in 1993.  It
@@ -628,12 +628,6 @@
 		129 = /dev/smpte1	Second MIDI port, SMPTE timed
 		130 = /dev/smpte2	Third MIDI port, SMPTE timed
 		131 = /dev/smpte3	Fourth MIDI port, SMPTE timed
-    block	Modular RAM disk device
-
-		This device number is provided for older kernels which
-		did not have the modular RAM disk in the standard
-		distribution.  See major number 1.  This assignment
-		will be removed when the 2.0 kernel is released.
 
  36 char	Netlink support
 		  0 = /dev/route	Routing, device updates, kernel to user
@@ -850,7 +844,83 @@
 
 		See http://www.coda.cs.cmu.edu for information about Coda.
 
- 68-119		UNALLOCATED
+ 68 char	CAPI 2.0 interface
+		  0 = /dev/capi20	Control device
+		  1 = /dev/capi20.00	First CAPI 2.0 application
+		  2 = /dev/capi20.01	Second CAPI 2.0 application
+		      ...
+		 20 = /dev/capi20.19	19th CAPI 2.0 application
+
+		ISDN CAPI 2.0 driver for use with CAPI 2.0
+		applications; currently supports the AVM B1 card.
+
+ 69 char	MA16 numeric accelerator card
+		  0 = /dev/ma16		Board memory access
+
+ 70 char	SpellCaster Protocol Services Interface
+		  0 = /dev/apscfg	Configuration interface
+		  1 = /dev/apsauth	Authentication interface
+		  2 = /dev/apslog	Logging interface
+		  3 = /dev/apsdbg	Debugging interface
+		 64 = /dev/apsisdn	ISDN command interface
+		 65 = /dev/apsasync	Async command interface
+		128 = /dev/apsmon	Monitor interface
+
+ 71 char	Computone IntelliPort II serial card
+		  0 = /dev/ttyF0	IntelliPort II board 0, port 0
+		  1 = /dev/ttyF1	IntelliPort II board 0, port 1
+		      ...
+		 63 = /dev/ttyF63	IntelliPort II board 0, port 63
+		 64 = /dev/ttyF64	IntelliPort II board 1, port 0
+		 65 = /dev/ttyF65	IntelliPort II board 1, port 1
+		      ...
+		127 = /dev/ttyF127	IntelliPort II board 1, port 63
+		128 = /dev/ttyF128	IntelliPort II board 2, port 0
+		129 = /dev/ttyF129	IntelliPort II board 2, port 1
+		      ...
+		191 = /dev/ttyF191	IntelliPort II board 2, port 63
+		192 = /dev/ttyF192	IntelliPort II board 3, port 0
+		193 = /dev/ttyF193	IntelliPort II board 3, port 1
+		      ...
+		255 = /dev/ttyF255	IntelliPort II board 3, port 63
+
+ 72 char	Computone IntelliPort II serial card - alternate devices
+		  0 = /dev/cuf0		Callout device corresponding to ttyF0
+		  1 = /dev/cuf1		Callout device corresponding to ttyF1
+		      ...
+		 63 = /dev/cuf63	Callout device corresponding to ttyF63
+		 64 = /dev/cuf64	Callout device corresponding to ttyF64
+		 65 = /dev/cuf65	Callout device corresponding to ttyF65
+		      ...
+		127 = /dev/cuf127	Callout device corresponding to ttyF127
+		128 = /dev/cuf128	Callout device corresponding to ttyF128
+		129 = /dev/cuf129	Callout device corresponding to ttyF129
+		      ...
+		191 = /dev/cuf191	Callout device corresponding to ttyF191
+		192 = /dev/cuf192	Callout device corresponding to ttyF192
+		193 = /dev/cuf193	Callout device corresponding to ttyF193
+		      ...
+		255 = /dev/cuf255	Callout device corresponding to ttyF255
+
+ 73 char	Computone IntelliPort II serial card - control devices
+		  0 = /dev/ip2ipl0	Loadware device for board 0
+		  1 = /dev/ip2stat0	Status device for board 0
+		  4 = /dev/ip2ipl1	Loadware device for board 1
+		  5 = /dev/ip2stat1	Status device for board 1
+		  8 = /dev/ip2ipl2	Loadware device for board 2
+		  9 = /dev/ip2stat2	Status device for board 2
+		 12 = /dev/ip2ipl3	Loadware device for board 3
+		 13 = /dev/ip2stat3	Status device for board 3
+
+ 74 char	SCI bridge
+		  0 = /dev/SCI/0	SCI device 0
+		  1 = /dev/SCI/1	SCI device 1
+		      ...
+
+		Currently for Dolphin Interconnect Solutions' PCI-SCI
+		bridge.
+
+ 75-119		UNALLOCATED
 
 120-127		LOCAL/EXPERIMENTAL USE
 
@@ -864,7 +934,7 @@
 
 
 
-	ADDITIONAL /dev DIRECTORY ENTRIES
+ ****	ADDITIONAL /dev DIRECTORY ENTRIES
 
 This section details additional entries that should or may exist in
 the /dev directory.  It is preferred that symbolic links use the same
@@ -932,3 +1002,104 @@
 /dev/printer	socket		lpd local socket
 /dev/log	socket		syslog local socket
 /dev/gpmdata	socket		gpm mouse multiplexer
+
+
+ ****	TERMINAL DEVICES
+
+Terminal, or TTY devices are a special class of character devices.  A
+terminal device is any device that could act as a controlling terminal
+for a session; this includes virtual consoles, serial ports, and
+pseudoterminals (PTYs).
+
+All terminal devices share a common set of capabilities known as line
+diciplines; these include the common terminal line dicipline as well
+as SLIP and PPP modes.
+
+All terminal devices are named similarly; this section explains the
+naming and use of the various types of TTYs.  Note that the naming
+conventions include several historical warts; some of these are
+Linux-specific, some were inherited from other systems, and some
+reflect Linux outgrowing a borrowed convention.
+
+A hash mark (#) in a device name is used here to indicate a decimal
+number without leading zeroes.
+
+	Virtual consoles and the console device
+
+Virtual consoles are full-screen terminal displays on the system video
+monitor.  Virtual consoles are named /dev/tty#, with numbering
+starting at /dev/tty1; /dev/tty0 is the current virtual console.
+/dev/tty0 is the device that should be used to access the system video
+card on those architectures for which the frame buffer devices
+(/dev/fb*) do not exist (including the x86).  Do not use /dev/console
+for this purpose.
+
+The console device, /dev/console, is the device to which system
+messages should be sent, and on which logins should be permitted in
+single-user mode.  /dev/console should be a symbolic link to either
+/dev/tty0, a specific virtual console such as /dev/tty1, or to a
+serial port primary (tty*, not cu*) device, depending on the
+configuration of the system.
+
+	Serial ports
+
+Serial ports are RS-232 serial ports and any device which simulates
+one, either in hardware (such as internal modems) or in software (such
+as the ISDN driver.)  Under Linux, each serial ports has two device
+names, the primary or callin device and the alternate or callout one.
+Each kind of device is indicated by a different letter.  For any
+letter X, the names of the devices are /dev/ttyX# and /dev/cux#,
+respectively; for historical reasons, /dev/ttyS# and /dev/ttyC#
+correspond to /dev/cua# and /dev/cub#.  In the future, it should be
+expected that multiple letters will be used; all letters will be upper
+case for the "tty" device and lower case for the "cu" device.
+
+The alternate devices provide for kernel-based exclusion and somewhat
+different defaults than the primary devices.  Their main purpose is to
+allow the use of serial ports with programs with no inherent or broken
+support for serial ports.  For programs with proper knowledge of
+serial port operation, their use should probably be avoided.
+
+Arbitration of serial ports is provided by the use of lock files with
+the names /var/lock/LCK..ttyX#.  The contents of the lock file should
+be the PID of the locking process as an ASCII number.
+
+It is common practice to install links such as /dev/modem
+which point to serial ports.  In order to ensure proper locking in the
+presence of these links, it is recommended that software chase
+symlinks and lock all possible names; additionally, it is recommended
+that a lock file be installed with the corresponding alternate
+device.  In order to avoid deadlocks, it is recommended that the locks
+are acquired in the following order, and released in the reverse:
+
+	1. The symbolic link name, if any (/var/lock/LCK..modem)
+	2. The "tty" name (/var/lock/LCK..ttyS2)
+	3. The alternate device name (/var/lock/LCK..cua2)
+
+In the case of nested symbolic links, the lock files should be
+installed in the order the symlinks are resolved.
+
+Under no circumstances should an application hold a lock while waiting
+for another to be released.  In addition, applications which attempt
+to create lock files for the corresponding alternate device names
+should take into account the possibility of being used on a non-serial
+port TTY, for which no alternate device would exist.
+
+	Pseudoterminals (PTYs)
+
+Pseudoterminals, or PTYs, are used to create login sessions or provide
+other capabilities requiring a TTY line dicipline (including SLIP or
+PPP capability) to arbitrary data-generation processes.  Each PTY has
+a master side, named /dev/pty[p-za-e][0-9a-f], and a slave side, named
+/dev/tty[p-za-e][0-9a-f].  The kernel arbitrates the use of PTYs by
+allowing each master side to be opened only once.
+
+Once the master side has been opened, the corresponding slave device
+can be used in the same manner as any TTY device.  The master and
+slave devices are connected by the kernel, generating the equivalent
+of a bidirectional pipe with TTY capabilities.
+
+The entire namespace /dev/[pt]ty[p-za-o][0-9a-z] should be considered
+reserved for the future use of PTYs.  Should more than 936 PTYs ever
+become a necessity, we will likely adopt a System V-like scheme by
+which PTYs use a subdirectory of /dev.
diff -u --recursive --new-file v2.1.28/linux/Documentation/stallion.txt linux/Documentation/stallion.txt
--- v2.1.28/linux/Documentation/stallion.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/stallion.txt	Tue Mar  4 13:33:21 1997
@@ -0,0 +1,293 @@
+
+Stallion Multiport Serial Driver Readme
+---------------------------------------
+
+Copyright (C) 1994-1997,  Stallion Technologies (support@stallion.oz.au).
+
+Version:   5.3.2
+Date:      11FEB97
+
+
+
+1. INTRODUCTION
+
+There are two drivers that work with the different families of Stallion
+multiport serial boards. One is for the Stallion smart boards - that is
+EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent
+multiport boards - EasyConnection 8/64, ONboard and Brumby.
+
+If you are using any of the Stallion intelligent multiport boards (Brumby,
+ONboard, EasyConnection 8/64) with Linux you will need to get the driver
+utility package. This package is available at most of the Linux archive
+sites (and on CD's that contain these archives). The file will be called
+stallion-X.X.X.tar.gz where X.X.X will be the version number. In particular
+this package contains the board embedded executable images that are
+required for these boards. It also contains the downloader program.
+These boards cannot be used without this.
+
+The following ftp sites (and their mirrors) definitely have the stallion
+driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu.
+
+ftp.stallion.com:/drivers/ata5/Linux/stallion-5.3.1.tar.gz
+tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.3.1.tar.gz
+sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.3.1.tar.gz
+
+As of the printing of this document the latest version of the driver
+utility package is 5.3.1. If a later version is now available then you
+should use the latest version.
+
+If you are using the EasyIO or EasyConnection 8/32 boards then you don't
+need this package. Although it does have a handy script to create the
+/dev device nodes for these boards, and a serial stats display program.
+
+If you require DIP switch settings, EISA/MCA configuration files, or any
+other information related to Stallion boards then have a look at Stallion's
+web pages at http://www.stallion.com.
+
+
+
+2. INSTALLATION
+
+The drivers can be used as loadable modules or compiled into the kernel.
+You can choose which when doing a "config" on the kernel.
+
+All ISA, EISA and MCA boards that you want to use need to be entered into
+the driver(s) configuration structures. All PCI boards will be automatically
+detected when you load the driver - so they do not need to be entered into
+the driver(s) configuration structure. (Note that kernel PCI BIOS32 support
+is required to use PCI boards.)
+
+Entering ISA, EISA and MCA boards into the driver(s) configuration structure
+involves editing the driver(s) source file. It's pretty easy if you follow
+the instructions below. Both drivers can support up to 4 boards. The smart
+card driver (the stallion.c driver) supports any combination of EasyIO and
+EasyConnection 8/32 boards (up to a total of 4). The intelligent driver
+supports any combination of ONboards, Brumbys, Stallions and EasyConnection
+8/64 boards (up to a total of 4).
+
+To set up the driver(s) for the boards that you want to use you need to
+edit the appropriate driver file and add configuration entries.
+
+If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do:
+   vi /usr/src/linux/drivers/char/stallion.c
+      - find the definition of the stl_brdconf array (of structures)
+        near the top of the file
+      - modify this to match the boards you are going to install
+	(the comments before this structure should help)
+      - save and exit
+
+If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
+   vi /usr/src/linux/drivers/char/istallion.c
+      - find the definition of the stli_brdconf array (of structures)
+        near the top of the file
+      - modify this to match the boards you are going to install
+	(the comments before this structure should help)
+      - save and exit
+
+Once you have set up the board configurations then you are ready to build
+the kernel or modules.
+
+When the new kernel is booted, or the loadable module loaded then the
+driver will emit some kernel trace messages about whether the configured
+boards where detected or not. Depending on how your system logger is set
+up these may come out on the console, or just be logged to
+/var/adm/messages. You should check the messages to confirm that all is well.
+
+
+2.1 SHARING INTERRUPTS
+
+It is possible to share interrupts between multiple EasyIO and
+EasyConnection 8/32 boards in an EISA system. To do this you will need to
+do a couple of things:
+
+1. When entering the board resources into the stallion.c file you need to
+   mark the boards as using level triggered interrupts. Do this by replacing
+   the "0" entry at field position 6 (the last field) in the board
+   configuration structure with a "1". (This is the structure that defines
+   the board type, I/O locations, etc. for each board). All boards that are
+   sharing an interrupt must be set this way, and each board should have the
+   same interrupt number specified here as well. Now build the module or
+   kernel as you would normally.
+
+2. When physically installing the boards into the system you must enter
+   the system EISA configuration utility. You will need to install the EISA
+   configuration files for *all* the EasyIO and EasyConnection 8/32 boards
+   that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32
+   EISA configuration files required are supplied by Stallion Technologies
+   on the EASY Utilities floppy (usually supplied in the box with the board
+   when purchased. If not, you can pick it up from Stallion's FTP site,
+   ftp.stallion.com). You will need to edit the board resources to choose
+   level triggered interrupts, and make sure to set each board's interrupt
+   to the same IRQ number.
+
+You must complete both the above steps for this to work. When you reboot
+or load the driver your EasyIO and EasyConnection 8/32 boards will be
+sharing interrupts.
+
+
+2.2 USING HIGH SHARED MEMORY
+
+The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of
+using shared memory addresses above the usual 640K - 1Mb range. The ONboard
+ISA and the Stallion boards can be programmed to use memory addresses up to
+16Mb (the ISA bus addressing limit), and the EasyConnection 8/64-EI and
+ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus
+addressing limit).
+
+The higher than 1Mb memory addresses are fully supported by this driver.
+Just enter the address as you normally would for a lower than 1Mb address
+(in the drivers board configuration structure).
+
+
+
+2.3 TROUBLE SHOOTING
+
+If a board is not found by the driver but is actually in the system then the
+most likely problem is that the I/O address is wrong. Change it in the driver
+stallion.c or istallion.c configuration structure and rebuild the kernel or
+modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards
+the IRQ is software programmable, so if there is a conflict you may need to
+change the IRQ used for a board in the stallion.c configuration structure.
+There are no interrupts to worry about for ONboard, Brumby or EasyConnection
+8/64 boards. The memory region on EasyConnection 8/64 and ONboard boards is
+software programmable, but not on the Brumby boards.
+
+
+
+3. USING THE DRIVERS
+
+3.1 INTELLIGENT DRIVER OPERATION
+
+The intelligent boards also need to have their "firmware" code downloaded
+to them. This is done via a user level application supplied in the driver
+utility package called "stlload". Compile this program where ever you dropped
+the package files, by typing "make". In its simplest form you can then type
+    ./stlload -i cdk.sys
+in this directory and that will download board 0 (assuming board 0 is an
+EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
+    ./stlload -i 2681.sys
+
+Normally you would want all boards to be downloaded as part of the standard
+system startup. To achieve this, add one of the lines above into the
+/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add
+the "-b <brd-number>" option to the line. You will need to download code for
+every board. You should probably move the stlload program into a system
+directory, such as /usr/sbin. Also, the default location of the cdk.sys image
+file in the stlload down-loader is /usr/lib/stallion. Create that directory
+and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
+them anyway). As an example your /etc/rc.d/rc.S file might have the
+following lines added to it (if you had 3 boards):
+    /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
+    /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
+    /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
+
+The image files cdk.sys and 2681.sys are specific to the board types. The
+cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly
+the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards.
+If you load the wrong image file into a board it will fail to start up, and
+of course the ports will not be operational!
+
+If you are using the modularized version of the driver you might want to put
+the insmod calls in the startup script as well (before the download lines
+obviously).
+
+
+3.2 USING THE SERIAL PORTS
+
+Once the driver is installed you will need to setup some device nodes to
+access the serial ports. The simplest method is to use the stallion utility
+"mkdevnods" script. It will automatically create device entries for Stallion
+boards. This will create the normal serial port devices as /dev/ttyE# where
+# is the port number starting from 0. A bank of 64 minor device numbers is
+allocated to each board, so the first port on the second board is port 64,
+etc. A set of callout type devices is also created. They are created as the
+devices /dev/cue# where # is the same as for the ttyE devices.
+
+For the most part the Stallion driver tries to emulate the standard PC system
+COM ports and the standard Linux serial driver. The idea is that you should
+be able to use Stallion board ports and COM ports interchangeably without
+modifying anything but the device name. Anything that doesn't work like that
+should be considered a bug in this driver!
+
+If you look at the driver code you will notice that it is fairly closely
+based on the Linux serial driver (linux/drivers/char/serial.c). This is
+intentional, obviously this is the easiest way to emulate its behavior!
+
+Since this driver tries to emulate the standard serial ports as much as
+possible, most system utilities should work as they do for the standard
+COM ports. Most importantly "stty" works as expected and "setserial" can be
+also be used (excepting the ability to auto-configure the I/O and IRQ
+addresses of boards). Higher baud rates are supported in the usual fashion
+through setserial or using the CBAUDEX extensions. Note that the EasyIO and
+EasyConnection (all types) support at least 57600 and 115200 baud. The newer
+EasyConnection XP modules and new EasyIO boards support 230400 and 460800
+baud as well. The older boards including ONboard and Brumby support a
+maximum baud rate of 38400.
+
+If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO
+by Greg Hankins. It will explain everything you need to know!
+
+
+
+4. NOTES
+
+You can use both drivers at once if you have a mix of board types installed
+in a system. However to do this you will need to change the major numbers
+used by one of the drivers. Currently both drivers use major numbers 24, 25
+and 28 for their devices. Change one driver to use some other major numbers,
+and then modify the mkdevnods script to make device nodes based on those new
+major numbers. For example, you could change the istallion.c driver to use
+major numbers 60, 61 and 62. You will also need to create device nodes with
+different names for the ports, for example ttyF# and cuf#.
+
+The original Stallion board is no longer supported by Stallion Technologies.
+Although it is known to work with the istallion driver.
+
+Finding a free physical memory address range can be a problem. The older
+boards like the Stallion and ONboard need large areas (64K or even 128K), so
+they can be very difficult to get into a system. If you have 16 Mb of RAM
+then you have no choice but to put them somewhere in the 640K -> 1Mb range.
+ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some
+systems. If you have an original Stallion board, "V4.0" or Rev.O, then you
+need a 64K memory address space, so again 0xd0000 and 0xe0000 are good.
+Older Stallion boards are a much bigger problem. They need 128K of address
+space and must be on a 128K boundary. If you don't have a VGA card then
+0xc0000 might be usable - there is really no other place you can put them
+below 1Mb.
+
+Both the ONboard and old Stallion boards can use higher memory addresses as
+well, but you must have less than 16Mb of RAM to be able to use them. Usual
+high memory addresses used include 0xec0000 and 0xf00000.
+
+The Brumby boards only require 16Kb of address space, so you can usually
+squeeze them in somewhere. Common addresses are 0xc8000, 0xcc000, or in
+the 0xd0000 range. EasyConnection 8/64 boards are even better, they only
+require 4Kb of address space, again usually 0xc8000, 0xcc000 or 0xd0000
+are good.
+
+If you are using an EasyConnection 8/64-EI or ONboard/E then usually the
+0xd0000 or 0xe0000 ranges are the best options below 1Mb. If neither of
+them can be used then the high memory support to use the really high address
+ranges is the best option. Typically the 2Gb range is convenient for them,
+and gets them well out of the way.
+
+The ports of the EasyIO-8M board do not have DCD or DTR signals. So these
+ports cannot be used as real modem devices. Generally, when using these
+ports you should only use the cueX devices.
+
+The driver utility package contains a couple of very useful programs. One 
+is a serial port statistics collection and display program - very handy
+for solving serial port problems. The other is an extended option setting
+program that works with the intelligent boards.
+
+
+
+5. DISCLAIMER
+
+The information contained in this document is believed to be accurate and
+reliable. However, no responsibility is assumed by Stallion Technologies
+Pty. Ltd. for its use, nor any infringements of patents or other rights
+of third parties resulting from its use. Stallion Technologies reserves
+the right to modify the design of its products and will endeavour to change
+the information in manuals and accompanying documentation accordingly.
+
diff -u --recursive --new-file v2.1.28/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.28/linux/MAINTAINERS	Tue Mar  4 10:25:22 1997
+++ linux/MAINTAINERS	Tue Mar  4 13:19:06 1997
@@ -102,6 +102,11 @@
 W:	http://www.dgii.com/linux/
 S:	Maintained
 
+WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
+P:	Jean Tourrilhes
+M:	jt@hplb.hpl.hp.com
+S:	Maintained
+
 APM DRIVER
 P:	Rik Faith & Stephen Rothwell
 M:	faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
diff -u --recursive --new-file v2.1.28/linux/Makefile linux/Makefile
--- v2.1.28/linux/Makefile	Tue Mar  4 10:25:22 1997
+++ linux/Makefile	Wed Mar  5 16:21:54 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 28
+SUBLEVEL = 29
 
 ARCH = i386
 
Binary files v2.1.28/linux/arch/i386/boot/map and linux/arch/i386/boot/map differ
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/Makefile linux/arch/sparc/Makefile
--- v2.1.28/linux/arch/sparc/Makefile	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/Makefile	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.23 1997/01/02 14:14:17 jj Exp $
+# $Id: Makefile,v 1.26 1997/03/04 16:26:50 jj Exp $
 # sparc/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -15,8 +15,8 @@
 # Uncomment the first CFLAGS if you are doing kgdb source level
 # debugging of the kernel to get the proper debugging information.
 
-#CFLAGS := $(CFLAGS) -g -pipe
-CFLAGS := $(CFLAGS) -pipe
+#CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7
+CFLAGS := $(CFLAGS) -pipe -fcall-used-g5 -fcall-used-g7
 
 #LINKFLAGS = -N -Ttext 0xf0004000
 LINKFLAGS = -T arch/sparc/vmlinux.lds
@@ -41,3 +41,6 @@
 archclean:
 
 archdep:
+
+check_asm:
+	$(MAKE) -C arch/sparc/kernel check_asm
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.28/linux/arch/sparc/config.in	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/config.in	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.32 1997/01/25 23:10:12 ecd Exp $
+# $Id: config.in,v 1.33 1997/02/05 14:25:01 tdyas Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -41,6 +41,7 @@
 	define_bool CONFIG_SUN_AUXIO y
 	define_bool CONFIG_SUN_IO y
 	source drivers/sbus/char/Config.in
+	source drivers/sbus/audio/Config.in
 fi
 
 tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.28/linux/arch/sparc/defconfig	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/defconfig	Wed Mar  5 17:04:30 1997
@@ -40,16 +40,20 @@
 SUN_FB_BWTWO=y
 SUN_FB_LEO=y
 TADPOLE_FB_WEITEK=y
-SUN_FB_FAST_ONE=y
-SUN_FB_FAST_TWO=y
-SUN_FB_FAST_MONO=y
-SUN_FB_GENERIC=y
 
 #
 # Misc Linux/SPARC drivers
 #
 CONFIG_SUN_OPENPROMIO=m
 CONFIG_SUN_MOSTEK_RTC=y
+# CONFIG_SUN_BPP is not set
+
+#
+# Linux/SPARC audio subsystem (EXPERIMENTAL)
+#
+# CONFIG_SPARCAUDIO is not set
+# CONFIG_SPARCAUDIO_AMD7930 is not set
+# CONFIG_SPARCAUDIO_CS4231 is not set
 CONFIG_SUN_OPENPROMFS=m
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
@@ -76,7 +80,6 @@
 CONFIG_FIREWALL=y
 CONFIG_NET_ALIAS=y
 CONFIG_INET=y
-CONFIG_IP_FORWARD=y
 CONFIG_IP_MULTICAST=y
 CONFIG_IP_FIREWALL=y
 # CONFIG_IP_FIREWALL_NETLINK is not set
@@ -90,7 +93,7 @@
 # CONFIG_IP_ALWAYS_DEFRAG is not set
 # CONFIG_IP_ACCT is not set
 # CONFIG_IP_ROUTER is not set
-CONFIG_NET_IPIP=m
+# CONFIG_NET_IPIP is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=m
 # CONFIG_ARPD is not set
@@ -112,11 +115,13 @@
 # CONFIG_IPX_INTERN is not set
 # CONFIG_IPX_PPROP_ROUTING is not set
 CONFIG_ATALK=m
+# CONFIG_IPDDP is not set
 # CONFIG_AX25 is not set
 CONFIG_X25=m
 # CONFIG_LAPB is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
 
 #
 # SCSI support
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/Makefile linux/arch/sparc/kernel/Makefile
--- v2.1.28/linux/arch/sparc/kernel/Makefile	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/kernel/Makefile	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.37 1997/01/06 06:52:15 davem Exp $
+# $Id: Makefile,v 1.38 1997/03/04 16:26:29 jj Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -59,5 +59,20 @@
 	$(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o
 
 endif
+
+check_asm: dummy
+	@echo "#include <linux/sched.h>" > tmp.c
+	$(CC) -E tmp.c -o tmp.i
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+	$(SH) ./check_asm.sh task tmp.i check_asm.c
+	$(SH) ./check_asm.sh mm tmp.i check_asm.c
+	$(SH) ./check_asm.sh thread tmp.i check_asm.c
+	@echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+	@rm -f tmp.[ci]
+	$(CC) -o check_asm check_asm.c
+	./check_asm > asm_offsets.h
+	@if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+	@rm -f check_asm check_asm.c
+
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/auxio.c linux/arch/sparc/kernel/auxio.c
--- v2.1.28/linux/arch/sparc/kernel/auxio.c	Fri Dec 13 01:37:30 1996
+++ linux/arch/sparc/kernel/auxio.c	Wed Mar  5 17:04:30 1997
@@ -3,6 +3,7 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/stddef.h>
 #include <linux/init.h>
 #include <asm/oplib.h>
 #include <asm/io.h>
@@ -49,4 +50,34 @@
 		auxio_register = (unsigned char *) ((int)auxio_register | 3);
 
 	TURN_ON_LED;
+}
+
+
+/* sun4m power control register (AUXIO2) */
+
+volatile unsigned char * auxio_power_register = NULL;
+
+__initfunc(void auxio_power_probe(void))
+{
+	struct linux_prom_registers regs;
+	int node;
+
+	/* Attempt to find the sun4m power control node. */
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "obio");
+	node = prom_getchild(node);
+	node = prom_searchsiblings(node, "power");
+	if (node == 0 || node == -1)
+		return;
+
+	/* Map the power control register. */
+	prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
+	prom_apply_obio_ranges(&regs, 1);
+	auxio_power_register = (volatile unsigned char *)
+		sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
+			       "power off control", regs.which_io, 0);
+
+	/* Display a quick message on the console. */
+	if (auxio_power_register)
+		printk(KERN_INFO "Power off control detected.\n");
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/check_asm.sh linux/arch/sparc/kernel/check_asm.sh
--- v2.1.28/linux/arch/sparc/kernel/check_asm.sh	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc/kernel/check_asm.sh	Wed Mar  5 17:04:30 1997
@@ -0,0 +1,3 @@
+#!/bin/sh
+sed -n -e '/struct[ 	]*'$1'_struct[ 	]*{/,/};/p' < $2 | sed '/struct[ 	]*'$1'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+/g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/printf ("#define AOFF_'$1'_\0	0x%08x\\n#define ASIZ_'$1'_\0	0x%08x\\n", ((char *)\&_'$1'.\0) - ((char *)\&_'$1'), sizeof(_'$1'.\0));/' >> $3
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- v2.1.28/linux/arch/sparc/kernel/devices.c	Thu Dec 19 01:03:32 1996
+++ linux/arch/sparc/kernel/devices.c	Wed Mar  5 17:04:30 1997
@@ -64,10 +64,12 @@
 	linux_num_cpus = cpu_ctr;
 
 	cpu_probe();
-#if CONFIG_SUN_AUXIO
+#ifdef CONFIG_SUN_AUXIO
 	{
-	  extern void auxio_probe(void);
-	  auxio_probe();
+		extern void auxio_probe(void);
+		extern void auxio_power_probe(void);
+		auxio_probe();
+		auxio_power_probe();
 	}
 #endif
 	clock_stop_probe();
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.1.28/linux/arch/sparc/kernel/entry.S	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/kernel/entry.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.131 1997/01/12 09:06:55 davem Exp $
+/* $Id: entry.S,v 1.133 1997/03/04 16:26:22 jj Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -888,11 +888,11 @@
 
 	! Flush segment from the cache.
 	sethi	%hi((64 * 1024)), %l7
-1:
+9:
 C_LABEL(vac_hwflush_patch1):
 C_LABEL(vac_linesize_patch):
 	subcc	%l7, 16, %l7
-	bg	1b
+	bg	9b
 C_LABEL(vac_hwflush_patch2):
 	 sta	%g0, [%l3 + %l7] ASI_FLUSHSEG
 
@@ -1209,7 +1209,7 @@
 	rd	%wim, %g5
 	WRITE_PAUSE
 	mov	%fp, %o1			! arg1:	usp
-	std	%g4, [%curptr + THREAD_FORK_KPSR]
+	std	%g4, [%curptr + AOFF_task_tss + AOFF_thread_fork_kpsr]
 	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	call	C_LABEL(do_fork)
 	 mov	%l5, %o7
@@ -1231,7 +1231,7 @@
 	 mov	%fp, %o1			! yes, use callers usp
 	andn	%o1, 7, %o1			! no, align to 8 bytes
 1:
-	std	%g4, [%curptr + THREAD_FORK_KPSR]
+	std	%g4, [%curptr + AOFF_task_tss + AOFF_thread_fork_kpsr]
 	add	%sp, REGWIN_SZ, %o2		! arg2:	pt_regs ptr
 	call	C_LABEL(do_fork)
 	 mov	%l5, %o7
@@ -1652,7 +1652,7 @@
 	 * traps with the old method of just doing flush_user_windows().
 	 */
 C_LABEL(kill_user_windows):
-	ld	[%g6 + THREAD_UMASK], %o0	! get current umask
+	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], %o0	! get current umask
 	orcc	%g0, %o0, %g0			! if no bits set, we are done
 	be	3f				! nothing to do
 	 rd	%psr, %o5			! must clear interrupts
@@ -1660,7 +1660,7 @@
 	wr	%o4, 0x0, %psr			! the uwinmask state
 	WRITE_PAUSE				! burn them cycles
 1:
-	ld	[%g6 + THREAD_UMASK], %o0	! get consistant state
+	ld	[%g6 + AOFF_task_tss + AOFF_thread_uwinmask], %o0	! get consistant state
 	orcc	%g0, %o0, %g0			! did an interrupt come in?
 	be	4f				! yep, we are done
 	 rd	%wim, %o3			! get current wim
@@ -1672,12 +1672,12 @@
 	bne	kuw_patch1			! not done yet
 	 srl	%o3, 1, %o4			! begin another save simulation
 	wr	%o3, 0x0, %wim			! set the new wim
-	st	%g0, [%g6 + THREAD_UMASK]	! clear uwinmask
+	st	%g0, [%g6 + AOFF_task_tss + AOFF_thread_uwinmask]	! clear uwinmask
 4:
 	wr	%o5, 0x0, %psr			! re-enable interrupts
 	WRITE_PAUSE				! burn baby burn
 3:
 	retl					! return
-	 st	%g0, [%g6 + THREAD_W_SAVED]	! no windows saved
+	 st	%g0, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]	! no windows saved
 
 /* End of entry.S */
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/etrap.S linux/arch/sparc/kernel/etrap.S
--- v2.1.28/linux/arch/sparc/kernel/etrap.S	Fri Dec 13 01:37:30 1996
+++ linux/arch/sparc/kernel/etrap.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: etrap.S,v 1.22 1996/12/03 08:44:33 jj Exp $
+/* $Id: etrap.S,v 1.23 1997/03/04 16:26:25 jj Exp $
  * etrap.S: Sparc trap window preparation for entry into the
  *          Linux kernel.
  *
@@ -100,7 +100,7 @@
 	 mov	%t_kstack, %sp		! jump onto new stack
 
 trap_setup_kernel_spill:
-	ld	[%curptr + THREAD_UMASK], %g1
+	ld	[%curptr + AOFF_task_tss + AOFF_thread_uwinmask], %g1
 	orcc	%g0, %g1, %g0
 	bne	trap_setup_user_spill	! there are some user windows, yuck
 	/* Spill from kernel, but only kernel windows, adjust
@@ -130,7 +130,7 @@
 	/* We can't use %curptr yet. */
 	LOAD_CURRENT(t_kstack, t_twinmask)
 	mov	1, %t_twinmask
-	ld	[%t_kstack + TASK_SAVED_KSTACK], %t_kstack
+	ld	[%t_kstack + AOFF_task_saved_kernel_stack], %t_kstack
 	sll	%t_twinmask, %t_psr, %t_twinmask ! t_twinmask = (1 << psr)
 
 	/* Build pt_regs frame. */
@@ -138,7 +138,7 @@
 
 	/* Clear current->tss.w_saved */
 	LOAD_CURRENT(curptr, g1)
-	st	%g0, [%curptr + THREAD_W_SAVED]
+	st	%g0, [%curptr + AOFF_task_tss + AOFF_thread_w_saved]
 
 	/* See if we are in the trap window. */
 	andcc	%t_twinmask, %t_wim, %g0
@@ -169,7 +169,7 @@
 	andn	%g2, %t_twinmask, %g2
 tsetup_patch3:
 	and	%g2, 0xff, %g2			! patched on 7win Sparcs
-	st	%g2, [%curptr + THREAD_UMASK]	! store new umask
+	st	%g2, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]	! store new umask
 
 	jmpl	%t_retpc + 0x8, %g0		! return to caller
 	 mov	%t_kstack, %sp			! and onto kernel stack
@@ -190,7 +190,7 @@
 tsetup_patch6:
 	and	%g2, 0xff, %g2		! patched on 7win Sparcs
 	andn	%g1, %g2, %g1		! clear this bit in %g1
-	st	%g1, [%curptr + THREAD_UMASK]
+	st	%g1, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]
 
 	save	%g0, %g0, %g0
 
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.1.28/linux/arch/sparc/kernel/head.S	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/kernel/head.S	Wed Mar  5 17:04:30 1997
@@ -1,9 +1,10 @@
-/* $Id: head.S,v 1.78 1997/01/06 06:52:20 davem Exp $
+/* $Id: head.S,v 1.79 1997/03/04 16:26:31 jj Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter Zaitcev   (Zaitcev@ipmce.su)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  */
 
 #include <linux/version.h>
@@ -67,10 +68,6 @@
         .asciz  "Sparc-Linux sun4e support does not exist\n\n"
 	.align 4
 
-sun4u_notsup:
-        .asciz  "Sparc-Linux sun4u support does not exist\n\n"
-	.align 4
-
 	/* The Sparc trap table, bootloader gives us control at _start. */
 	.text
 	.globl	start, _stext, _start, __stext
@@ -477,8 +474,11 @@
 1:
 		mov	%o7, %g3
 
-got_pc:
-		mov	%g4, %o7		/* Previous %o7. */
+#ifndef CONFIG_AP1000
+		tst	%o0
+		be	no_sun4u_here
+#endif
+		 mov	%g4, %o7		/* Previous %o7. */
 	
 		mov	%o0, %l0		! stash away romvec
 		mov	%o0, %g7		! put it here too
@@ -991,8 +991,8 @@
 		st	%g6, [%g2]
 
 		set	C_LABEL(bootup_kernel_stack), %g3
-		st	%g3, [%g6 + TASK_KSTACK_PG]
-		st	%g0, [%g6 + THREAD_UMASK]
+		st	%g3, [%g6 + AOFF_task_kernel_stack_page]
+		st	%g0, [%g6 + AOFF_task_tss + AOFF_thread_uwinmask]
 
 /* Compute NWINDOWS and stash it away. Now uses %wim trick explained
  * in the V8 manual. Ok, this method seems to work, Sparc is cool...
@@ -1133,14 +1133,90 @@
 		b	halt_me
 		 nop
 
+		__INITDATA
+
+sun4u_1:
+		.asciz "finddevice"
+		.align	4
+sun4u_2:
+		.asciz "/chosen"
+		.align	4
+sun4u_3:
+		.asciz "getprop"
+		.align	4
+sun4u_4:
+		.asciz "stdout"
+		.align	4
+sun4u_5:
+		.asciz "write"
+		.align	4
+sun4u_6:
+        	.asciz  "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
+sun4u_6e:
+		.align	4
+sun4u_7:
+		.asciz "exit"
+		.align	8
+sun4u_a1:
+		.word	0, sun4u_1, 0, 1, 0, 1, 0, sun4u_2, 0
+sun4u_r1:
+		.word	0
+sun4u_a2:
+		.word	0, sun4u_3, 0, 4, 0, 1, 0
+sun4u_i2:
+		.word	0, 0, sun4u_4, 0, sun4u_1, 0, 8, 0
+sun4u_r2:
+		.word	0
+sun4u_a3:
+		.word	0, sun4u_5, 0, 3, 0, 1, 0
+sun4u_i3:
+		.word	0, 0, sun4u_6, 0, sun4u_6e - sun4u_6 - 1, 0
+sun4u_r3:
+		.word	0
+sun4u_a4:
+		.word	0, sun4u_7, 0, 0, 0, 0
+sun4u_r4:
+
+		__INIT
 no_sun4u_here:
-		ld	[%g7 + 0x68], %o1
-		set	sun4u_notsup, %o0
-		call	%o1
-		 nop
-		b	halt_me
-		 nop
+		set	sun4u_a1, %o0
+		set	current_pc, %l2
+		cmp	%l2, %g3
+		be	1f
+		 mov	%o4, %l0
+		sub	%g3, %l2, %l6
+		add	%o0, %l6, %o0
+		mov	%o0, %l4
+		mov	sun4u_r4 - sun4u_a1, %l3
+		ld	[%l4], %l5
+2:
+		add	%l4, 4, %l4
+		cmp	%l5, %l2
+		add	%l5, %l6, %l5
+		bgeu,a	3f
+		 st	%l5, [%l4 - 4]
+3:
+		subcc	%l3, 4, %l3
+		bne	2b
+		 ld	[%l4], %l5
+1:
+		call	%l0
+		 mov	%o0, %l1
+
+		ld	[%l1 + (sun4u_r1 - sun4u_a1)], %o1
+		add	%l1, (sun4u_a2 - sun4u_a1), %o0
+		call	%l0
+		 st	%o1, [%o0 + (sun4u_i2 - sun4u_a2)]
+
+		ld	[%l1 + (sun4u_1 - sun4u_a1)], %o1
+		add	%l1, (sun4u_a3 - sun4u_a1), %o0
+		call	%l0
+		st	%o1, [%o0 + (sun4u_i3 - sun4u_a3)]
+
+		call	%l0
+		 add	%l1, (sun4u_a4 - sun4u_a1), %o0
 
+		/* Not reached */
 halt_me:
 		ld	[%g7 + 0x74], %o0
 		call	%o0			! Get us out of here...
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c
--- v2.1.28/linux/arch/sparc/kernel/process.c	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/kernel/process.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.89 1997/01/06 06:52:23 davem Exp $
+/*  $Id: process.c,v 1.90 1997/01/31 23:26:16 tdyas Exp $
  *  linux/arch/sparc/kernel/process.c
  *
  *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -25,6 +25,7 @@
 #include <linux/config.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/reboot.h>
 
 #include <asm/auxio.h>
 #include <asm/oplib.h>
@@ -154,7 +155,7 @@
 extern int serial_console;
 #endif
 
-void halt_now(void)
+void machine_halt(void)
 {
 	sti();
 	udelay(8000);
@@ -167,7 +168,7 @@
 	panic("Halt failed!");
 }
 
-void hard_reset_now(void)
+void machine_restart(char * cmd)
 {
 	char *p;
 	
@@ -181,10 +182,19 @@
 	if (!serial_console)
 		console_restore_palette ();
 #endif
+	if (cmd)
+		prom_reboot(cmd);
 	if (*reboot_command)
-		prom_reboot (reboot_command);
+		prom_reboot(reboot_command);
 	prom_feval ("reset");
 	panic("Reboot failed!");
+}
+
+void machine_power_off(void)
+{
+	if (auxio_power_register)
+		*auxio_power_register |= AUXIO_POWER_OFF;
+	machine_halt();
 }
 
 void show_regwindow(struct reg_window *rw)
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.1.28/linux/arch/sparc/kernel/ptrace.c	Sun Jan 26 02:07:06 1997
+++ linux/arch/sparc/kernel/ptrace.c	Wed Mar  5 17:04:30 1997
@@ -741,7 +741,7 @@
 			pt_error_return(regs, -i);
 			goto out;
 		}
-		copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * 4));
+		copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
 		__get_user(child->tss.fsr, (&fps->fsr));
 		__get_user(child->tss.fpqdepth, (&fps->fpqd));
 		for(i = 0; i < 16; i++) {
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/rtrap.S linux/arch/sparc/kernel/rtrap.S
--- v2.1.28/linux/arch/sparc/kernel/rtrap.S	Sun Jan 26 02:07:07 1997
+++ linux/arch/sparc/kernel/rtrap.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.44 1997/01/14 05:56:17 davem Exp $
+/* $Id: rtrap.S,v 1.45 1997/03/04 16:26:27 jj Exp $
  * rtrap.S: Return from Sparc trap low-level code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -84,8 +84,8 @@
 	 nop
 
 signal_p:
-	ld	[%curptr + TASK_SIGNAL], %g2
-	ld	[%curptr + TASK_BLOCKED], %o0
+	ld	[%curptr + AOFF_task_signal], %g2
+	ld	[%curptr + AOFF_task_blocked], %o0
 	andncc	%g2, %o0, %g0
 	be,a	ret_trap_continue
 	 ld	[%sp + REGWIN_SZ + PT_PSR], %t_psr
@@ -102,7 +102,7 @@
 	wr	%t_psr, 0x0, %psr
 	WRITE_PAUSE
 
-	ld	[%curptr + THREAD_W_SAVED], %twin_tmp1
+	ld	[%curptr + AOFF_task_tss + AOFF_thread_w_saved], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	be	ret_trap_nobufwins
 	 nop
@@ -125,7 +125,7 @@
 	/* If there are already live user windows in the
 	 * set we can return from trap safely.
 	 */
-	ld	[%curptr + THREAD_UMASK], %twin_tmp1
+	ld	[%curptr + AOFF_task_tss + AOFF_thread_uwinmask], %twin_tmp1
 	orcc	%g0, %twin_tmp1, %g0
 	bne	ret_trap_userwins_ok
 	 nop
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/sclow.S linux/arch/sparc/kernel/sclow.S
--- v2.1.28/linux/arch/sparc/kernel/sclow.S	Sat Nov  9 00:11:43 1996
+++ linux/arch/sparc/kernel/sclow.S	Wed Mar  5 17:04:30 1997
@@ -34,27 +34,34 @@
 LABEL(sunosnop):
 	CC_AND_RETT
 
+#if 0
+/* Not SMP safe */
 	.globl	LABEL(sunosgetpid)
 LABEL(sunosgetpid):
 	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 108], %i0
-	ld	[%l4 + 256], %l5
-	ld	[%l5 + 108], %i1
+	ld	[%l4 + AOFF_task_pid], %i0
+	ld	[%l4 + AOFF_task_p_opptr], %l5
+	ld	[%l5 + AOFF_task_pid], %i1
 	CC_AND_RETT
+#endif
 
+#if (ASIZ_task_uid == 2 && ASIZ_task_euid == 2)
 	.globl	LABEL(sunosgetuid)
 LABEL(sunosgetuid):
 	LOAD_CURRENT(l4, l5)
-	lduh	[%l4 + 280], %i0
-	lduh	[%l4 + 282], %i1
+	lduh	[%l4 + AOFF_task_uid], %i0
+	lduh	[%l4 + AOFF_task_euid], %i1
 	CC_AND_RETT
+#endif
 
+#if (ASIZ_task_gid == 2 && ASIZ_task_egid == 2)
 	.globl	LABEL(sunosgetgid)
 LABEL(sunosgetgid):
 	LOAD_CURRENT(l4, l5)
-	lduh	[%l4 + 288], %i0
-	lduh	[%l4 + 290], %i1
+	lduh	[%l4 + AOFF_task_gid], %i0
+	lduh	[%l4 + AOFF_task_egid], %i1
 	CC_AND_RETT
+#endif
 
 	.globl	LABEL(sunosmctl)
 LABEL(sunosmctl):
@@ -71,9 +78,9 @@
 	LOAD_CURRENT(l4, l5)
 	set	-65793, %l5
 	and	%i0, %l5, %l5
-	ld	[%l4 + TASK_BLOCKED], %i0
+	ld	[%l4 + AOFF_task_blocked], %i0
 	or	%i0, %l5, %l5
-	st	%l5, [%l4 + TASK_BLOCKED]
+	st	%l5, [%l4 + AOFF_task_blocked]
 	CC_AND_RETT
 
 	.globl	LABEL(sunossmask)
@@ -81,102 +88,14 @@
 	LOAD_CURRENT(l4, l5)
 	set	-65793, %l5
 	and	%i0, %l5, %l5
-	ld	[%l4 + TASK_BLOCKED], %i0
-	st	%l5, [%l4 + TASK_BLOCKED]
+	ld	[%l4 + AOFF_task_blocked], %i0
+	st	%l5, [%l4 + AOFF_task_blocked]
 	CC_AND_RETT
 
 	.globl	LABEL(getpagesize)
 LABEL(getpagesize):
 	set	4096, %i0
 	CC_AND_RETT
-
-	.globl	LABEL(umask)
-LABEL(umask):
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 1560], %l5
-	and	%i0, 511, %l4
-	lduh	[%l5 + 4], %i0
-	sth	%l4, [%l5 + 4]
-	CC_AND_RETT
-
-#if 0
-	.globl	LABEL(write)
-LABEL(write):
-	cmp	%i0, 255		/* fd >= NR_OPEN */
-	bgu,a	write_error_return
-	 mov	EBADF, %i0
-
-	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + 1564], %l5
-	sll	%i0, 2, %l6
-	add	%l5, %l6, %l5
-	ld	[%l5 + 36], %l6
-	cmp	%l6, 0			/* !(file=current->files->fd[fd]) */
-	be,a	write_error_return
-	 mov	EBADF, %i0	
-
-	ld	[%l6 + 36], %l5
-	cmp	%l5, 0			/* !(inode=file->f_inode) */
-	be,a	write_error_return
-	 mov	EBADF, %i0
-
-	lduh	[%l6], %l5		/* !(file->f_mode & 2) */
-	andcc	%l5, 2, %g0
-	be,a	write_error_return
-	 mov	EBADF, %i0
-
-	ld	[%l6 + 40], %l5
-	cmp	%l5, 0			/* !file->f_op */
-	be,a	write_error_return
-	 mov	EINVAL, %i0
-
-	ld	[%l5 + 8], %l5		/* !file->f_op->write */
-	cmp	%l5, 0
-	be,a	write_error_return
-	 mov	EINVAL, %i0
-
-	cmp	%i2, 0			/* count == 0 */
-	bne	1f
-	 nop
-
-	mov	0, %i0
-	CC_AND_RETT
-
-1:
-	/* See if we can do the optimization... */
-	ld	[%l6 + 36], %l5
-	lduh	[%l5 + 16], %l5
-	srl	%l5, 8, %l6
-	cmp	%l6, 1			/* MEM_MAJOR */
-	bne,a	write_is_too_hard
-	 sethi	%hi(C_LABEL(quick_sys_write)), %l7
-
-	and	%l5, 0xff, %l5
-	cmp	%l5, 3			/* NULL_MINOR */
-	bne,a	write_is_too_hard
-	 sethi	%hi(C_LABEL(quick_sys_write)), %l7
-
-	/* We only optimize for the /dev/null case currently,
-	 * however to stay POSIX4 compliant we must check the
-	 * validity of the passed buffer.  Blowlaris2.x does not
-	 * do this and is therefore not POSIX4 compliant!
-	 * If you are going to optimize for benchmarks, fine,
-	 * but to break behavior of a system call in the process
-	 * is complete brain damage...
-	 */
-
-	/* XXX write verify_area thingy for full POSIX conformance! XXX */
-
-	mov	%i2, %i0
-	CC_AND_RETT
-
-write_is_too_hard:
-	b	syscall_is_too_hard
-	 or	%l7, %lo(C_LABEL(quick_sys_write)), %l7
-
-write_error_return:
-	SC_AND_RETT
-#endif
 
 	/* XXX sys_nice() XXX */
 	/* XXX sys_setpriority() XXX */
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.28/linux/arch/sparc/kernel/setup.c	Sun Jan 26 02:07:07 1997
+++ linux/arch/sparc/kernel/setup.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.80 1997/01/25 02:39:54 miguel Exp $
+/*  $Id: setup.c,v 1.81 1997/01/29 10:32:55 davem Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -395,6 +395,7 @@
 	init_task.mm->mmap->vm_page_prot = PAGE_SHARED;
 	init_task.mm->mmap->vm_start = KERNBASE;
 	init_task.mm->mmap->vm_end = *memory_end_p;
+	init_task.mm->context = (unsigned long) NO_CONTEXT;
 	init_task.tss.kregs = &fake_swapper_regs;
 
 #ifdef CONFIG_SUN_SERIAL
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c
--- v2.1.28/linux/arch/sparc/kernel/signal.c	Sun Jan 26 02:07:07 1997
+++ linux/arch/sparc/kernel/signal.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.71 1997/01/19 22:32:21 ecd Exp $
+/*  $Id: signal.c,v 1.72 1997/03/03 16:51:43 jj Exp $
  *  linux/arch/sparc/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -144,7 +144,7 @@
 	current->flags &= ~PF_USEDFPU;
 
 	copy_from_user(&current->tss.float_regs[0], &fpu->si_float_regs[0],
-		       (sizeof(unsigned long) * 64));
+		       (sizeof(unsigned long) * 32));
 	__get_user(current->tss.fsr, &fpu->si_fsr);
 	__get_user(current->tss.fpqdepth, &fpu->si_fpqdepth);
 	if (current->tss.fpqdepth != 0)
@@ -343,7 +343,7 @@
 	}
 #endif
 	copy_to_user(&fpu->si_float_regs[0], &current->tss.float_regs[0],
-		     (sizeof(unsigned long) * 64));
+		     (sizeof(unsigned long) * 32));
 	__put_user(current->tss.fsr, &fpu->si_fsr);
 	__put_user(current->tss.fpqdepth, &fpu->si_fpqdepth);
 	if (current->tss.fpqdepth != 0)
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.28/linux/arch/sparc/kernel/sparc_ksyms.c	Sun Jan 26 02:07:07 1997
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Wed Mar  5 17:04:30 1997
@@ -1,10 +1,12 @@
-/* $Id: sparc_ksyms.c,v 1.43 1997/01/26 07:12:30 davem Exp $
+/* $Id: sparc_ksyms.c,v 1.47 1997/03/03 16:51:41 jj Exp $
  * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
  */
 
+#define PROMLIB_INTERNAL
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -43,19 +45,14 @@
 				unsigned long, unsigned long, unsigned long);
 void _sigpause_common (unsigned int set, struct pt_regs *);
 extern void __copy_1page(void *, const void *);
-extern void __memcpy(void *, const void *, __kernel_size_t);
 extern void __memmove(void *, const void *, __kernel_size_t);
-extern void *__memset(void *, int, __kernel_size_t);
 extern void *bzero_1page(void *);
 extern void *__bzero(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 int __copy_user(unsigned long to, unsigned long from, int size);
-extern int __clear_user(unsigned long addr, int size);
-extern int __strncpy_from_user(unsigned long dest, unsigned long src, int count);
+extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *);
 
 extern void bcopy (const char *, char *, int);
 extern int __ashrdi3(int, int);
@@ -144,7 +141,6 @@
 EXPORT_SYMBOL(prom_getproperty);
 EXPORT_SYMBOL(prom_node_has_property);
 EXPORT_SYMBOL(prom_setprop);
-EXPORT_SYMBOL(prom_nodeops);
 EXPORT_SYMBOL(prom_getbootargs);
 EXPORT_SYMBOL(prom_apply_obio_ranges);
 EXPORT_SYMBOL(prom_getname);
@@ -154,6 +150,8 @@
 EXPORT_SYMBOL(prom_getint);
 EXPORT_SYMBOL(prom_getintdefault);
 EXPORT_SYMBOL(romvec);
+EXPORT_SYMBOL(__prom_getchild);
+EXPORT_SYMBOL(__prom_getsibling);
 
 /* sparc library symbols */
 EXPORT_SYMBOL(bcopy);
@@ -185,9 +183,10 @@
 EXPORT_SYMBOL(__strncmp);
 EXPORT_SYMBOL(__memmove);
 
+EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
+
 /* Moving data to/from userspace. */
 EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__clear_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 
 /* No version information on this, heavily used in inline asm,
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/sunos_ioctl.c linux/arch/sparc/kernel/sunos_ioctl.c
--- v2.1.28/linux/arch/sparc/kernel/sunos_ioctl.c	Sun Jan 26 02:07:08 1997
+++ linux/arch/sparc/kernel/sunos_ioctl.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: sunos_ioctl.c,v 1.27 1997/01/06 06:52:33 davem Exp $
+/* $Id: sunos_ioctl.c,v 1.28 1997/02/15 01:17:05 davem Exp $
  * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility.
  * 
  * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -28,6 +28,9 @@
 extern char sunkbd_layout;
 #endif
 
+/* NR_OPEN is now larger and dynamic in recent kernels. */
+#define SUNOS_NR_OPEN	256
+
 extern asmlinkage int sys_ioctl(unsigned int, unsigned int, unsigned long);
 extern asmlinkage int sys_setsid(void);
 
@@ -37,7 +40,7 @@
 	int ret = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(filp = current->files->fd [fd]))
+	if (fd >= SUNOS_NR_OPEN || !(filp = current->files->fd [fd]))
 		goto out;
 
 	/* First handle an easy compat. case for tty ldisc. */
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.1.28/linux/arch/sparc/kernel/sys_sunos.c	Sun Jan 26 02:07:08 1997
+++ linux/arch/sparc/kernel/sys_sunos.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.75 1997/01/26 07:12:31 davem Exp $
+/* $Id: sys_sunos.c,v 1.77 1997/02/15 01:17:04 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -55,6 +55,9 @@
 #include <linux/time.h>
 #include <linux/personality.h>
 
+/* NR_OPEN is now larger and dynamic in recent kernels. */
+#define SUNOS_NR_OPEN	256
+
 extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len);
 
 /* We use the SunOS mmap() semantics. */
@@ -74,7 +77,7 @@
 	}
 	retval = -EBADF;
 	if(!(flags & MAP_ANONYMOUS))
-		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+		if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
 			goto out;
 	retval = -ENOMEM;
 	if(!(flags & MAP_FIXED) && !addr) {
@@ -340,7 +343,7 @@
  */
 asmlinkage long sunos_getdtablesize(void)
 {
-	return NR_OPEN;
+	return SUNOS_NR_OPEN;
 }
 #define _S(nr) (1<<((nr)-1))
 
@@ -429,7 +432,7 @@
 	int error = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
 		goto out;
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
@@ -504,7 +507,7 @@
 	int error = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
 		goto out;
 	error = -ENOTDIR;
 	if (!file->f_op || !file->f_op->readdir)
@@ -927,6 +930,16 @@
 	unlock_kernel();
 	return ret;
 }
+
+/* sysconf options, for SunOS compatibility */
+#define   _SC_ARG_MAX             1
+#define   _SC_CHILD_MAX           2
+#define   _SC_CLK_TCK             3
+#define   _SC_NGROUPS_MAX         4
+#define   _SC_OPEN_MAX            5
+#define   _SC_JOB_CONTROL         6
+#define   _SC_SAVED_IDS           7
+#define   _SC_VERSION             8
 
 extern asmlinkage long sunos_sysconf (int name)
 {
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/systbls.S linux/arch/sparc/kernel/systbls.S
--- v2.1.28/linux/arch/sparc/kernel/systbls.S	Sun Jan 26 02:07:08 1997
+++ linux/arch/sparc/kernel/systbls.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.58 1997/01/26 07:12:31 davem Exp $
+/* $Id: systbls.S,v 1.59 1997/02/14 03:12:54 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -42,7 +42,7 @@
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink)
 	.long C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot)
 	.long C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize)
-	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
+	.long C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall)
 	.long C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap)
 	.long C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup)
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/wof.S linux/arch/sparc/kernel/wof.S
--- v2.1.28/linux/arch/sparc/kernel/wof.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/kernel/wof.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: wof.S,v 1.32 1997/01/06 06:52:43 davem Exp $
+/* $Id: wof.S,v 1.33 1997/03/04 16:26:35 jj Exp $
  * wof.S: Sparc window overflow handler.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -96,7 +96,7 @@
 	 save	%g0, %g0, %g0				! Go where saving will occur
 	
 	/* See if any user windows are active in the set. */
-	ld	[%curptr + THREAD_UMASK], %twin_tmp	! grab win mask
+	ld	[%curptr + AOFF_task_tss + AOFF_thread_uwinmask], %twin_tmp	! grab win mask
 	orcc	%g0, %twin_tmp, %g0			! check for set bits
 	bne	spwin_exist_uwins			! yep, there are some
 	 andn	%twin_tmp, %glob_tmp, %twin_tmp		! compute new umask
@@ -139,7 +139,7 @@
 	 * But first, store the new user window mask calculated
 	 * above.
 	 */
-	st	%twin_tmp, [%curptr + THREAD_UMASK]
+	st	%twin_tmp, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]
 	save	%g0, %g0, %g0		! Go to where the saving will occur
 
 spwin_fromuser:
@@ -212,15 +212,15 @@
 	/* Oh well, throw this one window into the per-task window
 	 * buffer, the first one.
 	 */
-	st	%sp, [%curptr + THREAD_STACK_PTRS]
-	STORE_WINDOW(curptr + THREAD_REG_WINDOW)
+	st	%sp, [%curptr + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]
+	STORE_WINDOW(curptr + AOFF_task_tss + AOFF_thread_reg_window)
 	restore	%g0, %g0, %g0
 
 	/* LOCATION: Trap Window */
 
 	/* Back in the trap window, update winbuffer save count. */
 	mov	1, %glob_tmp
-	st	%glob_tmp, [%curptr + THREAD_W_SAVED]
+	st	%glob_tmp, [%curptr + AOFF_task_tss + AOFF_thread_w_saved]
 
 		/* Compute new user window mask.  What we are basically
 		 * doing is taking two windows, the invalid one at trap
@@ -232,10 +232,10 @@
 		or	%twin_tmp, %t_wim, %twin_tmp
 		not	%twin_tmp
 spnwin_patch3:	and	%twin_tmp, 0xff, %twin_tmp	! patched on 7win Sparcs
-		st	%twin_tmp, [%curptr + THREAD_UMASK]
+		st	%twin_tmp, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]
 
 	/* Jump onto kernel stack for this process... */
-	ld	[%curptr + TASK_SAVED_KSTACK], %sp
+	ld	[%curptr + AOFF_task_saved_kernel_stack], %sp
 
 	/* Restore the saved globals and build a pt_regs frame. */
 	mov	%saved_g5, %g5
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/kernel/wuf.S linux/arch/sparc/kernel/wuf.S
--- v2.1.28/linux/arch/sparc/kernel/wuf.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/kernel/wuf.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: wuf.S,v 1.30 1997/01/06 06:52:44 davem Exp $
+/* $Id: wuf.S,v 1.31 1997/03/04 16:26:37 jj Exp $
  * wuf.S: Window underflow trap handler for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller
@@ -145,7 +145,7 @@
 	 * to the trap window and call c-code to deal with this.
 	 */
 	LOAD_CURRENT(l4, l5)
-	ld	[%l4 + TASK_SAVED_KSTACK], %l5
+	ld	[%l4 + AOFF_task_saved_kernel_stack], %l5
 
 	/* Store globals into pt_regs frame. */
 	STORE_PT_GLOBALS(l5)
@@ -178,8 +178,8 @@
 	/* Fix users window mask and buffer save count. */
 	mov	0x1, %g5
 	sll	%g5, %g3, %g5
-	st	%g5, [%curptr + THREAD_UMASK]		! one live user window still
-	st	%g0, [%curptr + THREAD_W_SAVED]		! no windows in the buffer
+	st	%g5, [%curptr + AOFF_task_tss + AOFF_thread_uwinmask]		! one live user window still
+	st	%g0, [%curptr + AOFF_task_tss + AOFF_thread_w_saved]		! no windows in the buffer
 
 	wr	%t_psr, PSR_ET, %psr			! enable traps
 	nop
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/lib/checksum.S linux/arch/sparc/lib/checksum.S
--- v2.1.28/linux/arch/sparc/lib/checksum.S	Sat Nov  9 00:12:01 1996
+++ linux/arch/sparc/lib/checksum.S	Wed Mar  5 17:04:30 1997
@@ -3,6 +3,7 @@
  *  Copyright(C) 1995 Linus Torvalds
  *  Copyright(C) 1995 Miguel de Icaza
  *  Copyright(C) 1996 David S. Miller
+ *  Copyright(C) 1997 Jakub Jelinek
  *
  * derived from:
  *	Linux/Alpha checksum c-code
@@ -12,7 +13,8 @@
  *	BSD4.4 portable checksum routine
  */
 
-#include <asm-sparc/cprefix.h>
+#include <asm/cprefix.h>
+#include <asm/errno.h>
 
 #define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)	\
 	ldd	[buf + offset + 0x00], t0;			\
@@ -140,9 +142,49 @@
 cpout:	retl						! get outta here
 	 mov	%o2, %o0				! return computed csum
 
+	.globl C_LABEL(__csum_partial_copy_start), C_LABEL(__csum_partial_copy_end)
+C_LABEL(__csum_partial_copy_start):
+
+#define EX(x,y,a,b,z)                           \
+98:     x,y;                                    \
+        .section .fixup,z##alloc,z##execinstr;  \
+        .align  4;                              \
+99:     ba 30f;                                 \
+         a, b, %o3;                             \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 99b;                       \
+        .text;                                  \
+        .align  4
+
+#define EX2(x,y,z)                          	\
+98:     x,y;                                    \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 30f;                       \
+        .text;                                  \
+        .align  4
+
+#define EX3(x,y,z)                          	\
+98:     x,y;                                    \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   98b, 96f;                       \
+        .text;                                  \
+        .align  4
+
+#define EXT(start,end,handler,z)                \
+        .section __ex_table,z##alloc;           \
+        .align  4;                              \
+        .word   start, 0, end, handler;         \
+        .text;                                  \
+        .align  4
+
 	/* This aligned version executes typically in 8.5 superscalar cycles, this
 	 * is the best I can do.  I say 8.5 because the final add will pair with
 	 * the next ldd in the main unrolled loop.  Thus the pipe is always full.
+	 * If you change these macros (including order of instructions),
+	 * please check the fixup code below as well.
 	 */
 #define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7)	\
 	ldd	[src + off + 0x00], t0;							\
@@ -205,38 +247,38 @@
 cc_end_cruft:
 	be	1f
 	 andcc	%o3, 4, %g0
-	ldd	[%o0 + 0x00], %g2
+	EX(ldd	[%o0 + 0x00], %g2, and %o3, 0xf,#)
 	add	%o1, 8, %o1
 	addcc	%g2, %g7, %g7
 	add	%o0, 8, %o0
 	addxcc	%g3, %g7, %g7
-	st	%g2, [%o1 - 0x08]
+	EX2(st	%g2, [%o1 - 0x08],#)
 	addx	%g0, %g7, %g7
 	andcc	%o3, 4, %g0
-	st	%g3, [%o1 - 0x04]
+	EX2(st	%g3, [%o1 - 0x04],#)
 1:	be	1f
 	 andcc	%o3, 3, %o3
-	ld	[%o0 + 0x00], %g2
+	EX(ld	[%o0 + 0x00], %g2, add %o3, 4,#)
 	add	%o1, 4, %o1
 	addcc	%g2, %g7, %g7
-	st	%g2, [%o1 - 0x04]
+	EX2(st	%g2, [%o1 - 0x04],#)
 	addx	%g0, %g7, %g7
-	add	%o0, 4, %o0
 	andcc	%o3, 3, %g0
+	add	%o0, 4, %o0
 1:	be	1f
 	 addcc	%o3, -1, %g0
 	bne	2f
 	 subcc	%o3, 2, %o3
 	b	4f
 	 or	%g0, %g0, %o4
-2:	lduh	[%o0 + 0x00], %o4
+2:	EX(lduh	[%o0 + 0x00], %o4, add %o3, 2,#)
 	add	%o0, 2, %o0
-	sth	%o4, [%o1 + 0x00]
+	EX2(sth	%o4, [%o1 + 0x00],#)
 	be	6f
 	 add	%o1, 2, %o1
 	sll	%o4, 16, %o4
-4:	ldub	[%o0 + 0x00], %o5
-	stb	%o5, [%o1 + 0x00]
+4:	EX(ldub	[%o0 + 0x00], %o5, add %g0, 1,#)
+	EX2(stb	%o5, [%o1 + 0x00],#)
 	sll	%o5, 8, %o5
 	or	%o5, %o4, %o4
 6:	addcc	%o4, %g7, %g7
@@ -253,27 +295,27 @@
 	 andcc	%o0, 0x2, %g0
 	be	1f
 	 andcc	%o0, 0x4, %g0
-	lduh	[%o0 + 0x00], %g2
+	EX(lduh	[%o0 + 0x00], %g4, add %g1, 0,#)
 	sub	%g1, 2, %g1
-	sth	%g2, [%o1 + 0x00]
+	EX2(sth	%g4, [%o1 + 0x00],#)
 	add	%o0, 2, %o0
-	sll	%g2, 16, %g2
-	addcc	%g2, %g7, %g7
+	sll	%g4, 16, %g4
+	addcc	%g4, %g7, %g7
 	add	%o1, 2, %o1
 	srl	%g7, 16, %g3
-	addx	%g0, %g3, %g2
+	addx	%g0, %g3, %g4
 	sll	%g7, 16, %g7
-	sll	%g2, 16, %g3
+	sll	%g4, 16, %g3
 	srl	%g7, 16, %g7
 	andcc	%o0, 0x4, %g0
 	or	%g3, %g7, %g7
 1:	be	3f
 	 andcc	%g1, 0xffffff80, %g0
-	ld	[%o0 + 0x00], %g2
+	EX(ld	[%o0 + 0x00], %g4, add %g1, 0,#)
 	sub	%g1, 4, %g1
-	st	%g2, [%o1 + 0x00]
+	EX2(st	%g4, [%o1 + 0x00],#)
 	add	%o0, 4, %o0
-	addcc	%g2, %g7, %g7
+	addcc	%g4, %g7, %g7
 	add	%o1, 4, %o1
 	addx	%g0, %g7, %g7
 	b	3f
@@ -284,14 +326,13 @@
 	 * out of you, game over, lights out.
 	 */
 	.align	8
-	.globl	C_LABEL(csum_partial_copy)
-C_LABEL(csum_partial_copy):		/* %o0=src, %o1=dest, %o2=len, %o3=sum */
+	.globl	C_LABEL(__csum_partial_copy_sparc_generic)
+C_LABEL(__csum_partial_copy_sparc_generic):
+					/* %o0=src, %o1=dest, %g1=len, %g7=sum */
 	xor	%o0, %o1, %o4		! get changing bits
-	mov	%o2, %g1		! free up %o2
 	andcc	%o4, 3, %g0		! check for mismatched alignment
 	bne	ccslow			! better this than unaligned/fixups
 	 andcc	%o0, 7, %g0		! need to align things?
-	mov	%o3, %g7		! free up %o3
 	bne	cc_dword_align		! yes, we check for short lengths there
 	 andcc	%g1, 0xffffff80, %g0	! can we use unrolled loop?
 3:	be	3f			! nope, less than one loop remains
@@ -301,6 +342,7 @@
 	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
 	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
 	CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+10:	EXT(5b, 10b, 20f,#)		! note for exception handling
 	sub	%g1, 128, %g1		! detract from length
 	addx	%g0, %g7, %g7		! add in last carry bit
 	andcc	%g1, 0xffffff80, %g0	! more to csum?
@@ -311,12 +353,12 @@
 ccmerge:be	ccte			! nope, go and check for end cruft
 	 andcc	%g1, 0xf, %o3		! get low bits of length (clears carry btw)
 	srl	%o2, 1, %o4		! begin negative offset computation
-	sethi	%hi(ccte - 8), %o5	! set up table ptr end
+	sethi	%hi(12f), %o5		! set up table ptr end
 	add	%o0, %o2, %o0		! advance src ptr
 	sub	%o5, %o4, %o5		! continue table calculation
 	sll	%o2, 1, %g2		! constant multiplies are fun...
 	sub	%o5, %g2, %o5		! some more adjustments
-	jmp	%o5 + %lo(ccte - 8)	! jump into it, duff style, wheee...
+	jmp	%o5 + %lo(12f)		! jump into it, duff style, wheee...
 	 add	%o1, %o2, %o1		! advance dest ptr (carry is clear btw)
 cctbl:	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
 	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
@@ -325,6 +367,7 @@
 	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
 	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
 	CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
+12:	EXT(cctbl, 12b, 22f,#)		! note for exception table handling
 	addx	%g0, %g7, %g7
 	andcc	%o3, 0xf, %g0		! check for low bits set
 ccte:	bne	cc_end_cruft		! something left, handle it out of band
@@ -335,6 +378,7 @@
 	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
 	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
 	CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+11:	EXT(ccdbl, 11b, 21f,#)		! note for exception table handling
 	sub	%g1, 128, %g1		! detract from length
 	addx	%g0, %g7, %g7		! add in last carry bit
 	andcc	%g1, 0xffffff80, %g0	! more to csum?
@@ -344,96 +388,194 @@
 	b	ccmerge			! finish it off, above
 	 andcc	%g1, 0x70, %o2		! can use table? (clears carry btw)
 
-ccslow:
+ccslow:	cmp	%g1, 0
+	mov	0, %g5
+	bleu	4f
+	 andcc	%o0, 1, %o5		
+	be,a	1f
+	 srl	%g1, 1, %g4		
+	sub	%g1, 1, %g1	
+	EX(ldub	[%o0], %g5, add %g1, 1,#)
+	add	%o0, 1, %o0	
+	EX2(stb	%g5, [%o1],#)
+	srl	%g1, 1, %g4
+	add	%o1, 1, %o1
+1:	cmp	%g4, 0		
+	be,a	3f
+	 andcc	%g1, 1, %g0
+	andcc	%o0, 2, %g0	
+	be,a	1f
+	 srl	%g4, 1, %g4
+	EX(lduh	[%o0], %o4, add %g1, 0,#)
+	sub	%g1, 2, %g1	
+	srl	%o4, 8, %g2
+	sub	%g4, 1, %g4	
+	EX2(stb	%g2, [%o1],#)
+	add	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 1],#)
+	add	%o0, 2, %o0	
+	srl	%g4, 1, %g4
+	add	%o1, 2, %o1
+1:	cmp	%g4, 0		
+	be,a	2f
+	 andcc	%g1, 2, %g0
+	EX3(ld	[%o0], %o4,#)
+5:	srl	%o4, 24, %g2
+	srl	%o4, 16, %g3
+	EX2(stb	%g2, [%o1],#)
+	srl	%o4, 8, %g2
+	EX2(stb	%g3, [%o1 + 1],#)
+	add	%o0, 4, %o0
+	EX2(stb	%g2, [%o1 + 2],#)
+	addcc	%o4, %g5, %g5
+	EX2(stb	%o4, [%o1 + 3],#)
+	addx	%g5, %g0, %g5	! I am now to lazy to optimize this (question it
+	add	%o1, 4, %o1	! is worthy). Maybe some day - with the sll/srl
+	subcc	%g4, 1, %g4	! tricks
+	bne,a	5b
+	 EX3(ld	[%o0], %o4,#)
+	sll	%g5, 16, %g2
+	srl	%g5, 16, %g5
+	srl	%g2, 16, %g2
+	andcc	%g1, 2, %g0
+	add	%g2, %g5, %g5 
+2:	be,a	3f		
+	 andcc	%g1, 1, %g0
+	EX(lduh	[%o0], %o4, and %g1, 3,#)
+	andcc	%g1, 1, %g0
+	srl	%o4, 8, %g2
+	add	%o0, 2, %o0	
+	EX2(stb	%g2, [%o1],#)
+	add	%g5, %o4, %g5
+	EX2(stb	%o4, [%o1 + 1],#)
+	add	%o1, 2, %o1
+3:	be,a	1f		
+	 sll	%g5, 16, %o4
+	EX(ldub	[%o0], %g2, add %g0, 1,#)
+	sll	%g2, 8, %o4	
+	EX2(stb	%g2, [%o1],#)
+	add	%g5, %o4, %g5
+	sll	%g5, 16, %o4
+1:	addcc	%o4, %g5, %g5
+	srl	%g5, 16, %o4
+	addx	%g0, %o4, %g5
+	orcc	%o5, %g0, %g0
+	be	4f
+	 srl	%g5, 8, %o4
+	and	%g5, 0xff, %g2
+	and	%o4, 0xff, %o4
+	sll	%g2, 8, %g2
+	or	%g2, %o4, %g5
+4:	addcc	%g7, %g5, %g7
+	retl	
+	 addx	%g0, %g7, %o0
+C_LABEL(__csum_partial_copy_end):
+
+        .section .fixup,#alloc,#execinstr
+        .align  4
+/* We do these strange calculations for the csum_*_from_user case only, ie.
+ * we only bother with faults on loads... */
+
+/* o2 = ((g2%20)&3)*8
+ * o3 = g1 - (g2/20)*32 - o2 */
+20:
+	cmp	%g2, 20
+	blu,a	1f
+	 and	%g2, 3, %o2
+	sub	%g1, 32, %g1
+	b	20b
+	 sub	%g2, 20, %g2
+1:
+	sll	%o2, 3, %o2
+	b	31f
+	 sub	%g1, %o2, %o3
+
+/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8)
+ * o3 = g1 - (g2/16)*32 - o2 */
+21:
+	andcc	%g2, 15, %o3
+	srl	%g2, 4, %g2
+	be,a	1f
+	 clr	%o2
+	add	%o3, 1, %o3
+	and	%o3, 14, %o3
+	sll	%o3, 3, %o2
+1:
+	sll	%g2, 5, %g2
+	sub	%g1, %g2, %o3
+	b	31f
+	 sub	%o3, %o2, %o3
+
+/* o0 += (g2/10)*16 - 0x70
+ * 01 += (g2/10)*16 - 0x70
+ * o2 = (g2 % 10) ? 8 : 0
+ * o3 += 0x70 - (g2/10)*16 - o2 */
+22:
+	cmp	%g2, 10
+	blu,a	1f
+	 sub	%o0, 0x70, %o0
+	add	%o0, 16, %o0
+	add	%o1, 16, %o1
+	sub	%o3, 16, %o3
+	b	22b
+	 sub	%g2, 10, %g2
+1:
+	sub	%o1, 0x70, %o1
+	add	%o3, 0x70, %o3
+	clr	%o2
+	tst	%g2
+	bne,a	1f
+	 mov	8, %o2
+1:
+	b	31f
+	 sub	%o3, %o2, %o3
+96:
+	and	%g1, 3, %g1
+	sll	%g4, 2, %g4
+	add	%g1, %g4, %o3
+30:
+/* %o1 is dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occured */
+	clr	%o2
+31:
+/* %o0 is src
+ * %o1 is dst
+ * %o2 is # of bytes to copy from src to dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occured */
 	save	%sp, -104, %sp
-	mov	%i0, %g2
-	mov	%g2, %o4
-	orcc	%i2, %g0, %o5
-	ble	.LL37
-	 mov	0, %o3
-	andcc	%g2, 1, %g3
-	be	.LL50
-	 sra	%o5, 1, %o1
-	ldub	[%g2], %o3
-	add	%i2, -1, %o5
-	add	%g2, 1, %o4
-	sra	%o5, 1, %o1
-.LL50:
-	cmp	%o1, 0
-	be	.LL39
-	 andcc	%o4, 2, %g0
-	be,a	.LL51
-	 sra	%o1, 1, %o1
-	add	%o1, -1, %o1
-	lduh	[%o4], %o0
-	add	%o5, -2, %o5
-	add	%o3, %o0, %o3
-	add	%o4, 2, %o4
-	sra	%o1, 1, %o1
-.LL51:
-	cmp	%o1, 0
-	be	.LL41
-	 mov	0, %o2
-.LL42:
-	ld	[%o4], %o0
-	add	%o3, %o2, %o3
-	add	%o3, %o0, %o3
-	cmp	%o3, %o0
-	 addx	%g0, 0, %o2
-	addcc	%o1, -1, %o1
-	bne	.LL42
-	 add	%o4, 4, %o4
-	add	%o3, %o2, %o3
-	sethi	%hi(65535), %o0
-	or	%o0, %lo(65535), %o0
-	and	%o3, %o0, %o0
-	srl	%o3, 16, %o1
-	add	%o0, %o1, %o3
-.LL41:
-	andcc	%o5, 2, %g0
-	be	.LL52
-	 andcc	%o5, 1, %g0
-	lduh	[%o4], %o0
-	add	%o3, %o0, %o3
-	add	%o4, 2, %o4
-.LL39:
-	andcc	%o5, 1, %g0
-.LL52:
-	be	.LL53
-	 sethi	%hi(65535), %o0
-	ldub	[%o4], %o0
-	sll	%o0, 8, %o0
-	add	%o3, %o0, %o3
-	sethi	%hi(65535), %o0
-.LL53:
-	or	%o0, %lo(65535), %o0
-	and	%o3, %o0, %o2
-	srl	%o3, 16, %o1
-	add	%o2, %o1, %o1
-	and	%o1, %o0, %o2
-	srl	%o1, 16, %o1
-	add	%o2, %o1, %o1
-	and	%o1, %o0, %o0
-	srl	%o1, 16, %o1
-	add	%o0, %o1, %o1
-	sll	%o1, 16, %o0
-	cmp	%g3, 0
-	be	.LL37
-	 srl	%o0, 16, %o3
-	srl	%o0, 24, %o1
-	and	%o3, 255, %o0
-	sll	%o0, 8, %o0
-	or	%o1, %o0, %o3
-.LL37:
-	add	%o3, %i3, %o1
-	sethi	%hi(65535), %o0
-	or	%o0, %lo(65535), %o0
-	and	%o1, %o0, %o0
-	srl	%o1, 16, %o1
-	add	%o0, %o1, %i0
+        mov     %i5, %o0
+        mov     %i7, %o1
+        mov	%i4, %o2
+        call    C_LABEL(lookup_fault)
+	 mov	%g7, %i4
+	cmp	%o0, 2
+	bne	1f	
+	 add	%g0, -EFAULT, %i5
+	tst	%i2
+	be	2f
+	 mov	%i0, %o1
 	mov	%i1, %o0
-	mov	%g2, %o1
+5:
 	call	C_LABEL(__memcpy)
 	 mov	%i2, %o2
+	tst	%o0
+	bne,a	2f
+	 add	%i3, %i2, %i3
+	add	%i1, %i2, %i1
+2:
+	mov	%i1, %o0
+	call	C_LABEL(__bzero)
+	 mov	%i3, %o1
+1:
+	ld	[%sp + 168], %o2		! struct_ptr of parent
+	st	%i5, [%o2]
 	ret
 	 restore
+
+        .section __ex_table,#alloc
+        .align 4
+        .word 5b,2
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/lib/locks.S linux/arch/sparc/lib/locks.S
--- v2.1.28/linux/arch/sparc/lib/locks.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/lib/locks.S	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: locks.S,v 1.3 1997/01/12 11:36:44 davem Exp $
+/* $Id: locks.S,v 1.4 1997/03/04 16:26:41 jj Exp $
  * locks.S: SMP low-level lock primitives on Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -64,7 +64,7 @@
 ___lock_kernel:
 	addcc	%g2, -1, %g2
 	bcs,a	9f
-	 st	%g2, [%g6 + TASK_LOCK_DEPTH]
+	 st	%g2, [%g6 + AOFF_task_lock_depth]
 	rd	%psr, %g3
 	or	%g3, PSR_PIL, %g2
 	wr	%g2, 0x0, %psr
@@ -86,7 +86,7 @@
 	st	%g7, [%g2]
 #endif
 2:	mov	-1, %g2
-	st	%g2, [%g6 + TASK_LOCK_DEPTH]
+	st	%g2, [%g6 + AOFF_task_lock_depth]
 	wr	%g3, 0x0, %psr
 	nop; nop; nop
 9:	jmpl	%o7 + 0x8, %g0
@@ -99,7 +99,7 @@
 ___unlock_kernel:
 	addcc	%g2, 1, %g2
 	bne,a	1f
-	 st	%g2, [%g6 + TASK_LOCK_DEPTH]
+	 st	%g2, [%g6 + AOFF_task_lock_depth]
 	rd	%psr, %g3
 	or	%g3, PSR_PIL, %g2
 	wr	%g2, 0x0, %psr
@@ -107,7 +107,7 @@
 	mov	NO_PROC_ID, %g2
 	stb	%g2, [%g1 + 1]
 	stb	%g0, [%g1 + 0]
-	st	%g0, [%g6 + TASK_LOCK_DEPTH]
+	st	%g0, [%g6 + AOFF_task_lock_depth]
 	wr	%g3, 0x0, %psr
 	nop; nop; nop;
 1:	jmpl	%o7 + 0x8, %g0
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/lib/memset.S linux/arch/sparc/lib/memset.S
--- v2.1.28/linux/arch/sparc/lib/memset.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/lib/memset.S	Wed Mar  5 17:04:30 1997
@@ -1,14 +1,37 @@
-/* linux/arch/sparc/lib/memset.S: Sparc optimized memset and bzero code
- * Hand optimized from GNU libc's memset
+/* linux/arch/sparc/lib/memset.S: Sparc optimized memset, bzero and clear_user code
  * Copyright (C) 1991,1996 Free Software Foundation
  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Returns 0, if ok, and number of bytes not yet set if exception
+ * occurs and we were called as clear_user.
  */
 
 #include <asm/cprefix.h>
 #include <asm/ptrace.h>
 
-	/* Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
+#define EX(x,y,a,b,z) 				\
+98: 	x,y;					\
+	.section .fixup,z##alloc,z##execinstr;	\
+	.align	4;				\
+99:	ba 30f;					\
+	 a, b, %o0;				\
+	.section __ex_table,z##alloc;		\
+	.align	4;				\
+	.word	98b, 99b;			\
+	.text;					\
+	.align	4
+
+#define EXT(start,end,handler,z) 		\
+	.section __ex_table,z##alloc;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
+	.text;					\
+	.align	4
+
+/* Please don't change these macros, unless you change the logic
+ * in the .fixup section below as well.
+ * Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
 #define ZERO_BIG_BLOCK(base, offset, source)    \
 	std	source, [base + offset + 0x00]; \
 	std	source, [base + offset + 0x08]; \
@@ -32,7 +55,10 @@
 	.text
 	.align 4
 
-	.globl	C_LABEL(__bzero), C_LABEL(__memset), C_LABEL(memset)
+	.globl	C_LABEL(__bzero), C_LABEL(__memset), 
+	.globl	C_LABEL(memset)
+	.globl	C_LABEL(__memset_start), C_LABEL(__memset_end)
+C_LABEL(__memset_start):
 C_LABEL(__memset):
 C_LABEL(memset):
 	and	%o1, 0xff, %g3
@@ -45,13 +71,13 @@
 3:
 	cmp	%o2, 3
 	be	2f
-	 stb	%g3, [%o0]
+	 EX(stb	%g3, [%o0], sub %o1, 0,#)
 
 	cmp	%o2, 2
 	be	2f
-	 stb	%g3, [%o0 + 0x01]
+	 EX(stb	%g3, [%o0 + 0x01], sub %o1, 1,#)
 
-	stb	%g3, [%o0 + 0x02]
+	EX(stb	%g3, [%o0 + 0x02], sub %o1, 2,#)
 2:
 	sub	%o2, 4, %o2
 	add	%o1, %o2, %o1
@@ -61,7 +87,6 @@
 C_LABEL(__bzero):
 	mov	%g0, %g3
 1:
- 	mov	%o0, %g1
 	cmp	%o1, 7
 	bleu	7f
 	 andcc	%o0, 3, %o2
@@ -73,57 +98,59 @@
 	be	2f
 	 mov	%g3, %g2
 
-	st	%g3, [%o0]
+	EX(st	%g3, [%o0], sub %o1, 0,#)
 	sub	%o1, 4, %o1
 	add	%o0, 4, %o0
 2:
 	andcc	%o1, 0xffffff80, %o3	! Now everything is 8 aligned and o1 is len to run
 	be	9f
 	 andcc	%o1, 0x78, %o2
-4:
+10:
 	ZERO_BIG_BLOCK(%o0, 0x00, %g2)
 	subcc	%o3, 128, %o3
 	ZERO_BIG_BLOCK(%o0, 0x40, %g2)
-	bne	4b
+11:
+	EXT(10b, 11b, 20f,#)
+	bne	10b
 	 add	%o0, 128, %o0
 
 	orcc	%o2, %g0, %g0
 9:
-	be	6f
+	be	13f
 	 andcc	%o1, 7, %o1
 
 	srl	%o2, 1, %o3
-	set	bzero_table + 64, %o4
+	set	13f, %o4
 	sub	%o4, %o3, %o4
 	jmp	%o4
 	 add	%o0, %o2, %o0
 
-bzero_table:
+12:
 	ZERO_LAST_BLOCKS(%o0, 0x48, %g2)
 	ZERO_LAST_BLOCKS(%o0, 0x08, %g2)
-6:
+13:
 	be	8f
 	 andcc	%o1, 4, %g0
 
 	be	1f
 	 andcc	%o1, 2, %g0
 
-	st	%g3, [%o0]
+	EX(st	%g3, [%o0], and %o1, 7,#)
 	add	%o0, 4, %o0
 1:
 	be	1f
 	 andcc	%o1, 1, %g0
 
-	sth	%g3, [%o0]
+	EX(sth	%g3, [%o0], and %o1, 3,#)
 	add	%o0, 2, %o0
 1:
 	bne,a	8f
-	 stb	%g3, [%o0]
+	 EX(stb	%g3, [%o0], and %o1, 1,#)
 8:
 	retl
-	 mov	%g1, %o0
+	 clr	%o0
 7:
-	be	6b
+	be	13b
 	 orcc	%o1, 0, %g0
 
 	be	0f
@@ -131,7 +158,38 @@
 	 add	%o0, 1, %o0
 	subcc	%o1, 1, %o1
 	bne,a	8b
-	 stb	%g3, [%o0 - 1]
+	 EX(stb	%g3, [%o0 - 1], add %o1, 1,#)
 0:
 	retl
-	 mov	%g1, %o0
+	 clr	%o0
+C_LABEL(__memset_end):
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+20:
+	cmp	%g2, 8
+	bleu	1f
+	 and	%o1, 0x7f, %o1
+	sub	%g2, 9, %g2
+	add	%o3, 64, %o3
+1:
+	sll	%g2, 3, %g2
+	add	%o3, %o1, %o0
+	b 30f
+	 sub	%o0, %g2, %o0
+21:
+	mov	8, %o0
+	and	%o1, 7, %o1
+	sub	%o0, %g2, %o0
+	sll	%o0, 3, %o0
+	b 30f
+	 add	%o0, %o1, %o0
+30:
+/* %o4 is faulting address, %o5 is %pc where fault occured */
+	save	%sp, -104, %sp
+	mov	%i5, %o0
+	mov	%i7, %o1
+	call	C_LABEL(lookup_fault)
+	 mov	%i4, %o2
+	ret
+	 restore
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
--- v2.1.28/linux/arch/sparc/mm/fault.c	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/mm/fault.c	Wed Mar  5 17:04:30 1997
@@ -1,8 +1,9 @@
-/* $Id: fault.c,v 1.86 1997/01/06 06:52:52 davem Exp $
+/* $Id: fault.c,v 1.89 1997/03/04 16:26:46 jj Exp $
  * fault.c:  Page fault handlers for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/head.h>
@@ -133,6 +134,58 @@
 	prom_halt();
 }
 
+void unhandled_fault(unsigned long address, struct task_struct *tsk,
+                     struct pt_regs *regs)
+{
+	if((unsigned long) address < PAGE_SIZE) {
+		printk(KERN_ALERT "Unable to handle kernel NULL "
+		       "pointer dereference");
+	} else {
+		printk(KERN_ALERT "Unable to handle kernel paging request "
+		       "at virtual address %08lx\n", address);
+	}
+	printk(KERN_ALERT "tsk->mm->context = %08lx\n",
+	       (unsigned long) tsk->mm->context);
+	printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
+	       (unsigned long) tsk->mm->pgd);
+	die_if_kernel("Oops", regs);
+}
+
+asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 
+			    unsigned long address)
+{
+	unsigned long g2;
+	int i;
+	unsigned insn;
+	struct pt_regs regs;
+	
+	i = search_exception_table (ret_pc, &g2);
+	switch (i) {
+	/* load & store will be handled by fixup */
+	case 3: return 3;
+	/* store will be handled by fixup, load will bump out */
+	/* for _to_ macros */
+	case 1: insn = (unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
+	/* load will be handled by fixup, store will bump out */
+	/* for _from_ macros */
+	case 2: insn = (unsigned *)pc; 
+		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) return 2; 
+		break; 
+	default: break;
+	}
+	memset (&regs, 0, sizeof (regs));
+	regs.pc = pc;
+	regs.npc = pc + 4;
+	__asm__ __volatile__ ("
+		rd %%psr, %0
+		nop
+		nop
+		nop" : "=r" (regs.psr));
+	unhandled_fault (address, current, &regs);
+	/* Not reached */
+	return 0;
+}
+
 asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
 			       unsigned long address)
 {
@@ -142,34 +195,11 @@
 	unsigned int fixup;
 	unsigned long g2;
 	int from_user = !(regs->psr & PSR_PS);
-#if 0
-	static unsigned long last_one;
-#endif
 	lock_kernel();
 	down(&mm->mmap_sem);
 	if(text_fault)
 		address = regs->pc;
 
-#if 0
-	if(current->tss.ex.count) {
-		printk("f<pid=%d,tf=%d,wr=%d,addr=%08lx,pc=%08lx>\n",
-		       tsk->pid, text_fault, write, address, regs->pc);
-		printk("EX: count<%d> pc<%08lx> expc<%08lx> address<%08lx>\n",
-		       (int) current->tss.ex.count, current->tss.ex.pc,
-		       current->tss.ex.expc, current->tss.ex.address);
-#if 0
-		if(last_one == address) {
-			printk("Twice in a row, AIEEE.  Spinning so you can see the dump.\n");
-			show_regs(regs);
-			sti();
-			while(1)
-				barrier();
-		}
-		last_one = address;
-#endif
-	}
-#endif
-
 	/* The kernel referencing a bad kernel pointer can lock up
 	 * a sun4c machine completely, so we must attempt recovery.
 	 */
@@ -211,32 +241,25 @@
 	
 	g2 = regs->u_regs[UREG_G2];
 	if (!from_user && (fixup = search_exception_table (regs->pc, &g2))) {
-		printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
-		printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
-			regs->pc, fixup, g2);
-		regs->pc = fixup;
-		regs->npc = regs->pc + 4;
-		regs->u_regs[UREG_G2] = g2;
-		goto out;
-	}
-	/* Did we have an exception handler installed? */
-	if(current->tss.ex.count == 1) {
-		if(from_user) {
-			printk("Yieee, exception signalled from user mode.\n");
-		} else {
-			/* Set pc to %g1, set %g1 to -EFAULT and %g2 to
-			 * the faulting address so we can cleanup.
-			 */
+		if (fixup > 10) { /* Values below are reserved for other things */
+			extern const unsigned __memset_start[];
+			extern const unsigned __memset_end[];
+			extern const unsigned __csum_partial_copy_start[];
+			extern const unsigned __csum_partial_copy_end[];
+
 			printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
-			printk("EX: count<%d> pc<%08lx> expc<%08lx> address<%08lx>\n",
-			       (int) current->tss.ex.count, current->tss.ex.pc,
-			       current->tss.ex.expc, current->tss.ex.address);
-			current->tss.ex.count = 0;
-			regs->pc = current->tss.ex.expc;
+			printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
+				regs->pc, fixup, g2);
+			if ((regs->pc >= (unsigned long)__memset_start &&
+			     regs->pc < (unsigned long)__memset_end) ||
+			    (regs->pc >= (unsigned long)__csum_partial_copy_start &&
+			     regs->pc < (unsigned long)__csum_partial_copy_end)) {
+			        regs->u_regs[UREG_I4] = address;
+				regs->u_regs[UREG_I5] = regs->pc;
+			}
+			regs->u_regs[UREG_G2] = g2;
+			regs->pc = fixup;
 			regs->npc = regs->pc + 4;
-			regs->u_regs[UREG_G1] = -EFAULT;
-			regs->u_regs[UREG_G2] = address - current->tss.ex.address;
-			regs->u_regs[UREG_G3] = current->tss.ex.pc;
 			goto out;
 		}
 	}
@@ -250,18 +273,7 @@
 		send_sig(SIGSEGV, tsk, 1);
 		goto out;
 	}
-	if((unsigned long) address < PAGE_SIZE) {
-		printk(KERN_ALERT "Unable to handle kernel NULL "
-		       "pointer dereference");
-	} else {
-		printk(KERN_ALERT "Unable to handle kernel paging request "
-		       "at virtual address %08lx\n", address);
-	}
-	printk(KERN_ALERT "tsk->mm->context = %08lx\n",
-	       (unsigned long) tsk->mm->context);
-	printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
-	       (unsigned long) tsk->mm->pgd);
-	die_if_kernel("Oops", regs);
+	unhandled_fault (address, tsk, regs);
 out:
 	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v2.1.28/linux/arch/sparc/mm/srmmu.c	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc/mm/srmmu.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: srmmu.c,v 1.128 1997/01/12 12:07:00 davem Exp $
+/* $Id: srmmu.c,v 1.130 1997/02/10 23:33:49 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
@@ -2795,7 +2795,7 @@
 	tally = 0;
 	for(entry = 0; sp_banks[entry].num_bytes; entry++)
 		tally += sp_banks[entry].num_bytes;
-	if(tally >= (0xfd000000 - KERNBASE))
+	if(tally > (0xfd000000 - KERNBASE))
 		lots_of_ram = 1;
 	else
 		lots_of_ram = 0;
@@ -2846,7 +2846,7 @@
 		MKTRACE(("<%d> base=%08lx bs=%08lx ", entry, sp_banks[entry].base_addr, bank_size));
 		if(!bank_size)
 			break;
-		if(((vaddr + bank_size) >= 0xfd000000) ||
+		if(((vaddr + bank_size) > 0xfd000000) ||
 		   ((vaddr + bank_size) < KERNBASE)) {
 			unsigned long orig_base = sp_banks[entry].base_addr;
 			unsigned long orig_len = sp_banks[entry].num_bytes;
@@ -3104,7 +3104,7 @@
 
 					if((pte_val(*ptep) & SRMMU_ET_MASK) == SRMMU_VALID) {
 #if 1
-						printk("Fixing USER/USER alias [%d:%08lx]\n",
+						printk("Fixing USER/USER alias [%ld:%08lx]\n",
 						       vmaring->vm_mm->context, start);
 #endif
 						flush_cache_page(vmaring, start);
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/mm/sun4c.c linux/arch/sparc/mm/sun4c.c
--- v2.1.28/linux/arch/sparc/mm/sun4c.c	Mon Dec 30 01:59:58 1996
+++ linux/arch/sparc/mm/sun4c.c	Wed Mar  5 17:04:30 1997
@@ -1,4 +1,4 @@
-/* $Id: sun4c.c,v 1.137 1996/12/30 06:16:36 davem Exp $
+/* $Id: sun4c.c,v 1.139 1997/01/31 08:05:59 davem Exp $
  * sun4c.c: Doing in software what should be done in hardware.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -1878,6 +1878,7 @@
 {
 	if(address >= SUN4C_LOCK_VADDR)
 		return NULL;
+	address = (address >> PAGE_SHIFT) & (SUN4C_PTRS_PER_PTE - 1);
 	if (sun4c_pmd_none(*pmd))
 		panic("sun4c_pmd_none for kernel pmd, can't happen...");
 	if (sun4c_pmd_bad(*pmd)) {
@@ -2007,7 +2008,7 @@
 
 					if(pte_val(*ptep) & _SUN4C_PAGE_PRESENT) {
 #if 1
-						printk("Fixing USER/USER alias [%d:%08lx]\n",
+						printk("Fixing USER/USER alias [%ld:%08lx]\n",
 						       vmaring->vm_mm->context, start);
 #endif
 						sun4c_flush_cache_page(vmaring, start);
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/prom/ranges.c linux/arch/sparc/prom/ranges.c
--- v2.1.28/linux/arch/sparc/prom/ranges.c	Fri Dec 13 01:37:32 1996
+++ linux/arch/sparc/prom/ranges.c	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.7 1996/11/13 05:10:12 davem Exp $
+/* $Id: ranges.c,v 1.8 1997/02/04 07:28:29 davem Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -57,11 +57,21 @@
 }
 
 /* Apply probed sbus ranges to registers passed, if no ranges return. */
-void
-prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs, int nregs)
+void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs,
+			    int nregs, struct linux_sbus_device *sdev)
 {
-	if(sbus->num_sbus_ranges)
-	prom_adjust_regs(regs, nregs, sbus->sbus_ranges, sbus->num_sbus_ranges);
+	if(sbus->num_sbus_ranges) {
+		if(sdev && (sdev->ranges_applied == 0)) {
+			sdev->ranges_applied = 1;
+			prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
+					 sbus->num_sbus_ranges);
+		} else if(!sdev) {
+			printk("PROMLIB: Aieee, old SBUS driver, update it to use new "
+			       "prom_apply_sbus_ranges interface now!\n");
+			prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
+					 sbus->num_sbus_ranges);
+		}
+	}
 }
 
 __initfunc(void prom_ranges_init(void))
diff -u --recursive --new-file v2.1.28/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v2.1.28/linux/arch/sparc/prom/tree.c	Mon Dec 30 01:59:58 1996
+++ linux/arch/sparc/prom/tree.c	Wed Mar  5 17:04:31 1997
@@ -1,10 +1,12 @@
-/* $Id: tree.c,v 1.14 1996/12/29 20:46:12 davem Exp $
+/* $Id: tree.c,v 1.15 1997/01/31 00:17:04 tdyas Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#define PROMLIB_INTERNAL
+
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -13,67 +15,80 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 
+
+/* Macro to restore "current" to the g6 register. */
+#define restore_current() __asm__ __volatile__("ld [%0], %%g6\n\t" : : \
+			  "r" (&current_set[smp_processor_id()]) : \
+			  "memory")
+
 static char promlib_buf[128];
 
+/* Internal version of prom_getchild that does not alter return values. */
+int __prom_getchild(int node)
+{
+	unsigned long flags;
+	int cnode;
+
+	save_and_cli(flags);
+	cnode = prom_nodeops->no_child(node);
+	restore_current();
+	restore_flags(flags);
+
+	return cnode;
+}
+
 /* Return the child of node 'node' or zero if no this node has no
  * direct descendent.
  */
-int
-prom_getchild(int node)
+int prom_getchild(int node)
 {
-	int cnode, ret;
-	unsigned long flags;
+	int cnode;
 
-	save_flags(flags); cli();
+	if (node == -1)
+		return 0;
 
-	if(node == -1) {
-		ret = 0;
-	} else {
-		cnode = prom_nodeops->no_child(node);
-		if((cnode == 0) || (cnode == -1))
-			ret = 0;
-		else
-			ret = cnode;
-	}
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	cnode = __prom_getchild(node);
+	if (cnode == 0 || cnode == -1)
+		return 0;
+
+	return cnode;
+}
+
+/* Internal version of prom_getsibling that does not alter return values. */
+int __prom_getsibling(int node)
+{
+	unsigned long flags;
+	int cnode;
+
+	save_and_cli(flags);
+	cnode = prom_nodeops->no_nextnode(node);
+	restore_current();
 	restore_flags(flags);
-	return ret;
+
+	return cnode;
 }
 
 /* Return the next sibling of node 'node' or zero if no more siblings
  * at this level of depth in the tree.
  */
-int
-prom_getsibling(int node)
+int prom_getsibling(int node)
 {
-	int sibnode, ret;
-	unsigned long flags;
+	int sibnode;
 
-	save_flags(flags); cli();
+	if (node == -1)
+		return 0;
 
-	if(node == -1) {
-		ret = 0;
-	} else {
-		sibnode = prom_nodeops->no_nextnode(node);
-		if((sibnode == 0) || (sibnode == -1))
-			ret = 0;
-		else
-			ret = sibnode;
-	}
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
-	restore_flags(flags);
-	return ret;
+	sibnode = __prom_getsibling(node);
+	if (sibnode == 0 || sibnode == -1)
+		return 0;
+
+	return sibnode;
 }
 
 /* Return the length in bytes of property 'prop' at node 'node'.
  * Return -1 on error.
  */
-int
-prom_getproplen(int node, char *prop)
+int prom_getproplen(int node, char *prop)
 {
 	int ret;
 	unsigned long flags;
@@ -84,9 +99,7 @@
 		ret = -1;
 	else
 		ret = prom_nodeops->no_proplen(node, prop);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
@@ -95,8 +108,7 @@
  * 'buffer' which has a size of 'bufsize'.  If the acquisition
  * was successful the length will be returned, else -1 is returned.
  */
-int
-prom_getproperty(int node, char *prop, char *buffer, int bufsize)
+int prom_getproperty(int node, char *prop, char *buffer, int bufsize)
 {
 	int plen, ret;
 	unsigned long flags;
@@ -110,9 +122,7 @@
 		/* Ok, things seem all right. */
 		ret = prom_nodeops->no_getprop(node, prop, buffer);
 	}
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
@@ -120,8 +130,7 @@
 /* Acquire an integer property and return its value.  Returns -1
  * on failure.
  */
-int
-prom_getint(int node, char *prop)
+int prom_getint(int node, char *prop)
 {
 	static int intprop;
 
@@ -134,9 +143,7 @@
 /* Acquire an integer property, upon error return the passed default
  * integer.
  */
-
-int
-prom_getintdefault(int node, char *property, int deflt)
+int prom_getintdefault(int node, char *property, int deflt)
 {
 	int retval;
 
@@ -147,8 +154,7 @@
 }
 
 /* Acquire a boolean property, 1=TRUE 0=FALSE. */
-int
-prom_getbool(int node, char *prop)
+int prom_getbool(int node, char *prop)
 {
 	int retval;
 
@@ -161,8 +167,7 @@
  * string on error.  The char pointer is the user supplied string
  * buffer.
  */
-void
-prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
+void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
 {
 	int len;
 
@@ -176,8 +181,7 @@
 /* Does the device at node 'node' have name 'name'?
  * YES = 1   NO = 0
  */
-int
-prom_nodematch(int node, char *name)
+int prom_nodematch(int node, char *name)
 {
 	static char namebuf[128];
 	prom_getproperty(node, "name", namebuf, sizeof(namebuf));
@@ -188,8 +192,7 @@
 /* Search siblings at 'node_start' for a node with name
  * 'nodename'.  Return node if successful, zero if not.
  */
-int
-prom_searchsiblings(int node_start, char *nodename)
+int prom_searchsiblings(int node_start, char *nodename)
 {
 
 	int thisnode, error;
@@ -207,8 +210,7 @@
 }
 
 /* Gets name in the form prom v2+ uses it (name@x,yyyyy or name (if no reg)) */
-int 
-prom_getname (int node, char *buffer, int len)
+int prom_getname (int node, char *buffer, int len)
 {
 	int i;
 	struct linux_prom_registers reg[PROMREG_MAX];
@@ -227,8 +229,7 @@
 
 /* Return the first property type for node 'node'.
  */
-char *
-prom_firstprop(int node)
+char * prom_firstprop(int node)
 {
 	unsigned long flags;
 	char *ret;
@@ -236,9 +237,7 @@
 	if(node == -1) return "";
 	save_flags(flags); cli();
 	ret = prom_nodeops->no_nextprop(node, (char *) 0x0);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
@@ -247,8 +246,7 @@
  * at node 'node' .  Returns NULL string if no more
  * property types for this node.
  */
-char *
-prom_nextprop(int node, char *oprop)
+char * prom_nextprop(int node, char *oprop)
 {
 	char *ret;
 	unsigned long flags;
@@ -256,15 +254,12 @@
 	if(node == -1) return "";
 	save_flags(flags); cli();
 	ret = prom_nodeops->no_nextprop(node, oprop);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
 
-int
-prom_node_has_property(int node, char *prop)
+int prom_node_has_property(int node, char *prop)
 {
 	char *current_property = "";
 
@@ -279,8 +274,7 @@
 /* Set property 'pname' at node 'node' to value 'value' which has a length
  * of 'size' bytes.  Return the number of bytes the prom accepted.
  */
-int
-prom_setprop(int node, char *pname, char *value, int size)
+int prom_setprop(int node, char *pname, char *value, int size)
 {
 	unsigned long flags;
 	int ret;
@@ -289,24 +283,19 @@
 	if((pname == 0) || (value == 0)) return 0;
 	save_flags(flags); cli();
 	ret = prom_nodeops->no_setprop(node, pname, value, size);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-			     "r" (&current_set[smp_processor_id()]) :
-			     "memory");
+	restore_current();
 	restore_flags(flags);
 	return ret;
 }
 
-int
-prom_inst2pkg(int inst)
+int prom_inst2pkg(int inst)
 {
 	int node;
 	unsigned long flags;
 	
 	save_flags(flags); cli();
 	node = (*romvec->pv_v2devops.v2_inst2pkg)(inst);
-	__asm__ __volatile__("ld [%0], %%g6\n\t" : :
-		"r" (&current_set[smp_processor_id()]) :
-		"memory");
+	restore_current();
 	restore_flags(flags);
 	if (node == -1) return 0;
 	return node;
@@ -315,8 +304,7 @@
 /* Return 'node' assigned to a particular prom 'path'
  * FIXME: Should work for v0 as well
  */
-int
-prom_pathtoinode(char *path)
+int prom_pathtoinode(char *path)
 {
 	int node, inst;
 	
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.1.28/linux/arch/sparc64/Makefile	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc64/Makefile	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.3 1997/01/02 14:14:35 jj Exp $
+# $Id: Makefile,v 1.6 1997/03/04 16:27:18 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -22,8 +22,8 @@
 # Uncomment the first CFLAGS if you are doing kgdb source level
 # debugging of the kernel to get the proper debugging information.
 
-#CFLAGS := $(CFLAGS) -g -pipe
-CFLAGS := $(CFLAGS) -pipe
+#CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7 
+CFLAGS := $(CFLAGS) -pipe -fcall-used-g5 -fcall-used-g7
 
 LINKFLAGS = -T arch/sparc64/vmlinux.lds
 
@@ -40,3 +40,16 @@
 archclean:
 
 archdep:
+
+# <hack> Temporary hack, until we get a clean compile of everything...
+vmlinux64: $(CONFIGURATION) init/main.o init/version.o
+	set -e; for i in arch/sparc64/kernel arch/sparc64/lib arch/sparc64/prom lib; do $(MAKE) -C $$i; done
+	$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
+		arch/sparc64/kernel/kernel.o \
+		lib/lib.a arch/sparc64/prom/promlib.a arch/sparc64/lib/lib.a \
+		-o vmlinux
+	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\(\.\.ng$$\)' | sort > System.map
+# </hack>
+
+check_asm:
+	$(MAKE) -C arch/sparc64/kernel check_asm
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/Makefile linux/arch/sparc64/kernel/Makefile
--- v2.1.28/linux/arch/sparc64/kernel/Makefile	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/Makefile	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.1 1996/12/26 10:16:41 davem Exp $
+# $Id: Makefile,v 1.5 1997/03/04 16:26:54 jj Exp $
 # Makefile for the linux kernel.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -13,20 +13,33 @@
 .S.o:
 	$(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
 
-all: kernel.o head.o initobj.o finitobj.o
+all: kernel.o head.o
 
 O_TARGET := kernel.o
-O_OBJS   := etrap.o rtrap.o signal32.o
+O_OBJS   := etrap.o rtrap.o hack.o process.o # signal32.o
 OX_OBJS  := sparc64_ksyms.o
 
 head.o: head.S
 	$(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o
 
-initobj.o: initobj.S
-	$(CC) -D__ASSEMBLY__ -ansi -c initobj.S -o initobj.o
-
-finitobj.o: initobj.S
-	$(CC) -D__ASSEMBLY__ -ansi -c finitobj.S -o finitobj.o
+check_asm: dummy
+	@echo "#include <linux/sched.h>" > tmp.c
+	$(CC) -E tmp.c -o tmp.i
+	@echo "/* Automatically generated. Do not edit. */" > check_asm.c; echo "#include <linux/sched.h>" >> check_asm.c; echo 'struct task_struct _task; struct mm_struct _mm; struct thread_struct _thread; int main(void) { printf ("/* Automatically generated. Do not edit. */\n#ifndef __ASM_OFFSETS_H__\n#define __ASM_OFFSETS_H__\n\n");' >> check_asm.c
+	$(SH) ./check_asm.sh task tmp.i check_asm.c
+	$(SH) ./check_asm.sh mm tmp.i check_asm.c
+	$(SH) ./check_asm.sh thread tmp.i check_asm.c
+	@echo 'printf ("\n#endif /* __ASM_OFFSETS_H__ */\n"); return 0; }' >> check_asm.c
+	@rm -f tmp.[ci]
+	#$(CC) -o check_asm check_asm.c
+	# <hack> Until we can do this natively, a hack has to take place
+	$(CC) -mmedlow -S -o check_asm.s check_asm.c
+	$(HOSTCC) -o check_asm check_asm.s
+	@rm -f check_asm.s
+	# </hack>
+	./check_asm > asm_offsets.h
+	@if test -r $(HPATH)/asm/asm_offsets.h; then if cmp -s asm_offsets.h $(HPATH)/asm/asm_offsets.h; then echo $(HPATH)/asm/asm_offsets.h is unchanged; rm -f asm_offsets.h; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi; else mv -f asm_offsets.h $(HPATH)/asm/asm_offsets.h; fi
+	@rm -f check_asm check_asm.c
 
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/check_asm.sh linux/arch/sparc64/kernel/check_asm.sh
--- v2.1.28/linux/arch/sparc64/kernel/check_asm.sh	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/check_asm.sh	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,3 @@
+#!/bin/sh
+sed -n -e '/struct[ 	]*'$1'_struct[ 	]*{/,/};/p' < $2 | sed '/struct[ 	]*'$1'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+/g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/printf ("#define AOFF_'$1'_\0	0x%08x\\n#define ASIZ_'$1'_\0	0x%08x\\n", ((char *)\&_'$1'.\0) - ((char *)\&_'$1'), sizeof(_'$1'.\0));/' >> $3
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/dtlb_miss.S linux/arch/sparc64/kernel/dtlb_miss.S
--- v2.1.28/linux/arch/sparc64/kernel/dtlb_miss.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/dtlb_miss.S	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: dtlb_miss.S,v 1.4 1996/12/28 18:39:40 davem Exp $
+/* $Id: dtlb_miss.S,v 1.5 1997/02/25 20:00:02 jj Exp $
  * dtlb_miss.S:	Data TLB miss code, this is included directly
  *              into the trap table.
  *
@@ -45,10 +45,9 @@
 	 * XXX I think I can knock off two more instructions here...
 	 */
 
-dtlb_miss:
 	/* I-cache line 0 */
 	ldxa		[%g0] ASI_DMMU, %g1		! grab Tag Target either way
-	brlz,pnt	%g1, 3f				! special kernel processing
+	brlz,pn		%g1, 3f				! special kernel processing
 	 srlx		%g1, 8, %g3			! put high vaddr bits in place
 
 1:
@@ -68,15 +67,15 @@
 2:
 	retry						! return from trap
 
-#define KTTE_HIGH_BITS	(_PAGE_VALID | _PAGE_SZ4MB)
-#define KTTE_LOW_BITS	(_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G)
+#define KTTE_HIGH_BITS	_PAGE_VALID | _PAGE_SZ4MB
+#define KTTE_LOW_BITS	_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G
 
 	nop						! align next insn on cache line
 3:
 	/* I-cache line 2 */
 	srax		%g1, 19, %g5			! mask down high bits
 	cmp		%g5, -1				! if -1 this is VMALLOC area
-	be,pnt		%xcc, 1b			! yep
+	be,pn		%xcc, 1b			! yep
 	 sethi		%uhi(KTTE_HIGH_BITS), %g4	! begin pte formation
 
 	sllx		%g1, 23, %g1			! begin masking for physpage
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S
--- v2.1.28/linux/arch/sparc64/kernel/dtlb_prot.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/dtlb_prot.S	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: dtlb_prot.S,v 1.3 1996/12/28 18:39:41 davem Exp $
+/* $Id: dtlb_prot.S,v 1.5 1997/02/26 11:09:26 jj Exp $
  * dtlb_prot.S:	Fast TLB protection trap processing.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -35,7 +35,6 @@
 	 * from the miss handlers probing the page tables.
 	 */
 
-dtlb_prot:
 	/* I-cache line 0 */
 	ldxa		[%g0] ASI_DMMU, %g1
 	srlx		%g1, 8, %g3
@@ -49,11 +48,11 @@
 	srlx		%g1, 1, %g1
 	ldxa		[%g4 + %g1] ASI_PHYS_USE_EC, %g3
 	andcc		%g3, _PAGE_WRITE, %g0
-	be,pnt		%xcc, sparc64_dtlb_fault
+	be,pn		%xcc, sparc64_dtlb_fault
 	 or		%g3, (_PAGE_WRITE|_PAGE_W|_PAGE_MODIFIED|_PAGE_ACCESSED), %g3
 
 	/* Blamo... */
-	stxa		%g3, [%g4 + %g1] %asi
+	stxa		%g3, [%g4 + %g1] ASI_DMMU
 	stxa		%g3, [%g0] ASI_DTLB_DATA_IN
 	retry
 
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/etrap.S linux/arch/sparc64/kernel/etrap.S
--- v2.1.28/linux/arch/sparc64/kernel/etrap.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/etrap.S	Wed Mar  5 17:04:31 1997
@@ -1,7 +1,8 @@
-/* $Id: etrap.S,v 1.1 1996/12/26 10:16:42 davem Exp $
+/* $Id: etrap.S,v 1.4 1997/03/04 16:26:58 jj Exp $
  * etrap.S: Preparing for entry into the kernel on Sparc V9.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #include <asm/pstate.h>
@@ -11,42 +12,52 @@
 	.align	4
 	.globl	etrap
 etrap:
+	rdpr	%wstate, %g2
 	sethi	%uhi(current_set), %g6
-	rd	%tstate, %g1
-	or	%g6, %ulo(current_set), %g6
-	rd	%tpc, %g2
-	sllx	%g6, 32, %g6
-	rd	%tnpc, %g3
+	andcc	%g2, 7, %g2
+	rdpr	%tstate, %g1
+	be,pn	%xcc, 1f		/* What happens more often? etrap when already in priv or from userland? */
+	 sllx	%g6, 32, %g6
+	sll	%g2, 3, %g2
+	wrpr	%g2, %wstate
+1:
 	sethi	%hi(current_set), %g4
 	or	%g4, %lo(current_set), %g4
-	or	%g6, %g4, %g6
+	rdpr	%tpc, %g2
+	rdpr	%canrestore, %g5
+	rdpr	%tnpc, %g3
+	wrpr	%g5, 0, %otherwin
+	wrpr	%g0, 0, %canrestore
+	ldx	[%g6 + %g4], %g6
+#ifdef __SMP__
+/* FIXME: Fix the above insn for SMP */
+#endif
 	rd	%y, %g4
-	ldx	[%g6 + THREAD_KSTACK], %g5
+	ldx	[%g6 + AOFF_task_tss + AOFF_thread_ksp], %g5
 	stx	%g1, [%g5 + REGWIN_SZ + PT_TSTATE]
 	stx	%g2, [%g5 + REGWIN_SZ + PT_TPC]
 	stx	%g3, [%g5 + REGWIN_SZ + PT_TNPC]
 	stx	%g4, [%g5 + REGWIN_SZ + PT_Y]
-	rd	%pstate, %g1
-	andn	%g1, (PSTATE_IG | PSTATE_MG | PSTATE_AG), %g2
-	save	%g5, 0x0, %sp
-	mov	%g2, %l1
-	wr	%g0, 0x0, %tl
+	rdpr	%pstate, %g1
+	save	%g5, -STACK_BIAS, %sp
+	andn	%g1, (PSTATE_IG | PSTATE_MG | PSTATE_AG), %l1
+	wrpr	%g0, 0x0, %tl
 	mov	%g7, %l2
-	wr	%l1, 0x0, %pstate
-	stx	%g1, [%sp + REGWIN_SZ + PT_G1]
-	stx	%g2, [%sp + REGWIN_SZ + PT_G2]
-	stx	%g3, [%sp + REGWIN_SZ + PT_G3]
-	stx	%g4, [%sp + REGWIN_SZ + PT_G4]
-	stx	%g5, [%sp + REGWIN_SZ + PT_G5]
-	stx	%g6, [%sp + REGWIN_SZ + PT_G6]
-	stx	%g7, [%sp + REGWIN_SZ + PT_G7]
-	stx	%i0, [%sp + REGWIN_SZ + PT_I0]
-	stx	%i1, [%sp + REGWIN_SZ + PT_I1]
-	stx	%i2, [%sp + REGWIN_SZ + PT_I2]
-	stx	%i3, [%sp + REGWIN_SZ + PT_I3]
-	stx	%i4, [%sp + REGWIN_SZ + PT_I4]
-	stx	%i5, [%sp + REGWIN_SZ + PT_I5]
-	stx	%i6, [%sp + REGWIN_SZ + PT_I6]
-	stx	%i7, [%sp + REGWIN_SZ + PT_I7]
+	wrpr	%l1, 0x0, %pstate
+	stx	%g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_G1]
+	stx	%g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_G2]
+	stx	%g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_G3]
+	stx	%g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_G4]
+	stx	%g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_G5]
+	stx	%g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_G6]
+	stx	%g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_G7]
+	stx	%i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_I0]
+	stx	%i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_I1]
+	stx	%i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_I2]
+	stx	%i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_I3]
+	stx	%i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_I4]
+	stx	%i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_I5]
+	stx	%i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_I6]
+	stx	%i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_I7]
 	jmpl	%l2 + 0x4, %g0
-	 wr	%l1, PSTATE_IE, %pstate
+	 wrpr	%l1, PSTATE_IE, %pstate
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/hack.S linux/arch/sparc64/kernel/hack.S
--- v2.1.28/linux/arch/sparc64/kernel/hack.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/hack.S	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,398 @@
+/* <hack>
+   This is just a huge ugly hack to get things compiled.
+   Hopefully will disappear quickly, once we get everything
+   to compile... */
+	.text
+	.globl ROOT_DEV
+ROOT_DEV:
+	nop
+	.globl ___lock_kernel
+___lock_kernel:
+	nop
+	.globl ___unlock_kernel
+___unlock_kernel:
+	nop
+	.globl __memcpy
+__memcpy:
+	nop
+	.globl __strncmp
+__strncmp:
+	nop
+	.globl _ctype
+_ctype:
+	nop
+	.globl _stext
+_stext:
+	nop
+	.globl bad_trap
+bad_trap:
+	nop
+	.globl bad_trap_tl1
+bad_trap_tl1:
+	nop
+	.globl bdflush
+bdflush:
+	nop
+	.globl breakpoint_trap
+breakpoint_trap:
+	nop
+	.globl buff_setup
+buff_setup:
+	nop
+	.globl buffer_init
+buffer_init:
+	nop
+	.globl change_root
+change_root:
+	nop
+	.globl console_init
+console_init:
+	nop
+	.globl console_loglevel
+console_loglevel:
+	nop
+	.globl console_restore_palette
+console_restore_palette:
+	nop
+	.globl current_set
+current_set:
+	nop
+	.globl do_cee
+do_cee:
+	nop
+	.globl do_cee_tl1
+do_cee_tl1:
+	nop
+	.globl do_dae
+do_dae:
+	nop
+	.globl do_dae_tl1
+do_dae_tl1:
+	nop
+	.globl do_dax
+do_dax:
+	nop
+	.globl do_dax_tl1
+do_dax_tl1:
+	nop
+	.globl do_div0
+do_div0:
+	nop
+	.globl do_div0_tl1
+do_div0_tl1:
+	nop
+	.globl do_fpdis
+do_fpdis:
+	nop
+	.globl do_fpdis_tl1
+do_fpdis_tl1:
+	nop
+	.globl do_fpieee
+do_fpieee:
+	nop
+	.globl do_fpieee_tl1
+do_fpieee_tl1:
+	nop
+	.globl do_fpother
+do_fpother:
+	nop
+	.globl do_fpother_tl1
+do_fpother_tl1:
+	nop
+	.globl do_iae
+do_iae:
+	nop
+	.globl do_iae_tl1
+do_iae_tl1:
+	nop
+	.globl do_iax
+do_iax:
+	nop
+	.globl do_iax_tl1
+do_iax_tl1:
+	nop
+	.globl do_ill
+do_ill:
+	nop
+	.globl do_ill_tl1
+do_ill_tl1:
+	nop
+	.globl do_irq
+do_irq:
+	nop
+	.globl do_irq_tl1
+do_irq_tl1:
+	nop
+	.globl do_ivec
+do_ivec:
+	nop
+	.globl do_ivec_tl1
+do_ivec_tl1:
+	nop
+	.globl do_lddfmna
+do_lddfmna:
+	nop
+	.globl do_lddfmna_tl1
+do_lddfmna_tl1:
+	nop
+	.globl do_mna
+do_mna:
+	nop
+	.globl do_mna_tl1
+do_mna_tl1:
+	nop
+	.globl do_paw
+do_paw:
+	nop
+	.globl do_paw_tl1
+do_paw_tl1:
+	nop
+	.globl do_privact
+do_privact:
+	nop
+	.globl do_privop
+do_privop:
+	nop
+	.globl do_stdfmna
+do_stdfmna:
+	nop
+	.globl do_stdfmna_tl1
+do_stdfmna_tl1:
+	nop
+	.globl do_tof
+do_tof:
+	nop
+	.globl do_tof_tl1
+do_tof_tl1:
+	nop
+	.globl do_vaw
+do_vaw:
+	nop
+	.globl do_vaw_tl1
+do_vaw_tl1:
+	nop
+	.globl dquot_init
+dquot_init:
+	nop
+	.globl errno
+errno:
+	nop
+	.globl eth_setup
+eth_setup:
+	nop
+	.globl file_table_init
+file_table_init:
+	nop
+	.globl floppy_setup
+floppy_setup:
+	nop
+	.globl getcc
+getcc:
+	nop
+	.globl indirect_syscall
+indirect_syscall:
+	nop
+	.globl init_IRQ
+init_IRQ:
+	nop
+	.globl init_task
+init_task:
+	nop
+	.globl initrd_below_start_ok
+initrd_below_start_ok:
+	nop
+	.globl initrd_start
+initrd_start:
+	nop
+	.globl inode_init
+inode_init:
+	nop
+	.globl install_linux_ticker
+install_linux_ticker:
+	nop
+	.globl install_obp_ticker
+install_obp_ticker:
+	nop
+	.globl ipc_init
+ipc_init:
+	nop
+	.globl jiffies
+jiffies:
+	nop
+	.globl kernel_enter_debugger
+kernel_enter_debugger:
+	nop
+	.globl kmalloc_init
+kmalloc_init:
+	nop
+	.globl kmem_cache_init
+kmem_cache_init:
+	nop
+	.globl kmem_cache_sizes_init
+kmem_cache_sizes_init:
+	nop
+	.globl kswapd
+kswapd:
+	nop
+	.globl linux32_syscall
+linux32_syscall:
+	nop
+	.globl linux64_syscall
+linux64_syscall:
+	nop
+	.globl lookup_fault
+lookup_fault:
+	nop
+	.globl mem_init
+mem_init:
+	nop
+	.globl memcpy
+memcpy:
+	nop
+	.globl mount_initrd
+mount_initrd:
+	nop
+	.globl name_cache_init
+name_cache_init:
+	nop
+	.globl netbsd_syscall
+netbsd_syscall:
+	nop
+	.globl no_scroll
+no_scroll:
+	nop
+	.globl paging_init
+paging_init:
+	nop
+	.globl panic
+panic:
+	nop
+	.globl panic_setup
+panic_setup:
+	nop
+	.globl printk
+printk:
+	nop
+	.globl prof_buffer
+prof_buffer:
+	nop
+	.globl prof_len
+prof_len:
+	nop
+	.globl prof_shift
+prof_shift:
+	nop
+	.globl pseudo_root
+pseudo_root:
+	nop
+	.globl rd_doload
+rd_doload:
+	nop
+	.globl rd_image_start
+rd_image_start:
+	nop
+	.globl rd_prompt
+rd_prompt:
+	nop
+	.globl rd_size
+rd_size:
+	nop
+	.globl reboot_command
+reboot_command:
+	nop
+	.globl reserve_setup
+reserve_setup:
+	nop
+	.globl sbus_init
+sbus_init:
+	nop
+	.globl sched_init
+sched_init:
+	nop
+	.globl scsi_luns_setup
+scsi_luns_setup:
+	nop
+	.globl serial_console
+serial_console:
+	nop
+	.globl set_palette
+set_palette:
+	nop
+	.globl setcc
+setcc:
+	nop
+	.globl setup_arch
+setup_arch:
+	nop
+	.globl simple_strtoul
+simple_strtoul:
+	nop
+	.globl sock_init
+sock_init:
+	nop
+	.globl solaris_syscall
+solaris_syscall:
+	nop
+	.globl sparc64_dtlb_fault
+sparc64_dtlb_fault:
+	nop
+	.globl sparc64_dtlb_refbit_catch
+sparc64_dtlb_refbit_catch:
+	nop
+	.globl sparc64_itlb_refbit_catch
+sparc64_itlb_refbit_catch:
+	nop
+	.globl sprintf
+sprintf:
+	nop
+	.globl st_setup
+st_setup:
+	nop
+	.globl strchr
+strchr:
+	nop
+	.globl strcmp
+strcmp:
+	nop
+	.globl strcpy
+strcpy:
+	nop
+	.globl strlen
+strlen:
+	nop
+	.globl strncpy
+strncpy:
+	nop
+	.globl sunos_syscall
+sunos_syscall:
+	nop
+	.globl swap_setup
+swap_setup:
+	nop
+	.globl sysctl_init
+sysctl_init:
+	nop
+	.globl time_init
+time_init:
+	nop
+	.globl trap_init
+trap_init:
+	nop
+	.globl vma_init
+vma_init:
+	nop
+	.globl vsprintf
+vsprintf:
+	nop
+	.globl schedule
+schedule:
+	nop
+	.globl getname
+getname:
+	nop
+	.globl do_execve
+do_execve:
+	nop
+	.globl putname
+putname:
+	nop
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/head.S linux/arch/sparc64/kernel/head.S
--- v2.1.28/linux/arch/sparc64/kernel/head.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc64/kernel/head.S	Wed Mar  5 17:04:31 1997
@@ -1,21 +1,53 @@
-/* $Id: head.S,v 1.6 1997/01/06 20:32:44 jj Exp $
+/* $Id: head.S,v 1.9 1997/02/26 11:09:25 jj Exp $
  * head.S: Initial boot code for the Sparc64 port of Linux.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
+#include <linux/version.h>
 #include <asm/pstate.h>
+#include <asm/ptrace.h>
 #include <asm/spitfire.h>
+#include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/errno.h>
 
-	.text
+/* This section from from _start to sparc64_boot_end should fit into
+   0xfffff80000004000 to 0xfffff80000008000 and will be sharing space
+   with bootup_user_stack, which is from 0xfffff80000004000 to
+   0xfffff80000006000 and bootup_kernel_stack, which is from
+   0xfffff80000006000 to 0xfffff80000008000. */
 
-#include "ttable.S"
+	.text
+	.globl	start, _start
+_start:
+start:
+bootup_user_stack:
+! 0xfffff80000004000
+	b	sparc64_boot
+	 rdpr	%ver, %g1			/* Get VERSION register.	*/
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+        .ascii  "HdrS"
+        .word   LINUX_VERSION_CODE
+        .half   0x0201          /* HdrS version */
+root_flags:
+        .half   1
+root_dev:
+        .half   0
+ram_flags:
+        .half   0
+ramdisk_image:
+        .word   0
+ramdisk_size:
+        .word   0
+        .word   reboot_command
 
 sparc64_boot:
-	rdpr	%ver, %g1			/* Get VERSION register.	*/
-
 	/* We must be careful, 32-bit OpenBOOT will get confused if it
 	 * tries to save away a register window to a 64-bit kernel
 	 * stack address.  Flush all windows, disable interrupts,
@@ -26,22 +58,19 @@
 	wrpr	%g0, 0xf, %pil			/* Interrupts off.           */
 
 	/* Remap ourselves to upper 64-bit addresses if necessary.
-	 * SILO64 will have loaded us to the right location already.
 	 */
-	mov	%o7, %g4
+	sethi	%uhi(PAGE_OFFSET), %g4
 current_pc:
-	call	1f
-	 mov	%o7, %g3
-1:
-	mov	%g4, %o7
-
-	set	current_pc, %g7
-	cmp	%g3, %g7
+	rd	%pc, %g2
+	sllx	%g4, 32, %g4
+	sethi	%hi(current_pc), %g3
+	or	%g3, %lo(current_pc), %g3
+	add	%g4, %g3, %g3
+	cmp	%g3, %g2
 	be	go_to_highmem
 	 nop
 
 	/* Remap ourselves into high addresses. */
-	set	PAGE_OFFSET, %g4
 	sethi	%uhi(_PAGE_VALID | _PAGE_SZ4MB), %g5
 	sllx	%g5, 32, %g5
 	or	%g5, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G | _PAGE_L), %g5
@@ -63,44 +92,84 @@
 	membar	#Sync
 	flush	%g4
 
+/* FIXME: Should clean here the page @ phys. 0 and map one page @ */
+
 go_to_highmem:
-	set	execute_in_high_mem, %g7
-	jmpl	%g7, %g0
+	jmpl	%g3 + (execute_in_high_mem - current_pc), %g0
 	 nop
 
 execute_in_high_mem:
 
-	set	nwindows, %g7
+	/* Remap our prom interface code */
+	sethi	%hi(__p1275_loc), %g7
+	or	%g7, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_G | _PAGE_L), %g7
+	/* The lock bit has to be removed from this page later on, 
+	   but before firing up init we will use PROM a lot, so we
+	   lock it there now... */
+	sethi	%uhi(_PAGE_VALID), %g5
+	sethi	%hi(0x8000), %g3
+	sllx	%g5, 32, %g5
+	mov	TLB_TAG_ACCESS, %g6
+	or	%g5, %g7, %g5
+	stxa	%g3, [%g6] ASI_IMMU
+	stxa	%g5, [%g0] ASI_ITLB_DATA_IN
+	membar	#Sync
+	flush	%g3
+	stxa	%g3, [%g6] ASI_DMMU
+	stxa	%g5, [%g0] ASI_DTLB_DATA_IN
+	membar	#Sync
+	flush	%g3
+
+	sethi	%hi(nwindows), %g7
 	and	%g1, VERS_MAXWIN, %g5
-	add	%g5, 1, %g4
-	stx	%g4, [%g7]
-	set	nwindowsm1, %g6
-	stx	%g5, [%g6]
+	add	%g7, %lo(nwindows), %g7
+	add	%g5, 1, %g6
+	add	%g7, (nwindows - nwindowsm1), %g3
+	stx	%g6, [%g7 + %g4]
+	stx	%g5, [%g3 + %g4]
 	mov	%sp, %o1			! second argument to prom_init
-	set	swapper_pg_dir, %g6
-	set	PAGE_OFFSET, %g4		! this stays here for a long time
-	sub	%g6, %g4, %g5
-	set	init_task, %g6			! g6 usage is fixed as well
+	sethi	%hi(init_task), %g6
+	or	%g6, %lo(init_task), %g6
+	add	%g6, %g4, %g6			! g6 usage is fixed as well
 
-	set	sparc64_ttable_tl0, %g5
+/* FIXME: Initialize MMU globals??? */
+
+	sethi	%hi(sparc64_ttable_tl0), %g5
+	add	%g5, %g4, %g5
 	wrpr	%g5, %tba
 
-	set	bootup_kernel_stack, %sp
+	sethi	%hi(bootup_kernel_stack + 0x2000 - STACK_BIAS - REGWIN_SZ), %g5
+	or	%g5, %lo(bootup_kernel_stack + 0x2000 - STACK_BIAS - REGWIN_SZ), %g5
+	add	%g5, %g4, %sp
 	mov	0, %fp
-	wrpr	%g0, PSTATE_KERNEL, %pstate
-	wrpr	%g0, WSTATE_KERNEL, %wstate
+	wrpr	%g0, PSTATE_PEF | PSTATE_PRIV, %pstate
+	wrpr	%g0, 0, %wstate
 	wrpr	%g0, 0x0, %tl
 
-	/* XXX Map in PROM 32-bit trampoline code. */
-	
 	call	prom_init
 	 mov	%o4, %o0			! OpenPROM cif handler
 
 	/* Off we go.... */
 	call	start_kernel
 	 nop
-
 	/* Not reached... */
+
+sparc64_boot_end:
+	.skip	0x2000 + _start - sparc64_boot_end
+bootup_user_stack_end:
+
+bootup_kernel_stack:
+	.skip	0x2000
+
+! 0xfffff80000008000
+
+#include "ttable.S"
+
+        .global root_flags
+        .global ram_flags
+        .global root_dev
+        .global ramdisk_image
+        .global ramdisk_size
 
 	.data
 	.align	8
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/itlb_miss.S linux/arch/sparc64/kernel/itlb_miss.S
--- v2.1.28/linux/arch/sparc64/kernel/itlb_miss.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/itlb_miss.S	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: itlb_miss.S,v 1.4 1996/12/28 18:39:42 davem Exp $
+/* $Id: itlb_miss.S,v 1.5 1997/02/25 20:00:05 jj Exp $
  * itlb_miss.S:	Instruction TLB miss code, this is included directly
  *              into the trap table.
  *
@@ -45,7 +45,6 @@
 	 * processes startup when the TSB is cold.
 	 */
 
-itlb_miss:
 	/* I-cache line 0 */
 	ldxa		[%g0] ASI_IMMU, %g1		! grab Tag Target
 	srlx		%g1, 8, %g3			! put high vaddr bits in place
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- v2.1.28/linux/arch/sparc64/kernel/process.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/kernel/process.c	Wed Mar  5 17:04:31 1997
@@ -1,8 +1,9 @@
-/*  $Id: process.c,v 1.1 1996/12/28 18:39:39 davem Exp $
+/*  $Id: process.c,v 1.3 1997/03/04 16:26:56 jj Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
  *  Copyright (C) 1996 Eddie C. Dost   (ecd@skynet.be)
+ *  Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 /*
@@ -23,6 +24,7 @@
 #include <linux/user.h>
 #include <linux/a.out.h>
 #include <linux/config.h>
+#include <linux/reboot.h>
 
 #include <asm/oplib.h>
 #include <asm/uaccess.h>
@@ -56,7 +58,7 @@
 #else
 
 /*
- * the idle loop on a SparcMultiPenguin...
+ * the idle loop on a UltraMultiPenguin...
  */
 asmlinkage int sys_idle(void)
 {
@@ -105,7 +107,7 @@
 extern int serial_console;
 #endif
 
-void halt_now(void)
+void machine_halt(void)
 {
 	sti();
 	udelay(8000);
@@ -118,7 +120,7 @@
 	panic("Halt failed!");
 }
 
-void hard_reset_now(void)
+void machine_restart(char * cmd)
 {
 	char *p;
 	
@@ -132,12 +134,19 @@
 	if (!serial_console)
 		console_restore_palette ();
 #endif
+	if (cmd)
+		prom_reboot(cmd);
 	if (*reboot_command)
-		prom_reboot (reboot_command);
+		prom_reboot(reboot_command);
 	prom_feval ("reset");
 	panic("Reboot failed!");
 }
 
+void machine_power_off(void)
+{
+	machine_halt();
+}
+
 void show_regwindow(struct reg_window *rw)
 {
 	printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n"
@@ -152,12 +161,12 @@
 
 void show_regwindow32(struct reg_window32 *rw)
 {
-	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
-	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
+	printk("l0: %08x l1: %08x l2: %08x l3: %08x\n"
+	       "l4: %08x l5: %08x l6: %08x l7: %08x\n",
 	       rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
 	       rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
-	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
-	       "i4: %08lx i5: %08lx i6: %08lx i7: %08lx\n",
+	printk("i0: %08x i1: %08x i2: %08x i3: %08x\n"
+	       "i4: %08x i5: %08x i6: %08x i7: %08x\n",
 	       rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
 	       rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
 }
@@ -193,29 +202,29 @@
 void show_stackframe32(struct sparc_stackf32 *sf)
 {
 	unsigned long size;
-	unsigned long *stk;
+	unsigned *stk;
 	int i;
 
-	printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx\n"
-	       "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
+	printk("l0: %08x l1: %08x l2: %08x l3: %08x\n"
+	       "l4: %08x l5: %08x l6: %08x l7: %08x\n",
 	       sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
 	       sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
-	printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx\n"
-	       "i4: %08lx i5: %08lx fp: %08lx ret_pc: %08lx\n",
+	printk("i0: %08x i1: %08x i2: %08x i3: %08x\n"
+	       "i4: %08x i5: %08x fp: %08x ret_pc: %08x\n",
 	       sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
-	       sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
-	printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx\n"
-	       "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
-	       (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
+	       sf->ins[4], sf->ins[5], sf->fp, sf->callers_pc);
+	printk("sp: %08x x0: %08x x1: %08x x2: %08x\n"
+	       "x3: %08x x4: %08x x5: %08x xx: %08x\n",
+	       sf->structptr, sf->xargs[0], sf->xargs[1],
 	       sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
 	       sf->xxargs[0]);
 	size = ((unsigned long)sf->fp) - ((unsigned long)sf);
-	size -= STACKFRAME_SZ;
-	stk = (unsigned long *)((unsigned long)sf + STACKFRAME_SZ);
+	size -= STACKFRAME32_SZ;
+	stk = (unsigned *)((unsigned long)sf + STACKFRAME32_SZ);
 	i = 0;
 	do {
-		printk("s%d: %08lx\n", i++, *stk++);
-	} while ((size -= sizeof(unsigned long)));
+		printk("s%d: %08x\n", i++, *stk++);
+	} while ((size -= sizeof(unsigned)));
 }
 
 void show_regs(struct pt_regs * regs)
@@ -245,29 +254,31 @@
 #if __MPP__
 	printk("CID: %d\n",mpp_cid());
 #endif
-        printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx\n", regs->psr,
+        printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr,
 	       regs->pc, regs->npc, regs->y);
-	printk("g0: %08lx g1: %08lx g2: %08lx g3: %08lx\n",
+	printk("g0: %08x g1: %08x g2: %08x g3: %08x\n",
 	       regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
 	       regs->u_regs[3]);
-	printk("g4: %08lx g5: %08lx g6: %08lx g7: %08lx\n",
+	printk("g4: %08x g5: %08x g6: %08x g7: %08x\n",
 	       regs->u_regs[4], regs->u_regs[5], regs->u_regs[6],
 	       regs->u_regs[7]);
-	printk("o0: %08lx o1: %08lx o2: %08lx o3: %08lx\n",
+	printk("o0: %08x o1: %08x o2: %08x o3: %08x\n",
 	       regs->u_regs[8], regs->u_regs[9], regs->u_regs[10],
 	       regs->u_regs[11]);
-	printk("o4: %08lx o5: %08lx sp: %08lx ret_pc: %08lx\n",
+	printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n",
 	       regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
 	       regs->u_regs[15]);
-	show_regwindow32((struct reg_window32 *)regs->u_regs[14]);
+	show_regwindow32((struct reg_window32 *)((unsigned long)regs->u_regs[14]));
 }
 
 void show_thread(struct thread_struct *tss)
 {
 	int i;
 
+#if 0
 	printk("kregs:             0x%016lx\n", (unsigned long)tss->kregs);
 	show_regs(tss->kregs);
+#endif	
 	printk("sig_address:       0x%016lx\n", tss->sig_address);
 	printk("sig_desc:          0x%016lx\n", tss->sig_desc);
 	printk("ksp:               0x%016lx\n", tss->ksp);
@@ -292,7 +303,7 @@
 	printk("sstk_info.status:  0x%016lx\n",
 	        (unsigned long)tss->sstk_info.cur_status);
 	printk("flags:             0x%016lx\n", tss->flags);
-	printk("current_ds:        0x%016lx\n", tss->current_ds);
+	printk("current_ds:        0x%016x\n", tss->current_ds);
 
 	/* XXX missing: core_exec */
 }
@@ -302,6 +313,7 @@
  */
 void exit_thread(void)
 {
+#if 0
 	kill_user_windows();
 #ifndef __SMP__
 	if(last_task_used_math == current) {
@@ -319,10 +331,12 @@
 #endif
 	}
 	mmu_exit_hook();
+#endif	
 }
 
 void flush_thread(void)
 {
+#if 0
 	kill_user_windows();
 	current->tss.w_saved = 0;
 	current->tss.uwinmask = 0;
@@ -346,8 +360,8 @@
 		current->flags &= ~PF_USEDFPU;
 #endif
 	}
-
 	mmu_flush_hook();
+#endif
 	/* Now, this task is no longer a kernel thread. */
 	current->tss.flags &= ~SPARC_FLAG_KTHREAD;
 	current->tss.current_ds = USER_DS;
@@ -407,6 +421,7 @@
 	unsigned long size;
 	struct sparc_stackf *sp;
 
+#if 0
 	size = ((unsigned long)src->fp) - ((unsigned long)src);
 	sp = (struct sparc_stackf *)(((unsigned long)dst) - size); 
 
@@ -414,6 +429,7 @@
 		return 0;
 	if (put_user(dst, &sp->fp))
 		return 0;
+#endif		
 	return sp;
 }
 
@@ -439,6 +455,7 @@
 	struct reg_window *new_stack;
 	unsigned long stack_offset;
 
+#if 0
 #ifndef __SMP__
 	if(last_task_used_math == current) {
 #else
@@ -451,23 +468,29 @@
 		current->flags &= ~PF_USEDFPU;
 #endif
 	}
+#endif	
 
 	/* Calculate offset to stack_frame & pt_regs */
 	stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ);
 
+#if 0
 	if(regs->psr & PSR_PS)
 		stack_offset -= REGWIN_SZ;
+#endif		
 	childregs = ((struct pt_regs *) (p->kernel_stack_page + stack_offset));
 	copy_regs(childregs, regs);
 	new_stack = (((struct reg_window *) childregs) - 1);
 	copy_regwin(new_stack, (((struct reg_window *) regs) - 1));
 
+#if 0
 	p->tss.ksp = p->saved_kernel_stack = (unsigned long) new_stack;
 	p->tss.kpc = (((unsigned long) ret_from_syscall) - 0x8);
 	p->tss.kpsr = current->tss.fork_kpsr;
 	p->tss.kwim = current->tss.fork_kwim;
 	p->tss.kregs = childregs;
+#endif	
 
+#if 0
 	if(regs->psr & PSR_PS) {
 		childregs->u_regs[UREG_FP] = p->tss.ksp;
 		p->tss.flags |= SPARC_FLAG_KTHREAD;
@@ -507,6 +530,7 @@
 			childregs->u_regs[UREG_FP] = (unsigned long)childstack;
 		}
 	}
+#endif	
 
 	/* Set the return value for the child. */
 	childregs->u_regs[UREG_I0] = current->pid;
@@ -514,7 +538,6 @@
 
 	/* Set the return value for the parent. */
 	regs->u_regs[UREG_I1] = 0;
-
 	return 0;
 }
 
@@ -524,7 +547,7 @@
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
 	unsigned long first_stack_page;
-
+#if 0
 	dump->magic = SUNOS_CORE_MAGIC;
 	dump->len = sizeof(struct user);
 	dump->regs.psr = regs->psr;
@@ -548,6 +571,7 @@
 	memcpy(&dump->fpu.fpstatus.fpq[0], &current->tss.fpqueue[0],
 	       ((sizeof(unsigned long) * 2) * 16));
 	dump->sigcode = current->tss.sig_desc;
+#endif	
 }
 
 /*
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- v2.1.28/linux/arch/sparc64/kernel/rtrap.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/rtrap.S	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,14 @@
+/* $Id: rtrap.S,v 1.2 1997/02/26 11:09:25 jj Exp $
+ * rtrap.S: Preparing for entry into the kernel on Sparc V9.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+
+	.text
+	.align	4
+	.globl	rtrap
+rtrap:
+	/*not*/ done /*yet*/
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c
--- v2.1.28/linux/arch/sparc64/kernel/signal32.c	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc64/kernel/signal32.c	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/*  $Id: signal32.c,v 1.3 1997/01/19 22:32:30 ecd Exp $
+/*  $Id: signal32.c,v 1.4 1997/03/03 16:51:46 jj Exp $
  *  arch/sparc64/kernel/signal32.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -53,12 +53,12 @@
  * ---------------------------------- <-- New %sp
  */
 struct signal_sframe32 {
-	struct reg_window_32 sig_window;
+	struct reg_window32 sig_window;
 	int sig_num;
 	int sig_code;
-	struct sigcontext_32 *sig_scptr;
+	struct sigcontext32 *sig_scptr;
 	int sig_address;
-	struct sigcontext_32 sig_context;
+	struct sigcontext32 sig_context;
 };
 
 /* 
@@ -68,7 +68,7 @@
  */
 
 struct new_signal_frame32 {
-	struct sparc_stackf_32	ss;
+	struct sparc_stackf32	ss;
 	__siginfo32_t		info;
 	__siginfo_fpu32_t	*fpu_save;
 	unsigned int		insns [2];
@@ -123,23 +123,23 @@
 
 
 static inline void
-restore_fpu_state(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
+restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
 {
 #ifdef __SMP__
 	if (current->flags & PF_USEDFPU)
-		regs->psr &= ~(TSTATE_PEF);
+		regs->tstate &= ~(TSTATE_PEF);
 #else
 	if (current == last_task_used_math) {
 		last_task_used_math = 0;
-		regs->psr &= ~(TSTATE_PEF);
+		regs->tstate &= ~(TSTATE_PEF);
 	}
 #endif
 	current->used_math = 1;
 	current->flags &= ~PF_USEDFPU;
 
 	copy_32bit_to_kernel_fpuregs(&current->tss.float_regs[0],
-				     &sf->info.si_float_regs[0],
-				     (sizeof(unsigned int) * 64));
+				     &fpu->si_float_regs[0],
+				     (sizeof(unsigned int) * 32));
 	__get_user(current->tss.fsr, &fpu->si_fsr);
 	__get_user(current->tss.fpqdepth, &fpu->si_fpqdepth);
 	if (current->tss.fpqdepth != 0)
@@ -160,7 +160,7 @@
 		do_exit (SIGSEGV);
 		return;
 	}
-	if (((uint) sf) & 3){
+	if (((unsigned long) sf) & 3){
 		do_exit (SIGSEGV);
 		return;
 	}
@@ -179,23 +179,23 @@
 	regs->tstate |= (sf->info.si_regs.psr & PSR_EF);
 
 	if (sf->fpu_save)
-		restore_fpu_state(regs, sf->fpu_state);
+		restore_fpu_state32(regs, sf->fpu_state);
 
 	current->blocked = sf->info.si_mask & _BLOCKABLE;
 }
 
 asmlinkage void do_sigreturn32(struct pt_regs *regs)
 {
-	struct sigcontext *scptr;
+	struct sigcontext32 *scptr;
 	unsigned long pc, npc, psr;
 
 	synchronize_user_stack();
 	if (current->tss.new_signal)
 		return do_new_sigreturn32(regs);
 
-	scptr = (struct sigcontext *) regs->u_regs[UREG_I0];
+	scptr = (struct sigcontext32 *) regs->u_regs[UREG_I0];
 	/* Check sanity of the user arg. */
-	if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext)) ||
+	if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) ||
 	   (((unsigned long) scptr) & 3)) {
 		printk("%s [%d]: do_sigreturn, scptr is invalid at "
 		       "pc<%08lx> scptr<%p>\n",
@@ -236,7 +236,7 @@
 	      struct pt_regs *regs, int signr, unsigned long oldmask)
 {
 	struct signal_sframe32 *sframep;
-	struct sigcontext *sc;
+	struct sigcontext32 *sc;
 	int window = 0;
 	int old_status = current->tss.sstk_info.cur_status;
 
@@ -302,7 +302,7 @@
 
 
 static inline void
-save_fpu_state(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
+save_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
 {
 #ifdef __SMP__
 	if (current->flags & PF_USEDFPU) {
@@ -322,7 +322,7 @@
 	}
 #endif
 	copy_to_user(&fpu->si_float_regs[0], &current->tss.float_regs[0],
-		     (sizeof(unsigned long) * 64));
+		     (sizeof(unsigned int) * 32));
 	__put_user(current->tss.fsr, &fpu->si_fsr);
 	__put_user(current->tss.fpqdepth, &fpu->si_fpqdepth);
 	if (current->tss.fpqdepth != 0)
@@ -363,7 +363,7 @@
 	memcpy (&sf->info.si_regs, regs, sizeof (struct pt_regs));
 
 	if (current->used_math) {
-		save_fpu_state(regs, &sf->fpu_state);
+		save_fpu_state32(regs, &sf->fpu_state);
 		sf->fpu_save = &sf->fpu_state;
 	} else {
 		sf->fpu_save = NULL;
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/sparc64_ksyms.c linux/arch/sparc64/kernel/sparc64_ksyms.c
--- v2.1.28/linux/arch/sparc64/kernel/sparc64_ksyms.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/kernel/sparc64_ksyms.c	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,179 @@
+/* $Id: sparc64_ksyms.c,v 1.1 1997/03/03 16:51:45 jj Exp $
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#define PROMLIB_INTERNAL
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/oplib.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/idprom.h>
+#include <asm/svr4.h>
+#include <asm/head.h>
+#include <asm/smp.h>
+#include <asm/mostek.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_SBUS
+#include <asm/sbus.h>
+#include <asm/dma.h>
+#endif
+#include <asm/a.out.h>
+
+struct poll {
+	int fd;
+	short events;
+	short revents;
+};
+
+extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *);
+extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *);
+extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
+				unsigned long, unsigned long, unsigned long);
+void _sigpause_common (unsigned int set, struct pt_regs *);
+extern void __copy_1page(void *, const void *);
+extern void *bzero_1page(void *);
+extern void *__bzero(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_sparc_generic (const char *, char *);
+
+extern void bcopy (const char *, char *, int);
+extern int __ashrdi3(int, int);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+/* One thing to note is that the way the symbols of the mul/div
+ * support routines are named is a mess, they all start with
+ * a '.' which makes it a bitch to export, here is the trick:
+ */
+
+#define EXPORT_SYMBOL_PRIVATE(sym)				\
+extern int __sparc_priv_ ## sym (int) __asm__("__" ## #sym);	\
+const struct module_symbol __export_priv_##sym			\
+__attribute__((section("__ksymtab"))) =				\
+{ (unsigned long) &__sparc_priv_ ## sym, "__" ## #sym }
+
+/* used by various drivers */
+#ifdef __SMP__
+EXPORT_SYMBOL(klock_info);
+#endif
+EXPORT_SYMBOL_PRIVATE(_lock_kernel);
+EXPORT_SYMBOL_PRIVATE(_unlock_kernel);
+EXPORT_SYMBOL(page_offset);
+EXPORT_SYMBOL(stack_top);
+
+EXPORT_SYMBOL(mstk48t02_regs);
+EXPORT_SYMBOL(request_fast_irq);
+EXPORT_SYMBOL(sparc_alloc_io);
+EXPORT_SYMBOL(sparc_free_io);
+EXPORT_SYMBOL(io_remap_page_range);
+EXPORT_SYMBOL(mmu_v2p);
+EXPORT_SYMBOL(mmu_unlockarea);
+EXPORT_SYMBOL(mmu_lockarea);
+EXPORT_SYMBOL(mmu_get_scsi_sgl);
+EXPORT_SYMBOL(mmu_get_scsi_one);
+EXPORT_SYMBOL(mmu_release_scsi_sgl);
+EXPORT_SYMBOL(mmu_release_scsi_one);
+EXPORT_SYMBOL(sparc_dvma_malloc);
+EXPORT_SYMBOL(sun4c_unmapioaddr);
+EXPORT_SYMBOL(srmmu_unmapioaddr);
+#if CONFIG_SBUS
+EXPORT_SYMBOL(SBus_chain);
+EXPORT_SYMBOL(dma_chain);
+#endif
+
+/* Solaris/SunOS binary compatibility */
+EXPORT_SYMBOL(svr4_setcontext);
+EXPORT_SYMBOL(svr4_getcontext);
+EXPORT_SYMBOL(_sigpause_common);
+EXPORT_SYMBOL(sunos_mmap);
+
+/* Should really be in linux/kernel/ksyms.c */
+EXPORT_SYMBOL(dump_thread);
+
+/* prom symbols */
+EXPORT_SYMBOL(idprom);
+EXPORT_SYMBOL(prom_root_node);
+EXPORT_SYMBOL(prom_getchild);
+EXPORT_SYMBOL(prom_getsibling);
+EXPORT_SYMBOL(prom_searchsiblings);
+EXPORT_SYMBOL(prom_firstprop);
+EXPORT_SYMBOL(prom_nextprop);
+EXPORT_SYMBOL(prom_getproplen);
+EXPORT_SYMBOL(prom_getproperty);
+EXPORT_SYMBOL(prom_node_has_property);
+EXPORT_SYMBOL(prom_setprop);
+EXPORT_SYMBOL(prom_getbootargs);
+EXPORT_SYMBOL(prom_apply_obio_ranges);
+EXPORT_SYMBOL(prom_getname);
+EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getstring);
+EXPORT_SYMBOL(prom_apply_sbus_ranges);
+EXPORT_SYMBOL(prom_getint);
+EXPORT_SYMBOL(prom_getintdefault);
+EXPORT_SYMBOL(romvec);
+EXPORT_SYMBOL(__prom_getchild);
+EXPORT_SYMBOL(__prom_getsibling);
+
+/* sparc library symbols */
+EXPORT_SYMBOL(bcopy);
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strspn);
+
+/* Special internal versions of library functions. */
+EXPORT_SYMBOL(__copy_1page);
+EXPORT_SYMBOL(__memcpy);
+EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(bzero_1page);
+EXPORT_SYMBOL(__bzero);
+EXPORT_SYMBOL(__memscan_zero);
+EXPORT_SYMBOL(__memscan_generic);
+EXPORT_SYMBOL(__memcmp);
+EXPORT_SYMBOL(__strncmp);
+EXPORT_SYMBOL(__memmove);
+
+EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
+
+/* Moving data to/from userspace. */
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+
+/* No version information on this, heavily used in inline asm,
+ * and will always be 'void __ret_efault(void)'.
+ */
+EXPORT_SYMBOL_NOVERS(__ret_efault);
+
+/* No version information on these, as gcc produces such symbols. */
+EXPORT_SYMBOL_NOVERS(memcmp);
+EXPORT_SYMBOL_NOVERS(memcpy);
+EXPORT_SYMBOL_NOVERS(memset);
+EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(__ashrdi3);
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/kernel/ttable.S linux/arch/sparc64/kernel/ttable.S
--- v2.1.28/linux/arch/sparc64/kernel/ttable.S	Sun Jan 26 02:07:09 1997
+++ linux/arch/sparc64/kernel/ttable.S	Wed Mar  5 17:04:31 1997
@@ -1,13 +1,11 @@
-/* $Id: ttable.S,v 1.4 1997/01/16 13:43:24 jj Exp $
+/* $Id: ttable.S,v 1.5 1997/02/25 12:40:09 jj Exp $
  * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
-	.globl	start, _start
 	.globl	sparc64_ttable_tl0, sparc64_ttable_tl1
-_start:
-start:
+
 sparc64_ttable_tl0:
 tl0_resv000:	BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
 tl0_resv004:	BTRAP(0x4)  BTRAP(0x5) BTRAP(0x6) BTRAP(0x7)
@@ -127,6 +125,15 @@
 tl0_resv173:	BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177)
 tl0_resv178:	BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c)
 tl0_resv17d:	BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f)
+#define BTRAPS(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) BTRAP(x+4) BTRAP(x+5) BTRAP(x+6) BTRAP(x+7)
+tl0_resv180:	BTRAPS(0x180)
+tl0_resv190:	BTRAPS(0x190)
+tl0_resv1a0:	BTRAPS(0x1a0)
+tl0_resv1b0:	BTRAPS(0x1b0)
+tl0_resv1c0:	BTRAPS(0x1c0)
+tl0_resv1d0:	BTRAPS(0x1d0)
+tl0_resv1e0:	BTRAPS(0x1e0)
+tl0_resv1f0:	BTRAPS(0x1f0)
 
 sparc64_ttable_tl1:
 tl1_resv000:	BOOT_KERNEL    BTRAPTL1(0x1) BTRAPTL1(0x2) BTRAPTL1(0x3)
@@ -136,7 +143,7 @@
 tl1_iae:	TRAP(do_iae_tl1)
 tl1_resv00b:	BTRAPTL1(0xb) BTRAPTL1(0xc) BTRAPTL1(0xd) BTRAPTL1(0xe) BTRAPTL1(0xf)
 tl1_ill:	TRAP(do_ill_tl1)
-tl1_privop:	BAD_TRAP(0x11)
+tl1_privop:	BTRAPTL1(0x11)
 tl1_resv012:	BTRAPTL1(0x12) BTRAPTL1(0x13) BTRAPTL1(0x14) BTRAPTL1(0x15)
 tl1_resv016:	BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19)
 tl1_resv01a:	BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d)
@@ -184,38 +191,38 @@
 tl1_resv074:	BTRAPTL1(0x74) BTRAPTL1(0x75) BTRAPTL1(0x76) BTRAPTL1(0x77)
 tl1_resv078:	BTRAPTL1(0x78) BTRAPTL1(0x79) BTRAPTL1(0x7a) BTRAPTL1(0x7b)
 tl1_resv07c:	BTRAPTL1(0x7c) BTRAPTL1(0x7d) BTRAPTL1(0x7e) BTRAPTL1(0x7f)
-tl1_s0n:	SPILL_0_NORMAL_TL1
-tl1_s1n:	SPILL_1_NORMAL_TL1
-tl1_s2n:	SPILL_2_NORMAL_TL1
-tl1_s3n:	SPILL_3_NORMAL_TL1
-tl1_s4n:	SPILL_4_NORMAL_TL1
-tl1_s5n:	SPILL_5_NORMAL_TL1
-tl1_s6n:	SPILL_6_NORMAL_TL1
-tl1_s7n:	SPILL_7_NORMAL_TL1
-tl1_s0o:	SPILL_0_OTHER_TL1
-tl1_s1o:	SPILL_1_OTHER_TL1
-tl1_s2o:	SPILL_2_OTHER_TL1
-tl1_s3o:	SPILL_3_OTHER_TL1
-tl1_s4o:	SPILL_4_OTHER_TL1
-tl1_s5o:	SPILL_5_OTHER_TL1
-tl1_s6o:	SPILL_6_OTHER_TL1
-tl1_s7o:	SPILL_7_OTHER_TL1
-tl1_f0n:	FILL_0_NORMAL_TL1
-tl1_f1n:	FILL_1_NORMAL_TL1
-tl1_f2n:	FILL_2_NORMAL_TL1
-tl1_f3n:	FILL_3_NORMAL_TL1
-tl1_f4n:	FILL_4_NORMAL_TL1
-tl1_f5n:	FILL_5_NORMAL_TL1
-tl1_f6n:	FILL_6_NORMAL_TL1
-tl1_f7n:	FILL_7_NORMAL_TL1
-tl1_f0o:	FILL_0_OTHER_TL1
-tl1_f1o:	FILL_1_OTHER_TL1
-tl1_f2o:	FILL_2_OTHER_TL1
-tl1_f3o:	FILL_3_OTHER_TL1
-tl1_f4o:	FILL_4_OTHER_TL1
-tl1_f5o:	FILL_5_OTHER_TL1
-tl1_f6o:	FILL_6_OTHER_TL1
-tl1_f7o:	FILL_7_OTHER_TL1
+tl1_s0n:	SPILL_0_NORMAL
+tl1_s1n:	SPILL_1_NORMAL
+tl1_s2n:	SPILL_2_NORMAL
+tl1_s3n:	SPILL_3_NORMAL
+tl1_s4n:	SPILL_4_NORMAL
+tl1_s5n:	SPILL_5_NORMAL
+tl1_s6n:	SPILL_6_NORMAL
+tl1_s7n:	SPILL_7_NORMAL
+tl1_s0o:	SPILL_0_OTHER
+tl1_s1o:	SPILL_1_OTHER
+tl1_s2o:	SPILL_2_OTHER
+tl1_s3o:	SPILL_3_OTHER
+tl1_s4o:	SPILL_4_OTHER
+tl1_s5o:	SPILL_5_OTHER
+tl1_s6o:	SPILL_6_OTHER
+tl1_s7o:	SPILL_7_OTHER
+tl1_f0n:	FILL_0_NORMAL
+tl1_f1n:	FILL_1_NORMAL
+tl1_f2n:	FILL_2_NORMAL
+tl1_f3n:	FILL_3_NORMAL
+tl1_f4n:	FILL_4_NORMAL
+tl1_f5n:	FILL_5_NORMAL
+tl1_f6n:	FILL_6_NORMAL
+tl1_f7n:	FILL_7_NORMAL
+tl1_f0o:	FILL_0_OTHER
+tl1_f1o:	FILL_1_OTHER
+tl1_f2o:	FILL_2_OTHER
+tl1_f3o:	FILL_3_OTHER
+tl1_f4o:	FILL_4_OTHER
+tl1_f5o:	FILL_5_OTHER
+tl1_f6o:	FILL_6_OTHER
+tl1_f7o:	FILL_7_OTHER
 
 #if 0
 /* Unless we are going to have software trap insns in the kernel code, we
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/lib/blockops.S linux/arch/sparc64/lib/blockops.S
--- v2.1.28/linux/arch/sparc64/lib/blockops.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/lib/blockops.S	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: blockops.S,v 1.1 1996/12/22 07:42:15 davem Exp $
+/* $Id: blockops.S,v 1.3 1997/02/25 20:00:10 jj Exp $
  * arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -6,16 +6,15 @@
 
 #include <asm/asi.h>
 
-	/* Zero out 64 bytes of memory at (buf + offset). */
+/* FIXME: Write this. */
+#define BZERO_TEST
+
+	/* Zero out 256 bytes of memory at (buf + offset). */
 #define BLAST_BLOCK(buf, offset)				\
-	stx		%g0, [buf + offset + 0x38];		\
-	stx		%g0, [buf + offset + 0x30];		\
-	stx		%g0, [buf + offset + 0x28];		\
-	stx		%g0, [buf + offset + 0x20];		\
-	stx		%g0, [buf + offset + 0x18];		\
-	stx		%g0, [buf + offset + 0x10];		\
-	stx		%g0, [buf + offset + 0x08];		\
-	stx		%g0, [buf + offset + 0x00];
+	stda		%f48, [buf + offset + 0x00] %asi;	\
+	stda		%f48, [buf + offset + 0x40] %asi;	\
+	stda		%f48, [buf + offset + 0x80] %asi;	\
+	stda		%f48, [buf + offset + 0xc0] %asi;	\
 
 	/* Copy 32 bytes of memory at (src + offset) to
 	 * (dst + offset).
@@ -33,41 +32,45 @@
 	.text
 	.align	4
 
-	.globl	C_LABEL(bzero_2page), C_LABEL(bzero_1page)
-C_LABEL(bzero_2page):
+	.globl		bzero_2page, bzero_1page
+bzero_2page:
 	/* %o0 = buf */
-	or		%o0, %g0, %o1
-	or		%g0, 0x40, %g2
+	mov		%o0, %o1
+	wr		%g0, ASI_BLK_P, %asi
+	mov		0x10, %g2
+	BZERO_TEST
 1:
-	BLAST_BLOCK(%o0, 0x00)
-	BLAST_BLOCK(%o0, 0x40)
-	BLAST_BLOCK(%o0, 0x80)
-	BLAST_BLOCK(%o0, 0xc0)
+	BLAST_BLOCK(%o0, 0x000)
+	BLAST_BLOCK(%o0, 0x100)
+	BLAST_BLOCK(%o0, 0x200)
+	BLAST_BLOCK(%o0, 0x300)
 	subcc		%g2, 1, %g2
 	bne,pt		%icc, 1b
-	 add		%o0, 0x100, %o0
+	 add		%o0, 0x400, %o0
 
 	retl
 	 mov		%o1, %o0
 
-C_LABEL(bzero_1page):
+bzero_1page:
 	/* %o0 = buf */
-	or		%o0, %g0, %o1
-	or		%g0, 0x20, %g2
+	mov		%o0, %o1
+	wr		%g0, ASI_BLK_P, %asi
+	mov		0x08, %g2
+	BZERO_TEST
 1:
-	BLAST_BLOCK(%o0, 0x00)
-	BLAST_BLOCK(%o0, 0x40)
-	BLAST_BLOCK(%o0, 0x80)
-	BLAST_BLOCK(%o0, 0xc0)
+	BLAST_BLOCK(%o0, 0x000)
+	BLAST_BLOCK(%o0, 0x100)
+	BLAST_BLOCK(%o0, 0x200)
+	BLAST_BLOCK(%o0, 0x300)
 	subcc		%g2, 1, %g2
 	bne,pt		%icc, 1b
-	 add		%o0, 0x100, %o0
+	 add		%o0, 0x400, %o0
 
 	retl
 	 mov		%o1, %o0
 
-	.globl	C_LABEL(__copy_1page)
-C_LABEL(__copy_1page):
+	.globl		__copy_1page
+__copy_1page:
 	/* %o0 = dst, %o1 = src */
 	or		%g0, 0x10, %g1
 1:
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/lib/memset.S linux/arch/sparc64/lib/memset.S
--- v2.1.28/linux/arch/sparc64/lib/memset.S	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/lib/memset.S	Wed Mar  5 17:04:31 1997
@@ -1,12 +1,36 @@
-/* $Id: memset.S,v 1.1 1996/12/22 07:42:16 davem Exp $
- * arch/sparc64/lib/memset.S: UltraSparc optimized memset and bzero code
- *
+/* linux/arch/sparc64/lib/memset.S: Sparc optimized memset, bzero and clear_user code
+ * Copyright (C) 1991,1996 Free Software Foundation
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * Returns 0, if ok, and number of bytes not yet set if exception
+ * occurs and we were called as clear_user.
  */
 
-#include <asm/asi.h>
+#include <asm/ptrace.h>
 
-	/* Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
+#define EX(x,y,a,b,z) 				\
+98: 	x,y;					\
+	.section .fixup,z##alloc,z##execinstr;	\
+	.align	4;				\
+99:	ba,pt	%xcc, 30f;			\
+	 a, b, %o0;				\
+	.section __ex_table,z##alloc;		\
+	.align	4;				\
+	.word	98b, 99b;			\
+	.text;					\
+	.align	4
+
+#define EXT(start,end,handler,z) 		\
+	.section __ex_table,z##alloc;		\
+	.align	4;				\
+	.word	start, 0, end, handler;		\
+	.text;					\
+	.align	4
+
+/* Please don't change these macros, unless you change the logic
+ * in the .fixup section below as well.
+ * Store 64 bytes at (BASE + OFFSET) using value SOURCE. */
 #define ZERO_BIG_BLOCK(base, offset, source)    \
 	stx	source, [base + offset + 0x00]; \
 	stx	source, [base + offset + 0x08]; \
@@ -30,104 +54,139 @@
 	.text
 	.align 4
 
-	.globl	C_LABEL(__bzero), C_LABEL(__memset), C_LABEL(memset)
-C_LABEL(__memset):
-C_LABEL(memset):
-	and		%o1, 0xff, %g3
-	sll		%g3, 8, %g2
-	or		%g3, %g2, %g3
-	sll		%g3, 16, %g2
-	or		%g3, %g2, %g3
-	sllx		%g3, 32, %g2
-	or		%g3, %g2, %g3
-	b		1f
-	 mov		%o2, %o1
-
-3:
-	cmp		%o2, 3
-	be		2f
-	 stb		%g3, [%o0]
-
-	cmp		%o2, 2
-	be		2f
-	 stb		%g3, [%o0 + 0x01]
-
-	stb		%g3, [%o0 + 0x02]
-2:
-	sub		%o2, 4, %o2
-	add		%o1, %o2, %o1
-	b		4f
-	 sub		%o0, %o2, %o0
-
-C_LABEL(__bzero):
-	mov		%g0, %g3
+	.globl	__bzero, __memset, 
+	.globl	memset, __memset_start, __memset_end
+__memset_start:
+__memset:
+memset:
+	and	%o1, 0xff, %g3
+	sll	%g3, 8, %g2
+	or	%g3, %g2, %g3
+	sll	%g3, 16, %g2
+	or	%g3, %g2, %g3
+	mov	%o2, %o1
+	sllx	%g3, 32, %g2
+	ba,pt	%xcc, 1f
+	 or	%g3, %g2, %g3
+__bzero:
+	mov	%g0, %g3
 1:
-	cmp		%o1, 7
-	bleu,pnt	%icc, 7f
-	 mov		%o0, %g1
+	cmp	%o1, 7
+	bleu,pn	%xcc, 7f
+	 andcc	%o0, 3, %o2
+
+	be,a,pt	%icc, 4f
+	 andcc	%o0, 4, %g0
+
+	cmp	%o2, 3
+	be,pn	%icc, 2f
+	 EX(stb	%g3, [%o0], sub %o1, 0,#)
+
+	cmp	%o2, 2
+	be,pt	%icc, 2f
+	 EX(stb	%g3, [%o0 + 0x01], sub %o1, 1,#)
 
-	andcc		%o0, 3, %o2
-	bne,pnt		%icc, 3b
+	EX(stb	%g3, [%o0 + 0x02], sub %o1, 2,#)
+2:
+	sub	%o2, 4, %o2
+	sub	%o0, %o2, %o0
+	add	%o1, %o2, %o1
+	andcc	%o0, 4, %g0
 4:
-	 andcc		%o0, 4, %g0
+	be,a,pt	%icc, 2f
+	 andncc	%o1, 0x7f, %o3
 
-	be,a,pt		%icc, 2f
-	 andcc		%o1, 0xffffff80, %o3	! everything 8 aligned, o1 is len to run
-
-	stw		%g3, [%o0]
-	sub		%o1, 4, %o1
-	add		%o0, 4, %o0
-	andcc		%o1, 0xffffff80, %o3	! everything 8 aligned, o1 is len to run
+	EX(st	%g3, [%o0], sub %o1, 0,#)
+	sub	%o1, 4, %o1
+	add	%o0, 4, %o0
+	andncc	%o1, 0x7f, %o3		! Now everything is 8 aligned and o1 is len to run
 2:
-	be		9f
-	 andcc		%o1, 0x78, %o2
-4:
-	ZERO_BIG_BLOCK(%o0, 0x00, %g2)
-	subcc		%o3, 128, %o3
-	ZERO_BIG_BLOCK(%o0, 0x40, %g2)
-	bne,pt		%icc, 4b
-	 add		%o0, 128, %o0
+	be,pn	%xcc, 9f
+	 andcc	%o1, 0x78, %o2
+10:
+	ZERO_BIG_BLOCK(%o0, 0x00, %g3)
+	subcc	%o3, 128, %o3
+	ZERO_BIG_BLOCK(%o0, 0x40, %g3)
+11:
+	EXT(10b, 11b, 20f,#)
+	bne,pt	%xcc, 10b
+	 add	%o0, 128, %o0
 
-	orcc		%o2, %g0, %g0
+	tst	%o2
 9:
-	be,pnt		%icc, 6f
-	 andcc		%o1, 7, %o1
+	be,pn	%xcc, 13f
+	 andcc	%o1, 7, %o1
+14:
+	rd	%pc, %o4
+	srl	%o2, 1, %o3
+	sub	%o4, %o3, %o4
+	jmpl	%o4 + (13f - 14b), %g0
+	 add	%o0, %o2, %o0
+12:
+	ZERO_LAST_BLOCKS(%o0, 0x48, %g3)
+	ZERO_LAST_BLOCKS(%o0, 0x08, %g3)
+13:
+	be,pn	%icc, 8f
+	 andcc	%o1, 4, %g0
 
-	srl		%o2, 1, %o3
-	set		bzero_table + 64, %o4
-	sub		%o4, %o3, %o4
-	jmp		%o4
-	 add		%o0, %o2, %o0
-
-bzero_table:
-	ZERO_LAST_BLOCKS(%o0, 0x48, %g2)
-	ZERO_LAST_BLOCKS(%o0, 0x08, %g2)
-
-6:
-	be,pt		%icc, 8f
-	 andcc		%o1, 4, %g0
+	be,pn	%icc, 1f
+	 andcc	%o1, 2, %g0
 
-	be,pnt		%icc, 1f
-	 andcc		%o1, 2, %g0
-
-	stw		%g3, [%o0]
-	add		%o0, 4, %o0
+	EX(st	%g3, [%o0], and %o1, 7,#)
+	add	%o0, 4, %o0
 1:
-	be,pt		%icc, 1f
-	 andcc		%o1, 1, %g0
+	be,pn	%icc, 1f
+	 andcc	%o1, 1, %g0
 
-	sth		%g3, [%o0]
-	add		%o0, 2, %o0
+	EX(sth	%g3, [%o0], and %o1, 3,#)
+	add	%o0, 2, %o0
 1:
-	bne,a,pnt	%icc, 8f
-	 stb		%g3, [%o0]
+	bne,a,pn %icc, 8f
+	 EX(stb	%g3, [%o0], and %o1, 1,#)
 8:
 	retl
-	 mov		%g1, %o0
-
-/* Don't care about alignment here. It is highly 
- * unprobable and at most two traps may happen
- */
+	 clr	%o0
 7:
-	ba,pt		%xcc, 6b
-	 orcc		%o1, 0, %g0
+	be,pn	%icc, 13b
+	 orcc	%o1, 0, %g0
+
+	be,pn	%icc, 0f
+8:
+	 add	%o0, 1, %o0
+	subcc	%o1, 1, %o1
+	bne,a,pt %icc, 8b
+	 EX(stb	%g3, [%o0 - 1], add %o1, 1,#)
+0:
+	retl
+	 clr	%o0
+__memset_end:
+
+	.section .fixup,#alloc,#execinstr
+	.align	4
+20:
+	cmp	%g2, 8
+	bleu,pn	%xcc, 1f
+	 and	%o1, 0x7f, %o1
+	sub	%g2, 9, %g2
+	add	%o3, 64, %o3
+1:
+	sll	%g2, 3, %g2
+	add	%o3, %o1, %o0
+	ba,pt	%xcc, 30f
+	 sub	%o0, %g2, %o0
+21:
+	mov	8, %o0
+	and	%o1, 7, %o1
+	sub	%o0, %g2, %o0
+	sll	%o0, 3, %o0
+	ba,pt	%xcc, 30f
+	 add	%o0, %o1, %o0
+30:
+/* %o4 is faulting address, %o5 is %pc where fault occured */
+	save	%sp, -160, %sp
+	mov	%i5, %o0
+	mov	%i7, %o1
+	call	lookup_fault
+	 mov	%i4, %o2
+	ret
+	 restore
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/mm/fault.c linux/arch/sparc64/mm/fault.c
--- v2.1.28/linux/arch/sparc64/mm/fault.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/mm/fault.c	Wed Mar  5 17:04:31 1997
@@ -1,7 +1,8 @@
-/* $Id: fault.c,v 1.2 1996/12/26 18:03:04 davem Exp $
+/* $Id: fault.c,v 1.3 1997/03/04 16:27:02 jj Exp $
  * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
@@ -70,6 +71,54 @@
 	return total;
 }
 
+void unhandled_fault(unsigned long address, struct task_struct *tsk,
+                     struct pt_regs *regs)
+{
+	if((unsigned long) address < PAGE_SIZE) {
+		printk(KERN_ALERT "Unable to handle kernel NULL "
+		       "pointer dereference");
+	} else {
+		printk(KERN_ALERT "Unable to handle kernel paging request "
+		       "at virtual address %016lx\n", (unsigned long)address);
+	}
+	printk(KERN_ALERT "tsk->mm->context = %016lx\n",
+	       (unsigned long) tsk->mm->context);
+	printk(KERN_ALERT "tsk->mm->pgd = %016lx\n",
+	       (unsigned long) tsk->mm->pgd);
+	die_if_kernel("Oops", regs);
+}
+
+asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 
+			    unsigned long address)
+{
+	unsigned long g2;
+	int i;
+	unsigned insn;
+	struct pt_regs regs;
+	
+	i = search_exception_table (ret_pc, &g2);
+	switch (i) {
+	/* load & store will be handled by fixup */
+	case 3: return 3;
+	/* store will be handled by fixup, load will bump out */
+	/* for _to_ macros */
+	case 1: insn = (unsigned *)pc; if ((insn >> 21) & 1) return 1; break;
+	/* load will be handled by fixup, store will bump out */
+	/* for _from_ macros */
+	case 2: insn = (unsigned *)pc; 
+		if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) return 2; 
+		break; 
+	default: break;
+	}
+	memset (&regs, 0, sizeof (regs));
+	regs.pc = pc;
+	regs.npc = pc + 4;
+	/* FIXME: Should set up regs->tstate? */
+	unhandled_fault (address, current, &regs);
+	/* Not reached */
+	return 0;
+}
+
 asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write,
 				 unsigned long address)
 {
@@ -80,6 +129,7 @@
 	unsigned long g2;
 	int from_user = !(regs->tstate & TSTATE_PRIV);
 
+	lock_kernel ();
 	down(&mm->mmap_sem);
 	vma = find_vma(mm, address);
 	if(!vma)
@@ -105,33 +155,7 @@
 	}
 	handle_mm_fault(vma, address, write);
 	up(&mm->mmap_sem);
-	return;
-
-	vma = find_vma(mm, address);
-	if(!vma)
-		goto bad_area;
-	if(vma->vm_start <= address)
-		goto good_area;
-	if(!(vma->vm_flags & VM_GROWSDOWN))
-		goto bad_area;
-	if(expand_stack(vma, address))
-		goto bad_area;
-	/*
-	 * Ok, we have a good vm_area for this memory access, so
-	 * we can handle it..
-	 */
-good_area:
-	if(write) {
-		if(!(vma->vm_flags & VM_WRITE))
-			goto bad_area;
-	} else {
-		/* Allow reads even for write-only mappings */
-		if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
-			goto bad_area;
-	}
-	handle_mm_fault(vma, address, write);
-	up(&mm->mmap_sem);
-	return;
+	goto out;
 	/*
 	 * Something tried to access memory that isn't in our memory map..
 	 * Fix it, but check if it's kernel or user first..
@@ -142,34 +166,13 @@
 	
 	g2 = regs->u_regs[UREG_G2];
 	if (!from_user && (fixup = search_exception_table (regs->pc, &g2))) {
-		printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
-		printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
+		printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->pc, address);
+		printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n",
 			regs->pc, fixup, g2);
 		regs->pc = fixup;
 		regs->npc = regs->pc + 4;
 		regs->u_regs[UREG_G2] = g2;
-		return;
-	}
-	/* Did we have an exception handler installed? */
-	if(current->tss.ex.count == 1) {
-		if(from_user) {
-			printk("Yieee, exception signalled from user mode.\n");
-		} else {
-			/* Set pc to %g1, set %g1 to -EFAULT and %g2 to
-			 * the faulting address so we can cleanup.
-			 */
-			printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
-			printk("EX: count<%d> pc<%08lx> expc<%08lx> address<%08lx>\n",
-			       (int) current->tss.ex.count, current->tss.ex.pc,
-			       current->tss.ex.expc, current->tss.ex.address);
-			current->tss.ex.count = 0;
-			regs->pc = current->tss.ex.expc;
-			regs->npc = regs->pc + 4;
-			regs->u_regs[UREG_G1] = -EFAULT;
-			regs->u_regs[UREG_G2] = address - current->tss.ex.address;
-			regs->u_regs[UREG_G3] = current->tss.ex.pc;
-			return;
-		}
+		goto out;
 	}
 	if(from_user) {
 #if 0
@@ -179,18 +182,9 @@
 		tsk->tss.sig_address = address;
 		tsk->tss.sig_desc = SUBSIG_NOMAPPING;
 		send_sig(SIGSEGV, tsk, 1);
-		return;
+		goto out;
 	}
-	if((unsigned long) address < PAGE_SIZE) {
-		printk(KERN_ALERT "Unable to handle kernel NULL "
-		       "pointer dereference");
-	} else {
-		printk(KERN_ALERT "Unable to handle kernel paging request "
-		       "at virtual address %08lx\n", address);
-	}
-	printk(KERN_ALERT "tsk->mm->context = %08lx\n",
-	       (unsigned long) tsk->mm->context);
-	printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
-	       (unsigned long) tsk->mm->pgd);
-	die_if_kernel("Oops", regs);
+	unhandled_fault (address, tsk, regs);
+out:
+	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/Makefile linux/arch/sparc64/prom/Makefile
--- v2.1.28/linux/arch/sparc64/prom/Makefile	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/Makefile	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.1 1996/12/27 08:49:10 jj Exp $
+# $Id: Makefile,v 1.2 1997/02/25 12:40:25 jj Exp $
 # Makefile for the Sun Boot PROM interface library under
 # Linux.
 #
@@ -8,8 +8,8 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-OBJS  = bootstr.o devops.o init.o memory.o misc.o mp.o \
-        ranges.o tree.o console.o printf.o
+OBJS  = bootstr.o devops.o init.o memory.o misc.o \
+        ranges.o tree.o console.o printf.o p1275.o
 
 all: promlib.a
 
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/bootstr.c linux/arch/sparc64/prom/bootstr.c
--- v2.1.28/linux/arch/sparc64/prom/bootstr.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/bootstr.c	Wed Mar  5 17:04:31 1997
@@ -1,11 +1,10 @@
-/* $Id: bootstr.c,v 1.1 1996/12/27 08:49:10 jj Exp $
+/* $Id: bootstr.c,v 1.3 1997/03/04 16:27:06 jj Exp $
  * bootstr.c:  Boot string/argument acquisition from the PROM.
  *
  * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
  * Copyright(C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
-#include <linux/config.h>
 #include <linux/string.h>
 #include <asm/oplib.h>
 
@@ -18,7 +17,7 @@
 {
 	/* This check saves us from a panic when bootfd patches args. */
 	if (fetched) return barg_buf;
-	prom_getstring(prom_finddevice ("/chosen"), "bootargs", barg_buf, BARG_LEN);
+	prom_getstring(prom_chosen_node, "bootargs", barg_buf, BARG_LEN);
 	fetched = 1;
 	return barg_buf;
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/console.c linux/arch/sparc64/prom/console.c
--- v2.1.28/linux/arch/sparc64/prom/console.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/console.c	Wed Mar  5 17:04:31 1997
@@ -1,12 +1,11 @@
-/* $Id: console.c,v 1.1 1996/12/27 08:49:11 jj Exp $
+/* $Id: console.c,v 1.4 1997/03/04 16:27:07 jj Exp $
  * console.c: Routines that deal with sending and receiving IO
  *            to/from the current console device using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -25,9 +24,9 @@
 {
 	char inc;
 
-	if ((*prom_command)("read", P1275_ARG(1,P1275_ARG_IN_BUF)|
-				    P1275_INOUT(3,1),
-				    prom_stdin, &inc, P1275_SIZE(1)) == 1)
+	if (p1275_cmd("read", P1275_ARG(1,P1275_ARG_OUT_BUF)|
+			      P1275_INOUT(3,1),
+			      prom_stdin, &inc, P1275_SIZE(1)) == 1)
 		return inc;
 	else
 		return -1;
@@ -42,9 +41,9 @@
 	char outc;
 	
 	outc = c;
-	if ((*prom_command)("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
-				     P1275_INOUT(3,1),
-				     prom_stdin, &inc, P1275_SIZE(1)) == 1)
+	if (p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
+			       P1275_INOUT(3,1),
+			       prom_stdin, &outc, P1275_SIZE(1)) == 1)
 		return 0;
 	else
 		return -1;
@@ -73,7 +72,6 @@
 {
 	int st_p;
 	char propb[64];
-	char *p;
 
 	st_p = prom_inst2pkg(prom_stdin);
 	if(prom_node_has_property(st_p, "keyboard"))
@@ -100,7 +98,6 @@
 {
 	int st_p;
 	char propb[64];
-	char *p;
 	int propl;
 
 	st_p = prom_inst2pkg(prom_stdout);
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/devops.c linux/arch/sparc64/prom/devops.c
--- v2.1.28/linux/arch/sparc64/prom/devops.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/devops.c	Wed Mar  5 17:04:31 1997
@@ -1,8 +1,8 @@
-/* $Id: devops.c,v 1.1 1996/12/27 08:49:11 jj Exp $
+/* $Id: devops.c,v 1.2 1997/02/25 12:40:20 jj Exp $
  * devops.c:  Device operations using the PROM.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -18,16 +18,16 @@
 int
 prom_devopen(char *dstr)
 {
-	return (*prom_command)("open", P1275_ARG(0,P1275_ARG_IN_STRING)|
-				       P1275_INOUT(1,1),
-				       dstr);
+	return p1275_cmd ("open", P1275_ARG(0,P1275_ARG_IN_STRING)|
+				  P1275_INOUT(1,1),
+				  dstr);
 }
 
 /* Close the device described by device handle 'dhandle'. */
 int
 prom_devclose(int dhandle)
 {
-	(*prom_command)("close", P1275_INOUT(1,0), dhandle);
+	p1275_cmd ("close", P1275_INOUT(1,0), dhandle);
 	return 0;
 }
 
@@ -37,5 +37,5 @@
 void
 prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
 {
-	(*prom_command)("seek", P1275_INOUT(3,1), dhandle, seekhi, seeklo);
+	p1275_cmd ("seek", P1275_INOUT(3,1), dhandle, seekhi, seeklo);
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/init.c linux/arch/sparc64/prom/init.c
--- v2.1.28/linux/arch/sparc64/prom/init.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/init.c	Wed Mar  5 17:04:31 1997
@@ -1,14 +1,14 @@
-/* $Id: init.c,v 1.1 1996/12/27 08:49:11 jj Exp $
+/* $Id: init.c,v 1.4 1997/03/04 16:27:09 jj Exp $
  * init.c:  Initialize internal variables used by the PROM
  *          library functions.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/string.h>
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
@@ -19,7 +19,7 @@
 /* The root node of the prom device tree. */
 int prom_root_node;
 int prom_stdin, prom_stdout;
-(long)(*prom_command)(char *, long, ...);
+int prom_chosen_node;
 
 /* You must call prom_init() before you attempt to use any of the
  * routines in the prom library.  It returns 0 on success, 1 on
@@ -43,18 +43,18 @@
 	if((prom_root_node == 0) || (prom_root_node == -1))
 		prom_halt();
 
-	node = prom_finddevice("/chosen");
-	if (!node || node == -1)
+	prom_chosen_node = prom_finddevice("/chosen");
+	if (!prom_chosen_node || prom_chosen_node == -1)
 		prom_halt();
 		
-	prom_stdin = prom_getint (node, "stdin");
-	prom_stdout = prom_getint (node, "stdout");
+	prom_stdin = prom_getint (prom_chosen_node, "stdin");
+	prom_stdout = prom_getint (prom_chosen_node, "stdout");
 
 	node = prom_finddevice("/openprom");
 	if (!node || node == -1)
 		prom_halt();
 		
-	prom_getstring (openprom_node, "version", buffer, sizeof (buffer));
+	prom_getstring (node, "version", buffer, sizeof (buffer));
 	if (strncmp (buffer, "OPB ", 4) || buffer[5] != '.' || buffer[7] != '.') {
 		prom_printf ("Strange OPB version.\n");
 		prom_halt ();
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/k1275d.c linux/arch/sparc64/prom/k1275d.c
--- v2.1.28/linux/arch/sparc64/prom/k1275d.c	Sun Jan 26 02:07:10 1997
+++ linux/arch/sparc64/prom/k1275d.c	Wed Mar  5 17:04:31 1997
@@ -1,123 +0,0 @@
-/* $Id: k1275d.c,v 1.2 1997/01/02 14:14:44 jj Exp $
- * k1275d.c: Sun IEEE 1275 PROM kernel daemon
- *
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-
-#define p1275sect __attribute__ ((__section__ (".p1275")))
-
-static void (*prom_cif_handler)(long *) p1275sect;
-static long prom_cif_stack;
-static void (*prom_do_it)(void);
-static long prom_args [23] p1275sect;
-static char prom_buffer [4096] p1275sect;
-
-static void prom_doit (void) p1275sect;
-
-static void prom_doit (void)
-{
-	/* FIXME: The PROM stack is grossly misaligned.
-	 * It needs some special way of handling spills/fills.
-	 * Either using %otherwin or doing flushw; wrpr somevalue, %wstate
-	 * should be done here to make difference between PROM stack
-	 * saving (32bit, misaligned stack) and kernel fill/spill).
-	 */
-	__asm__ __volatile__ ("
-	save	%%sp, -0xc0, %%sp;
-	mov	%%sp, %%l1;
-	mov	%0, %%sp;
-	call	%1;
-	 mov	%0, %%o0;
-	mov	%%l1, %%sp;
-	restore;
-	" : : "r" (prom_cif_stack), "r" (prom_cif_handler));
-}
-
-long prom_handle_command(char *service, long fmt, ...)
-{
-	char *p = prom_buffer;
-	unsigned long flags;
-	int nargs, nrets, i;
-	va_list list;
-	long attrs, x;
-	
-	save_and_cli(flags);
-	prom_args[0] = p;				/* service */
-	strcpy (p, function);
-	p = strchr (p, 0) + 1;
-	prom_args[1] = nargs = (fmt & 0x0f);		/* nargs */
-	prom_args[2] = nrets = ((fmt & 0xf0) >> 4); 	/* nrets */
-	attrs = (fmt & (~0xff));
-	va_start(list, fmt);
-	for (i = 0; i < nargs; i++, attrs >>= 3) {
-		switch (attrs & 0x7) {
-		case P1275_ARG_NUMBER:
-			prom_args[i + 3] = va_arg(list, long); break;
-		case P1275_ARG_IN_STRING:
-			strcpy (p, va_arg(list, char *));
-			prom_args[i + 3] = p;
-			p = strchr (p, 0) + 1;
-			break;
-		case P1275_ARG_OUT_BUF:
-			va_arg(list, char *);
-			prom_args[i + 3] = p;
-			x = va_arg(list, long);
-			i++; attrs >>= 3;
-			p += (int)x;
-			prom_args[i + 3] = x;
-			break;
-		case P1275_ARG_OUT_32B:
-			va_arg(list, char *);
-			prom_args[i + 3] = p;
-			p += 32;
-			break;
-		case P1275_ARG_IN_FUNCTION:
-			/* FIXME: This should make a function in our <4G
-			 * section, which will call the argument,
-			 * so that PROM can call it.
-			 */
-			prom_args[i + 3] = va_arg(list, long); break;
-		}
-	}
-	va_end(list);
-	(*prom_do_it)();
-	attrs = fmt & (~0xff);
-	va_start(list, fmt);
-	for (i = 0; i < nargs; i++, attrs >>= 3) {
-		switch (attrs & 0x7) {
-		case P1275_ARG_NUMBER:
-		case P1275_ARG_IN_STRING:
-		case P1275_ARG_IN_FUNCTION:
-			break;
-		case P1275_ARG_OUT_BUF:
-			p = va_arg(list, char *);
-			x = va_arg(list, long);
-			memcpy (p, (char *)prom_args[i + 3], (int)x);
-			i++; attrs >>= 3;
-			break;
-		case P1275_ARG_OUT_32B:
-			p = va_arg(list, char *);
-			memcpy (p, (char *)prom_args[i + 3], 32);
-			break;
-		}
-	}
-	va_end(list);
-	x = prom_args [nargs + 3];
-	restore_flags(flags);
-	return x;
-}
-
-void prom_cif_init(void *cif_handler, void *cif_stack)
-{
-	prom_cif_handler = (void (*)(long *))cif_handler;
-	prom_cif_stack = cif_stack;
-	prom_command = prom_handle_command;
-        prom_do_it = prom_doit;
-}
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/memory.c linux/arch/sparc64/prom/memory.c
--- v2.1.28/linux/arch/sparc64/prom/memory.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/prom/memory.c	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,152 @@
+/* $Id: memory.c,v 1.3 1997/03/04 16:27:10 jj Exp $
+ * memory.c: Prom routine for acquiring various bits of information
+ *           about RAM on the machine, both virtual and physical.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* This routine, for consistency, returns the ram parameters in the
+ * V0 prom memory descriptor format.  I choose this format because I
+ * think it was the easiest to work with.  I feel the religious
+ * arguments now... ;)  Also, I return the linked lists sorted to
+ * prevent paging_init() upset stomach as I have not yet written
+ * the pepto-bismol kernel module yet.
+ */
+
+struct linux_prom64_registers prom_reg_memlist[64];
+struct linux_prom64_registers prom_reg_tmp[64];
+
+struct linux_mlist_p1275 prom_phys_total[64];
+struct linux_mlist_p1275 prom_prom_taken[64];
+struct linux_mlist_p1275 prom_phys_avail[64];
+
+struct linux_mlist_p1275 *prom_ptot_ptr = prom_phys_total;
+struct linux_mlist_p1275 *prom_ptak_ptr = prom_prom_taken;
+struct linux_mlist_p1275 *prom_pavl_ptr = prom_phys_avail;
+
+struct linux_mem_p1275 prom_memlist;
+
+
+/* Internal Prom library routine to sort a linux_mlist_p1275 memory
+ * list.  Used below in initialization.
+ */
+__initfunc(static void
+prom_sortmemlist(struct linux_mlist_p1275 *thislist))
+{
+	int swapi = 0;
+	int i, mitr, tmpsize;
+	unsigned long tmpaddr;
+	unsigned long lowest;
+
+	for(i=0; thislist[i].theres_more != 0; i++) {
+		lowest = thislist[i].start_adr;
+		for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++)
+			if(thislist[mitr].start_adr < lowest) {
+				lowest = thislist[mitr].start_adr;
+				swapi = mitr;
+			}
+		if(lowest == thislist[i].start_adr) continue;
+		tmpaddr = thislist[swapi].start_adr;
+		tmpsize = thislist[swapi].num_bytes;
+		for(mitr = swapi; mitr > i; mitr--) {
+			thislist[mitr].start_adr = thislist[mitr-1].start_adr;
+			thislist[mitr].num_bytes = thislist[mitr-1].num_bytes;
+		}
+		thislist[i].start_adr = tmpaddr;
+		thislist[i].num_bytes = tmpsize;
+	}
+}
+
+/* Initialize the memory lists based upon the prom version. */
+__initfunc(void prom_meminit(void))
+{
+	int node = 0;
+	unsigned int iter, num_regs;
+
+	node = prom_finddevice("/memory");
+	num_regs = prom_getproperty(node, "available",
+				    (char *) prom_reg_memlist,
+				    sizeof(prom_reg_memlist));
+	num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+	for(iter=0; iter<num_regs; iter++) {
+		prom_phys_avail[iter].start_adr =
+			prom_reg_memlist[iter].phys_addr;
+		prom_phys_avail[iter].num_bytes =
+			(unsigned long) prom_reg_memlist[iter].reg_size;
+		prom_phys_avail[iter].theres_more =
+			&prom_phys_avail[iter+1];
+	}
+	prom_phys_avail[iter-1].theres_more = 0x0;
+
+	num_regs = prom_getproperty(node, "reg",
+				    (char *) prom_reg_memlist,
+				    sizeof(prom_reg_memlist));
+	num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+	for(iter=0; iter<num_regs; iter++) {
+		prom_phys_total[iter].start_adr =
+			prom_reg_memlist[iter].phys_addr;
+		prom_phys_total[iter].num_bytes =
+			(unsigned long) prom_reg_memlist[iter].reg_size;
+		prom_phys_total[iter].theres_more =
+			&prom_phys_total[iter+1];
+	}
+	prom_phys_total[iter-1].theres_more = 0x0;
+
+	node = prom_finddevice("/virtual-memory");
+	num_regs = prom_getproperty(node, "available",
+				    (char *) prom_reg_memlist,
+				    sizeof(prom_reg_memlist));
+	num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+
+	/* Convert available virtual areas to taken virtual
+	 * areas.  First sort, then convert.
+	 */
+	for(iter=0; iter<num_regs; iter++) {
+		prom_prom_taken[iter].start_adr =
+			prom_reg_memlist[iter].phys_addr;
+		prom_prom_taken[iter].num_bytes =
+			(unsigned long) prom_reg_memlist[iter].reg_size;
+		prom_prom_taken[iter].theres_more =
+			&prom_phys_total[iter+1];
+	}
+	prom_prom_taken[iter-1].theres_more = 0x0;
+
+	prom_sortmemlist(prom_prom_taken);
+
+	/* Finally, convert. */
+	for(iter=0; iter<num_regs; iter++) {
+		prom_prom_taken[iter].start_adr =
+			prom_prom_taken[iter].start_adr +
+			prom_prom_taken[iter].num_bytes;
+		prom_prom_taken[iter].num_bytes =
+			prom_prom_taken[iter+1].start_adr -
+			prom_prom_taken[iter].start_adr;
+	}
+	prom_prom_taken[iter-1].num_bytes =
+		((unsigned long)-1) - (unsigned long) prom_prom_taken[iter-1].start_adr;
+
+	/* Sort the other two lists. */
+	prom_sortmemlist(prom_phys_total);
+	prom_sortmemlist(prom_phys_avail);
+
+	/* Link all the lists into the top-level descriptor. */
+	prom_memlist.p1275_totphys=&prom_ptot_ptr;
+	prom_memlist.p1275_prommap=&prom_ptak_ptr;
+	prom_memlist.p1275_available=&prom_pavl_ptr;
+}
+
+/* This returns a pointer to our libraries internal p1275 format
+ * memory descriptor.
+ */
+struct linux_mem_p1275 *
+prom_meminfo(void)
+{
+	return &prom_memlist;
+}
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/misc.c linux/arch/sparc64/prom/misc.c
--- v2.1.28/linux/arch/sparc64/prom/misc.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/misc.c	Wed Mar  5 17:04:31 1997
@@ -1,12 +1,11 @@
-/* $Id: misc.c,v 1.1 1996/12/27 08:49:12 jj Exp $
+/* $Id: misc.c,v 1.4 1997/03/04 16:27:11 jj Exp $
  * misc.c:  Miscellaneous prom functions that don't belong
  *          anywhere else.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -17,8 +16,8 @@
 void
 prom_reboot(char *bcommand)
 {
-	(*prom_command)("boot", P1275_ARG(0,P1275_ARG_IN_STRING)|
-				P1275_INOUT(1,0), bcommand);
+	p1275_cmd ("boot", P1275_ARG(0,P1275_ARG_IN_STRING)|
+		           P1275_INOUT(1,0), bcommand);
 }
 
 /* Forth evaluate the expression contained in 'fstring'. */
@@ -27,8 +26,8 @@
 {
 	if(!fstring || fstring[0] == 0)
 		return;
-	(*prom_command)("interpret", P1275_ARG(0,P1275_ARG_IN_STRING)|
-				     P1275_INOUT(1,1), fstring);
+	p1275_cmd ("interpret", P1275_ARG(0,P1275_ARG_IN_STRING)|
+				P1275_INOUT(1,1), fstring);
 }
 
 /* We want to do this more nicely some day. */
@@ -56,7 +55,7 @@
 #endif
 	install_obp_ticker();
 	save_flags(flags); cli();
-	(*prom_command)("enter", P1275_INOUT(0,0));
+	p1275_cmd ("enter", P1275_INOUT(0,0));
 	restore_flags(flags);
 	install_linux_ticker();
 #ifdef CONFIG_SUN_CONSOLE
@@ -71,18 +70,16 @@
 void
 prom_halt(void)
 {
-	(*prom_command)("exit", P1275_INOUT(0,0));
+	p1275_cmd ("exit", P1275_INOUT(0,0));
 }
 
-typedef void (*sfunc_t)(void);
-
 /* Set prom sync handler to call function 'funcp'. */
 void
-prom_setsync(sfunc_t funcp)
+prom_setsync(sync_func_t funcp)
 {
 	if(!funcp) return;
-	(*prom_command)("set-callback", P1275_ARG(0,P1275_IN_FUNCTION)|
-					P1275_INOUT(1,1), funcp);
+	p1275_cmd ("set-callback", P1275_ARG(0,P1275_ARG_IN_FUNCTION)|
+				   P1275_INOUT(1,1), funcp);
 }
 
 /* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/p1275.c linux/arch/sparc64/prom/p1275.c
--- v2.1.28/linux/arch/sparc64/prom/p1275.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/prom/p1275.c	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,149 @@
+/* $Id: p1275.c,v 1.4 1997/03/04 16:27:12 jj Exp $
+ * p1275.c: Sun IEEE 1275 PROM low level interface routines
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/spitfire.h>
+#include <asm/system.h>
+
+/* If you change layout of this structure, please change the prom_doit
+   function below as well. */
+typedef struct {
+	unsigned prom_doit_code [16];
+	void (*prom_cif_handler)(long *);
+	unsigned long prom_cif_stack;
+	unsigned long prom_args [23];
+	char prom_buffer [7928];
+} at0x8000;
+
+static void (*prom_do_it)(void);
+
+void prom_cif_interface (void) __attribute__ ((__section__ (".p1275")));
+
+/* At most 16 insns (including the retl; nop; afterwards) */
+void prom_cif_interface (void)
+{
+	__asm__ __volatile__ ("
+	sethi	%hi(0x8000), %o0
+	ldx	[%o0 + 0x048], %o1	! prom_cif_stack
+	save	%o1, -0xc0, %sp
+	ldx	[%i0 + 0x040], %l2	! prom_cif_handler
+	mov	%g4, %l0
+	mov	%g6, %l1
+	call	%l2
+	 or	%i0, 0x050, %o0		! prom_args
+	mov	%l0, %g4
+	mov	%l1, %g6
+	restore
+	");
+}
+
+long p1275_cmd (char *service, long fmt, ...)
+{
+	char *p, *q;
+	unsigned long flags;
+	int nargs, nrets, i;
+	va_list list;
+	long attrs, x;
+	long ctx = 0;
+	at0x8000 *low = (at0x8000 *)(0x8000);
+	
+	p = low->prom_buffer;
+	save_and_cli(flags);
+	ctx = spitfire_get_primary_context ();
+	if (ctx) {
+		flushw_user ();
+		spitfire_set_primary_context (0);
+	}
+	low->prom_args[0] = (unsigned long)p;			/* service */
+	strcpy (p, service);
+	p = strchr (p, 0) + 1;
+	low->prom_args[1] = nargs = (fmt & 0x0f);		/* nargs */
+	low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); 	/* nrets */
+	attrs = (fmt & (~0xff));
+	va_start(list, fmt);
+	for (i = 0; i < nargs; i++, attrs >>= 3) {
+		switch (attrs & 0x7) {
+		case P1275_ARG_NUMBER:
+			low->prom_args[i + 3] = va_arg(list, long); break;
+		case P1275_ARG_IN_STRING:
+			strcpy (p, va_arg(list, char *));
+			low->prom_args[i + 3] = (unsigned long)p;
+			p = strchr (p, 0) + 1;
+			break;
+		case P1275_ARG_OUT_BUF:
+			(void) va_arg(list, char *);
+			low->prom_args[i + 3] = (unsigned long)p;
+			x = va_arg(list, long);
+			i++; attrs >>= 3;
+			p += (int)x;
+			low->prom_args[i + 3] = x;
+			break;
+		case P1275_ARG_IN_BUF:
+			q = va_arg(list, char *);
+			low->prom_args[i + 3] = (unsigned long)p;
+			x = va_arg(list, long);
+			i++; attrs >>= 3;
+			p += (int)x;
+			memcpy (p, q, (int)x);
+			low->prom_args[i + 3] = x;
+			break;
+		case P1275_ARG_OUT_32B:
+			(void) va_arg(list, char *);
+			low->prom_args[i + 3] = (unsigned long)p;
+			p += 32;
+			break;
+		case P1275_ARG_IN_FUNCTION:
+			/* FIXME: This should make a function in our <4G
+			 * section, which will call the argument,
+			 * so that PROM can call it.
+			 */
+			low->prom_args[i + 3] = va_arg(list, long); break;
+		}
+	}
+	va_end(list);
+	(*prom_do_it)();
+	attrs = fmt & (~0xff);
+	va_start(list, fmt);
+	for (i = 0; i < nargs; i++, attrs >>= 3) {
+		switch (attrs & 0x7) {
+		case P1275_ARG_NUMBER:
+		case P1275_ARG_IN_STRING:
+		case P1275_ARG_IN_FUNCTION:
+		case P1275_ARG_IN_BUF:
+			break;
+		case P1275_ARG_OUT_BUF:
+			p = va_arg(list, char *);
+			x = va_arg(list, long);
+			memcpy (p, (char *)(low->prom_args[i + 3]), (int)x);
+			i++; attrs >>= 3;
+			break;
+		case P1275_ARG_OUT_32B:
+			p = va_arg(list, char *);
+			memcpy (p, (char *)(low->prom_args[i + 3]), 32);
+			break;
+		}
+	}
+	va_end(list);
+	x = low->prom_args [nargs + 3];
+	if (ctx)
+		spitfire_set_primary_context (ctx);
+	restore_flags(flags);
+	return x;
+}
+
+void prom_cif_init(void *cif_handler, void *cif_stack)
+{
+	at0x8000 *low = (at0x8000 *)(0x8000);
+	
+	low->prom_cif_handler = (void (*)(long *))cif_handler;
+	low->prom_cif_stack = (unsigned long)cif_stack;
+        prom_do_it = (void (*)(void))(0x8000);
+}
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/printf.c linux/arch/sparc64/prom/printf.c
--- v2.1.28/linux/arch/sparc64/prom/printf.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/printf.c	Wed Mar  5 17:04:31 1997
@@ -1,4 +1,4 @@
-/* $Id: printf.c,v 1.1 1996/12/27 08:49:13 jj Exp $
+/* $Id: printf.c,v 1.2 1997/03/04 16:27:13 jj Exp $
  * printf.c:  Internal prom library printf facility.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -8,7 +8,6 @@
  * about or use it!  It's simple and smelly anyway....
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 
 #include <asm/openprom.h>
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/ranges.c linux/arch/sparc64/prom/ranges.c
--- v2.1.28/linux/arch/sparc64/prom/ranges.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/sparc64/prom/ranges.c	Wed Mar  5 17:04:31 1997
@@ -0,0 +1,107 @@
+/* $Id: ranges.c,v 1.1 1997/02/25 12:40:28 jj Exp $
+ * ranges.c: Handle ranges in newer proms for obio/sbus.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/init.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/system.h>
+
+struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
+int num_obio_ranges;
+
+/* Adjust register values based upon the ranges parameters. */
+inline void
+prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
+		 struct linux_prom_ranges *rangep, int nranges)
+{
+	int regc, rngc;
+
+	for(regc=0; regc < nregs; regc++) {
+		for(rngc=0; rngc < nranges; rngc++)
+			if(regp[regc].which_io == rangep[rngc].ot_child_space)
+				break; /* Fount it */
+		if(rngc==nranges) /* oops */
+			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
+		regp[regc].which_io = rangep[rngc].ot_parent_space;
+		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
+	}
+}
+
+inline void
+prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
+		   struct linux_prom_ranges *ranges2, int nranges2)
+{
+	int rng1c, rng2c;
+
+	for(rng1c=0; rng1c < nranges1; rng1c++) {
+		for(rng2c=0; rng2c < nranges2; rng2c++)
+			if(ranges1[rng1c].ot_child_space ==
+			   ranges2[rng2c].ot_child_space) break;
+		if(rng2c == nranges2) /* oops */
+			prom_printf("adjust_ranges: Could not find matching bus type...\n");
+		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
+		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
+	}
+}
+
+/* Apply probed sbus ranges to registers passed, if no ranges return. */
+void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs,
+			    int nregs, struct linux_sbus_device *sdev)
+{
+	if(sbus->num_sbus_ranges) {
+		if(sdev && (sdev->ranges_applied == 0)) {
+			sdev->ranges_applied = 1;
+			prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
+					 sbus->num_sbus_ranges);
+		}
+	}
+}
+
+__initfunc(void prom_ranges_init(void))
+{
+}
+
+__initfunc(void prom_sbus_ranges_init(int parentnd, struct linux_sbus *sbus))
+{
+	int success;
+	
+	sbus->num_sbus_ranges = 0;
+	success = prom_getproperty(sbus->prom_node, "ranges",
+				   (char *) sbus->sbus_ranges,
+				   sizeof (sbus->sbus_ranges));
+	if (success != -1)
+		sbus->num_sbus_ranges = (success/sizeof(struct linux_prom_ranges));
+}
+
+void
+prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs)
+{
+	int success;
+	int num_ranges;
+	struct linux_prom_ranges ranges[PROMREG_MAX];
+	
+	success = prom_getproperty(node, "ranges",
+				   (char *) ranges,
+				   sizeof (ranges));
+	if (success != -1) {
+		num_ranges = (success/sizeof(struct linux_prom_ranges));
+		if (parent) {
+			struct linux_prom_ranges parent_ranges[PROMREG_MAX];
+			int num_parent_ranges;
+		
+			success = prom_getproperty(parent, "ranges",
+				   		   (char *) parent_ranges,
+				   		   sizeof (parent_ranges));
+			if (success != -1) {
+				num_parent_ranges = (success/sizeof(struct linux_prom_ranges));
+				prom_adjust_ranges (ranges, num_ranges, parent_ranges, num_parent_ranges);
+			}
+		}
+		prom_adjust_regs(regs, nregs, ranges, num_ranges);
+	}
+}
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/prom/tree.c linux/arch/sparc64/prom/tree.c
--- v2.1.28/linux/arch/sparc64/prom/tree.c	Mon Dec 30 01:59:59 1996
+++ linux/arch/sparc64/prom/tree.c	Wed Mar  5 17:04:31 1997
@@ -1,12 +1,11 @@
-/* $Id: tree.c,v 1.1 1996/12/27 08:49:13 jj Exp $
+/* $Id: tree.c,v 1.4 1997/03/04 16:27:14 jj Exp $
  * tree.c: Basic device tree traversal/scanning for the Linux
  *         prom library.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
-#include <linux/config.h>
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -24,7 +23,18 @@
 	long cnode;
 
 	if(node == -1) return 0;
-	cnode = (*prom_command)("child", P1275_INOUT(1, 1), node);
+	cnode = p1275_cmd ("child", P1275_INOUT(1, 1), node);
+	if(cnode == -1) return 0;
+	return (int)cnode;
+}
+
+__inline__ int
+prom_getparent(int node)
+{
+	long cnode;
+
+	if(node == -1) return 0;
+	cnode = p1275_cmd ("parent", P1275_INOUT(1, 1), node);
 	if(cnode == -1) return 0;
 	return (int)cnode;
 }
@@ -38,8 +48,8 @@
 	long sibnode;
 
 	if(node == -1) return 0;
-	sibnode = (*prom_command)("peer", P1275_INOUT(1, 1), node);
-	if(cnode == -1) return 0;
+	sibnode = p1275_cmd ("peer", P1275_INOUT(1, 1), node);
+	if(sibnode == -1) return 0;
 	return (int)sibnode;
 }
 
@@ -50,10 +60,10 @@
 prom_getproplen(int node, char *prop)
 {
 	if((!node) || (!prop)) return -1;
-	return (*prom_command)("getproplen", 
-			       P1275_ARG(1,P1275_ARG_IN_STRING)|
-			       P1275_INOUT(2, 1), 
-			       node, prop);
+	return p1275_cmd ("getproplen", 
+			  P1275_ARG(1,P1275_ARG_IN_STRING)|
+			  P1275_INOUT(2, 1), 
+			  node, prop);
 }
 
 /* Acquire a property 'prop' at node 'node' and place it in
@@ -70,11 +80,11 @@
 		return -1;
 	else {
 		/* Ok, things seem all right. */
-		return (*prom_command)("getprop", 
-				       P1275_ARG(1,P1275_ARG_IN_STRING)|
-				       P1275_ARG(2,P1275_ARG_OUT_BUF)|
-				       P1275_INOUT(4, 1), 
-				       node, prop, buffer, P1275_SIZE(plen));
+		return p1275_cmd ("getprop", 
+				  P1275_ARG(1,P1275_ARG_IN_STRING)|
+				  P1275_ARG(2,P1275_ARG_OUT_BUF)|
+				  P1275_INOUT(4, 1), 
+				  node, prop, buffer, P1275_SIZE(plen));
 	}
 }
 
@@ -172,18 +182,35 @@
 int 
 prom_getname (int node, char *buffer, int len)
 {
-	int i;
-	struct linux_prom_registers reg[PROMREG_MAX];
+	int i, sbus = 0;
+	struct linux_prom_registers *reg;
+	struct linux_prom64_registers reg64[PROMREG_MAX];
 	
+	for (sbus = prom_getparent (node); sbus; sbus = prom_getparent (sbus)) {
+		i = prom_getproperty (sbus, "name", buffer, len);
+		if (i > 0) {
+			buffer [i] = 0;
+			if (!strcmp (buffer, "sbus"))
+				break;
+		}
+	}
 	i = prom_getproperty (node, "name", buffer, len);
-	if (i <= 0) return -1;
+	if (i <= 0) {
+		buffer [0] = 0;
+		return -1;
+	}
 	buffer [i] = 0;
 	len -= i;
-	i = prom_getproperty (node, "reg", (char *)reg, sizeof (reg));
+	i = prom_getproperty (node, "reg", (char *)reg64, sizeof (reg64));
 	if (i <= 0) return 0;
-	if (len < 11) return -1;
+	if (len < 16) return -1;
 	buffer = strchr (buffer, 0);
-	sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+	if (sbus) {
+		reg = (struct linux_prom_registers *)reg64;
+		sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+	} else {
+		sprintf (buffer, "@%x,%x", (unsigned int)(reg64[0].phys_addr >> 36), (unsigned int)(reg64[0].phys_addr));
+	}
 	return 0;
 }
 
@@ -193,11 +220,11 @@
 __inline__ char *
 prom_firstprop(int node, char *buffer)
 {
-	if(node == -1) return "";
 	*buffer = 0;
-	(*prom_command)("nextprop", P1275_ARG(2,P1275_ARG_OUT_32B)|
-				    P1275_INOUT(3, 0), 
-				    node, (char *) 0x0, buffer);
+	if(node == -1) return buffer;
+	p1275_cmd ("nextprop", P1275_ARG(2,P1275_ARG_OUT_32B)|
+			       P1275_INOUT(3, 0), 
+			       node, (char *) 0x0, buffer);
 	return buffer;
 }
 
@@ -210,14 +237,18 @@
 {
 	char buf[32];
 
-	if(node == -1) return "";
-	*buffer = 0;
-	(*prom_command)("nextprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
+	if(node == -1) {
+		*buffer = 0;
+		return buffer;
+	}
+	if (oprop == buffer) {
+		strcpy (buf, oprop);
+		oprop = buf;
+	}
+	p1275_cmd ("nextprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
 				    P1275_ARG(2,P1275_ARG_OUT_32B)|
 				    P1275_INOUT(3, 0), 
-				    node, oprop, (oprop == buffer) ? 
-				    buf : buffer);
-	if (oprop == buffer) strcpy (buffer, buf);
+				    node, oprop, buffer); 
 	return buffer;
 }
 
@@ -225,42 +256,35 @@
 prom_finddevice(char *name)
 {
 	if(!name) return 0;
-	*buffer = 0;
-	(*prom_command)("finddevice", P1275_ARG(0,P1275_ARG_IN_STRING)|
-				      P1275_INOUT(1, 1), 
-				      name);
-	return buffer;
+	return p1275_cmd ("finddevice", P1275_ARG(0,P1275_ARG_IN_STRING)|
+				        P1275_INOUT(1, 1), 
+				        name);
 }
 
-int
-prom_node_has_property(int node, char *prop)
+int prom_node_has_property(int node, char *prop)
 {
-	char buf[32];
-	char *current_property = buf;
-	
+	char buf [32];
+        
 	*buf = 0;
 	do {
-		current_property = prom_nextprop(node, current_property, buf);
-		if(!strcmp(current_property, prop))
+		prom_nextprop(node, buf, buf);
+		if(!strcmp(buf, prop))
 			return 1;
-	} while (*current_property);
+	} while (*buf);
 	return 0;
 }
-
+                                                                                           
 /* Set property 'pname' at node 'node' to value 'value' which has a length
  * of 'size' bytes.  Return the number of bytes the prom accepted.
  */
 int
 prom_setprop(int node, char *pname, char *value, int size)
 {
-	unsigned long flags;
-	int ret;
-
 	if(size == 0) return 0;
 	if((pname == 0) || (value == 0)) return 0;
 	
-	return (*prom_command)("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
-					  P1275_ARG(2,P1275_ARG_OUT_BUF)|
+	return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
+					  P1275_ARG(2,P1275_ARG_IN_BUF)|
 					  P1275_INOUT(4, 1), 
 					  node, pname, value, P1275_SIZE(size));
 }
@@ -270,7 +294,7 @@
 {
 	int node;
 	
-	node = (*prom_command)("instance-to-package", P1275_INOUT(1, 1), inst);
+	node = p1275_cmd ("instance-to-package", P1275_INOUT(1, 1), inst);
 	if (node == -1) return 0;
 	return node;
 }
diff -u --recursive --new-file v2.1.28/linux/arch/sparc64/vmlinux.lds linux/arch/sparc64/vmlinux.lds
--- v2.1.28/linux/arch/sparc64/vmlinux.lds	Sun Jan 26 02:07:10 1997
+++ linux/arch/sparc64/vmlinux.lds	Wed Mar  5 17:04:31 1997
@@ -2,10 +2,11 @@
 OUTPUT_FORMAT("elf64-sparc", "elf64-sparc", "elf64-sparc")
 OUTPUT_ARCH(sparc:v9)
 ENTRY(_start)
+
 SECTIONS
 {
-  . = 0x100200 + SIZEOF_HEADERS;
-  .text 0xfffff80000008000 :
+  . = 0x4000;
+  .text 0xfffff80000004000 :
   {
     *(.text)
     *(.gnu.warning)
@@ -35,6 +36,12 @@
   .data.init : { *(.data.init) }
   . = ALIGN(8192);
   __init_end = .;
+  __p1275_loc = .;
+  .p1275 :
+  {
+	*(.p1275)
+	. = ALIGN(8192);
+  }
   __bss_start = .;
   .sbss      : { *(.sbss) *(.scommon) }
   .bss       :
@@ -45,12 +52,6 @@
   }
   _end = . ;
   PROVIDE (end = .);
-  __p1275_loc = .;
-  .p1275 0x0000000000008000 :
-  {
-	*(.p1275)
-  }
-  __p1275_end = .;
   /* Stabs debugging sections.  */
   .stab 0 : { *(.stab) }
   .stabstr 0 : { *(.stabstr) }
diff -u --recursive --new-file v2.1.28/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.28/linux/drivers/block/floppy.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/block/floppy.c	Tue Mar  4 13:58:37 1997
@@ -3924,10 +3924,12 @@
 	DPRINT("Read linux/drivers/block/README.fd\n");
 }
 
+static int have_no_fdc= -EIO;
+
 int floppy_init(void)
 {
 	int i,unit,drive;
-	int have_no_fdc= -EIO;
+
 
 	raw_cmd = 0;
 
@@ -4237,6 +4239,8 @@
 void floppy_eject(void)
 {
 	int dummy;
+	if(have_no_fdc)
+		return;
 	floppy_grab_irq_and_dma();
 	lock_fdc(MAXTIMEOUT,0);
 	dummy=fd_eject(0);
diff -u --recursive --new-file v2.1.28/linux/drivers/char/README.stallion linux/drivers/char/README.stallion
--- v2.1.28/linux/drivers/char/README.stallion	Wed Apr 24 02:48:04 1996
+++ linux/drivers/char/README.stallion	Wed Dec 31 16:00:00 1969
@@ -1,283 +0,0 @@
-
-Stallion Multiport Serial Drivers
----------------------------------
-
-Version: 1.1.3
-Date:    23APR96
-Author:  Greg Ungerer (gerg@stallion.oz.au)
-
-
-1. INTRODUCTION
-
-There are two drivers that work with the different families of Stallion
-multiport serial boards. One is for the Stallion smart boards - that is
-EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent
-multiport boards - EasyConnection 8/64, ONboard, Brumby and Stallion.
-
-If you are using any of the Stallion intelligent multiport boards (Brumby,
-ONboard, Stallion, EasyConnection 8/64) with Linux you will need to get the
-driver utility package. This package is available at most of the Linux
-archive sites (and on CD's that contain these archives). The file will be
-called stallion-X.X.X.tar.gz where X.X.X will be the version number. In
-particular this package contains the board embedded executable images that
-are required for these boards. It also contains the downloader program.
-These boards cannot be used without this.
-
-The following ftp sites (and their mirrors) definitely have the stallion
-driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu.
-
-ftp.stallion.com:/drivers/ata5/Linux/stallion-1.1.2.tar.gz
-tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-1.1.2.tar.gz
-sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-1.1.2.tar.gz
-
-If you are using the EasyIO or EasyConnection 8/32 boards then you don't
-need this package. Although it does have a handy script to create the
-/dev device nodes for these boards, and a serial stats display program.
-
-If you require DIP switch settings, EISA/MCA configuration files, or any
-other information related to Stallion boards then have a look at Stallion's
-web pages at http://www.stallion.com.
-
-
-
-2. INSTALLATION
-
-The drivers can be used as loadable modules or compiled into the kernel.
-You can choose which when doing a "make config" on the kernel.
-
-All ISA, EISA and MCA boards that you want to use need to be entered into
-the driver(s) configuration structures. All PCI boards will be automatically
-detected when you load the driver - so they do not need to be entered into
-the driver(s) configuration structure. (Note that kernel PCI BIOS32 support
-is required to use PCI boards.)
-
-Entering ISA, EISA and MCA boards into the driver(s) configuration structure
-involves editing the driver(s) source file. It's pretty easy if you follow
-the instructions below. Both drivers can support up to 4 boards. The smart
-card driver (the stallion.c driver) supports any combination of EasyIO and
-EasyConnection 8/32 boards (up to a total of 4). The intelligent driver
-supports any combination of ONboards, Brumbys, Stallions and EasyConnection
-8/64 boards (up to a total of 4).
-
-To set up the driver(s) for the boards that you want to use you need to
-edit the appropriate driver file and add configuration entries.
-
-If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do:
-   vi stallion.c
-      - find the definition of the stl_brdconf array (of structures)
-        near the top of the file
-      - modify this to match the boards you are going to install
-	(the comments before this structure should help)
-      - save and exit
-
-If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
-   vi istallion.c
-      - find the definition of the stli_brdconf array (of structures)
-        near the top of the file
-      - modify this to match the boards you are going to install
-	(the comments before this structure should help)
-      - save and exit
-
-Once you have set up the board configurations then you are ready to build
-the kernel or modules.
-
-When the new kernel is booted, or the loadable module loaded then the
-driver will emit some kernel trace messages about whether the configured
-boards where detected or not. Depending on how your system logger is set
-up these may come out on the console, or just be logged to
-/var/adm/messages. You should check the messages to confirm that all is well.
-
-
-2.1 SHARING INTERRUPTS
-
-It is possible to share interrupts between multiple EasyIO and
-EasyConnection 8/32 boards in an EISA system. To do this you will need to
-do a couple of things:
-
-1. When entering the board resources into the stallion.c file you need to
-   mark the boards as using level triggered interrupts. Do this by replacing
-   the "0" entry at field position 6 (the last field) in the board
-   configuration structure with a "1". (This is the structure that defines
-   the board type, I/O locations, etc. for each board). All boards that are
-   sharing an interrupt must be set this way, and each board should have the
-   same interrupt number specified here as well. Now build the module or
-   kernel as you would normally.
-
-2. When physically installing the boards into the system you must enter
-   the system EISA configuration utility. You will need to install the EISA
-   configuration files for *all* the EasyIO and EasyConnection 8/32 boards
-   that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32
-   EISA configuration files required are supplied by Stallion Technologies
-   on the DOS Utilities floppy (usually supplied in the box with the board
-   when purchased. If not, you can pick it up from Stallion's FTP site,
-   ftp.stallion.com). You will need to edit the board resources to choose
-   level triggered interrupts, and make sure to set each board's interrupt
-   to the same IRQ number.
-
-You must complete both the above steps for this to work. When you reboot
-or load the driver your EasyIO and EasyConnection 8/32 boards will be
-sharing interrupts.
-
-
-2.2 USING HIGH SHARED MEMORY
-
-The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of
-using shared memory addresses above the usual 640K - 1Mb range. The ONboard
-ISA and the Stallion boards can be programmed to use memory addresses up to
-16Mb (the ISA bus addressing limit), and the EasyConnection 8/64-EI and
-ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus
-addressing limit).
-
-The higher than 1Mb memory addresses are fully supported by this driver.
-Just enter the address as you normally would for a lower than 1Mb address
-(in the drivers board configuration structure).
-
-
-
-2.3 TROUBLE SHOOTING
-
-If a board is not found by the driver but is actually in the system then the
-most likely problem is that the I/O address is wrong. Change it in the driver
-stallion.c or istallion.c configuration structure and rebuild the kernel or
-modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards
-the IRQ is software programmable, so if there is a conflict you may need to
-change the IRQ used for a board in the stallion.c configuration structure.
-There are no interrupts to worry about for ONboard, Brumby, Stallion or
-EasyConnection 8/64 boards. The memory region on EasyConnection 8/64 and
-ONboard boards is software programmable, but not on the Brumbys or Stallions.
-
-
-
-3. USING THE DRIVERS
-
-3.1 INTELLIGENT DRIVER OPERATION
-
-The intelligent boards also need to have their "firmware" code downloaded
-to them. This is done via a user level application supplied in the driver
-package called "stlload". Compile this program where ever you dropped the
-package files, by typing "make". In its simplest form you can then type
-    ./stlload -i cdk.sys
-in this directory and that will download board 0 (assuming board 0 is an
-EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
-    ./stlload -i 2681.sys
-
-Normally you would want all boards to be downloaded as part of the standard
-system startup. To achieve this, add one of the lines above into the
-/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add
-the "-b <brd-number>" option to the line. You will need to download code for
-every board. You should probably move the stlload program into a system
-directory, such as /usr/sbin. Also, the default location of the cdk.sys image
-file in the stlload down-loader is /usr/lib/stallion. Create that directory
-and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
-them anyway). As an example your /etc/rc.d/rc.S file might have the
-following lines added to it (if you had 3 boards):
-    /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
-    /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
-    /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
-
-The image files cdk.sys and 2681.sys are specific to the board types. The
-cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly
-the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards.
-If you load the wrong image file into a board it will fail to start up, and
-of course the ports will not be operational!
-
-If you are using the modularized version of the driver you might want to put
-the insmod calls in the startup script as well (before the download lines
-obviously).
-
-
-3.2 USING THE SERIAL PORTS
-
-Once the driver is installed you will need to setup some device nodes to
-access the serial ports. The simplest method is to use the stallion utility
-"mkdevnods" script. It will automatically create all possible device entries
-required for all 4 boards. This will create the normal serial port devices as
-/dev/ttyE# where # is the port number starting from 0. A bank of 64 minor
-device numbers is allocated to each board, so the first port on the second
-board is port 64, etc. A set of callout type devices is also created. They
-are created as the devices /dev/cue# where # is the same as for the ttyE
-devices.
-
-For the most part the Stallion driver tries to emulate the standard PC system
-COM ports and the standard Linux serial driver. The idea is that you should
-be able to use Stallion board ports and COM ports interchangeably without
-modifying anything but the device name. Anything that doesn't work like that
-should be considered a bug in this driver!
-
-If you look at the driver code you will notice that it is fairly closely
-based on the Linux serial driver (linux/drivers/char/serial.c). This is
-intentional, obviously this is the easiest way to emulate its behavior!
-
-Since this driver tries to emulate the standard serial ports as much as
-possible, most system utilities should work as they do for the standard
-COM ports. Most importantly "stty" works as expected and "setserial" can be
-also be used (excepting the ability to auto-configure the I/O and IRQ
-addresses of boards). Higher baud rates are supported in the usual fashion
-through setserial or using the CBAUDEX extensions. Note that the EasyIO and
-EasyConnection (all types) support 57600 and 115200 baud. The older boards
-including ONboard, Brumby and the original Stallion support a maximum baud
-rate of 38400.
-
-If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO
-by Greg Hankins. It will explain everything you need to know!
-
-
-
-4. NOTES
-
-You can use both drivers at once if you have a mix of board types installed
-in a system. However to do this you will need to change the major numbers
-used by one of the drivers. Currently both drivers use major numbers 24, 25
-and 28 for their devices. Change one driver to use some other major numbers,
-and then modify the mkdevnods script to make device nodes based on those new
-major numbers. For example, you could change the istallion.c driver to use
-major numbers 60, 61 and 62. You will also need to create device nodes with
-different names for the ports, for example ttyF# and cuf#.
-
-The original Stallion board is no longer supported by Stallion Technologies.
-Although it is known to work with the istallion driver.
-
-Finding a free physical memory address range can be a problem. The older
-boards like the Stallion and ONboard need large areas (64K or even 128K), so
-they can be very difficult to get into a system. If you have 16 Mb of RAM
-then you have no choice but to put them somewhere in the 640K -> 1Mb range.
-ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some
-systems. If you have an original Stallion board, "V4.0" or Rev.O, then you
-need a 64K memory address space, so again 0xd0000 and 0xe0000 are good.
-Older Stallion boards are a much bigger problem. They need 128K of address
-space and must be on a 128K boundary. If you don't have a VGA card then
-0xc0000 might be usable - there is really no other place you can put them
-below 1Mb.
-
-Both the ONboard and old Stallion boards can use higher memory addresses as
-well, but you must have less than 16Mb of RAM to be able to use them. Usual
-high memory addresses used include 0xec0000 and 0xf00000.
-
-The Brumby boards only require 16Kb of address space, so you can usually
-squeeze them in somewhere. Common addresses are 0xc8000, 0xcc000, or in
-the 0xd0000 range. EasyConnection 8/64 boards are even better, they only
-require 4Kb of address space, again usually 0xc8000, 0xcc000 or 0xd0000
-are good.
-
-If you are using an EasyConnection 8/64-EI or ONboard/E then usually the
-0xd0000 or 0xe0000 ranges are the best options below 1Mb. If neither of
-them can be used then the high memory support to use the really high address
-ranges is the best option. Typically the 2Gb range is convenient for them,
-and gets them well out of the way.
-
-The ports of the EasyIO-8M board do not have DCD or DTR signals. So these
-ports cannot be used as real modem devices. Generally, when using these
-ports you should only use the cueX devices.
-
-The driver utility package contains a couple of very useful programs. One 
-is a serial port statistics collection and display program - very handy
-for solving serial port problems. The other is an extended option setting
-program that works with the intelligent boards.
-
-
-
-5. DISCLAIMER
-
-I do not speak for Stallion Technologies in any capacity, officially or
-unofficially.
-
diff -u --recursive --new-file v2.1.28/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.28/linux/drivers/char/apm_bios.c	Wed Jan 29 03:32:58 1997
+++ linux/drivers/char/apm_bios.c	Fri Mar  7 12:51:45 1997
@@ -375,12 +375,6 @@
 	&apm_bios_fops
 };
 
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry	apm_proc_entry = {
-        0, 3, "apm", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, apm_get_info
-};
-#endif
-
 typedef struct callback_list_t {
 	int (*				callback)(apm_event_t);
 	struct callback_list_t *	next;
@@ -1072,6 +1066,7 @@
 	unsigned short	error;
 	char *		power_stat;
 	char *		bat_stat;
+	static struct proc_dir_entry *ent;
 
 	if (apm_bios_info.version == 0) {
 		printk("APM BIOS not found.\n");
@@ -1196,7 +1191,8 @@
 	add_timer(&apm_timer);
 
 #ifdef CONFIG_PROC_FS
-	proc_register_dynamic(&proc_root, &apm_proc_entry);
+	ent = create_proc_entry("apm", 0, 0);
+	ent->get_info = apm_get_info;
 #endif
 
 	misc_register(&apm_device);
diff -u --recursive --new-file v2.1.28/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.1.28/linux/drivers/char/istallion.c	Sat Nov 30 00:48:33 1996
+++ linux/drivers/char/istallion.c	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	istallion.c  -- stallion intelligent multiport serial driver.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -92,6 +93,12 @@
  *	boards can share the same shared memory address space. No interrupt
  *	is required for this board type.
  *	Another example:
+ *		{ BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 },
+ *	This line will configure an EasyConnection 8/64 EISA in slot 5 and
+ *	shared memory address of 0x80000000 (2 GByte). Multiple
+ *	EasyConnection 8/64 EISA boards can share the same shared memory
+ *	address space. No interrupt is required for this board type.
+ *	Another example:
  *		{ BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
  *	This line will configure an ONboard (ISA type) at io address 240,
  *	and shared memory address of d0000. Multiple ONboards can share
@@ -121,7 +128,8 @@
 } stlconf_t;
 
 static stlconf_t	stli_brdconf[] = {
- 	{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
+ 	/*{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },*/
+ 	{ BRD_ECP, 0x2b0, 0, 0xcc000, 0, 0 },
 };
 
 static int	stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t);
@@ -159,7 +167,7 @@
  *	all the local structures required by a serial tty driver.
  */
 static char	*stli_drvname = "Stallion Intelligent Multiport Serial Driver";
-static char	*stli_drvversion = "1.1.4";
+static char	*stli_drvversion = "5.3.2";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
@@ -475,7 +483,7 @@
 /*
  *	Define the maximal baud rate, and the default baud base for ports.
  */
-#define	STL_MAXBAUD	230400
+#define	STL_MAXBAUD	921600
 #define	STL_BAUDBASE	115200
 #define	STL_CLOSEDELAY	50
 
@@ -494,7 +502,7 @@
  */
 static unsigned int	stli_baudrates[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
 };
 
 /*****************************************************************************/
@@ -543,8 +551,9 @@
 static long	stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count);
 static long	stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count);
 static int	stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void	stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
 static void	stli_poll(unsigned long arg);
-static int	stli_hostcmd(stlibrd_t *brdp, int channr);
+static int	stli_hostcmd(stlibrd_t *brdp, stliport_t *portp);
 static int	stli_initopen(stlibrd_t *brdp, stliport_t *portp);
 static int	stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
 static int	stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait);
@@ -722,8 +731,7 @@
 			}
 		}
 
-		if (brdp->memaddr >= 0x100000)
-			iounmap(brdp->membase);
+		iounmap(brdp->membase);
 		if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC))
 			release_region(brdp->iobase, ECP_IOSIZE);
 		else
@@ -1056,7 +1064,6 @@
 	cp->openarg = arg;
 	cp->open = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
 	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
@@ -1131,7 +1138,6 @@
 	cp->closearg = arg;
 	cp->close = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
 	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
@@ -1432,7 +1438,6 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
 	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
@@ -1564,7 +1569,6 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
 	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
@@ -1753,7 +1757,8 @@
 {
 	stliport_t	*portp;
 	stlibrd_t	*brdp;
-	unsigned long	val;
+	unsigned long	lval;
+	unsigned int	ival;
 	int		rc;
 
 #if DEBUG
@@ -1771,6 +1776,12 @@
 	if (brdp == (stlibrd_t *) NULL)
 		return(0);
 
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ 			(cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return(-EIO);
+	}
+
 	rc = 0;
 
 	switch (cmd) {
@@ -1778,16 +1789,16 @@
 		if ((rc = tty_check_change(tty)) == 0) {
 			tty_wait_until_sent(tty, 0);
 			if (! arg) {
-				val = 250;
-				rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+				lval = 250;
+				rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0);
 			}
 		}
 		break;
 	case TCSBRKP:
 		if ((rc = tty_check_change(tty)) == 0) {
 			tty_wait_until_sent(tty, 0);
-			val = (arg ? (arg * 100) : 250);
-			rc = stli_cmdwait(brdp, portp, A_BREAK, &val, sizeof(unsigned long), 0);
+			lval = (arg ? (arg * 100) : 250);
+			rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0);
 		}
 		break;
 	case TIOCGSOFTCAR:
@@ -1796,40 +1807,36 @@
 		break;
 	case TIOCSSOFTCAR:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int value;
-			get_user(value, (unsigned int *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (value ? CLOCAL : 0);
+			get_user(ival, (unsigned int *) arg);
+			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
 		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
 			if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
 				return(rc);
-			val = stli_mktiocm(portp->asig.sigvalue);
-			put_user(val, (unsigned int *) arg);
+			lval = stli_mktiocm(portp->asig.sigvalue);
+			put_user(lval, (unsigned int *) arg);
 		}
 		break;
 	case TIOCMBIS:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int value;
-			get_user(value, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((value & TIOCM_DTR) ? 1 : -1), ((value & TIOCM_RTS) ? 1 : -1));
+			get_user(ival, (unsigned int *) arg);
+			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1));
 			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMBIC:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int value;
-			get_user(value, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((value & TIOCM_DTR) ? 0 : -1), ((value & TIOCM_RTS) ? 0 : -1));
+			get_user(ival, (unsigned int *) arg);
+			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1));
 			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMSET:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int value;
-			get_user(value, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((value & TIOCM_DTR) ? 1 : 0), ((value & TIOCM_RTS) ? 1 : 0));
+			get_user(ival, (unsigned int *) arg);
+			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0));
 			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
@@ -2223,7 +2230,6 @@
 	cp->status = 0;
 	cp->cmd = cmd;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	hdrp->slavereq |= portp->reqbit;
 	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_CMDING, &portp->state);
@@ -2348,15 +2354,17 @@
  *	enabled and interrupts off when called. Notice that by servicing the
  *	read data last we don't need to change the shared memory pointer
  *	during processing (which is a slow IO operation).
+ *	Return value indicates if this port is still awaiting actions from
+ *	the slave (like open, command, or even TX data being sent). If 0
+ *	then port is still busy, otherwise no longer busy.
  */
 
-static inline int stli_hostcmd(stlibrd_t *brdp, int channr)
+static inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
 {
 	volatile cdkasy_t	*ap;
 	volatile cdkctrl_t	*cp;
 	struct tty_struct	*tty;
 	asynotify_t		nt;
-	stliport_t		*portp;
 	unsigned long		oldsigs;
 	int			rc, donerx;
 
@@ -2364,7 +2372,6 @@
 	printk("stli_hostcmd(brdp=%x,channr=%d)\n", (int) brdp, channr);
 #endif
 
-	portp = brdp->ports[(channr - 1)];
 	ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
 	cp = &ap->ctrl;
 
@@ -2496,7 +2503,73 @@
 		stli_read(brdp, portp);
 	}
 
-	return(0);
+	return((test_bit(ST_OPENING, &portp->state) ||
+		test_bit(ST_CLOSING, &portp->state) ||
+		test_bit(ST_CMDING, &portp->state) ||
+		test_bit(ST_TXBUSY, &portp->state) ||
+		test_bit(ST_RXING, &portp->state)) ? 0 : 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Service all ports on a particular board. Assumes that the boards
+ *	shared memory is enabled, and that the page pointer is pointed
+ *	at the cdk header structure.
+ */
+
+static inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
+{
+	stliport_t	*portp;
+	unsigned char	hostbits[(STL_MAXCHANS / 8) + 1];
+	unsigned char	slavebits[(STL_MAXCHANS / 8) + 1];
+	unsigned char	*slavep;
+	int		bitpos, bitat, bitsize;
+	int 		channr, nrdevs, slavebitchange;
+
+	bitsize = brdp->bitsize;
+	nrdevs = brdp->nrdevs;
+
+/*
+ *	Check if slave wants any service. Basically we try to do as
+ *	little work as possible here. There are 2 levels of service
+ *	bits. So if there is nothing to do we bail early. We check
+ *	8 service bits at a time in the inner loop, so we can bypass
+ *	the lot if none of them want service.
+ */
+	memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
+
+	memset(&slavebits[0], 0, bitsize);
+	slavebitchange = 0;
+
+	for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+		if (hostbits[bitpos] == 0)
+			continue;
+		channr = bitpos * 8;
+		for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
+			if (hostbits[bitpos] & bitat) {
+				portp = brdp->ports[(channr - 1)];
+				if (stli_hostcmd(brdp, portp)) {
+					slavebitchange++;
+					slavebits[bitpos] |= bitat;
+				}
+			}
+		}
+	}
+
+/*
+ *	If any of the ports are no longer busy then update them in the
+ *	slave request bits. We need to do this after, since a host port
+ *	service may initiate more slave requests.
+ */
+	if (slavebitchange) {
+		hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+		slavep = ((unsigned char *) hdrp) + brdp->slaveoffset;
+		for (bitpos = 0; (bitpos < bitsize); bitpos++) {
+			if (slavebits[bitpos])
+				slavep[bitpos] &= ~slavebits[bitpos];
+		}
+	}
 }
 
 /*****************************************************************************/
@@ -2513,12 +2586,8 @@
 static void stli_poll(unsigned long arg)
 {
 	volatile cdkhdr_t	*hdrp;
-	unsigned char		bits[(STL_MAXCHANS / 8) + 1];
-	unsigned char		hostreq, slavereq;
-	stliport_t		*portp;
 	stlibrd_t		*brdp;
-	int			bitpos, bitat, bitsize;
-	int 			brdnr, channr, nrdevs;
+	int 			brdnr;
 
 	stli_timerlist.expires = STLI_TIMEOUT;
 	add_timer(&stli_timerlist);
@@ -2535,67 +2604,8 @@
 
 		EBRDENABLE(brdp);
 		hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-		hostreq = hdrp->hostreq;
-		slavereq = hdrp->slavereq;
-		bitsize = brdp->bitsize;
-		nrdevs = brdp->nrdevs;
-
-/*
- *		Check if slave wants any service. Basically we try to do as
- *		little work as possible here. There are 2 levels of service
- *		bits. So if there is nothing to do we bail early. We check
- *		8 service bits at a time in the inner loop, so we can bypass
- *		the lot if none of them want service.
- */
-		if (hostreq) {
-			memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
-
-			for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-				if (bits[bitpos] == 0)
-					continue;
-				channr = bitpos * 8;
-				for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
-					if (bits[bitpos] & bitat) {
-						stli_hostcmd(brdp, channr);
-					}
-				}
-			}
-		}
-
-/*
- *		Check if any of the out-standing host commands have completed.
- *		It is a bit unfortunate that we need to check stuff that we
- *		initiated!  This ain't pretty, but it needs to be fast.
- */
-		if (slavereq) {
-			slavereq = 0;
-			hostreq = 0;
-			memcpy(&bits[0], (((unsigned char *) hdrp) + brdp->slaveoffset), bitsize);
-
-			for (bitpos = 0; (bitpos < bitsize); bitpos++) {
-				if (bits[bitpos] == 0)
-					continue;
-				channr = bitpos * 8;
-				for (bitat = 0x1; (channr < nrdevs); channr++, bitat <<= 1) {
-					if (bits[bitpos] & bitat) {
-						portp = brdp->ports[(channr - 1)];
-						if (test_bit(ST_OPENING, &portp->state) ||
-								test_bit(ST_CLOSING, &portp->state) ||
-								test_bit(ST_CMDING, &portp->state) ||
-								test_bit(ST_TXBUSY, &portp->state)) {
-							slavereq |= portp->reqbit;
-						} else {
-							bits[bitpos] &= ~bitat;
-							hostreq++;
-						}
-					}
-				}
-			}
-			hdrp->slavereq = slavereq;
-			if (hostreq)
-				memcpy((((unsigned char *) hdrp) + brdp->slaveoffset), &bits[0], bitsize);
-		}
-
+		if (hdrp->hostreq)
+			stli_brdpoll(brdp, hdrp);
 		EBRDDISABLE(brdp);
 	}
 }
@@ -2621,7 +2631,7 @@
 	pp->baudout = tiosp->c_cflag & CBAUD;
 	if (pp->baudout & CBAUDEX) {
 		pp->baudout &= ~CBAUDEX;
-		if ((pp->baudout < 1) || (pp->baudout > 2))
+		if ((pp->baudout < 1) || (pp->baudout > 5))
 			tiosp->c_cflag &= ~CBAUDEX;
 		else
 			pp->baudout += 15;
@@ -3342,6 +3352,11 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
+	if (check_region(brdp->iobase, ECP_IOSIZE)) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+			brdp->brdnr, brdp->iobase);
+	}
+
 /*
  *	Based on the specific board type setup the common vars to access
  *	and enable shared memory. Set all board specific information now
@@ -3394,16 +3409,14 @@
 /*
  *	The per-board operations structure is all set up, so now let's go
  *	and get the board operational. Firstly initialize board configuration
- *	registers. Then if we are using the higher 1Mb support then set up
- *	the memory mapping info so we can get at the boards shared memory.
+ *	registers. Set the memory mapping info so we can get at the boards
+ *	shared memory.
  */
 	EBRDINIT(brdp);
 
-	if (brdp->memaddr > 0x100000) {
-		brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
-		if (brdp->membase == (void *) NULL)
-			return(-ENOMEM);
-	}
+	brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
+	if (brdp->membase == (void *) NULL)
+		return(-ENOMEM);
 
 /*
  *	Now that all specific code is set up, enable the shared memory and
@@ -3475,6 +3488,11 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
+	if (check_region(brdp->iobase, ONB_IOSIZE)) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+			brdp->brdnr, brdp->iobase);
+	}
+
 /*
  *	Based on the specific board type setup the common vars to access
  *	and enable shared memory. Set all board specific information now
@@ -3550,16 +3568,14 @@
 /*
  *	The per-board operations structure is all set up, so now let's go
  *	and get the board operational. Firstly initialize board configuration
- *	registers. Then if we are using the higher 1Mb support then set up
- *	the memory mapping info so we can get at the boards shared memory.
+ *	registers. Set the memory mapping info so we can get at the boards
+ *	shared memory.
  */
 	EBRDINIT(brdp);
 
-	if (brdp->memaddr > 0x100000) {
-		brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
-		if (brdp->membase == (void *) NULL)
-			return(-ENOMEM);
-	}
+	brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
+	if (brdp->membase == (void *) NULL)
+		return(-ENOMEM);
 
 /*
  *	Now that all specific code is set up, enable the shared memory and
@@ -3677,6 +3693,8 @@
 		portp->portbit = (unsigned char) (0x1 << (i % 8));
 	}
 
+	hdrp->slavereq = 0xff;
+
 /*
  *	For each port setup a local copy of the RX and TX buffer offsets
  *	and sizes. We do this separate from the above, because we need to
@@ -3820,11 +3838,10 @@
 	for (i = 0; (i < stli_eisamempsize); i++) {
 		brdp->memaddr = stli_eisamemprobeaddrs[i];
 		brdp->membase = (void *) brdp->memaddr;
-		if (brdp->memaddr > 0x100000) {
-			brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
-			if (brdp->membase == (void *) NULL)
-				continue;
-		}
+		brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
+		if (brdp->membase == (void *) NULL)
+			continue;
+
 		if (brdp->brdtype == BRD_ECPE) {
 			ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__);
 			memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
@@ -3837,8 +3854,8 @@
 					(onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3))
 				foundit = 1;
 		}
-		if (brdp->memaddr >= 0x100000)
-			iounmap(brdp->membase);
+
+		iounmap(brdp->membase);
 		if (foundit)
 			break;
 	}
@@ -4051,8 +4068,7 @@
  *	the slave image (and debugging :-)
  */
 
-static long stli_memread(struct inode *ip, struct file *fp,
-	char *buf, unsigned long count)
+static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count)
 {
 	unsigned long	flags;
 	void		*memptr;
@@ -4101,8 +4117,7 @@
  *	the slave image (and debugging :-)
  */
 
-static long stli_memwrite(struct inode *ip, struct file *fp,
-	const char *buf, unsigned long count)
+static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count)
 {
 	unsigned long	flags;
 	void		*memptr;
@@ -4111,7 +4126,7 @@
 	int		brdnr, size, n;
 
 #if DEBUG
-	printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%lx)\n", (int) ip, (int) fp, (int) buf, count);
+	printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count);
 #endif
 
 	brdnr = MINOR(ip->i_rdev);
diff -u --recursive --new-file v2.1.28/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.1.28/linux/drivers/char/keyboard.c	Tue Nov 12 03:08:43 1996
+++ linux/drivers/char/keyboard.c	Wed Mar  5 11:10:36 1997
@@ -1,3 +1,4 @@
+extern void allow_interrupts(void);
 /*
  * linux/drivers/char/keyboard.c
  *
@@ -1080,6 +1081,7 @@
 		reply_expected = 1;
 		outb_p(data, 0x60);
 		for(i=0; i<0x200000; i++) {
+			allow_interrupts();
 			inb_p(0x64);		/* just as a delay */
 			if (acknowledge)
 				return 1;
diff -u --recursive --new-file v2.1.28/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.28/linux/drivers/char/misc.c	Sun Jan 26 02:07:16 1997
+++ linux/drivers/char/misc.c	Fri Mar  7 12:51:45 1997
@@ -74,7 +74,8 @@
 extern int rtc_init(void);
 
 #ifdef CONFIG_PROC_FS
-static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
+static int misc_read_proc(char *buf, char **start, off_t offset,
+			  int len, int *eof, void *private)
 {
 	struct miscdevice *p;
 
@@ -183,19 +184,16 @@
 #endif
 
 #if defined(CONFIG_PROC_FS) && !defined(MODULE)
-static struct proc_dir_entry proc_misc = {
-	0, 4, "misc",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, NULL /* ops -- default to array */,
-	&proc_misc_read /* get_info */,
-};
+static struct proc_dir_entry *proc_misc;	
 #endif
 
 int misc_init(void)
 {
 #ifndef MODULE
 #ifdef CONFIG_PROC_FS
-	proc_register_dynamic(&proc_root, &proc_misc);	
+	proc_misc = create_proc_entry("misc", 0, 0);
+	if (proc_misc)
+		proc_misc->read_proc = misc_read_proc;
 #endif /* PROC_FS */
 #ifdef CONFIG_BUSMOUSE
 	bus_mouse_init();
diff -u --recursive --new-file v2.1.28/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.28/linux/drivers/char/serial.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/char/serial.c	Fri Mar  7 12:51:45 1997
@@ -52,7 +52,7 @@
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.23";
+static char *serial_version = "4.24";
 
 static DECLARE_TASK_QUEUE(tq_serial);
 
@@ -1053,7 +1053,7 @@
 static int startup(struct async_struct * info)
 {
 	unsigned long flags;
-	int	retval;
+	int	retval=0;
 	void (*handler)(int, void *, struct pt_regs *);
 	struct serial_state *state= info->state;
 	unsigned long page;
@@ -1070,16 +1070,14 @@
 
 	if (info->flags & ASYNC_INITIALIZED) {
 		free_page(page);
-		restore_flags(flags);
-		return 0;
+		goto errout;
 	}
 
 	if (!state->port || !state->type) {
 		if (info->tty)
 			set_bit(TTY_IO_ERROR, &info->tty->flags);
 		free_page(page);
-		restore_flags(flags);
-		return 0;
+		goto errout;
 	}
 	if (info->xmit_buf)
 		free_page(page);
@@ -1118,13 +1116,12 @@
 	 * here.
 	 */
 	if (serial_inp(info, UART_LSR) == 0xff) {
-		restore_flags(flags);
 		if (suser()) {
 			if (info->tty)
 				set_bit(TTY_IO_ERROR, &info->tty->flags);
-			return 0;
 		} else
-			return -ENODEV;
+			retval = -ENODEV;
+		goto errout;
 	}
 	
 	/*
@@ -1142,7 +1139,8 @@
 #endif
 				handler = rs_interrupt;
 #else
-			return -EBUSY;
+			retval = -EBUSY;
+			goto errout;
 #endif /* CONFIG_SERIAL_SHARE_IRQ */
 		} else 
 			handler = rs_interrupt_single;
@@ -1150,14 +1148,13 @@
 		retval = request_irq(state->irq, handler, IRQ_T(info),
 				     "serial", NULL);
 		if (retval) {
-			restore_flags(flags);
 			if (suser()) {
 				if (info->tty)
 					set_bit(TTY_IO_ERROR,
 						&info->tty->flags);
-				return 0;
-			} else
-				return retval;
+				retval = 0;
+			}
+			goto errout;
 		}
 	}
 
@@ -1246,6 +1243,10 @@
 	info->flags |= ASYNC_INITIALIZED;
 	restore_flags(flags);
 	return 0;
+	
+errout:
+	restore_flags(flags);
+	return retval;
 }
 
 /*
@@ -2879,8 +2880,8 @@
 	return ret;
 }
 
-int rs_read_proc(char *page, char **start, off_t off, int count, void
-		 *data)
+int rs_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
 {
 	int i, len = 0;
 	off_t	begin = 0;
@@ -2889,12 +2890,14 @@
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
 		len += line_info(page + len, &rs_table[i]);
 		if (len+begin > off+count)
-			break;
+			goto done;
 		if (len+begin < off) {
 			begin += len;
 			len = 0;
 		}
 	}
+	*eof = 1;
+done:
 	if (off >= len+begin)
 		return 0;
 	*start = page + (begin-off);
@@ -2919,6 +2922,18 @@
  	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
 #ifdef CONFIG_HUB6
 	printk(" HUB-6");
+#define SERIAL_OPT
+#endif
+#ifdef CONFIG_SERIAL_MANY_PORTS
+	printk(" MANY_PORTS");
+#define SERIAL_OPT
+#endif
+#ifdef CONFIG_SERIAL_MULTIPORT
+	printk(" MULTIPORT");
+#define SERIAL_OPT
+#endif
+#ifdef CONFIG_SERIAL_SHARE_IRQ
+	printk(" SHARE_IRQ");
 #define SERIAL_OPT
 #endif
 #ifdef SERIAL_OPT
diff -u --recursive --new-file v2.1.28/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.1.28/linux/drivers/char/stallion.c	Sat Nov 30 00:48:34 1996
+++ linux/drivers/char/stallion.c	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	stallion.c  -- stallion multiport serial driver.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -37,12 +38,13 @@
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/cd1400.h>
+#include <linux/sc26198.h>
 #include <linux/comstats.h>
 #include <linux/stallion.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -55,11 +57,9 @@
 /*****************************************************************************/
 
 /*
- *	Define different board types. At the moment I have only declared
- *	those boards that this driver supports. But I will use the standard
- *	"assigned" board numbers. In the future this driver will support
- *	some of the other Stallion boards. Currently supported boards are
- *	abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32.
+ *	Define different board types. Use the standard Stallion "assigned"
+ *	board numbers. Boards supported in this driver are abbreviated as
+ *	EIO = EasyIO and ECH = EasyConnection 8/32.
  */
 #define	BRD_EASYIO	20
 #define	BRD_ECH		21
@@ -101,7 +101,8 @@
 } stlconf_t;
 
 static stlconf_t	stl_brdconf[] = {
-	{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+	/*{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },*/
+	{ BRD_ECH, 0x2a0, 0x280, 0, 15, 0 },
 };
 
 static int	stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
@@ -126,12 +127,11 @@
 #define	STL_DRVTYPCALLOUT	2
 
 /*
- *	I haven't really decided (or measured) what TX buffer size gives
- *	a good balance between performance and memory usage. These seem
- *	to work pretty well...
+ *	Set the TX buffer size. Bigger is better, but we don't want
+ *	to chew too much memory with buffers!
  */
-#define	STL_TXBUFLOW		256
-#define	STL_TXBUFSIZE		2048
+#define	STL_TXBUFLOW		512
+#define	STL_TXBUFSIZE		4096
 
 /*****************************************************************************/
 
@@ -140,7 +140,7 @@
  *	all the local structures required by a serial tty driver.
  */
 static char	*stl_drvname = "Stallion Multiport Serial Driver";
-static char	*stl_drvversion = "1.1.4";
+static char	*stl_drvversion = "5.3.2";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -185,6 +185,11 @@
 static stlport_t	stl_dummyport;
 
 /*
+ *	Define global place to put buffer overflow characters.
+ */
+static char		stl_unwanted[SC26198_RXFIFOSIZE];
+
+/*
  *	Keep track of what interrupts we have requested for us.
  *	We don't need to request an interrupt twice if it is being
  *	shared with another Stallion board.
@@ -198,7 +203,7 @@
 
 /*
  *	Per board state flags. Used with the state field of the board struct.
- *	Not really much here yet!
+ *	Not really much here!
  */
 #define	BRD_FOUND	0x1
 
@@ -210,6 +215,7 @@
 #define	ASYI_TXBUSY	1
 #define	ASYI_TXLOW	2
 #define	ASYI_DCDCHANGE	3
+#define	ASYI_TXFLOWED	4
 
 /*
  *	Define an array of board names as printable strings. Handy for
@@ -249,14 +255,21 @@
 
 /*
  *	Hardware ID bits for the EasyIO and ECH boards. These defines apply
- *	to the directly accessible io ports of these boards (not the cd1400
- *	uarts - they are in cd1400.h).
+ *	to the directly accessible io ports of these boards (not the uarts -
+ *	they are in cd1400.h and sc26198.h).
  */
 #define	EIO_8PORTRS	0x04
 #define	EIO_4PORTRS	0x05
 #define	EIO_8PORTDI	0x00
 #define	EIO_8PORTM	0x06
+#define	EIO_MK3		0x03
 #define	EIO_IDBITMASK	0x07
+
+#define	EIO_BRDMASK	0xf0
+#define	ID_BRD4		0x10
+#define	ID_BRD8		0x20
+#define	ID_BRD16	0x30
+
 #define	EIO_INTRPEND	0x08
 #define	EIO_INTEDGE	0x00
 #define	EIO_INTLEVEL	0x08
@@ -278,24 +291,16 @@
 #define	ECH_PNLSTATUS	2
 #define	ECH_PNL16PORT	0x20
 #define	ECH_PNLIDMASK	0x07
+#define	ECH_PNLXPID	0x40
 #define	ECH_PNLINTRPEND	0x80
-#define	ECH_ADDR2MASK	0x1e0
 
-#define	EIO_CLK		25000000
-#define	EIO_CLK8M	20000000
-#define	ECH_CLK		EIO_CLK
+#define	ECH_ADDR2MASK	0x1e0
 
 /*
- *	Define the offsets within the register bank for all io registers.
- *	These io address offsets are common to both the EIO and ECH.
+ *	Define real Stallion PCI vemdor and device ID.
  */
-#define	EREG_ADDR	0
-#define	EREG_DATA	4
-#define	EREG_RXACK	5
-#define	EREG_TXACK	6
-#define	EREG_MDACK	7
-
-#define	EREG_BANKSIZE	8
+#define	PCI_VENDOR_ID_STALLION	0x124d
+#define	PCI_DEVICE_ID_ECHPCI832	0x0000
 
 /*
  *	Define the vector mapping bits for the programmable interrupt board
@@ -326,16 +331,7 @@
 		outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),	\
 			stl_brds[(brdnr)]->ioctrl);
 
-/*
- *	Define the cd1400 baud rate clocks. These are used when calculating
- *	what clock and divisor to use for the required baud rate. Also
- *	define the maximum baud rate allowed, and the default base baud.
- */
-static int	stl_cd1400clkdivs[] = {
-	CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
-};
-
-#define	STL_MAXBAUD	230400
+#define	STL_MAXBAUD	921600
 #define	STL_BAUDBASE	115200
 #define	STL_CLOSEDELAY	50
 
@@ -349,16 +345,14 @@
 
 /*
  *	Define a baud rate table that converts termios baud rate selector
- *	into the actual baud rate value. All baud rate calculates are based
- *	on the actual baud rate required.
+ *	into the actual baud rate value. All baud rate calculations are
+ *	based on the actual baud rate required.
  */
 static unsigned int	stl_baudrates[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-	9600, 19200, 38400, 57600, 115200, 230400
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
 };
 
-/*****************************************************************************/
-
 /*
  *	Define some handy local macros...
  */
@@ -408,20 +402,13 @@
 static int	stl_clrportstats(stlport_t *portp, comstats_t *cp);
 static int	stl_getportstruct(unsigned long arg);
 static int	stl_getbrdstruct(unsigned long arg);
-static void	stl_setreg(stlport_t *portp, int regnr, int value);
-static int	stl_getreg(stlport_t *portp, int regnr);
-static int	stl_updatereg(stlport_t *portp, int regnr, int value);
-static void	stl_setport(stlport_t *portp, struct termios *tiosp);
-static int	stl_getsignals(stlport_t *portp);
-static void	stl_setsignals(stlport_t *portp, int dtr, int rts);
-static void	stl_ccrwait(stlport_t *portp);
-static void	stl_enablerxtx(stlport_t *portp, int rx, int tx);
-static void	stl_startrxtx(stlport_t *portp, int rx, int tx);
-static void	stl_disableintrs(stlport_t *portp);
-static void	stl_sendbreak(stlport_t *portp, long len);
 static int	stl_waitcarrier(stlport_t *portp, struct file *filp);
 static void	stl_delay(int len);
 static void	stl_intr(int irq, void *dev_id, struct pt_regs *regs);
+static void	stl_eiointr(stlbrd_t *brdp);
+static void	stl_echatintr(stlbrd_t *brdp);
+static void	stl_echmcaintr(stlbrd_t *brdp);
+static void	stl_echpciintr(stlbrd_t *brdp);
 static void	stl_offintr(void *private);
 static void	*stl_memalloc(int len);
 static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
@@ -430,6 +417,177 @@
 static int	stl_findpcibrds(void);
 #endif
 
+/*
+ *	CD1400 uart specific handling functions.
+ */
+static void	stl_cd1400setreg(stlport_t *portp, int regnr, int value);
+static int	stl_cd1400getreg(stlport_t *portp, int regnr);
+static int	stl_cd1400updatereg(stlport_t *portp, int regnr, int value);
+static int	stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void	stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void	stl_cd1400setport(stlport_t *portp, struct termios *tiosp);
+static int	stl_cd1400getsignals(stlport_t *portp);
+static void	stl_cd1400setsignals(stlport_t *portp, int dtr, int rts);
+static void	stl_cd1400ccrwait(stlport_t *portp);
+static void	stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx);
+static void	stl_cd1400startrxtx(stlport_t *portp, int rx, int tx);
+static void	stl_cd1400disableintrs(stlport_t *portp);
+static void	stl_cd1400sendbreak(stlport_t *portp, long len);
+static void	stl_cd1400flowctrl(stlport_t *portp, int state);
+static void	stl_cd1400flush(stlport_t *portp);
+static void	stl_cd1400intr(stlpanel_t *panelp, unsigned int iobase);
+static void	stl_cd1400txisr(stlpanel_t *panelp, int ioaddr);
+static void	stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr);
+static void	stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr);
+
+/*
+ *	SC26198 uart specific handling functions.
+ */
+static void	stl_sc26198setreg(stlport_t *portp, int regnr, int value);
+static int	stl_sc26198getreg(stlport_t *portp, int regnr);
+static int	stl_sc26198updatereg(stlport_t *portp, int regnr, int value);
+static int	stl_sc26198getglobreg(stlport_t *portp, int regnr);
+static int	stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp);
+static void	stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+static void	stl_sc26198setport(stlport_t *portp, struct termios *tiosp);
+static int	stl_sc26198getsignals(stlport_t *portp);
+static void	stl_sc26198setsignals(stlport_t *portp, int dtr, int rts);
+static void	stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx);
+static void	stl_sc26198startrxtx(stlport_t *portp, int rx, int tx);
+static void	stl_sc26198disableintrs(stlport_t *portp);
+static void	stl_sc26198sendbreak(stlport_t *portp, long len);
+static void	stl_sc26198flowctrl(stlport_t *portp, int state);
+static void	stl_sc26198flush(stlport_t *portp);
+static void	stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty);
+static void	stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase);
+static void	stl_sc26198txisr(stlport_t *port);
+static void	stl_sc26198rxisr(stlport_t *port, unsigned int iack);
+static void	stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch);
+static void	stl_sc26198rxbadchars(stlport_t *portp);
+static void	stl_sc26198otherisr(stlport_t *port, unsigned int iack);
+
+/*****************************************************************************/
+
+/*
+ *	Generic UART support structure.
+ */
+typedef struct uart {
+	int	(*panelinit)(stlbrd_t *brdp, stlpanel_t *panelp);
+	void	(*portinit)(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp);
+	void	(*setport)(stlport_t *portp, struct termios *tiosp);
+	int	(*getsignals)(stlport_t *portp);
+	void	(*setsignals)(stlport_t *portp, int dtr, int rts);
+	void	(*enablerxtx)(stlport_t *portp, int rx, int tx);
+	void	(*startrxtx)(stlport_t *portp, int rx, int tx);
+	void	(*disableintrs)(stlport_t *portp);
+	void	(*sendbreak)(stlport_t *portp, long len);
+	void	(*flowctrl)(stlport_t *portp, int state);
+	void	(*flush)(stlport_t *portp);
+	void	(*intr)(stlpanel_t *panelp, unsigned int iobase);
+} uart_t;
+
+/*
+ *	Define some macros to make calling these functions nice and clean.
+ */
+#define	stl_panelinit		(* ((uart_t *) panelp->uartp)->panelinit)
+#define	stl_portinit		(* ((uart_t *) portp->uartp)->portinit)
+#define	stl_setport		(* ((uart_t *) portp->uartp)->setport)
+#define	stl_getsignals		(* ((uart_t *) portp->uartp)->getsignals)
+#define	stl_setsignals		(* ((uart_t *) portp->uartp)->setsignals)
+#define	stl_enablerxtx		(* ((uart_t *) portp->uartp)->enablerxtx)
+#define	stl_startrxtx		(* ((uart_t *) portp->uartp)->startrxtx)
+#define	stl_disableintrs	(* ((uart_t *) portp->uartp)->disableintrs)
+#define	stl_sendbreak		(* ((uart_t *) portp->uartp)->sendbreak)
+#define	stl_flowctrl		(* ((uart_t *) portp->uartp)->flowctrl)
+#define	stl_flush		(* ((uart_t *) portp->uartp)->flush)
+
+/*****************************************************************************/
+
+/*
+ *	CD1400 UART specific data initialization.
+ */
+static uart_t stl_cd1400uart = {
+	stl_cd1400panelinit,
+	stl_cd1400portinit,
+	stl_cd1400setport,
+	stl_cd1400getsignals,
+	stl_cd1400setsignals,
+	stl_cd1400enablerxtx,
+	stl_cd1400startrxtx,
+	stl_cd1400disableintrs,
+	stl_cd1400sendbreak,
+	stl_cd1400flowctrl,
+	stl_cd1400flush,
+	stl_cd1400intr
+};
+
+/*
+ *	Define the offsets within the register bank of a cd1400 based panel.
+ *	These io address offsets are common to the EasyIO board as well.
+ */
+#define	EREG_ADDR	0
+#define	EREG_DATA	4
+#define	EREG_RXACK	5
+#define	EREG_TXACK	6
+#define	EREG_MDACK	7
+
+#define	EREG_BANKSIZE	8
+
+#define	CD1400_CLK	25000000
+#define	CD1400_CLK8M	20000000
+
+/*
+ *	Define the cd1400 baud rate clocks. These are used when calculating
+ *	what clock and divisor to use for the required baud rate. Also
+ *	define the maximum baud rate allowed, and the default base baud.
+ */
+static int	stl_cd1400clkdivs[] = {
+	CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4
+};
+
+/*****************************************************************************/
+
+/*
+ *	SC26198 UART specific data initization.
+ */
+static uart_t stl_sc26198uart = {
+	stl_sc26198panelinit,
+	stl_sc26198portinit,
+	stl_sc26198setport,
+	stl_sc26198getsignals,
+	stl_sc26198setsignals,
+	stl_sc26198enablerxtx,
+	stl_sc26198startrxtx,
+	stl_sc26198disableintrs,
+	stl_sc26198sendbreak,
+	stl_sc26198flowctrl,
+	stl_sc26198flush,
+	stl_sc26198intr
+};
+
+/*
+ *	Define the offsets within the register bank of a sc26198 based panel.
+ */
+#define	XP_DATA		0
+#define	XP_ADDR		1
+#define	XP_MODID	2
+#define	XP_STATUS	2
+#define	XP_IACK		3
+
+#define	XP_BANKSIZE	4
+
+/*
+ *	Define the sc26198 baud rate table. Offsets within the table
+ *	represent the actual baud rate selector of sc26198 registers.
+ */
+static unsigned int	sc26198_baudtable[] = {
+	50, 75, 150, 200, 300, 450, 600, 900, 1200, 1800, 2400, 3600,
+	4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 115200,
+	230400, 460800, 921600
+};
+
+#define	SC26198_NRBAUDS		(sizeof(sc26198_baudtable) / sizeof(unsigned int))
+
 /*****************************************************************************/
 
 /*
@@ -1066,7 +1224,6 @@
 #endif
 
 	memset(&sio, 0, sizeof(struct serial_struct));
-	sio.type = PORT_CIRRUS;
 	sio.line = portp->portnr;
 	sio.port = portp->ioaddr;
 	sio.flags = portp->flags;
@@ -1074,8 +1231,14 @@
 	sio.close_delay = portp->close_delay;
 	sio.closing_wait = portp->closing_wait;
 	sio.custom_divisor = portp->custom_divisor;
-	sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
 	sio.hub6 = 0;
+	if (portp->uartp == &stl_cd1400uart) {
+		sio.type = PORT_CIRRUS;
+		sio.xmit_fifo_size = CD1400_TXFIFOSIZE;
+	} else {
+		sio.type = PORT_UNKNOWN;
+		sio.xmit_fifo_size = SC26198_TXFIFOSIZE;
+	}
 
 	brdp = stl_brds[portp->brdnr];
 	if (brdp != (stlbrd_t *) NULL)
@@ -1122,6 +1285,7 @@
 static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	stlport_t	*portp;
+	unsigned int	ival;
 	int		rc;
 
 #if DEBUG
@@ -1134,6 +1298,12 @@
 	if (portp == (stlport_t *) NULL)
 		return(-ENODEV);
 
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ 			(cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return(-EIO);
+	}
+
 	rc = 0;
 
 	switch (cmd) {
@@ -1156,37 +1326,32 @@
 		break;
 	case TIOCSSOFTCAR:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int	val;
-			get_user(val, (unsigned int *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (val ? CLOCAL : 0);
+			get_user(ival, (unsigned int *) arg);
+			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
 		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int	val;
-			val = stl_getsignals(portp);
-			put_user(val, (unsigned int *) arg);
+			ival = stl_getsignals(portp);
+			put_user(ival, (unsigned int *) arg);
 		}
 		break;
 	case TIOCMBIS:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int	val;
-			get_user(val, (unsigned int *) arg);
-			stl_setsignals(portp, ((val & TIOCM_DTR) ? 1 : -1), ((val & TIOCM_RTS) ? 1 : -1));
+			get_user(ival, (unsigned int *) arg);
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1));
 		}
 		break;
 	case TIOCMBIC:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int	val;
-			get_user(val, (unsigned int *) arg);
-			stl_setsignals(portp, ((val & TIOCM_DTR) ? 0 : -1), ((val & TIOCM_RTS) ? 0 : -1));
+			get_user(ival, (unsigned int *) arg);
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1));
 		}
 		break;
 	case TIOCMSET:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
-			unsigned int	val;
-			get_user(val, (unsigned int *) arg);
-			stl_setsignals(portp, ((val & TIOCM_DTR) ? 1 : 0), ((val & TIOCM_RTS) ? 1 : 0));
+			get_user(ival, (unsigned int *) arg);
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0));
 		}
 		break;
 	case TIOCGSERIAL:
@@ -1261,7 +1426,6 @@
 static void stl_throttle(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_throttle(tty=%x)\n", (int) tty);
@@ -1272,24 +1436,7 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (tty->termios->c_iflag & IXOFF) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_SENDSCHR2);
-		portp->stats.rxxoff++;
-		stl_ccrwait(portp);
-	}
-	if (tty->termios->c_cflag & CRTSCTS) {
-		stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0));
-		stl_setreg(portp, MSVR2, 0);
-		portp->stats.rxrtsoff++;
-	}
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+	stl_flowctrl(portp, 0);
 }
 
 /*****************************************************************************/
@@ -1301,7 +1448,6 @@
 static void stl_unthrottle(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_unthrottle(tty=%x)\n", (int) tty);
@@ -1312,30 +1458,7 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (tty->termios->c_iflag & IXOFF) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_SENDSCHR1);
-		portp->stats.rxxon++;
-		stl_ccrwait(portp);
-	}
-/*
- *	Question: should we return RTS to what it was before? It may have
- *	been set by an ioctl... Suppose not, since if you have hardware
- *	flow control set then it is pretty silly to go and set the RTS line
- *	by hand.
- */
-	if (tty->termios->c_cflag & CRTSCTS) {
-		stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
-		stl_setreg(portp, MSVR2, MSVR2_RTS);
-		portp->stats.rxrtson++;
-	}
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+	stl_flowctrl(portp, 1);
 }
 
 /*****************************************************************************/
@@ -1358,7 +1481,6 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
 	stl_startrxtx(portp, -1, 0);
 }
 
@@ -1381,7 +1503,6 @@
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
 		return;
-
 	stl_startrxtx(portp, -1, 1);
 }
 
@@ -1433,7 +1554,6 @@
 static void stl_flushbuffer(struct tty_struct *tty)
 {
 	stlport_t	*portp;
-	unsigned long	flags;
 
 #if DEBUG
 	printk("stl_flushbuffer(tty=%x)\n", (int) tty);
@@ -1445,17 +1565,7 @@
 	if (portp == (stlport_t *) NULL)
 		return;
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_ccrwait(portp);
-	stl_setreg(portp, CCR, CCR_TXFLUSHFIFO);
-	stl_ccrwait(portp);
-	portp->tx.tail = portp->tx.head;
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
-
+	stl_flush(portp);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
@@ -1464,646 +1574,1308 @@
 /*****************************************************************************/
 
 /*
- *	These functions get/set/update the registers of the cd1400 UARTs.
- *	Access to the cd1400 registers is via an address/data io port pair.
- *	(Maybe should make this inline...)
+ *	All board interrupts are vectored through here first. This code then
+ *	calls off to the approrpriate board interrupt handlers.
  */
 
-static int stl_getreg(stlport_t *portp, int regnr)
+static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	return(inb(portp->ioaddr + EREG_DATA));
-}
+	stlbrd_t	*brdp;
+	int		i;
 
-static void stl_setreg(stlport_t *portp, int regnr, int value)
-{
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	outb(value, portp->ioaddr + EREG_DATA);
+#if DEBUG
+	printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+#endif
+
+	for (i = 0; (i < stl_nrbrds); i++) {
+		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
+			continue;
+		if (brdp->state == 0)
+			continue;
+		(* brdp->isr)(brdp);
+	}
 }
 
-static int stl_updatereg(stlport_t *portp, int regnr, int value)
+/*****************************************************************************/
+
+/*
+ *	Interrupt service routine for EasyIO board types.
+ */
+
+static void stl_eiointr(stlbrd_t *brdp)
 {
-	outb((regnr + portp->uartaddr), portp->ioaddr);
-	if (inb(portp->ioaddr + EREG_DATA) != value) {
-		outb(value, portp->ioaddr + EREG_DATA);
-		return(1);
-	}
-	return(0);
+	stlpanel_t	*panelp;
+	unsigned int	iobase;
+
+	panelp = brdp->panels[0];
+	iobase = panelp->iobase;
+	while (inb(brdp->iostatus) & EIO_INTRPEND)
+		(* panelp->isr)(panelp, iobase);
 }
 
 /*****************************************************************************/
 
 /*
- *	Transmit interrupt handler. This has gotta be fast!  Handling TX
- *	chars is pretty simple, stuff as many as possible from the TX buffer
- *	into the cd1400 FIFO. Must also handle TX breaks here, since they
- *	are embedded as commands in the data stream. Oh no, had to use a goto!
- *	This could be optimized more, will do when I get time...
- *	In practice it is possible that interrupts are enabled but that the
- *	port has been hung up. Need to handle not having any TX buffer here,
- *	this is done by using the side effect that head and tail will also
- *	be NULL if the buffer has been freed.
+ *	Interrupt service routine for ECH-AT board types.
  */
 
-static inline void stl_txisr(stlpanel_t *panelp, int ioaddr)
+static void stl_echatintr(stlbrd_t *brdp)
 {
-	stlport_t	*portp;
-	int		len, stlen;
-	char		*head, *tail;
-	unsigned char	ioack, srer;
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
 
-#if DEBUG
-	printk("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
-#endif
+	outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
 
-	ioack = inb(ioaddr + EREG_TXACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
-		printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
-		return;
+	while (inb(brdp->iostatus) & ECH_INTRPEND) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+			}
+		}
 	}
-	portp = panelp->ports[(ioack >> 3)];
+
+	outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+}
+
+/*****************************************************************************/
 
 /*
- *	Unfortunately we need to handle breaks in the data stream, since
- *	this is the only way to generate them on the cd1400. Do it now if
- *	a break is to be sent.
+ *	Interrupt service routine for ECH-MCA board types.
  */
-	if (portp->brklen != 0) {
-		if (portp->brklen > 0) {
-			outb((TDR + portp->uartaddr), ioaddr);
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_DELAY, (ioaddr + EREG_DATA));
-			outb(portp->brklen, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
-			portp->brklen = -1;
-			goto stl_txalldone;
-		} else {
-			outb((COR2 + portp->uartaddr), ioaddr);
-			outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
-			portp->brklen = 0;
+
+static void stl_echmcaintr(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
+
+	while (inb(brdp->iostatus) & ECH_INTRPEND) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+			}
 		}
 	}
+}
 
-	head = portp->tx.head;
-	tail = portp->tx.tail;
-	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
-		set_bit(ASYI_TXLOW, &portp->istate);
-		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
-	}
+/*****************************************************************************/
 
-	if (len == 0) {
-		outb((SRER + portp->uartaddr), ioaddr);
-		srer = inb(ioaddr + EREG_DATA);
-		if (srer & SRER_TXDATA) {
-			srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
-		} else {
-			srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
-			clear_bit(ASYI_TXBUSY, &portp->istate);
-		}
-		outb(srer, (ioaddr + EREG_DATA));
-	} else {
-		len = MIN(len, CD1400_TXFIFOSIZE);
-		portp->stats.txtotal += len;
-		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
-		outb((TDR + portp->uartaddr), ioaddr);
-		outsb((ioaddr + EREG_DATA), tail, stlen);
-		len -= stlen;
-		tail += stlen;
-		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
-			tail = portp->tx.buf;
-		if (len > 0) {
-			outsb((ioaddr + EREG_DATA), tail, len);
-			tail += len;
+/*
+ *	Interrupt service routine for ECH-PCI board types.
+ */
+
+static void stl_echpciintr(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr, recheck;
+
+	while (1) {
+		recheck = 0;
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			outb(brdp->bnkpageaddr[bnknr], brdp->ioctrl);
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+				recheck++;
+			}
 		}
-		portp->tx.tail = tail;
+		if (! recheck)
+			break;
 	}
-
-stl_txalldone:
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
 
 /*
- *	Receive character interrupt handler. Determine if we have good chars
- *	or bad chars and then process appropriately. Good chars are easy
- *	just shove the lot into the RX buffer and set all status byte to 0.
- *	If a bad RX char then process as required. This routine needs to be
- *	fast!  In practice it is possible that we get an interrupt on a port
- *	that is closed. This can happen on hangups - since they completely
- *	shutdown a port not in user context. Need to handle this case.
+ *	Service an off-level request for some channel.
  */
 
-static inline void stl_rxisr(stlpanel_t *panelp, int ioaddr)
+static void stl_offintr(void *private)
 {
 	stlport_t		*portp;
 	struct tty_struct	*tty;
-	unsigned int		ioack, len, buflen;
-	unsigned char		status;
-	char			ch;
-	static char		unwanted[CD1400_RXFIFOSIZE];
+	unsigned int		oldsigs;
+
+	portp = private;
 
 #if DEBUG
-	printk("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+	printk("stl_offintr(portp=%x)\n", (int) portp);
 #endif
 
-	ioack = inb(ioaddr + EREG_RXACK);
-	if ((ioack & panelp->ackmask) != 0) {
-		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+	if (portp == (stlport_t *) NULL)
 		return;
-	}
-	portp = panelp->ports[(ioack >> 3)];
 	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
 
-	if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
-		outb((RDCR + portp->uartaddr), ioaddr);
-		len = inb(ioaddr + EREG_DATA);
-		if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
-				((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
-			outb((RDSR + portp->uartaddr), ioaddr);
-			insb((ioaddr + EREG_DATA), &unwanted[0], len);
-			portp->stats.rxlost += len;
-			portp->stats.rxtotal += len;
-		} else {
-			len = MIN(len, buflen);
-			if (len > 0) {
-				outb((RDSR + portp->uartaddr), ioaddr);
-				insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
-				memset(tty->flip.flag_buf_ptr, 0, len);
-				tty->flip.flag_buf_ptr += len;
-				tty->flip.char_buf_ptr += len;
-				tty->flip.count += len;
-				tty_schedule_flip(tty);
-				portp->stats.rxtotal += len;
-			}
-		}
-	} else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
-		outb((RDSR + portp->uartaddr), ioaddr);
-		status = inb(ioaddr + EREG_DATA);
-		ch = inb(ioaddr + EREG_DATA);
-		if (status & ST_PARITY)
-			portp->stats.rxparity++;
-		if (status & ST_FRAMING)
-			portp->stats.rxframing++;
-		if (status & ST_OVERRUN)
-			portp->stats.rxoverrun++;
-		if (status & ST_BREAK)
-			portp->stats.rxbreaks++;
-		if (status & ST_SCHARMASK) {
-			if ((status & ST_SCHARMASK) == ST_SCHAR1)
-				portp->stats.txxon++;
-			if ((status & ST_SCHARMASK) == ST_SCHAR2)
-				portp->stats.txxoff++;
-			goto stl_rxalldone;
-		}
-		if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
-			if (portp->rxmarkmsk & status) {
-				if (status & ST_BREAK) {
-					status = TTY_BREAK;
-#ifndef MODULE
-					if (portp->flags & ASYNC_SAK) {
-						do_SAK(tty);
-						BRDENABLE(portp->brdnr, portp->pagenr);
-					}
-#endif
-				} else if (status & ST_PARITY) {
-					status = TTY_PARITY;
-				} else if (status & ST_FRAMING) {
-					status = TTY_FRAME;
-				} else if(status & ST_OVERRUN) {
-					status = TTY_OVERRUN;
-				} else {
-					status = 0;
-				}
-			} else {
-				status = 0;
-			}
-			if (tty->flip.char_buf_ptr != (char *) NULL) {
-				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
-					*tty->flip.flag_buf_ptr++ = status;
-					*tty->flip.char_buf_ptr++ = ch;
-					tty->flip.count++;
+	if (test_bit(ASYI_TXLOW, &portp->istate)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+	if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
+		clear_bit(ASYI_DCDCHANGE, &portp->istate);
+		oldsigs = portp->sigs;
+		portp->sigs = stl_getsignals(portp);
+		if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+			wake_up_interruptible(&portp->open_wait);
+		if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+			if (portp->flags & ASYNC_CHECK_CD) {
+				if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
+						(portp->flags & ASYNC_CALLOUT_NOHUP))) {
+					tty_hangup(tty);
 				}
-				tty_schedule_flip(tty);
 			}
 		}
-	} else {
-		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
-		return;
 	}
-
-stl_rxalldone:
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
 
 /*
- *	Modem interrupt handler. The is called when the modem signal line
- *	(DCD) has changed state. Leave most of the work to the off-level
- *	processing routine.
+ *	Map in interrupt vector to this driver. Check that we don't
+ *	already have this vector mapped, we might be sharing this
+ *	interrupt across multiple boards.
  */
 
-static inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr)
+static int stl_mapirq(int irq)
 {
-	stlport_t	*portp;
-	unsigned int	ioack;
-	unsigned char	misr;
+	int	rc, i;
 
 #if DEBUG
-	printk("stl_mdmisr(panelp=%x)\n", (int) panelp);
+	printk("stl_mapirq(irq=%d)\n", irq);
 #endif
 
-	ioack = inb(ioaddr + EREG_MDACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
-		printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
-		return;
+	rc = 0;
+	for (i = 0; (i < stl_numintrs); i++) {
+		if (stl_gotintrs[i] == irq)
+			break;
 	}
-	portp = panelp->ports[(ioack >> 3)];
-
-	outb((MISR + portp->uartaddr), ioaddr);
-	misr = inb(ioaddr + EREG_DATA);
-	if (misr & MISR_DCD) {
-		set_bit(ASYI_DCDCHANGE, &portp->istate);
-		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
-		portp->stats.modem++;
+	if (i >= stl_numintrs) {
+		if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
+			printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
+			rc = -ENODEV;
+		} else {
+			stl_gotintrs[stl_numintrs++] = irq;
+		}
 	}
-
-	outb((EOSRR + portp->uartaddr), ioaddr);
-	outb(0, (ioaddr + EREG_DATA));
+	return(rc);
 }
 
 /*****************************************************************************/
 
 /*
- *	Interrupt handler for EIO and ECH boards. This code ain't all that
- *	pretty, but the idea is to make it as fast as possible. This code is
- *	well suited to be assemblerized :-)  We don't use the general purpose
- *	register access functions here, for speed we will go strait to the
- *	io region.
+ *	Initialize all the ports on a panel.
  */
 
-static void stl_intr(int irq, void *dev_id, struct pt_regs *regs)
+static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
 {
-	stlbrd_t	*brdp;
-	stlpanel_t	*panelp;
-	unsigned char	svrtype;
-	int		i, panelnr, iobase;
+	stlport_t	*portp;
+	int		chipmask, i;
 
 #if DEBUG
-	printk("stl_intr(irq=%d,regs=%x)\n", irq, (int) regs);
+	printk("stl_initports(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
 #endif
 
-	panelp = (stlpanel_t *) NULL;
-	for (i = 0; (i < stl_nrbrds); ) {
-		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) {
-			i++;
-			continue;
-		}
-		if (brdp->state == 0) {
-			i++;
-			continue;
-		}
-/*
- *		The following section of code handles the subtle differences
- *		between board types. It is sort of similar, but different
- *		enough to handle each separately.
- */
-		if (brdp->brdtype == BRD_EASYIO) {
-			if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			panelp = brdp->panels[0];
-			iobase = panelp->iobase;
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			if (brdp->nrports > 4) {
-				outb((SVRR + 0x80), iobase);
-				svrtype |= inb(iobase + EREG_DATA);
-			}
-		} else if (brdp->brdtype == BRD_ECH) {
-			if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				iobase = panelp->iobase;
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					iobase += 0x8;
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
-			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else if (brdp->brdtype == BRD_ECHPCI) {
-			iobase = brdp->ioaddr2;
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				outb(panelp->pagenr, brdp->ioctrl);
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					outb((panelp->pagenr + 1), brdp->ioctrl);
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
-			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else if (brdp->brdtype == BRD_ECHMC) {
-			if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) {
-				i++;
-				continue;
-			}
-			for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
-				panelp = brdp->panels[panelnr];
-				iobase = panelp->iobase;
-				if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-					break;
-				if (panelp->nrports > 8) {
-					iobase += 0x8;
-					if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND)
-						break;
-				}
-			}	
-			if (panelnr >= brdp->nrpanels) {
-				i++;
-				continue;
-			}
-			outb(SVRR, iobase);
-			svrtype = inb(iobase + EREG_DATA);
-			outb((SVRR + 0x80), iobase);
-			svrtype |= inb(iobase + EREG_DATA);
-		} else {
-			printk("STALLION: unknown board type=%x\n", brdp->brdtype);
-			i++;
-			continue;
-		}
+	chipmask = stl_panelinit(brdp, panelp);
 
 /*
- *		We have determined what type of service is required for a
- *		port. From here on in the service of a port is the same no
- *		matter what the board type...
- */
-		if (svrtype & SVRR_RX)
-			stl_rxisr(panelp, iobase);
-		if (svrtype & SVRR_TX)
-			stl_txisr(panelp, iobase);
-		if (svrtype & SVRR_MDM)
-			stl_mdmisr(panelp, iobase);
+ *	All UART's are initialized (if found!). Now go through and setup
+ *	each ports data structures.
+ */
+	for (i = 0; (i < panelp->nrports); i++) {
+		portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
+		if (portp == (stlport_t *) NULL) {
+			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
+			break;
+		}
+		memset(portp, 0, sizeof(stlport_t));
 
-		if (brdp->brdtype == BRD_ECH)
-			outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+		portp->magic = STL_PORTMAGIC;
+		portp->portnr = i;
+		portp->brdnr = panelp->brdnr;
+		portp->panelnr = panelp->panelnr;
+		portp->uartp = panelp->uartp;
+		portp->clk = brdp->clk;
+		portp->baud_base = STL_BAUDBASE;
+		portp->close_delay = STL_CLOSEDELAY;
+		portp->closing_wait = 30 * HZ;
+		portp->normaltermios = stl_deftermios;
+		portp->callouttermios = stl_deftermios;
+		portp->tqueue.routine = stl_offintr;
+		portp->tqueue.data = portp;
+		portp->stats.brd = portp->brdnr;
+		portp->stats.panel = portp->panelnr;
+		portp->stats.port = portp->portnr;
+		panelp->ports[i] = portp;
+		stl_portinit(brdp, panelp, portp);
 	}
+
+	return(0);
 }
 
 /*****************************************************************************/
 
 /*
- *	Service an off-level request for some channel.
+ *	Try to find and initialize an EasyIO board.
  */
 
-static void stl_offintr(void *private)
+static int stl_initeio(stlbrd_t *brdp)
 {
-	stlport_t		*portp;
-	struct tty_struct	*tty;
-	unsigned int		oldsigs;
+	stlpanel_t	*panelp;
+	unsigned int	status;
+	int		rc;
 
-	portp = private;
 #if DEBUG
-	printk("stl_offintr(portp=%x)\n", (int) portp);
+	printk("stl_initeio(brdp=%x)\n", (int) brdp);
 #endif
 
-	if (portp == (stlport_t *) NULL)
-		return;
-	tty = portp->tty;
-	if (tty == (struct tty_struct *) NULL)
-		return;
-
-	if (test_bit(ASYI_TXLOW, &portp->istate)) {
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup)(tty);
-		wake_up_interruptible(&tty->write_wait);
+	if (check_region(brdp->ioaddr1, 8)) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+			brdp->brdnr, brdp->ioaddr1);
 	}
-	if (test_bit(ASYI_DCDCHANGE, &portp->istate)) {
-		clear_bit(ASYI_DCDCHANGE, &portp->istate);
-		oldsigs = portp->sigs;
-		portp->sigs = stl_getsignals(portp);
-		if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
-			wake_up_interruptible(&portp->open_wait);
-		if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
-			if (portp->flags & ASYNC_CHECK_CD) {
-				if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
-						(portp->flags & ASYNC_CALLOUT_NOHUP))) {
-					tty_hangup(tty);
-				}
-			}
+ 
+	brdp->ioctrl = brdp->ioaddr1 + 1;
+	brdp->iostatus = brdp->ioaddr1 + 2;
+	brdp->clk = CD1400_CLK;
+	brdp->isr = stl_eiointr;
+
+	status = inb(brdp->iostatus);
+	switch (status & EIO_IDBITMASK) {
+	case EIO_8PORTM:
+		brdp->clk = CD1400_CLK8M;
+		/* fall thru */
+	case EIO_8PORTRS:
+	case EIO_8PORTDI:
+		brdp->nrports = 8;
+		break;
+	case EIO_4PORTRS:
+		brdp->nrports = 4;
+		break;
+	case EIO_MK3:
+		switch (status & EIO_BRDMASK) {
+		case ID_BRD4:
+			brdp->nrports = 4;
+			break;
+		case ID_BRD8:
+			brdp->nrports = 8;
+			break;
+		case ID_BRD16:
+			brdp->nrports = 16;
+			break;
+		default:
+			return(-ENODEV);
 		}
+		brdp->ioctrl++;
+		break;
+	default:
+		return(-ENODEV);
 	}
-}
 
-/*****************************************************************************/
+	request_region(brdp->ioaddr1, 8, "serial(EIO)");
 
 /*
- *	Wait for the command register to be ready. We will poll this,
- *	since it won't usually take too long to be ready.
+ *	Check that the supplied IRQ is good and then use it to setup the
+ *	programmable interrupt bits on EIO board. Also set the edge/level
+ *	triggered interrupt bit.
  */
-
-static void stl_ccrwait(stlport_t *portp)
-{
-	int	i;
-
-	for (i = 0; (i < CCR_MAXWAIT); i++) {
-		if (stl_getreg(portp, CCR) == 0) {
-			return;
-		}
+	if ((brdp->irq < 0) || (brdp->irq > 15) ||
+			(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+		printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+		return(-EINVAL);
 	}
+	outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
 
-	printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+	if (panelp == (stlpanel_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+		return(-ENOMEM);
+	}
+	memset(panelp, 0, sizeof(stlpanel_t));
+
+	panelp->magic = STL_PANELMAGIC;
+	panelp->brdnr = brdp->brdnr;
+	panelp->panelnr = 0;
+	panelp->nrports = brdp->nrports;
+	panelp->iobase = brdp->ioaddr1;
+	panelp->hwid = status;
+	if ((status & EIO_IDBITMASK) == EIO_MK3) {
+		panelp->uartp = (void *) &stl_sc26198uart;
+		panelp->isr = stl_sc26198intr;
+	} else {
+		panelp->uartp = (void *) &stl_cd1400uart;
+		panelp->isr = stl_cd1400intr;
+	}
+
+	brdp->panels[0] = panelp;
+	brdp->nrpanels = 1;
+	brdp->state |= BRD_FOUND;
+	brdp->hwid = status;
+	rc = stl_mapirq(brdp->irq);
+	return(rc);
 }
 
 /*****************************************************************************/
 
 /*
- *	Set up the cd1400 registers for a port based on the termios port
- *	settings.
+ *	Try to find an ECH board and initialize it. This code is capable of
+ *	dealing with all types of ECH board.
  */
 
-static void stl_setport(stlport_t *portp, struct termios *tiosp)
+static int stl_initech(stlbrd_t *brdp)
 {
-	stlbrd_t	*brdp;
-	unsigned long	flags;
-	unsigned int	clkdiv, baudrate;
-	unsigned char	cor1, cor2, cor3;
-	unsigned char	cor4, cor5, ccr;
-	unsigned char	srer, sreron, sreroff;
-	unsigned char	mcor1, mcor2, rtpr;
-	unsigned char	clk, div;
+	stlpanel_t	*panelp;
+	unsigned int	status, nxtid, ioaddr, conflict;
+	int		panelnr, banknr, i;
 
-	cor1 = 0;
-	cor2 = 0;
-	cor3 = 0;
-	cor4 = 0;
-	cor5 = 0;
-	ccr = 0;
-	rtpr = 0;
-	clk = 0;
-	div = 0;
-	mcor1 = 0;
-	mcor2 = 0;
-	sreron = 0;
-	sreroff = 0;
+#if DEBUG
+	printk("stl_initech(brdp=%x)\n", (int) brdp);
+#endif
 
-	brdp = stl_brds[portp->brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return;
+	status = 0;
+	conflict = 0;
 
 /*
- *	Set up the RX char ignore mask with those RX error types we
- *	can ignore. We can get the cd1400 to help us out a little here,
- *	it will ignore parity errors and breaks for us.
+ *	Set up the initial board register contents for boards. This varies a
+ *	bit between the different board types. So we need to handle each
+ *	separately. Also do a check that the supplied IRQ is good.
  */
-	portp->rxignoremsk = 0;
-	if (tiosp->c_iflag & IGNPAR) {
-		portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
-		cor1 |= COR1_PARIGNORE;
-	}
-	if (tiosp->c_iflag & IGNBRK) {
-		portp->rxignoremsk |= ST_BREAK;
-		cor4 |= COR4_IGNBRK;
-	}
+	if (brdp->brdtype == BRD_ECH) {
+		brdp->isr = stl_echatintr;
+		brdp->ioctrl = brdp->ioaddr1 + 1;
+		brdp->iostatus = brdp->ioaddr1 + 1;
+		status = inb(brdp->iostatus);
+		if ((status & ECH_IDBITMASK) != ECH_ID)
+			return(-ENODEV);
 
-	portp->rxmarkmsk = ST_OVERRUN;
-	if (tiosp->c_iflag & (INPCK | PARMRK))
-		portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
-	if (tiosp->c_iflag & BRKINT)
-		portp->rxmarkmsk |= ST_BREAK;
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
+		status |= (stl_vecmap[brdp->irq] << 1);
+		outb((status | ECH_BRDRESET), brdp->ioaddr1);
+		brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+		outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+		conflict = check_region(brdp->ioaddr1, 2) ? brdp->ioaddr1 : 0;
+		if (conflict == 0)
+			conflict = check_region(brdp->ioaddr2, 32) ? brdp->ioaddr2 : 0;
+		request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
+		request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
+		outb(status, brdp->ioaddr1);
+	} else if (brdp->brdtype == BRD_ECHMC) {
+		brdp->isr = stl_echmcaintr;
+		brdp->ioctrl = brdp->ioaddr1 + 0x20;
+		brdp->iostatus = brdp->ioctrl;
+		status = inb(brdp->iostatus);
+		if ((status & ECH_IDBITMASK) != ECH_ID)
+			return(-ENODEV);
 
-/*
- *	Go through the char size, parity and stop bits and set all the
- *	option register appropriately.
- */
-	switch (tiosp->c_cflag & CSIZE) {
-	case CS5:
-		cor1 |= COR1_CHL5;
-		break;
-	case CS6:
-		cor1 |= COR1_CHL6;
-		break;
-	case CS7:
-		cor1 |= COR1_CHL7;
-		break;
-	default:
-		cor1 |= COR1_CHL8;
-		break;
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		outb(ECHMC_BRDRESET, brdp->ioctrl);
+		outb(ECHMC_INTENABLE, brdp->ioctrl);
+		conflict = check_region(brdp->ioaddr1, 64) ? brdp->ioaddr1 : 0;
+		request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
+	} else if (brdp->brdtype == BRD_ECHPCI) {
+		brdp->isr = stl_echpciintr;
+		brdp->ioctrl = brdp->ioaddr1 + 2;
+		conflict = check_region(brdp->ioaddr1, 4) ? brdp->ioaddr1 : 0;
+		if (conflict == 0)
+			conflict = check_region(brdp->ioaddr2, 8) ? brdp->ioaddr2 : 0;
+		request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
+		request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
 	}
 
-	if (tiosp->c_cflag & CSTOPB)
-		cor1 |= COR1_STOP2;
-	else
-		cor1 |= COR1_STOP1;
-
-	if (tiosp->c_cflag & PARENB) {
-		if (tiosp->c_cflag & PARODD)
-			cor1 |= (COR1_PARENB | COR1_PARODD);
-		else
-			cor1 |= (COR1_PARENB | COR1_PAREVEN);
-	} else {
-		cor1 |= COR1_PARNONE;
+	if (conflict) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
+			brdp->brdnr, conflict);
 	}
 
-/*
- *	Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
- *	space for hardware flow control and the like. This should be set to
- *	VMIN. Also here we will set the RX data timeout to 10ms - this should
- *	really be based on VTIME.
- */
-	cor3 |= FIFO_RXTHRESHOLD;
-	rtpr = 2;
+	brdp->clk = CD1400_CLK;
+	brdp->hwid = status;
 
 /*
- *	Calculate the baud rate timers. For now we will just assume that
- *	the input and output baud are the same. Could have used a baud
- *	table here, but this way we can generate virtually any baud rate
- *	we like!
+ *	Scan through the secondary io address space looking for panels.
+ *	As we find'em allocate and initialize panel structures for each.
  */
-	baudrate = tiosp->c_cflag & CBAUD;
-	if (baudrate & CBAUDEX) {
-		baudrate &= ~CBAUDEX;
-		if ((baudrate < 1) || (baudrate > 2))
-			tiosp->c_cflag &= ~CBAUDEX;
-		else
-			baudrate += 15;
-	}
-	baudrate = stl_baudrates[baudrate];
-	if ((tiosp->c_cflag & CBAUD) == B38400) {
-		if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			baudrate = 57600;
-		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			baudrate = 115200;
-		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-			baudrate = (portp->baud_base / portp->custom_divisor);
-	}
-	if (baudrate > STL_MAXBAUD)
-		baudrate = STL_MAXBAUD;
+	ioaddr = brdp->ioaddr2;
+	banknr = 0;
+	panelnr = 0;
+	nxtid = 0;
 
-	if (baudrate > 0) {
-		for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
-			clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / baudrate);
-			if (clkdiv < 0x100)
-				break;
+	for (i = 0; (i < STL_MAXPANELS); i++) {
+		if (brdp->brdtype == BRD_ECHPCI) {
+			outb(nxtid, brdp->ioctrl);
+			ioaddr = brdp->ioaddr2;
+		}
+		status = inb(ioaddr + ECH_PNLSTATUS);
+		if ((status & ECH_PNLIDMASK) != nxtid)
+			break;
+		panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
+		if (panelp == (stlpanel_t *) NULL) {
+			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+			break;
+		}
+		memset(panelp, 0, sizeof(stlpanel_t));
+		panelp->magic = STL_PANELMAGIC;
+		panelp->brdnr = brdp->brdnr;
+		panelp->panelnr = panelnr;
+		panelp->iobase = ioaddr;
+		panelp->pagenr = nxtid;
+		panelp->hwid = status;
+		brdp->bnk2panel[banknr] = panelp;
+		brdp->bnkpageaddr[banknr] = nxtid;
+		brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+
+		if (status & ECH_PNLXPID) {
+			panelp->uartp = (void *) &stl_sc26198uart;
+			panelp->isr = stl_sc26198intr;
+			if (status & ECH_PNL16PORT) {
+				panelp->nrports = 16;
+				brdp->bnk2panel[banknr] = panelp;
+				brdp->bnkpageaddr[banknr] = nxtid;
+				brdp->bnkstataddr[banknr++] = ioaddr + 4 + ECH_PNLSTATUS;
+			} else {
+				panelp->nrports = 8;
+			}
+		} else {
+			panelp->uartp = (void *) &stl_cd1400uart;
+			panelp->isr = stl_cd1400intr;
+			if (status & ECH_PNL16PORT) {
+				if ((brdp->nrports + 16) > 32)
+					break;
+				panelp->nrports = 16;
+				panelp->ackmask = 0x80;
+				if (brdp->brdtype != BRD_ECHPCI)
+					ioaddr += EREG_BANKSIZE;
+				brdp->bnk2panel[banknr] = panelp;
+				brdp->bnkpageaddr[banknr] = ++nxtid;
+				brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+			} else {
+				panelp->nrports = 8;
+				panelp->ackmask = 0xc0;
+			}
 		}
-		div = (unsigned char) clkdiv;
-	}
 
-/*
- *	Check what form of modem signaling is required and set it up.
- */
-	if ((tiosp->c_cflag & CLOCAL) == 0) {
-		mcor1 |= MCOR1_DCD;
-		mcor2 |= MCOR2_DCD;
-		sreron |= SRER_MODEM;
-		portp->flags |= ASYNC_CHECK_CD;
-	} else {
-		portp->flags &= ~ASYNC_CHECK_CD;
+		nxtid++;
+		ioaddr += EREG_BANKSIZE;
+		brdp->nrports += panelp->nrports;
+		brdp->panels[panelnr++] = panelp;
+		if (ioaddr >= (brdp->ioaddr2 + 0x20))
+			break;
 	}
 
+	brdp->nrpanels = panelnr;
+	brdp->nrbnks = banknr;
+	if (brdp->brdtype == BRD_ECH)
+		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+
+	brdp->state |= BRD_FOUND;
+	i = stl_mapirq(brdp->irq);
+	return(i);
+}
+
+/*****************************************************************************/
+
 /*
- *	Setup cd1400 enhanced modes if we can. In particular we want to
- *	handle as much of the flow control as possible automatically. As
- *	well as saving a few CPU cycles it will also greatly improve flow
- *	control reliability.
+ *	Initialize and configure the specified board.
+ *	Scan through all the boards in the configuration and see what we
+ *	can find. Handle EIO and the ECH boards a little differently here
+ *	since the initial search and setup is very different.
  */
-	if (tiosp->c_iflag & IXON) {
-		cor2 |= COR2_TXIBE;
-		cor3 |= COR3_SCD12;
-		if (tiosp->c_iflag & IXANY)
-			cor2 |= COR2_IXM;
-	}
+
+static int stl_brdinit(stlbrd_t *brdp)
+{
+	int	i;
+
+#if DEBUG
+	printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+	switch (brdp->brdtype) {
+	case BRD_EASYIO:
+		stl_initeio(brdp);
+		break;
+	case BRD_ECH:
+	case BRD_ECHMC:
+	case BRD_ECHPCI:
+		stl_initech(brdp);
+		break;
+	default:
+		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+		return(ENODEV);
+	}
+
+	stl_brds[brdp->brdnr] = brdp;
+	if ((brdp->state & BRD_FOUND) == 0) {
+		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
+		return(ENODEV);
+	}
+
+	for (i = 0; (i < STL_MAXPANELS); i++)
+		if (brdp->panels[i] != (stlpanel_t *) NULL)
+			stl_initports(brdp, brdp->panels[i]);
+
+	printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Find any ECH-PCI boards that might be installed. Initialize each
+ *	one as it is found.
+ */
+
+#ifdef	CONFIG_PCI
+
+static int stl_findpcibrds()
+{
+	stlbrd_t	*brdp;
+	unsigned char	busnr, devnr, irq;
+	unsigned short	class;
+	unsigned int	ioaddr;
+	int		i, rc;
+
+#if DEBUG
+	printk("stl_findpcibrds()\n");
+#endif
+
+	if (pcibios_present()) {
+		for (i = 0; (i < STL_MAXBRDS); i++) {
+			if (pcibios_find_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, i, &busnr, &devnr))
+				if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
+					break;
+
+/*
+ *			Found a device on the PCI bus that has our vendor and
+ *			device ID. Need to check now that it is really us.
+ */
+			if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
+				printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
+				continue;
+			}
+			if (class == PCI_CLASS_STORAGE_IDE)
+				continue;
+
+			if (stl_nrbrds >= STL_MAXBRDS) {
+				printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
+				break;
+			}
+
+/*
+ *			We have a Stallion board. Allocate a board structure
+ *			and initialize it. Read its IO and IRQ resources
+ *			from conf space.
+ */
+			brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+			if (brdp == (stlbrd_t *) NULL) {
+				printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+				return(-ENOMEM);
+			}
+			memset(brdp, 0, sizeof(stlbrd_t));
+			brdp->magic = STL_BOARDMAGIC;
+			brdp->brdnr = stl_nrbrds++;
+			brdp->brdtype = BRD_ECHPCI;
+
+			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
+				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+				continue;
+			}
+			brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+
+			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
+				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+				continue;
+			}
+			brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+#if DEBUG
+			printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+#endif
+
+			if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
+				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+				continue;
+			}
+			brdp->irq = irq;
+
+			stl_brdinit(brdp);
+		}
+	}
+
+	return(0);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/*
+ *	Scan through all the boards in the configuration and see what we
+ *	can find. Handle EIO and the ECH boards a little differently here
+ *	since the initial search and setup is too different.
+ */
+
+static int stl_initbrds()
+{
+	stlbrd_t	*brdp;
+	stlconf_t	*confp;
+	int		i;
+
+#if DEBUG
+	printk("stl_initbrds()\n");
+#endif
+
+	if (stl_nrbrds > STL_MAXBRDS) {
+		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+		stl_nrbrds = STL_MAXBRDS;
+	}
+
+/*
+ *	Firstly scan the list of static boards configured. Allocate
+ *	resources and initialize the boards as found.
+ */
+	for (i = 0; (i < stl_nrbrds); i++) {
+		confp = &stl_brdconf[i];
+		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+		if (brdp == (stlbrd_t *) NULL) {
+			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+			return(-ENOMEM);
+		}
+		memset(brdp, 0, sizeof(stlbrd_t));
+
+		brdp->magic = STL_BOARDMAGIC;
+		brdp->brdnr = i;
+		brdp->brdtype = confp->brdtype;
+		brdp->ioaddr1 = confp->ioaddr1;
+		brdp->ioaddr2 = confp->ioaddr2;
+		brdp->irq = confp->irq;
+		brdp->irqtype = confp->irqtype;
+		stl_brdinit(brdp);
+	}
+
+#ifdef CONFIG_PCI
+/*
+ *	If the PCI BIOS support is compiled in then let's go looking for
+ *	ECH-PCI boards.
+ */
+	stl_findpcibrds();
+#endif
+
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the board stats structure to user app.
+ */
+
+static int stl_getbrdstats(combrd_t *bp)
+{
+	stlbrd_t	*brdp;
+	stlpanel_t	*panelp;
+	int		i;
+
+	copy_from_user(&stl_brdstats, bp, sizeof(combrd_t));
+	if (stl_brdstats.brd >= STL_MAXBRDS)
+		return(-ENODEV);
+	brdp = stl_brds[stl_brdstats.brd];
+	if (brdp == (stlbrd_t *) NULL)
+		return(-ENODEV);
+
+	memset(&stl_brdstats, 0, sizeof(combrd_t));
+	stl_brdstats.brd = brdp->brdnr;
+	stl_brdstats.type = brdp->brdtype;
+	stl_brdstats.hwid = brdp->hwid;
+	stl_brdstats.state = brdp->state;
+	stl_brdstats.ioaddr = brdp->ioaddr1;
+	stl_brdstats.ioaddr2 = brdp->ioaddr2;
+	stl_brdstats.irq = brdp->irq;
+	stl_brdstats.nrpanels = brdp->nrpanels;
+	stl_brdstats.nrports = brdp->nrports;
+	for (i = 0; (i < brdp->nrpanels); i++) {
+		panelp = brdp->panels[i];
+		stl_brdstats.panels[i].panel = i;
+		stl_brdstats.panels[i].hwid = panelp->hwid;
+		stl_brdstats.panels[i].nrports = panelp->nrports;
+	}
+
+	copy_to_user(bp, &stl_brdstats, sizeof(combrd_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Resolve the referenced port number into a port struct pointer.
+ */
+
+static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
+{
+	stlbrd_t	*brdp;
+	stlpanel_t	*panelp;
+
+	if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
+		return((stlport_t *) NULL);
+	brdp = stl_brds[brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return((stlport_t *) NULL);
+	if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
+		return((stlport_t *) NULL);
+	panelp = brdp->panels[panelnr];
+	if (panelp == (stlpanel_t *) NULL)
+		return((stlport_t *) NULL);
+	if ((portnr < 0) || (portnr >= panelp->nrports))
+		return((stlport_t *) NULL);
+	return(panelp->ports[portnr]);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the port stats structure to user app. A NULL port struct
+ *	pointer passed in means that we need to find out from the app
+ *	what port to get stats for (used through board control device).
+ */
+
+static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+{
+	unsigned char	*head, *tail;
+	unsigned long	flags;
+
+	if (portp == (stlport_t *) NULL) {
+		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+		if (portp == (stlport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	portp->stats.state = portp->istate;
+	portp->stats.flags = portp->flags;
+	portp->stats.hwid = portp->hwid;
+
+	portp->stats.ttystate = 0;
+	portp->stats.cflags = 0;
+	portp->stats.iflags = 0;
+	portp->stats.oflags = 0;
+	portp->stats.lflags = 0;
+	portp->stats.rxbuffered = 0;
+
+	save_flags(flags);
+	cli();
+	if (portp->tty != (struct tty_struct *) NULL) {
+		if (portp->tty->driver_data == portp) {
+			portp->stats.ttystate = portp->tty->flags;
+			portp->stats.rxbuffered = portp->tty->flip.count;
+			if (portp->tty->termios != (struct termios *) NULL) {
+				portp->stats.cflags = portp->tty->termios->c_cflag;
+				portp->stats.iflags = portp->tty->termios->c_iflag;
+				portp->stats.oflags = portp->tty->termios->c_oflag;
+				portp->stats.lflags = portp->tty->termios->c_lflag;
+			}
+		}
+	}
+	restore_flags(flags);
+
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+
+	portp->stats.signals = (unsigned long) stl_getsignals(portp);
+
+	copy_to_user(cp, &portp->stats, sizeof(comstats_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Clear the port stats structure. We also return it zeroed out...
+ */
+
+static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+{
+	if (portp == (stlport_t *) NULL) {
+		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+		if (portp == (stlport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	memset(&portp->stats, 0, sizeof(comstats_t));
+	portp->stats.brd = portp->brdnr;
+	portp->stats.panel = portp->panelnr;
+	portp->stats.port = portp->portnr;
+	copy_to_user(cp, &portp->stats, sizeof(comstats_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the entire driver ports structure to a user app.
+ */
+
+static int stl_getportstruct(unsigned long arg)
+{
+	stlport_t	*portp;
+
+	copy_from_user(&stl_dummyport, (void *) arg, sizeof(stlport_t));
+	portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
+		 stl_dummyport.portnr);
+	if (portp == (stlport_t *) NULL)
+		return(-ENODEV);
+	copy_to_user((void *) arg, portp, sizeof(stlport_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the entire driver board structure to a user app.
+ */
+
+static int stl_getbrdstruct(unsigned long arg)
+{
+	stlbrd_t	*brdp;
+
+	copy_from_user(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
+	if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
+		return(-ENODEV);
+	brdp = stl_brds[stl_dummybrd.brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return(-ENODEV);
+	copy_to_user((void *) arg, brdp, sizeof(stlbrd_t));
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	The "staliomem" device is also required to do some special operations
+ *	on the board and/or ports. In this driver it is mostly used for stats
+ *	collection.
+ */
+
+static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	int	brdnr, rc;
+
+#if DEBUG
+	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+#endif
+
+	brdnr = MINOR(ip->i_rdev);
+	if (brdnr >= STL_MAXBRDS)
+		return(-ENODEV);
+	rc = 0;
+
+	switch (cmd) {
+	case COM_GETPORTSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+			rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
+		break;
+	case COM_CLRPORTSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+			rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
+		break;
+	case COM_GETBRDSTATS:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
+			rc = stl_getbrdstats((combrd_t *) arg);
+		break;
+	case COM_READPORT:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
+			rc = stl_getportstruct(arg);
+		break;
+	case COM_READBOARD:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
+			rc = stl_getbrdstruct(arg);
+		break;
+	default:
+		rc = -ENOIOCTLCMD;
+		break;
+	}
+
+	return(rc);
+}
+
+/*****************************************************************************/
+
+int stl_init(void)
+{
+	printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+
+	stl_initbrds();
+
+/*
+ *	Allocate a temporary write buffer.
+ */
+	stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
+	if (stl_tmpwritebuf == (char *) NULL)
+		printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+
+/*
+ *	Set up a character driver for per board stuff. This is mainly used
+ *	to do stats ioctls on the ports.
+ */
+	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
+		printk("STALLION: failed to register serial board device\n");
+
+/*
+ *	Set up the tty driver structure and register us as a driver.
+ *	Also setup the callout tty device.
+ */
+	memset(&stl_serial, 0, sizeof(struct tty_driver));
+	stl_serial.magic = TTY_DRIVER_MAGIC;
+	stl_serial.name = stl_serialname;
+	stl_serial.major = STL_SERIALMAJOR;
+	stl_serial.minor_start = 0;
+	stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
+	stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
+	stl_serial.subtype = STL_DRVTYPSERIAL;
+	stl_serial.init_termios = stl_deftermios;
+	stl_serial.flags = TTY_DRIVER_REAL_RAW;
+	stl_serial.refcount = &stl_refcount;
+	stl_serial.table = stl_ttys;
+	stl_serial.termios = stl_termios;
+	stl_serial.termios_locked = stl_termioslocked;
+	
+	stl_serial.open = stl_open;
+	stl_serial.close = stl_close;
+	stl_serial.write = stl_write;
+	stl_serial.put_char = stl_putchar;
+	stl_serial.flush_chars = stl_flushchars;
+	stl_serial.write_room = stl_writeroom;
+	stl_serial.chars_in_buffer = stl_charsinbuffer;
+	stl_serial.ioctl = stl_ioctl;
+	stl_serial.set_termios = stl_settermios;
+	stl_serial.throttle = stl_throttle;
+	stl_serial.unthrottle = stl_unthrottle;
+	stl_serial.stop = stl_stop;
+	stl_serial.start = stl_start;
+	stl_serial.hangup = stl_hangup;
+	stl_serial.flush_buffer = stl_flushbuffer;
+
+	stl_callout = stl_serial;
+	stl_callout.name = stl_calloutname;
+	stl_callout.major = STL_CALLOUTMAJOR;
+	stl_callout.subtype = STL_DRVTYPCALLOUT;
+
+	if (tty_register_driver(&stl_serial))
+		printk("STALLION: failed to register serial driver\n");
+	if (tty_register_driver(&stl_callout))
+		printk("STALLION: failed to register callout driver\n");
+
+	return(0);
+}
+
+/*****************************************************************************/
+/*                       CD1400 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
+
+/*
+ *	These functions get/set/update the registers of the cd1400 UARTs.
+ *	Access to the cd1400 registers is via an address/data io port pair.
+ *	(Maybe should make this inline...)
+ */
+
+static int stl_cd1400getreg(stlport_t *portp, int regnr)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	return(inb(portp->ioaddr + EREG_DATA));
+}
+
+static void stl_cd1400setreg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	outb(value, portp->ioaddr + EREG_DATA);
+}
+
+static int stl_cd1400updatereg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr + portp->uartaddr), portp->ioaddr);
+	if (inb(portp->ioaddr + EREG_DATA) != value) {
+		outb(value, portp->ioaddr + EREG_DATA);
+		return(1);
+	}
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Inbitialize the UARTs in a panel. We don't care what sort of board
+ *	these ports are on - since the port io registers are almost
+ *	identical when dealing with ports.
+ */
+
+static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
+{
+	unsigned int	gfrcr;
+	int		chipmask, i, j;
+	int		nrchips, uartaddr, ioaddr;
+
+#if DEBUG
+	printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
+#endif
+
+	BRDENABLE(panelp->brdnr, panelp->pagenr);
+
+/*
+ *	Check that each chip is present and started up OK.
+ */
+	chipmask = 0;
+	nrchips = panelp->nrports / CD1400_PORTS;
+	for (i = 0; (i < nrchips); i++) {
+		if (brdp->brdtype == BRD_ECHPCI) {
+			outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
+			ioaddr = panelp->iobase;
+		} else {
+			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
+		}
+		uartaddr = (i & 0x01) ? 0x080 : 0;
+		outb((GFRCR + uartaddr), ioaddr);
+		outb(0, (ioaddr + EREG_DATA));
+		outb((CCR + uartaddr), ioaddr);
+		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
+		outb((GFRCR + uartaddr), ioaddr);
+		for (j = 0; (j < CCR_MAXWAIT); j++) {
+			if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
+				break;
+		}
+		if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
+			printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+			continue;
+		}
+		chipmask |= (0x1 << i);
+		outb((PPR + uartaddr), ioaddr);
+		outb(PPR_SCALAR, (ioaddr + EREG_DATA));
+	}
+
+	BRDDISABLE(panelp->brdnr);
+	return(chipmask);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Initialize hardware specific port registers.
+ */
+
+static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
+{
+#if DEBUG
+	printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n",
+		(int) brdp, (int) panelp, (int) portp);
+#endif
+
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+		return;
+
+	portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
+		(portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
+	portp->uartaddr = (portp->portnr & 0x04) << 5;
+	portp->pagenr = panelp->pagenr + (portp->portnr >> 3);
+
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
+	portp->hwid = stl_cd1400getreg(portp, GFRCR);
+	BRDDISABLE(portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Wait for the command register to be ready. We will poll this,
+ *	since it won't usually take too long to be ready.
+ */
+
+static void stl_cd1400ccrwait(stlport_t *portp)
+{
+	int	i;
+
+	for (i = 0; (i < CCR_MAXWAIT); i++) {
+		if (stl_cd1400getreg(portp, CCR) == 0) {
+			return;
+		}
+	}
+
+	printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Set up the cd1400 registers for a port based on the termios port
+ *	settings.
+ */
+
+static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
+{
+	stlbrd_t	*brdp;
+	unsigned long	flags;
+	unsigned int	clkdiv, baudrate;
+	unsigned char	cor1, cor2, cor3;
+	unsigned char	cor4, cor5, ccr;
+	unsigned char	srer, sreron, sreroff;
+	unsigned char	mcor1, mcor2, rtpr;
+	unsigned char	clk, div;
+
+	cor1 = 0;
+	cor2 = 0;
+	cor3 = 0;
+	cor4 = 0;
+	cor5 = 0;
+	ccr = 0;
+	rtpr = 0;
+	clk = 0;
+	div = 0;
+	mcor1 = 0;
+	mcor2 = 0;
+	sreron = 0;
+	sreroff = 0;
+
+	brdp = stl_brds[portp->brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return;
+
+/*
+ *	Set up the RX char ignore mask with those RX error types we
+ *	can ignore. We can get the cd1400 to help us out a little here,
+ *	it will ignore parity errors and breaks for us.
+ */
+	portp->rxignoremsk = 0;
+	if (tiosp->c_iflag & IGNPAR) {
+		portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN);
+		cor1 |= COR1_PARIGNORE;
+	}
+	if (tiosp->c_iflag & IGNBRK) {
+		portp->rxignoremsk |= ST_BREAK;
+		cor4 |= COR4_IGNBRK;
+	}
+
+	portp->rxmarkmsk = ST_OVERRUN;
+	if (tiosp->c_iflag & (INPCK | PARMRK))
+		portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING);
+	if (tiosp->c_iflag & BRKINT)
+		portp->rxmarkmsk |= ST_BREAK;
+
+/*
+ *	Go through the char size, parity and stop bits and set all the
+ *	option register appropriately.
+ */
+	switch (tiosp->c_cflag & CSIZE) {
+	case CS5:
+		cor1 |= COR1_CHL5;
+		break;
+	case CS6:
+		cor1 |= COR1_CHL6;
+		break;
+	case CS7:
+		cor1 |= COR1_CHL7;
+		break;
+	default:
+		cor1 |= COR1_CHL8;
+		break;
+	}
+
+	if (tiosp->c_cflag & CSTOPB)
+		cor1 |= COR1_STOP2;
+	else
+		cor1 |= COR1_STOP1;
+
+	if (tiosp->c_cflag & PARENB) {
+		if (tiosp->c_cflag & PARODD)
+			cor1 |= (COR1_PARENB | COR1_PARODD);
+		else
+			cor1 |= (COR1_PARENB | COR1_PAREVEN);
+	} else {
+		cor1 |= COR1_PARNONE;
+	}
+
+/*
+ *	Set the RX FIFO threshold at 6 chars. This gives a bit of breathing
+ *	space for hardware flow control and the like. This should be set to
+ *	VMIN. Also here we will set the RX data timeout to 10ms - this should
+ *	really be based on VTIME.
+ */
+	cor3 |= FIFO_RXTHRESHOLD;
+	rtpr = 2;
+
+/*
+ *	Calculate the baud rate timers. For now we will just assume that
+ *	the input and output baud are the same. Could have used a baud
+ *	table here, but this way we can generate virtually any baud rate
+ *	we like!
+ */
+	baudrate = tiosp->c_cflag & CBAUD;
+	if (baudrate & CBAUDEX) {
+		baudrate &= ~CBAUDEX;
+		if ((baudrate < 1) || (baudrate > 2))
+			tiosp->c_cflag &= ~CBAUDEX;
+		else
+			baudrate += 15;
+	}
+	baudrate = stl_baudrates[baudrate];
+	if ((tiosp->c_cflag & CBAUD) == B38400) {
+		if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baudrate = 57600;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baudrate = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+			baudrate = (portp->baud_base / portp->custom_divisor);
+	}
+	if (baudrate > STL_MAXBAUD)
+		baudrate = STL_MAXBAUD;
+
+	if (baudrate > 0) {
+		for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
+			clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / baudrate);
+			if (clkdiv < 0x100)
+				break;
+		}
+		div = (unsigned char) clkdiv;
+	}
+
+/*
+ *	Check what form of modem signaling is required and set it up.
+ */
+	if ((tiosp->c_cflag & CLOCAL) == 0) {
+		mcor1 |= MCOR1_DCD;
+		mcor2 |= MCOR2_DCD;
+		sreron |= SRER_MODEM;
+		portp->flags |= ASYNC_CHECK_CD;
+	} else {
+		portp->flags &= ~ASYNC_CHECK_CD;
+	}
+
+/*
+ *	Setup cd1400 enhanced modes if we can. In particular we want to
+ *	handle as much of the flow control as possible automatically. As
+ *	well as saving a few CPU cycles it will also greatly improve flow
+ *	control reliability.
+ */
+	if (tiosp->c_iflag & IXON) {
+		cor2 |= COR2_TXIBE;
+		cor3 |= COR3_SCD12;
+		if (tiosp->c_iflag & IXANY)
+			cor2 |= COR2_IXM;
+	}
 
 	if (tiosp->c_cflag & CRTSCTS) {
 		cor2 |= COR2_CTSAE;
@@ -2111,89 +2883,332 @@
 	}
 
 /*
- *	All register cd1400 register values calculated so go through and set
+ *	All cd1400 register values calculated so go through and set
  *	them all up.
  */
 
 #if DEBUG
-	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
-	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
-	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
-	printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
-	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
+	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
+	printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
+	srer = stl_cd1400getreg(portp, SRER);
+	stl_cd1400setreg(portp, SRER, 0);
+	if (stl_cd1400updatereg(portp, COR1, cor1))
+		ccr = 1;
+	if (stl_cd1400updatereg(portp, COR2, cor2))
+		ccr = 1;
+	if (stl_cd1400updatereg(portp, COR3, cor3))
+		ccr = 1;
+	if (ccr) {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_CORCHANGE);
+	}
+	stl_cd1400setreg(portp, COR4, cor4);
+	stl_cd1400setreg(portp, COR5, cor5);
+	stl_cd1400setreg(portp, MCOR1, mcor1);
+	stl_cd1400setreg(portp, MCOR2, mcor2);
+	if (baudrate > 0) {
+		stl_cd1400setreg(portp, TCOR, clk);
+		stl_cd1400setreg(portp, TBPR, div);
+		stl_cd1400setreg(portp, RCOR, clk);
+		stl_cd1400setreg(portp, RBPR, div);
+	}
+	stl_cd1400setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
+	stl_cd1400setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
+	stl_cd1400setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
+	stl_cd1400setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
+	stl_cd1400setreg(portp, RTPR, rtpr);
+	mcor1 = stl_cd1400getreg(portp, MSVR1);
+	if (mcor1 & MSVR1_DCD)
+		portp->sigs |= TIOCM_CD;
+	else
+		portp->sigs &= ~TIOCM_CD;
+	stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Set the state of the DTR and RTS signals.
+ */
+
+static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
+{
+	unsigned char	msvr1, msvr2;
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
+
+	msvr1 = 0;
+	msvr2 = 0;
+	if (dtr > 0)
+		msvr1 = MSVR1_DTR;
+	if (rts > 0)
+		msvr2 = MSVR2_RTS;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	if (rts >= 0)
+		stl_cd1400setreg(portp, MSVR2, msvr2);
+	if (dtr >= 0)
+		stl_cd1400setreg(portp, MSVR1, msvr1);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the state of the signals.
+ */
+
+static int stl_cd1400getsignals(stlport_t *portp)
+{
+	unsigned char	msvr1, msvr2;
+	unsigned long	flags;
+	int		sigs;
+
+#if DEBUG
+	printk("stl_cd1400getsignals(portp=%x)\n", (int) portp);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	msvr1 = stl_cd1400getreg(portp, MSVR1);
+	msvr2 = stl_cd1400getreg(portp, MSVR2);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+
+	sigs = 0;
+	sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
+	sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
+	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+	sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
+	sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+	return(sigs);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Enable/Disable the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	ccr;
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+#endif
+	ccr = 0;
+
+	if (tx == 0)
+		ccr |= CCR_TXDISABLE;
+	else if (tx > 0)
+		ccr |= CCR_TXENABLE;
+	if (rx == 0)
+		ccr |= CCR_RXDISABLE;
+	else if (rx > 0)
+		ccr |= CCR_RXENABLE;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400ccrwait(portp);
+	stl_cd1400setreg(portp, CCR, ccr);
+	stl_cd1400ccrwait(portp);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Start/stop the Transmitter and/or Receiver.
+ */
+
+static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	sreron, sreroff;
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
+		rx, tx);
+#endif
+
+	sreron = 0;
+	sreroff = 0;
+	if (tx == 0)
+		sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
+	else if (tx == 1)
+		sreron |= SRER_TXDATA;
+	else if (tx >= 2)
+		sreron |= SRER_TXEMPTY;
+	if (rx == 0)
+		sreroff |= SRER_RXDATA;
+	else if (rx > 0)
+		sreron |= SRER_RXDATA;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
+	BRDDISABLE(portp->brdnr);
+	if (tx > 0)
+		set_bit(ASYI_TXBUSY, &portp->istate);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Disable all interrupts from this port.
+ */
+
+static void stl_cd1400disableintrs(stlport_t *portp)
+{
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp);
+#endif
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, SRER, 0);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+static void stl_cd1400sendbreak(stlport_t *portp, long len)
+{
+	unsigned long	flags;
+
+#if DEBUG
+	printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+#endif
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400setreg(portp, COR2, (stl_cd1400getreg(portp, COR2) | COR2_ETC));
+	stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
+	BRDDISABLE(portp->brdnr);
+	len = len / 5;
+	portp->brklen = (len > 255) ? 255 : len;
+	portp->stats.txbreaks++;
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Take flow control actions...
+ */
+
+static void stl_cd1400flowctrl(stlport_t *portp, int state)
+{
+	struct tty_struct	*tty;
+	unsigned long		flags;
+
+#if DEBUG
+	printk("stl_cd1400flowctrl(portp=%x,state=%x)\n", (int) portp, state);
 #endif
 
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
+
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x3));
-	srer = stl_getreg(portp, SRER);
-	stl_setreg(portp, SRER, 0);
-	if (stl_updatereg(portp, COR1, cor1))
-		ccr = 1;
-	if (stl_updatereg(portp, COR2, cor2))
-		ccr = 1;
-	if (stl_updatereg(portp, COR3, cor3))
-		ccr = 1;
-	if (ccr) {
-		stl_ccrwait(portp);
-		stl_setreg(portp, CCR, CCR_CORCHANGE);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+
+	if (state) {
+		if (tty->termios->c_iflag & IXOFF) {
+			stl_cd1400ccrwait(portp);
+			stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+			portp->stats.rxxon++;
+			stl_cd1400ccrwait(portp);
+		}
+/*
+ *		Question: should we return RTS to what it was before? It may
+ *		have been set by an ioctl... Suppose not, since if you have
+ *		hardware flow control set then it is pretty silly to go and
+ *		set the RTS line by hand.
+ */
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
+			stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
+			portp->stats.rxrtson++;
+		}
+	} else {
+		if (tty->termios->c_iflag & IXOFF) {
+			stl_cd1400ccrwait(portp);
+			stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+			portp->stats.rxxoff++;
+			stl_cd1400ccrwait(portp);
+		}
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) & 0xf0));
+			stl_cd1400setreg(portp, MSVR2, 0);
+			portp->stats.rxrtsoff++;
+		}
 	}
-	stl_setreg(portp, COR4, cor4);
-	stl_setreg(portp, COR5, cor5);
-	stl_setreg(portp, MCOR1, mcor1);
-	stl_setreg(portp, MCOR2, mcor2);
-	if (baudrate > 0) {
-		stl_setreg(portp, TCOR, clk);
-		stl_setreg(portp, TBPR, div);
-		stl_setreg(portp, RCOR, clk);
-		stl_setreg(portp, RBPR, div);
-	}
-	stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]);
-	stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]);
-	stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]);
-	stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]);
-	stl_setreg(portp, RTPR, rtpr);
-	mcor1 = stl_getreg(portp, MSVR1);
-	if (mcor1 & MSVR1_DCD)
-		portp->sigs |= TIOCM_CD;
-	else
-		portp->sigs &= ~TIOCM_CD;
-	stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron));
+
 	BRDDISABLE(portp->brdnr);
 	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
-/*
- *	Set the state of the DTR and RTS signals.
- */
-
-static void stl_setsignals(stlport_t *portp, int dtr, int rts)
+static void stl_cd1400flush(stlport_t *portp)
 {
-	unsigned char	msvr1, msvr2;
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+	printk("stl_cd1400flush(portp=%x)\n", (int) portp);
 #endif
 
-	msvr1 = 0;
-	msvr2 = 0;
-	if (dtr > 0)
-		msvr1 = MSVR1_DTR;
-	if (rts > 0)
-		msvr2 = MSVR2_RTS;
+	if (portp == (stlport_t *) NULL)
+		return;
 
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	if (rts >= 0)
-		stl_setreg(portp, MSVR2, msvr2);
-	if (dtr >= 0)
-		stl_setreg(portp, MSVR1, msvr1);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	stl_cd1400ccrwait(portp);
+	stl_cd1400setreg(portp, CCR, CCR_TXFLUSHFIFO);
+	stl_cd1400ccrwait(portp);
+	portp->tx.tail = portp->tx.head;
 	BRDDISABLE(portp->brdnr);
 	restore_flags(flags);
 }
@@ -2201,982 +3216,1189 @@
 /*****************************************************************************/
 
 /*
- *	Return the state of the signals.
+ *	Interrupt service routine for cd1400 panels.
+ */
+
+static void stl_cd1400intr(stlpanel_t *panelp, unsigned int iobase)
+{
+	unsigned char	svrtype;
+
+#if DEBUG
+	printk("stl_cd1400intr(panelp=%x,iobase=%x)\n", (int) panelp, iobase);
+#endif
+
+	outb(SVRR, iobase);
+	svrtype = inb(iobase + EREG_DATA);
+	outb((SVRR + 0x80), iobase);
+	svrtype |= inb(iobase + EREG_DATA);
+	if (svrtype & SVRR_RX)
+		stl_cd1400rxisr(panelp, iobase);
+	if (svrtype & SVRR_TX)
+		stl_cd1400txisr(panelp, iobase);
+	if (svrtype & SVRR_MDM)
+		stl_cd1400mdmisr(panelp, iobase);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *	chars is pretty simple, stuff as many as possible from the TX buffer
+ *	into the cd1400 FIFO. Must also handle TX breaks here, since they
+ *	are embedded as commands in the data stream. Oh no, had to use a goto!
+ *	This could be optimized more, will do when I get time...
+ *	In practice it is possible that interrupts are enabled but that the
+ *	port has been hung up. Need to handle not having any TX buffer here,
+ *	this is done by using the side effect that head and tail will also
+ *	be NULL if the buffer has been freed.
+ */
+
+static void stl_cd1400txisr(stlpanel_t *panelp, int ioaddr)
+{
+	stlport_t	*portp;
+	int		len, stlen;
+	char		*head, *tail;
+	unsigned char	ioack, srer;
+
+#if DEBUG
+	printk("stl_cd1400txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+	ioack = inb(ioaddr + EREG_TXACK);
+	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+		printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+
+/*
+ *	Unfortunately we need to handle breaks in the data stream, since
+ *	this is the only way to generate them on the cd1400. Do it now if
+ *	a break is to be sent.
+ */
+	if (portp->brklen != 0) {
+		if (portp->brklen > 0) {
+			outb((TDR + portp->uartaddr), ioaddr);
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_DELAY, (ioaddr + EREG_DATA));
+			outb(portp->brklen, (ioaddr + EREG_DATA));
+			outb(ETC_CMD, (ioaddr + EREG_DATA));
+			outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+			portp->brklen = -1;
+			goto stl_txalldone;
+		} else {
+			outb((COR2 + portp->uartaddr), ioaddr);
+			outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
+			portp->brklen = 0;
+		}
+	}
+
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+		set_bit(ASYI_TXLOW, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+	}
+
+	if (len == 0) {
+		outb((SRER + portp->uartaddr), ioaddr);
+		srer = inb(ioaddr + EREG_DATA);
+		if (srer & SRER_TXDATA) {
+			srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY;
+		} else {
+			srer &= ~(SRER_TXDATA | SRER_TXEMPTY);
+			clear_bit(ASYI_TXBUSY, &portp->istate);
+		}
+		outb(srer, (ioaddr + EREG_DATA));
+	} else {
+		len = MIN(len, CD1400_TXFIFOSIZE);
+		portp->stats.txtotal += len;
+		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+		outb((TDR + portp->uartaddr), ioaddr);
+		outsb((ioaddr + EREG_DATA), tail, stlen);
+		len -= stlen;
+		tail += stlen;
+		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+			tail = portp->tx.buf;
+		if (len > 0) {
+			outsb((ioaddr + EREG_DATA), tail, len);
+			tail += len;
+		}
+		portp->tx.tail = tail;
+	}
+
+stl_txalldone:
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *	Receive character interrupt handler. Determine if we have good chars
+ *	or bad chars and then process appropriately. Good chars are easy
+ *	just shove the lot into the RX buffer and set all status byte to 0.
+ *	If a bad RX char then process as required. This routine needs to be
+ *	fast!  In practice it is possible that we get an interrupt on a port
+ *	that is closed. This can happen on hangups - since they completely
+ *	shutdown a port not in user context. Need to handle this case.
+ */
+
+static void stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr)
+{
+	stlport_t		*portp;
+	struct tty_struct	*tty;
+	unsigned int		ioack, len, buflen;
+	unsigned char		status;
+	char			ch;
+
+#if DEBUG
+	printk("stl_cd1400rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr);
+#endif
+
+	ioack = inb(ioaddr + EREG_RXACK);
+	if ((ioack & panelp->ackmask) != 0) {
+		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+	tty = portp->tty;
+
+	if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
+		outb((RDCR + portp->uartaddr), ioaddr);
+		len = inb(ioaddr + EREG_DATA);
+		if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
+				((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			outb((RDSR + portp->uartaddr), ioaddr);
+			insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
+			portp->stats.rxlost += len;
+			portp->stats.rxtotal += len;
+		} else {
+			len = MIN(len, buflen);
+			if (len > 0) {
+				outb((RDSR + portp->uartaddr), ioaddr);
+				insb((ioaddr + EREG_DATA), tty->flip.char_buf_ptr, len);
+				memset(tty->flip.flag_buf_ptr, 0, len);
+				tty->flip.flag_buf_ptr += len;
+				tty->flip.char_buf_ptr += len;
+				tty->flip.count += len;
+				tty_schedule_flip(tty);
+				portp->stats.rxtotal += len;
+			}
+		}
+	} else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) {
+		outb((RDSR + portp->uartaddr), ioaddr);
+		status = inb(ioaddr + EREG_DATA);
+		ch = inb(ioaddr + EREG_DATA);
+		if (status & ST_PARITY)
+			portp->stats.rxparity++;
+		if (status & ST_FRAMING)
+			portp->stats.rxframing++;
+		if (status & ST_OVERRUN)
+			portp->stats.rxoverrun++;
+		if (status & ST_BREAK)
+			portp->stats.rxbreaks++;
+		if (status & ST_SCHARMASK) {
+			if ((status & ST_SCHARMASK) == ST_SCHAR1)
+				portp->stats.txxon++;
+			if ((status & ST_SCHARMASK) == ST_SCHAR2)
+				portp->stats.txxoff++;
+			goto stl_rxalldone;
+		}
+		if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
+			if (portp->rxmarkmsk & status) {
+				if (status & ST_BREAK) {
+					status = TTY_BREAK;
+#ifndef MODULE
+					if (portp->flags & ASYNC_SAK) {
+						do_SAK(tty);
+						BRDENABLE(portp->brdnr, portp->pagenr);
+					}
+#endif
+				} else if (status & ST_PARITY) {
+					status = TTY_PARITY;
+				} else if (status & ST_FRAMING) {
+					status = TTY_FRAME;
+				} else if(status & ST_OVERRUN) {
+					status = TTY_OVERRUN;
+				} else {
+					status = 0;
+				}
+			} else {
+				status = 0;
+			}
+			if (tty->flip.char_buf_ptr != (char *) NULL) {
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					*tty->flip.flag_buf_ptr++ = status;
+					*tty->flip.char_buf_ptr++ = ch;
+					tty->flip.count++;
+				}
+				tty_schedule_flip(tty);
+			}
+		}
+	} else {
+		printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+		return;
+	}
+
+stl_rxalldone:
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
+}
+
+/*****************************************************************************/
+
+/*
+ *	Modem interrupt handler. The is called when the modem signal line
+ *	(DCD) has changed state. Leave most of the work to the off-level
+ *	processing routine.
  */
 
-static int stl_getsignals(stlport_t *portp)
+static void stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr)
 {
-	unsigned char	msvr1, msvr2;
-	unsigned long	flags;
-	int		sigs;
+	stlport_t	*portp;
+	unsigned int	ioack;
+	unsigned char	misr;
 
 #if DEBUG
-	printk("stl_getsignals(portp=%x)\n", (int) portp);
+	printk("stl_cd1400mdmisr(panelp=%x)\n", (int) panelp);
 #endif
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	msvr1 = stl_getreg(portp, MSVR1);
-	msvr2 = stl_getreg(portp, MSVR2);
-	BRDDISABLE(portp->brdnr);
-	sigs = 0;
-	sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
-	sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
-	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
-	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
-	sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
-	sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
-	restore_flags(flags);
-	return(sigs);
+	ioack = inb(ioaddr + EREG_MDACK);
+	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+		printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
+		return;
+	}
+	portp = panelp->ports[(ioack >> 3)];
+
+	outb((MISR + portp->uartaddr), ioaddr);
+	misr = inb(ioaddr + EREG_DATA);
+	if (misr & MISR_DCD) {
+		set_bit(ASYI_DCDCHANGE, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+		portp->stats.modem++;
+	}
+
+	outb((EOSRR + portp->uartaddr), ioaddr);
+	outb(0, (ioaddr + EREG_DATA));
 }
 
 /*****************************************************************************/
+/*                      SC26198 HARDWARE FUNCTIONS                           */
+/*****************************************************************************/
 
 /*
- *	Enable/Disable the Transmitter and/or Receiver.
+ *	These functions get/set/update the registers of the sc26198 UARTs.
+ *	Access to the sc26198 registers is via an address/data io port pair.
+ *	(Maybe should make this inline...)
  */
 
-static void stl_enablerxtx(stlport_t *portp, int rx, int tx)
+static int stl_sc26198getreg(stlport_t *portp, int regnr)
 {
-	unsigned char	ccr;
-	unsigned long	flags;
-
-#if DEBUG
-	printk("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
-#endif
-	ccr = 0;
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	return(inb(portp->ioaddr + XP_DATA));
+}
 
-	if (tx == 0)
-		ccr |= CCR_TXDISABLE;
-	else if (tx > 0)
-		ccr |= CCR_TXENABLE;
-	if (rx == 0)
-		ccr |= CCR_RXDISABLE;
-	else if (rx > 0)
-		ccr |= CCR_RXENABLE;
+static void stl_sc26198setreg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	outb(value, (portp->ioaddr + XP_DATA));
+}
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_ccrwait(portp);
-	stl_setreg(portp, CCR, ccr);
-	stl_ccrwait(portp);
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
+static int stl_sc26198updatereg(stlport_t *portp, int regnr, int value)
+{
+	outb((regnr | portp->uartaddr), (portp->ioaddr + XP_ADDR));
+	if (inb(portp->ioaddr + XP_DATA) != value) {
+		outb(value, (portp->ioaddr + XP_DATA));
+		return(1);
+	}
+	return(0);
 }
 
 /*****************************************************************************/
 
 /*
- *	Start/stop the Transmitter and/or Receiver.
+ *	Functions to get and set the sc26198 global registers.
  */
 
-static void stl_startrxtx(stlport_t *portp, int rx, int tx)
+static int stl_sc26198getglobreg(stlport_t *portp, int regnr)
 {
-	unsigned char	sreron, sreroff;
-	unsigned long	flags;
-
-#if DEBUG
-	printk("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
-#endif
-
-	sreron = 0;
-	sreroff = 0;
-	if (tx == 0)
-		sreroff |= (SRER_TXDATA | SRER_TXEMPTY);
-	else if (tx == 1)
-		sreron |= SRER_TXDATA;
-	else if (tx >= 2)
-		sreron |= SRER_TXEMPTY;
-	if (rx == 0)
-		sreroff |= SRER_RXDATA;
-	else if (rx > 0)
-		sreron |= SRER_RXDATA;
+	outb(regnr, (portp->ioaddr + XP_ADDR));
+	return(inb(portp->ioaddr + XP_DATA));
+}
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron));
-	BRDDISABLE(portp->brdnr);
-	if (tx > 0)
-		set_bit(ASYI_TXBUSY, &portp->istate);
-	restore_flags(flags);
+#if 0
+static void stl_sc26198setglobreg(stlport_t *portp, int regnr, int value)
+{
+	outb(regnr, (portp->ioaddr + XP_ADDR));
+	outb(value, (portp->ioaddr + XP_DATA));
 }
+#endif
 
 /*****************************************************************************/
 
 /*
- *	Disable all interrupts from this port.
+ *	Inbitialize the UARTs in a panel. We don't care what sort of board
+ *	these ports are on - since the port io registers are almost
+ *	identical when dealing with ports.
  */
 
-static void stl_disableintrs(stlport_t *portp)
+static int stl_sc26198panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
 {
-	unsigned long	flags;
+	int	chipmask, i;
+	int	nrchips, ioaddr;
 
 #if DEBUG
-	printk("stl_disableintrs(portp=%x)\n", (int) portp);
+	printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
 #endif
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, SRER, 0);
-	BRDDISABLE(portp->brdnr);
-	restore_flags(flags);
-}
 
-/*****************************************************************************/
+	BRDENABLE(panelp->brdnr, panelp->pagenr);
 
-static void stl_sendbreak(stlport_t *portp, long len)
-{
-	unsigned long	flags;
+/*
+ *	Check that each chip is present and started up OK.
+ */
+	chipmask = 0;
+	nrchips = panelp->nrports / SC26198_PORTS;
+	if (brdp->brdtype == BRD_ECHPCI)
+		outb(panelp->pagenr, brdp->ioctrl);
 
-#if DEBUG
-	printk("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
-#endif
+	for (i = 0; (i < nrchips); i++) {
+		ioaddr = panelp->iobase + (i * 4); 
+		outb(SCCR, (ioaddr + XP_ADDR));
+		outb(CR_RESETALL, (ioaddr + XP_DATA));
+		outb(TSTR, (ioaddr + XP_ADDR));
+		if (inb(ioaddr + XP_DATA) != 0) {
+			printk("STALLION: sc26198 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+			continue;
+		}
+		chipmask |= (0x1 << i);
+		outb(GCCR, (ioaddr + XP_ADDR));
+		outb(GCCR_IVRTYPCHANACK, (ioaddr + XP_DATA));
+		outb(WDTRCR, (ioaddr + XP_ADDR));
+		outb(0xff, (ioaddr + XP_DATA));
+	}
 
-	save_flags(flags);
-	cli();
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC));
-	stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
-	BRDDISABLE(portp->brdnr);
-	len = len / 5;
-	portp->brklen = (len > 255) ? 255 : len;
-	portp->stats.txbreaks++;
-	restore_flags(flags);
+	BRDDISABLE(panelp->brdnr);
+	return(chipmask);
 }
 
 /*****************************************************************************/
 
 /*
- *	Map in interrupt vector to this driver. Check that we don't
- *	already have this vector mapped, we might be sharing this
- *	interrupt across multiple boards.
+ *	Initialize hardware specific port registers.
  */
 
-static int stl_mapirq(int irq)
+static void stl_sc26198portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
 {
-	int	rc, i;
-
 #if DEBUG
-	printk("stl_mapirq(irq=%d)\n", irq);
+	printk("stl_sc26198portinit(brdp=%x,panelp=%x,portp=%x)\n",
+		(int) brdp, (int) panelp, (int) portp);
 #endif
 
-	rc = 0;
-	for (i = 0; (i < stl_numintrs); i++) {
-		if (stl_gotintrs[i] == irq)
-			break;
-	}
-	if (i >= stl_numintrs) {
-		if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
-			printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
-			rc = -ENODEV;
-		} else {
-			stl_gotintrs[stl_numintrs++] = irq;
-		}
-	}
-	return(rc);
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+		return;
+
+	portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
+	portp->uartaddr = (portp->portnr & 0x07) << 4;
+	portp->pagenr = panelp->pagenr;
+	portp->hwid = 0x1;
+
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IOPCR, IOPCR_SETSIGS);
+	BRDDISABLE(portp->brdnr);
 }
 
 /*****************************************************************************/
 
 /*
- *	Try to find and initialize all the ports on a panel. We don't care
- *	what sort of board these ports are on - since the port io registers
- *	are almost identical when dealing with ports.
+ *	Set up the sc26198 registers for a port based on the termios port
+ *	settings.
  */
 
-static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
+static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
 {
-	stlport_t	*portp;
-	unsigned int	chipmask;
-	unsigned int	gfrcr;
-	int		nrchips, uartaddr, ioaddr;
-	int		i, j;
-
-#if DEBUG
-	printk("stl_initports(panelp=%x)\n", (int) panelp);
-#endif
+	stlbrd_t	*brdp;
+	unsigned long	flags;
+	unsigned int	baudrate;
+	unsigned char	mr0, mr1, mr2, clk;
+	unsigned char	imron, imroff, iopr, ipr;
+
+	mr0 = 0;
+	mr1 = 0;
+	mr2 = 0;
+	clk = 0;
+	iopr = 0;
+	imron = 0;
+	imroff = 0;
 
-	BRDENABLE(panelp->brdnr, panelp->pagenr);
+	brdp = stl_brds[portp->brdnr];
+	if (brdp == (stlbrd_t *) NULL)
+		return;
 
 /*
- *	Check that each chip is present and started up OK.
+ *	Set up the RX char ignore mask with those RX error types we
+ *	can ignore.
  */
-	chipmask = 0;
-	nrchips = panelp->nrports / CD1400_PORTS;
-	for (i = 0; (i < nrchips); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb((panelp->pagenr + (i >> 1)), brdp->ioctrl);
-			ioaddr = panelp->iobase;
-		} else {
-			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1));
-		}
-		uartaddr = (i & 0x01) ? 0x080 : 0;
-		outb((GFRCR + uartaddr), ioaddr);
-		outb(0, (ioaddr + EREG_DATA));
-		outb((CCR + uartaddr), ioaddr);
-		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-		outb(CCR_RESETFULL, (ioaddr + EREG_DATA));
-		outb((GFRCR + uartaddr), ioaddr);
-		for (j = 0; (j < CCR_MAXWAIT); j++) {
-			if ((gfrcr = inb(ioaddr + EREG_DATA)) != 0)
-				break;
-		}
-		if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
-			printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
-			continue;
-		}
-		chipmask |= (0x1 << i);
-		outb((PPR + uartaddr), ioaddr);
-		outb(PPR_SCALAR, (ioaddr + EREG_DATA));
-	}
+	portp->rxignoremsk = 0;
+	if (tiosp->c_iflag & IGNPAR)
+		portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN);
+	if (tiosp->c_iflag & IGNBRK)
+		portp->rxignoremsk |= SR_RXBREAK;
+
+	portp->rxmarkmsk = SR_RXOVERRUN;
+	if (tiosp->c_iflag & (INPCK | PARMRK))
+		portp->rxmarkmsk |= (SR_RXPARITY | SR_RXFRAMING);
+	if (tiosp->c_iflag & BRKINT)
+		portp->rxmarkmsk |= SR_RXBREAK;
 
 /*
- *	All cd1400's are initialized (if found!). Now go through and setup
- *	each ports data structures. Also init the LIVR register of cd1400
- *	for each port.
+ *	Go through the char size, parity and stop bits and set all the
+ *	option register appropriately.
  */
-	ioaddr = panelp->iobase;
-	for (i = 0; (i < panelp->nrports); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb((panelp->pagenr + (i >> 3)), brdp->ioctrl);
-			ioaddr = panelp->iobase;
-		} else {
-			ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3));
-		}
-		if ((chipmask & (0x1 << (i / 4))) == 0)
-			continue;
-		portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
-		if (portp == (stlport_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
-			break;
-		}
-		memset(portp, 0, sizeof(stlport_t));
-		portp->magic = STL_PORTMAGIC;
-		portp->portnr = i;
-		portp->brdnr = panelp->brdnr;
-		portp->panelnr = panelp->panelnr;
-		portp->ioaddr = ioaddr;
-		portp->uartaddr = (i & 0x04) << 5;
-		portp->pagenr = panelp->pagenr + (i >> 3);
-		portp->clk = brdp->clk;
-		portp->baud_base = STL_BAUDBASE;
-		portp->close_delay = STL_CLOSEDELAY;
-		portp->closing_wait = 30 * HZ;
-		portp->normaltermios = stl_deftermios;
-		portp->callouttermios = stl_deftermios;
-		portp->tqueue.routine = stl_offintr;
-		portp->tqueue.data = portp;
-		portp->stats.brd = portp->brdnr;
-		portp->stats.panel = portp->panelnr;
-		portp->stats.port = portp->portnr;
-		stl_setreg(portp, CAR, (i & 0x03));
-		stl_setreg(portp, LIVR, (i << 3));
-		portp->hwid = stl_getreg(portp, GFRCR);
-		panelp->ports[i] = portp;
+	switch (tiosp->c_cflag & CSIZE) {
+	case CS5:
+		mr1 |= MR1_CS5;
+		break;
+	case CS6:
+		mr1 |= MR1_CS6;
+		break;
+	case CS7:
+		mr1 |= MR1_CS7;
+		break;
+	default:
+		mr1 |= MR1_CS8;
+		break;
+	}
+
+	if (tiosp->c_cflag & CSTOPB)
+		mr2 |= MR2_STOP2;
+	else
+		mr2 |= MR2_STOP1;
+
+	if (tiosp->c_cflag & PARENB) {
+		if (tiosp->c_cflag & PARODD)
+			mr1 |= (MR1_PARENB | MR1_PARODD);
+		else
+			mr1 |= (MR1_PARENB | MR1_PAREVEN);
+	} else {
+		mr1 |= MR1_PARNONE;
 	}
 
-	BRDDISABLE(panelp->brdnr);
-	return(0);
-}
-
-/*****************************************************************************/
+	mr1 |= MR1_ERRBLOCK;
 
 /*
- *	Try to find and initialize an EasyIO board.
+ *	Set the RX FIFO threshold at 8 chars. This gives a bit of breathing
+ *	space for hardware flow control and the like. This should be set to
+ *	VMIN.
  */
+	mr2 |= MR2_RXFIFOHALF;
 
-static int stl_initeio(stlbrd_t *brdp)
-{
-	stlpanel_t	*panelp;
-	unsigned int	status;
-	int		rc;
-
-#if DEBUG
-	printk("stl_initeio(brdp=%x)\n", (int) brdp);
-#endif
-
-	brdp->ioctrl = brdp->ioaddr1 + 1;
-	brdp->iostatus = brdp->ioaddr1 + 2;
-	brdp->clk = EIO_CLK;
-
-	status = inb(brdp->iostatus);
-	switch (status & EIO_IDBITMASK) {
-	case EIO_8PORTM:
-		brdp->clk = EIO_CLK8M;
-		/* fall thru */
-	case EIO_8PORTRS:
-	case EIO_8PORTDI:
-		brdp->nrports = 8;
-		break;
-	case EIO_4PORTRS:
-		brdp->nrports = 4;
-		break;
-	default:
-		return(-ENODEV);
+/*
+ *	Calculate the baud rate timers. For now we will just assume that
+ *	the input and output baud are the same. The sc26198 has a fixed
+ *	baud rate table, so only discrete baud rates possible.
+ */
+	baudrate = tiosp->c_cflag & CBAUD;
+	if (baudrate & CBAUDEX) {
+		baudrate &= ~CBAUDEX;
+		if ((baudrate < 1) || (baudrate > 5))
+			tiosp->c_cflag &= ~CBAUDEX;
+		else
+			baudrate += 15;
+	}
+	baudrate = stl_baudrates[baudrate];
+	if ((tiosp->c_cflag & CBAUD) == B38400) {
+		if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baudrate = 57600;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baudrate = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+			baudrate = (portp->baud_base / portp->custom_divisor);
 	}
+	if (baudrate > STL_MAXBAUD)
+		baudrate = STL_MAXBAUD;
 
-	request_region(brdp->ioaddr1, 8, "serial(EIO)");
+	if (baudrate > 0) {
+		for (clk = 0; (clk < SC26198_NRBAUDS); clk++) {
+			if (baudrate <= sc26198_baudtable[clk])
+				break;
+		}
+	}
 
 /*
- *	Check that the supplied IRQ is good and then use it to setup the
- *	programmable interrupt bits on EIO board. Also set the edge/level
- *	triggered interrupt bit.
+ *	Check what form of modem signaling is required and set it up.
  */
-	if ((brdp->irq < 0) || (brdp->irq > 15) ||
-			(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-		printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-		return(-EINVAL);
+	if (tiosp->c_cflag & CLOCAL) {
+		portp->flags &= ~ASYNC_CHECK_CD;
+	} else {
+		iopr |= IOPR_DCDCOS;
+		imron |= IR_IOPORT;
+		portp->flags |= ASYNC_CHECK_CD;
 	}
-	outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
 
-	panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
-	if (panelp == (stlpanel_t *) NULL) {
-		printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
-		return(-ENOMEM);
+/*
+ *	Setup sc26198 enhanced modes if we can. In particular we want to
+ *	handle as much of the flow control as possible automatically. As
+ *	well as saving a few CPU cycles it will also greatly improve flow
+ *	control reliability.
+ */
+	if (tiosp->c_iflag & IXON) {
+		mr0 |= MR0_SWFTX | MR0_SWFT;
+		imron |= IR_XONXOFF;
+	} else {
+		imroff |= IR_XONXOFF;
 	}
-	memset(panelp, 0, sizeof(stlpanel_t));
-
-	panelp->magic = STL_PANELMAGIC;
-	panelp->brdnr = brdp->brdnr;
-	panelp->panelnr = 0;
-	panelp->nrports = brdp->nrports;
-	panelp->iobase = brdp->ioaddr1;
-	panelp->hwid = status;
-	brdp->panels[0] = panelp;
-	brdp->nrpanels = 1;
-	brdp->state |= BRD_FOUND;
-	brdp->hwid = status;
-	rc = stl_mapirq(brdp->irq);
-	return(rc);
-}
+	if (tiosp->c_iflag & IXOFF)
+		mr0 |= MR0_SWFRX;
 
-/*****************************************************************************/
+	if (tiosp->c_cflag & CRTSCTS) {
+		mr2 |= MR2_AUTOCTS;
+		mr1 |= MR1_AUTORTS;
+	}
 
 /*
- *	Try to find an ECH board and initialize it. This code is capable of
- *	dealing with all types of ECH board.
+ *	All sc26198 register values calculated so go through and set
+ *	them all up.
  */
 
-static int stl_initech(stlbrd_t *brdp)
-{
-	stlpanel_t	*panelp;
-	unsigned int	status, nxtid;
-	int		panelnr, ioaddr, i;
-
 #if DEBUG
-	printk("stl_initech(brdp=%x)\n", (int) brdp);
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	printk("    mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
+	printk("    iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
 #endif
 
-	status = 0;
-
-/*
- *	Set up the initial board register contents for boards. This varies a
- *	bit between the different board types. So we need to handle each
- *	separately. Also do a check that the supplied IRQ is good.
- */
-	if (brdp->brdtype == BRD_ECH) {
-		brdp->ioctrl = brdp->ioaddr1 + 1;
-		brdp->iostatus = brdp->ioaddr1 + 1;
-		status = inb(brdp->iostatus);
-		if ((status & ECH_IDBITMASK) != ECH_ID)
-			return(-ENODEV);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IMR, 0);
+	stl_sc26198updatereg(portp, MR0, mr0);
+	stl_sc26198updatereg(portp, MR1, mr1);
+	stl_sc26198setreg(portp, CCR, CR_RXERRBLOCK);
+	stl_sc26198updatereg(portp, MR2, mr2);
+	stl_sc26198updatereg(portp, IOPIOR,
+		((stl_sc26198getreg(portp, IOPIOR) & ~IPR_CHANGEMASK) | iopr));
 
-		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-			return(-EINVAL);
-		}
-		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
-		status |= (stl_vecmap[brdp->irq] << 1);
-		outb((status | ECH_BRDRESET), brdp->ioaddr1);
-		brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
-		outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-		outb(status, brdp->ioaddr1);
+	if (baudrate > 0) {
+		stl_sc26198setreg(portp, TXCSR, clk);
+		stl_sc26198setreg(portp, RXCSR, clk);
+	}
 
-		request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
-		request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
-	} else if (brdp->brdtype == BRD_ECHMC) {
-		brdp->ioctrl = brdp->ioaddr1 + 0x20;
-		brdp->iostatus = brdp->ioctrl;
-		status = inb(brdp->iostatus);
-		if ((status & ECH_IDBITMASK) != ECH_ID)
-			return(-ENODEV);
+	stl_sc26198setreg(portp, XONCR, tiosp->c_cc[VSTART]);
+	stl_sc26198setreg(portp, XOFFCR, tiosp->c_cc[VSTOP]);
 
-		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-			return(-EINVAL);
-		}
-		outb(ECHMC_BRDRESET, brdp->ioctrl);
-		outb(ECHMC_INTENABLE, brdp->ioctrl);
+	ipr = stl_sc26198getreg(portp, IPR);
+	if (ipr & MSVR1_DCD)
+		portp->sigs &= ~TIOCM_CD;
+	else
+		portp->sigs |= TIOCM_CD;
 
-		request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
-	} else if (brdp->brdtype == BRD_ECHPCI) {
-		brdp->ioctrl = brdp->ioaddr1 + 2;
-		request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
-		request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
-	}
+	portp->imr = (portp->imr & ~imroff) | imron;
+	stl_sc26198setreg(portp, IMR, portp->imr);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
 
-	brdp->clk = ECH_CLK;
-	brdp->hwid = status;
+/*****************************************************************************/
 
 /*
- *	Scan through the secondary io address space looking for panels.
- *	As we find'em allocate and initialize panel structures for each.
+ *	Set the state of the DTR and RTS signals.
  */
-	ioaddr = brdp->ioaddr2;
-	panelnr = 0;
-	nxtid = 0;
 
-	for (i = 0; (i < STL_MAXPANELS); i++) {
-		if (brdp->brdtype == BRD_ECHPCI) {
-			outb(nxtid, brdp->ioctrl);
-			ioaddr = brdp->ioaddr2;
-		}
-		status = inb(ioaddr + ECH_PNLSTATUS);
-		if ((status & ECH_PNLIDMASK) != nxtid)
-			break;
-		panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
-		if (panelp == (stlpanel_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
-			break;
-		}
-		memset(panelp, 0, sizeof(stlpanel_t));
-		panelp->magic = STL_PANELMAGIC;
-		panelp->brdnr = brdp->brdnr;
-		panelp->panelnr = panelnr;
-		panelp->iobase = ioaddr;
-		panelp->pagenr = nxtid;
-		panelp->hwid = status;
-		if (status & ECH_PNL16PORT) {
-			if ((brdp->nrports + 16) > 32)
-				break;
-			panelp->nrports = 16;
-			panelp->ackmask = 0x80;
-			brdp->nrports += 16;
-			ioaddr += (EREG_BANKSIZE * 2);
-			nxtid += 2;
-		} else {
-			panelp->nrports = 8;
-			panelp->ackmask = 0xc0;
-			brdp->nrports += 8;
-			ioaddr += EREG_BANKSIZE;
-			nxtid++;
-		}
-		brdp->panels[panelnr++] = panelp;
-		brdp->nrpanels++;
-		if (ioaddr >= (brdp->ioaddr2 + 0x20))
-			break;
-	}
+static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts)
+{
+	unsigned char	iopioron, iopioroff;
+	unsigned long	flags;
 
-	if (brdp->brdtype == BRD_ECH)
-		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
+#if DEBUG
+	printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+#endif
 
-	brdp->state |= BRD_FOUND;
-	i = stl_mapirq(brdp->irq);
-	return(i);
+	iopioron = 0;
+	iopioroff = 0;
+	if (dtr == 0)
+		iopioroff |= IPR_DTR;
+	else if (dtr > 0)
+		iopioron |= IPR_DTR;
+	if (rts == 0)
+		iopioroff |= IPR_RTS;
+	else if (rts > 0)
+		iopioron |= IPR_RTS;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IOPIOR,
+		((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Initialize and configure the specified board.
- *	Scan through all the boards in the configuration and see what we
- *	can find. Handle EIO and the ECH boards a little differently here
- *	since the initial search and setup is too different.
+ *	Return the state of the signals.
  */
 
-static int stl_brdinit(stlbrd_t *brdp)
+static int stl_sc26198getsignals(stlport_t *portp)
 {
-	int	i;
+	unsigned char	ipr;
+	unsigned long	flags;
+	int		sigs;
 
 #if DEBUG
-	printk("stl_brdinit(brdp=%x)\n", (int) brdp);
+	printk("stl_sc26198getsignals(portp=%x)\n", (int) portp);
 #endif
 
-	switch (brdp->brdtype) {
-	case BRD_EASYIO:
-		stl_initeio(brdp);
-		break;
-	case BRD_ECH:
-	case BRD_ECHMC:
-	case BRD_ECHPCI:
-		stl_initech(brdp);
-		break;
-	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
-		return(ENODEV);
-	}
-
-	stl_brds[brdp->brdnr] = brdp;
-	if ((brdp->state & BRD_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
-		return(ENODEV);
-	}
-
-	for (i = 0; (i < STL_MAXPANELS); i++)
-		if (brdp->panels[i] != (stlpanel_t *) NULL)
-			stl_initports(brdp, brdp->panels[i]);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	ipr = stl_sc26198getreg(portp, IPR);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 
-	printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
-	return(0);
+	sigs = 0;
+	sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
+	sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
+	sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
+	sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
+	return(sigs);
 }
 
 /*****************************************************************************/
 
 /*
- *	Find any ECH-PCI boards that might be installed. Initialize each
- *	one as it is found.
+ *	Enable/Disable the Transmitter and/or Receiver.
  */
 
-#ifdef	CONFIG_PCI
-
-static int stl_findpcibrds()
+static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx)
 {
-	stlbrd_t	*brdp;
-	unsigned char	busnr, devnr, irq;
-	unsigned short	class;
-	unsigned int	ioaddr;
-	int		i, rc;
+	unsigned char	ccr;
+	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_findpcibrds()\n");
+	printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
 #endif
 
-	if (pcibios_present()) {
-		for (i = 0; (i < STL_MAXBRDS); i++) {
-			if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
-				break;
+	ccr = portp->crenable;
+	if (tx == 0)
+		ccr &= ~CR_TXENABLE;
+	else if (tx > 0)
+		ccr |= CR_TXENABLE;
+	if (rx == 0)
+		ccr &= ~CR_RXENABLE;
+	else if (rx > 0)
+		ccr |= CR_RXENABLE;
 
-/*
- *			Found a device on the PCI bus that has our vendor and
- *			device ID. Need to check now that it is really us.
- */
-			if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
-				printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			if (class == PCI_CLASS_STORAGE_IDE)
-				continue;
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, ccr);
+	BRDDISABLE(portp->brdnr);
+	portp->crenable = ccr;
+	restore_flags(flags);
+}
 
-			if (stl_nrbrds >= STL_MAXBRDS) {
-				printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
-				break;
-			}
+/*****************************************************************************/
 
 /*
- *			We have a Stallion board. Allocate a board structure
- *			and initialize it. Read its IO and IRQ resources
- *			from conf space.
+ *	Start/stop the Transmitter and/or Receiver.
  */
-			brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-			if (brdp == (stlbrd_t *) NULL) {
-				printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
-				return(-ENOMEM);
-			}
-			memset(brdp, 0, sizeof(stlbrd_t));
-			brdp->magic = STL_BOARDMAGIC;
-			brdp->brdnr = stl_nrbrds++;
-			brdp->brdtype = BRD_ECHPCI;
 
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
+static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx)
+{
+	unsigned char	imr;
+	unsigned long	flags;
 
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
 #if DEBUG
-			printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
+	printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
+		rx, tx);
 #endif
 
-			if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->irq = irq;
-
-			stl_brdinit(brdp);
-		}
-	}
+	imr = portp->imr;
+	if (tx == 0)
+		imr &= ~IR_TXRDY;
+	else if (tx == 1)
+		imr |= IR_TXRDY;
+	if (rx == 0)
+		imr &= ~(IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG);
+	else if (rx > 0)
+		imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
 
-	return(0);
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, IMR, imr);
+	BRDDISABLE(portp->brdnr);
+	portp->imr = imr;
+	if (tx > 0)
+		set_bit(ASYI_TXBUSY, &portp->istate);
+	restore_flags(flags);
 }
 
-#endif
-
 /*****************************************************************************/
 
 /*
- *	Scan through all the boards in the configuration and see what we
- *	can find. Handle EIO and the ECH boards a little differently here
- *	since the initial search and setup is too different.
+ *	Disable all interrupts from this port.
  */
 
-static int stl_initbrds()
+static void stl_sc26198disableintrs(stlport_t *portp)
 {
-	stlbrd_t	*brdp;
-	stlconf_t	*confp;
-	int		i;
+	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_initbrds()\n");
+	printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp);
 #endif
 
-	if (stl_nrbrds > STL_MAXBRDS) {
-		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
-		stl_nrbrds = STL_MAXBRDS;
-	}
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	portp->imr = 0;
+	stl_sc26198setreg(portp, IMR, 0);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
 
-/*
- *	Firstly scan the list of static boards configured. Allocate
- *	resources and initialize the boards as found.
- */
-	for (i = 0; (i < stl_nrbrds); i++) {
-		confp = &stl_brdconf[i];
-		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-		if (brdp == (stlbrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
-			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlbrd_t));
+/*****************************************************************************/
 
-		brdp->magic = STL_BOARDMAGIC;
-		brdp->brdnr = i;
-		brdp->brdtype = confp->brdtype;
-		brdp->ioaddr1 = confp->ioaddr1;
-		brdp->ioaddr2 = confp->ioaddr2;
-		brdp->irq = confp->irq;
-		brdp->irqtype = confp->irqtype;
-		stl_brdinit(brdp);
-	}
+static void stl_sc26198sendbreak(stlport_t *portp, long len)
+{
+	unsigned long	flags;
 
-#ifdef CONFIG_PCI
-/*
- *	If the PCI BIOS support is compiled in then let's go looking for
- *	ECH-PCI boards.
- */
-	stl_findpcibrds();
+#if DEBUG
+	printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
 #endif
 
-	return(0);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (len / (1000 / HZ));
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
+	BRDDISABLE(portp->brdnr);
+	portp->stats.txbreaks++;
+
+	schedule();
+
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the board stats structure to user app.
+ *	Take flow control actions...
  */
 
-static int stl_getbrdstats(combrd_t *bp)
+static void stl_sc26198flowctrl(stlport_t *portp, int state)
 {
-	stlbrd_t	*brdp;
-	stlpanel_t	*panelp;
-	int		i;
-
-	copy_from_user(&stl_brdstats, bp, sizeof(combrd_t));
-	if (stl_brdstats.brd >= STL_MAXBRDS)
-		return(-ENODEV);
-	brdp = stl_brds[stl_brdstats.brd];
-	if (brdp == (stlbrd_t *) NULL)
-		return(-ENODEV);
-
-	memset(&stl_brdstats, 0, sizeof(combrd_t));
-	stl_brdstats.brd = brdp->brdnr;
-	stl_brdstats.type = brdp->brdtype;
-	stl_brdstats.hwid = brdp->hwid;
-	stl_brdstats.state = brdp->state;
-	stl_brdstats.ioaddr = brdp->ioaddr1;
-	stl_brdstats.ioaddr2 = brdp->ioaddr2;
-	stl_brdstats.irq = brdp->irq;
-	stl_brdstats.nrpanels = brdp->nrpanels;
-	stl_brdstats.nrports = brdp->nrports;
-	for (i = 0; (i < brdp->nrpanels); i++) {
-		panelp = brdp->panels[i];
-		stl_brdstats.panels[i].panel = i;
-		stl_brdstats.panels[i].hwid = panelp->hwid;
-		stl_brdstats.panels[i].nrports = panelp->nrports;
-	}
+	struct tty_struct	*tty;
+	unsigned long		flags;
+	unsigned char		mr0;
 
-	copy_to_user(bp, &stl_brdstats, sizeof(combrd_t));
-	return(0);
-}
+#if DEBUG
+	printk("stl_sc26198flowctrl(portp=%x,state=%x)\n", (int) portp, state);
+#endif
 
-/*****************************************************************************/
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
 
-/*
- *	Resolve the referenced port number into a port struct pointer.
- */
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
 
-static stlport_t *stl_getport(int brdnr, int panelnr, int portnr)
-{
-	stlbrd_t	*brdp;
-	stlpanel_t	*panelp;
+	if (state) {
+		if (tty->termios->c_iflag & IXOFF) {
+			mr0 = stl_sc26198getreg(portp, MR0);
+			stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+			stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+			mr0 |= MR0_SWFRX;
+			portp->stats.rxxon++;
+			stl_sc26198setreg(portp, MR0, mr0);
+		}
+/*
+ *		Question: should we return RTS to what it was before? It may
+ *		have been set by an ioctl... Suppose not, since if you have
+ *		hardware flow control set then it is pretty silly to go and
+ *		set the RTS line by hand.
+ */
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_sc26198setreg(portp, MR1,
+				(stl_sc26198getreg(portp, MR1) | MR1_AUTORTS));
+			stl_sc26198setreg(portp, IOPIOR,
+				(stl_sc26198getreg(portp, IOPIOR) | IOPR_RTS));
+			portp->stats.rxrtson++;
+		}
+	} else {
+		if (tty->termios->c_iflag & IXOFF) {
+			mr0 = stl_sc26198getreg(portp, MR0);
+			stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+			stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+			mr0 &= ~MR0_SWFRX;
+			portp->stats.rxxoff++;
+			stl_sc26198setreg(portp, MR0, mr0);
+		}
+		if (tty->termios->c_cflag & CRTSCTS) {
+			stl_sc26198setreg(portp, MR1,
+				(stl_sc26198getreg(portp, MR1) & ~MR1_AUTORTS));
+			stl_sc26198setreg(portp, IOPIOR,
+				(stl_sc26198getreg(portp, IOPIOR) & ~IOPR_RTS));
+			portp->stats.rxrtsoff++;
+		}
+	}
 
-	if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
-		return((stlport_t *) NULL);
-	brdp = stl_brds[brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return((stlport_t *) NULL);
-	if ((panelnr < 0) || (panelnr >= brdp->nrpanels))
-		return((stlport_t *) NULL);
-	panelp = brdp->panels[panelnr];
-	if (panelp == (stlpanel_t *) NULL)
-		return((stlport_t *) NULL);
-	if ((portnr < 0) || (portnr >= panelp->nrports))
-		return((stlport_t *) NULL);
-	return(panelp->ports[portnr]);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
 }
 
 /*****************************************************************************/
 
-/*
- *	Return the port stats structure to user app. A NULL port struct
- *	pointer passed in means that we need to find out from the app
- *	what port to get stats for (used through board control device).
- */
-
-static int stl_getportstats(stlport_t *portp, comstats_t *cp)
+static void stl_sc26198flush(stlport_t *portp)
 {
-	unsigned char	*head, *tail;
 	unsigned long	flags;
 
-	if (portp == (stlport_t *) NULL) {
-		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
-		if (portp == (stlport_t *) NULL)
-			return(-ENODEV);
-	}
-
-	portp->stats.state = portp->istate;
-	portp->stats.flags = portp->flags;
-	portp->stats.hwid = portp->hwid;
+#if DEBUG
+	printk("stl_sc26198flush(portp=%x)\n", (int) portp);
+#endif
 
-	portp->stats.ttystate = 0;
-	portp->stats.cflags = 0;
-	portp->stats.iflags = 0;
-	portp->stats.oflags = 0;
-	portp->stats.lflags = 0;
-	portp->stats.rxbuffered = 0;
+	if (portp == (stlport_t *) NULL)
+		return;
 
 	save_flags(flags);
 	cli();
-	if (portp->tty != (struct tty_struct *) NULL) {
-		if (portp->tty->driver_data == portp) {
-			portp->stats.ttystate = portp->tty->flags;
-			portp->stats.rxbuffered = portp->tty->flip.count;
-			if (portp->tty->termios != (struct termios *) NULL) {
-				portp->stats.cflags = portp->tty->termios->c_cflag;
-				portp->stats.iflags = portp->tty->termios->c_iflag;
-				portp->stats.oflags = portp->tty->termios->c_oflag;
-				portp->stats.lflags = portp->tty->termios->c_lflag;
-			}
-		}
-	}
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_sc26198setreg(portp, SCCR, CR_TXRESET);
+	stl_sc26198setreg(portp, SCCR, portp->crenable);
+	BRDDISABLE(portp->brdnr);
+	portp->tx.tail = portp->tx.head;
 	restore_flags(flags);
+}
 
-	head = portp->tx.head;
-	tail = portp->tx.tail;
-	portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+/*****************************************************************************/
 
-	portp->stats.signals = (unsigned long) stl_getsignals(portp);
+/*
+ *	If we are TX flow controlled and in IXANY mode then we may
+ *	need to unflow control here. We gotta do this because of the
+ *	automatic flow control modes of the sc26198.
+ */
+static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty)
+{
+	unsigned char	mr0;
 
-	copy_to_user(cp, &portp->stats, sizeof(comstats_t));
-	return(0);
+	mr0 = stl_sc26198getreg(portp, MR0);
+	stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+	stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
+	stl_sc26198setreg(portp, MR0, mr0);
+	clear_bit(ASYI_TXFLOWED, &portp->istate);
 }
 
 /*****************************************************************************/
 
 /*
- *	Clear the port stats structure. We also return it zeroed out...
+ *	Interrupt service routine for sc26198 panels.
  */
 
-static int stl_clrportstats(stlport_t *portp, comstats_t *cp)
+static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
 {
-	if (portp == (stlport_t *) NULL) {
-		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
-		if (portp == (stlport_t *) NULL)
-			return(-ENODEV);
-	}
+	stlport_t	*portp;
+	unsigned int	iack;
 
-	memset(&portp->stats, 0, sizeof(comstats_t));
-	portp->stats.brd = portp->brdnr;
-	portp->stats.panel = portp->panelnr;
-	portp->stats.port = portp->portnr;
-	copy_to_user(cp, &portp->stats, sizeof(comstats_t));
-	return(0);
+/* 
+ *	Work around bug in sc26198 chip... Cannot have A6 address
+ *	line of UART high, else iack will be returned as 0.
+ */
+	outb(0, (iobase + 1));
+
+	iack = inb(iobase + XP_IACK);
+	portp = panelp->ports[(iack & IVR_CHANMASK)];
+
+	if (iack & IVR_RXDATA)
+		stl_sc26198rxisr(portp, iack);
+	else if (iack & IVR_TXDATA)
+		stl_sc26198txisr(portp);
+	else
+		stl_sc26198otherisr(portp, iack);
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the entire driver ports structure to a user app.
+ *	Transmit interrupt handler. This has gotta be fast!  Handling TX
+ *	chars is pretty simple, stuff as many as possible from the TX buffer
+ *	into the sc26198 FIFO.
+ *	In practice it is possible that interrupts are enabled but that the
+ *	port has been hung up. Need to handle not having any TX buffer here,
+ *	this is done by using the side effect that head and tail will also
+ *	be NULL if the buffer has been freed.
  */
 
-static int stl_getportstruct(unsigned long arg)
+static void stl_sc26198txisr(stlport_t *portp)
 {
-	stlport_t	*portp;
+	unsigned int	ioaddr;
+	unsigned char	mr0;
+	int		len, stlen;
+	char		*head, *tail;
 
-	copy_from_user(&stl_dummyport, (void *) arg, sizeof(stlport_t));
-	portp = stl_getport(stl_dummyport.brdnr, stl_dummyport.panelnr,
-		 stl_dummyport.portnr);
-	if (portp == (stlport_t *) NULL)
-		return(-ENODEV);
-	copy_to_user((void *) arg, portp, sizeof(stlport_t));
-	return(0);
+#if DEBUG
+	printk("stl_sc26198txisr(portp=%x)\n", (int) portp);
+#endif
+
+	ioaddr = portp->ioaddr;
+	head = portp->tx.head;
+	tail = portp->tx.tail;
+	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
+	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+		set_bit(ASYI_TXLOW, &portp->istate);
+		queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+	}
+
+	if (len == 0) {
+		outb((MR0 | portp->uartaddr), (ioaddr + XP_ADDR));
+		mr0 = inb(ioaddr + XP_DATA);
+		if ((mr0 & MR0_TXMASK) == MR0_TXEMPTY) {
+			portp->imr &= ~IR_TXRDY;
+			outb((IMR | portp->uartaddr), (ioaddr + XP_ADDR));
+			outb(portp->imr, (ioaddr + XP_DATA));
+			clear_bit(ASYI_TXBUSY, &portp->istate);
+		} else {
+			mr0 |= ((mr0 & ~MR0_TXMASK) | MR0_TXEMPTY);
+			outb(mr0, (ioaddr + XP_DATA));
+		}
+	} else {
+		len = MIN(len, SC26198_TXFIFOSIZE);
+		portp->stats.txtotal += len;
+		stlen = MIN(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail));
+		outb((GTXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+		outsb((ioaddr + XP_DATA), tail, stlen);
+		len -= stlen;
+		tail += stlen;
+		if (tail >= (portp->tx.buf + STL_TXBUFSIZE))
+			tail = portp->tx.buf;
+		if (len > 0) {
+			outsb((ioaddr + XP_DATA), tail, len);
+			tail += len;
+		}
+		portp->tx.tail = tail;
+	}
 }
 
 /*****************************************************************************/
 
 /*
- *	Return the entire driver board structure to a user app.
+ *	Receive character interrupt handler. Determine if we have good chars
+ *	or bad chars and then process appropriately. Good chars are easy
+ *	just shove the lot into the RX buffer and set all status byte to 0.
+ *	If a bad RX char then process as required. This routine needs to be
+ *	fast!  In practice it is possible that we get an interrupt on a port
+ *	that is closed. This can happen on hangups - since they completely
+ *	shutdown a port not in user context. Need to handle this case.
  */
 
-static int stl_getbrdstruct(unsigned long arg)
+static void stl_sc26198rxisr(stlport_t *portp, unsigned int iack)
 {
-	stlbrd_t	*brdp;
+	struct tty_struct	*tty;
+	unsigned int		len, buflen, ioaddr;
 
-	copy_from_user(&stl_dummybrd, (void *) arg, sizeof(stlbrd_t));
-	if ((stl_dummybrd.brdnr < 0) || (stl_dummybrd.brdnr >= STL_MAXBRDS))
-		return(-ENODEV);
-	brdp = stl_brds[stl_dummybrd.brdnr];
-	if (brdp == (stlbrd_t *) NULL)
-		return(-ENODEV);
-	copy_to_user((void *) arg, brdp, sizeof(stlbrd_t));
-	return(0);
+#if DEBUG
+	printk("stl_sc26198rxisr(portp=%x,iack=%x)\n", (int) portp, iack);
+#endif
+
+	tty = portp->tty;
+	ioaddr = portp->ioaddr;
+	outb((GIBCR | portp->uartaddr), (ioaddr + XP_ADDR));
+	len = inb(ioaddr + XP_DATA) + 1;
+
+	if ((iack & IVR_TYPEMASK) == IVR_RXDATA) {
+		if ((tty == (struct tty_struct *) NULL) ||
+		    (tty->flip.char_buf_ptr == (char *) NULL) ||
+		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			outb((GRXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+			insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
+			portp->stats.rxlost += len;
+			portp->stats.rxtotal += len;
+		} else {
+			len = MIN(len, buflen);
+			if (len > 0) {
+				outb((GRXFIFO | portp->uartaddr), (ioaddr + XP_ADDR));
+				insb((ioaddr + XP_DATA), tty->flip.char_buf_ptr, len);
+				memset(tty->flip.flag_buf_ptr, 0, len);
+				tty->flip.flag_buf_ptr += len;
+				tty->flip.char_buf_ptr += len;
+				tty->flip.count += len;
+				tty_schedule_flip(tty);
+				portp->stats.rxtotal += len;
+			}
+		}
+	} else {
+		stl_sc26198rxbadchars(portp);
+	}
+
+/*
+ *	If we are TX flow controlled and in IXANY mode then we may need
+ *	to unflow control here. We gotta do this because of the automatic
+ *	flow control modes of the sc26198.
+ */
+	if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
+		if ((tty != (struct tty_struct *) NULL) &&
+				(tty->termios != (struct termios *) NULL) &&
+				(tty->termios->c_iflag & IXANY)) {
+			stl_sc26198txunflow(portp, tty);
+		}
+	}
 }
 
 /*****************************************************************************/
 
 /*
- *	The "staliomem" device is also required to do some special operations
- *	on the board and/or ports. In this driver it is mostly used for stats
- *	collection.
+ *	Process an RX bad character.
  */
 
-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static void inline stl_sc26198rxbadch(stlport_t *portp, unsigned char status, char ch)
 {
-	int	brdnr, rc;
+	struct tty_struct	*tty;
+	unsigned int		ioaddr;
 
-#if DEBUG
-	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+	tty = portp->tty;
+	ioaddr = portp->ioaddr;
+
+	if (status & SR_RXPARITY)
+		portp->stats.rxparity++;
+	if (status & SR_RXFRAMING)
+		portp->stats.rxframing++;
+	if (status & SR_RXOVERRUN)
+		portp->stats.rxoverrun++;
+	if (status & SR_RXBREAK)
+		portp->stats.rxbreaks++;
+
+	if ((tty != (struct tty_struct *) NULL) &&
+		    	((portp->rxignoremsk & status) == 0)) {
+		if (portp->rxmarkmsk & status) {
+			if (status & SR_RXBREAK) {
+				status = TTY_BREAK;
+#ifndef MODULE
+				if (portp->flags & ASYNC_SAK) {
+					do_SAK(tty);
+					BRDENABLE(portp->brdnr, portp->pagenr);
+				}
 #endif
+			} else if (status & SR_RXPARITY) {
+				status = TTY_PARITY;
+			} else if (status & SR_RXFRAMING) {
+				status = TTY_FRAME;
+			} else if(status & SR_RXOVERRUN) {
+				status = TTY_OVERRUN;
+			} else {
+				status = 0;
+			}
+		} else {
+			status = 0;
+		}
 
-	brdnr = MINOR(ip->i_rdev);
-	if (brdnr >= STL_MAXBRDS)
-		return(-ENODEV);
-	rc = 0;
+		if (tty->flip.char_buf_ptr != (char *) NULL) {
+			if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+				*tty->flip.flag_buf_ptr++ = status;
+				*tty->flip.char_buf_ptr++ = ch;
+				tty->flip.count++;
+			}
+			tty_schedule_flip(tty);
+		}
 
-	switch (cmd) {
-	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
-		break;
-	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
-		break;
-	case COM_GETBRDSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
-			rc = stl_getbrdstats((combrd_t *) arg);
-		break;
-	case COM_READPORT:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
-			rc = stl_getportstruct(arg);
-		break;
-	case COM_READBOARD:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
-			rc = stl_getbrdstruct(arg);
-		break;
-	default:
-		rc = -ENOIOCTLCMD;
-		break;
+		if (status == 0)
+			portp->stats.rxtotal++;
 	}
-
-	return(rc);
 }
 
 /*****************************************************************************/
 
-int stl_init(void)
-{
-	printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+/*
+ *	Process all characters in the RX FIFO of the UART. Check all char
+ *	status bytes as well, and process as required. We need to check
+ *	all bytes in the FIFO, in case some more enter the FIFO while we
+ *	are here. To get the exact character error type we need to switch
+ *	into CHAR error mode (that is why we need to make sure we empty
+ *	the FIFO).
+ */
 
-	stl_initbrds();
+static void stl_sc26198rxbadchars(stlport_t *portp)
+{
+	unsigned char	status, mr1;
+	char		ch;
 
 /*
- *	Allocate a temporary write buffer.
+ *	To get the precise error type for each character we must switch
+ *	back into CHAR error mode.
  */
-	stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
-	if (stl_tmpwritebuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+	mr1 = stl_sc26198getreg(portp, MR1);
+	stl_sc26198setreg(portp, MR1, (mr1 & ~MR1_ERRBLOCK));
+
+	while ((status = stl_sc26198getreg(portp, SR)) & SR_RXRDY) {
+		stl_sc26198setreg(portp, CCR, CR_CLEARRXERR);
+		ch = stl_sc26198getreg(portp, RXFIFO);
+		stl_sc26198rxbadch(portp, status, ch);
+	}
 
 /*
- *	Set up a character driver for per board stuff. This is mainly used
- *	to do stats ioctls on the ports.
+ *	To get correct interrupt class we must switch back into BLOCK
+ *	error mode.
  */
-	if (register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem))
-		printk("STALLION: failed to register serial board device\n");
+	stl_sc26198setreg(portp, MR1, mr1);
+}
+
+/*****************************************************************************/
 
 /*
- *	Set up the tty driver structure and register us as a driver.
- *	Also setup the callout tty device.
+ *	Other interrupt handler. This includes modem signals, flow
+ *	control actions, etc. Most stuff is left to off-level interrupt
+ *	processing time.
  */
-	memset(&stl_serial, 0, sizeof(struct tty_driver));
-	stl_serial.magic = TTY_DRIVER_MAGIC;
-	stl_serial.name = stl_serialname;
-	stl_serial.major = STL_SERIALMAJOR;
-	stl_serial.minor_start = 0;
-	stl_serial.num = STL_MAXBRDS * STL_MAXPORTS;
-	stl_serial.type = TTY_DRIVER_TYPE_SERIAL;
-	stl_serial.subtype = STL_DRVTYPSERIAL;
-	stl_serial.init_termios = stl_deftermios;
-	stl_serial.flags = TTY_DRIVER_REAL_RAW;
-	stl_serial.refcount = &stl_refcount;
-	stl_serial.table = stl_ttys;
-	stl_serial.termios = stl_termios;
-	stl_serial.termios_locked = stl_termioslocked;
-	
-	stl_serial.open = stl_open;
-	stl_serial.close = stl_close;
-	stl_serial.write = stl_write;
-	stl_serial.put_char = stl_putchar;
-	stl_serial.flush_chars = stl_flushchars;
-	stl_serial.write_room = stl_writeroom;
-	stl_serial.chars_in_buffer = stl_charsinbuffer;
-	stl_serial.ioctl = stl_ioctl;
-	stl_serial.set_termios = stl_settermios;
-	stl_serial.throttle = stl_throttle;
-	stl_serial.unthrottle = stl_unthrottle;
-	stl_serial.stop = stl_stop;
-	stl_serial.start = stl_start;
-	stl_serial.hangup = stl_hangup;
-	stl_serial.flush_buffer = stl_flushbuffer;
 
-	stl_callout = stl_serial;
-	stl_callout.name = stl_calloutname;
-	stl_callout.major = STL_CALLOUTMAJOR;
-	stl_callout.subtype = STL_DRVTYPCALLOUT;
+static void stl_sc26198otherisr(stlport_t *portp, unsigned int iack)
+{
+	unsigned char	cir, ipr, xisr;
 
-	if (tty_register_driver(&stl_serial))
-		printk("STALLION: failed to register serial driver\n");
-	if (tty_register_driver(&stl_callout))
-		printk("STALLION: failed to register callout driver\n");
+#if DEBUG
+	printk("stl_sc26198otherisr(portp=%x,iack=%x)\n", (int) portp, iack);
+#endif
 
-	return(0);
+	cir = stl_sc26198getglobreg(portp, CIR);
+
+	switch (cir & CIR_SUBTYPEMASK) {
+	case CIR_SUBCOS:
+		ipr = stl_sc26198getreg(portp, IPR);
+		if (ipr & IPR_DCDCHANGE) {
+			set_bit(ASYI_DCDCHANGE, &portp->istate);
+			queue_task_irq_off(&portp->tqueue, &tq_scheduler);
+			portp->stats.modem++;
+		}
+		break;
+	case CIR_SUBXONXOFF:
+		xisr = stl_sc26198getreg(portp, XISR);
+		if (xisr & XISR_RXXONGOT) {
+			set_bit(ASYI_TXFLOWED, &portp->istate);
+			portp->stats.txxoff++;
+		}
+		if (xisr & XISR_RXXOFFGOT) {
+			clear_bit(ASYI_TXFLOWED, &portp->istate);
+			portp->stats.txxon++;
+		}
+		break;
+	case CIR_SUBBREAK:
+		stl_sc26198setreg(portp, SCCR, CR_BREAKRESET);
+		stl_sc26198rxbadchars(portp);
+		break;
+	default:
+		/*printk("%s(%d): unknown other intr cir=%x, iack=%x!\n", __FILE__, __LINE__, cir, iack);*/
+		break;
+	}
 }
 
 /*****************************************************************************/
diff -u --recursive --new-file v2.1.28/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.1.28/linux/drivers/char/tty_ioctl.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/char/tty_ioctl.c	Wed Mar  5 17:04:31 1997
@@ -237,7 +237,6 @@
 
 static int get_sgttyb(struct tty_struct * tty, struct sgttyb * sgttyb)
 {
-	int retval;
 	struct sgttyb tmp;
 
 	tmp.sg_ispeed = 0;
@@ -298,7 +297,6 @@
 #ifdef TIOCGETC
 static int get_tchars(struct tty_struct * tty, struct tchars * tchars)
 {
-	int retval;
 	struct tchars tmp;
 
 	tmp.t_intrc = tty->termios->c_cc[VINTR];
@@ -314,7 +312,6 @@
 
 static int set_tchars(struct tty_struct * tty, struct tchars * tchars)
 {
-	int retval;
 	struct tchars tmp;
 
 	if (copy_from_user(&tmp, tchars, sizeof(tmp)))
@@ -332,7 +329,6 @@
 #ifdef TIOCGLTC
 static int get_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
 {
-	int retval;
 	struct ltchars tmp;
 
 	tmp.t_suspc = tty->termios->c_cc[VSUSP];
@@ -348,7 +344,6 @@
 
 static int set_ltchars(struct tty_struct * tty, struct ltchars * ltchars)
 {
-	int retval;
 	struct ltchars tmp;
 
 	if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
@@ -503,13 +498,16 @@
 				(real_tty->termios_locked,
 				 (struct termios *) arg);
 		case TIOCPKT:
+		{
+			int pktmode;
+
 			if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
 			    tty->driver.subtype != PTY_TYPE_MASTER)
 				return -ENOTTY;
-			retval = get_user(retval, (int *) arg);
+			retval = get_user(pktmode, (int *) arg);
 			if (retval)
 				return retval;
-			if (retval) {
+			if (pktmode) {
 				if (!tty->packet) {
 					tty->packet = 1;
 					tty->link->ctrl_status = 0;
@@ -517,6 +515,7 @@
 			} else
 				tty->packet = 0;
 			return 0;
+		}
 		/* These two ioctl's always return success; even if */
 		/* the driver doesn't support them. */
 		case TCSBRK: case TCSBRKP:
diff -u --recursive --new-file v2.1.28/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.1.28/linux/drivers/net/myri_sbus.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/net/myri_sbus.c	Wed Mar  5 17:04:32 1997
@@ -998,7 +998,8 @@
 	determine_reg_space_size(mp);
 
 	/* Map in the MyriCOM register/localram set. */
-	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], sdev->num_registers);
+	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+			       sdev->num_registers, sdev);
 	if(mp->eeprom.cpuvers < CPUVERS_4_0) {
 		/* XXX Makes no sense, if control reg is non-existant this
 		 * XXX driver cannot function at all... maybe pre-4.0 is
diff -u --recursive --new-file v2.1.28/linux/drivers/net/sunhme.c linux/drivers/net/sunhme.c
--- v2.1.28/linux/drivers/net/sunhme.c	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/sunhme.c	Wed Mar  5 17:04:32 1997
@@ -6,7 +6,7 @@
  */
 
 static char *version =
-        "sunhme.c:v1.1 10/Oct/96 David S. Miller (davem@caipfs.rutgers.edu)\n";
+        "sunhme.c:v1.2 10/Oct/96 David S. Miller (davem@caipfs.rutgers.edu)\n";
 
 #include <linux/module.h>
 
@@ -279,17 +279,9 @@
 {
 	hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
 
-	/* Downgrade duplex if full. */
-	if(hp->sw_bmcr & BMCR_FULLDPLX) {
-		hp->sw_bmcr &= ~(BMCR_FULLDPLX);
-		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
-		return 0;
-	}
-
 	/* Downgrade from 100 to 10. */
 	if(hp->sw_bmcr & BMCR_SPEED100) {
 		hp->sw_bmcr &= ~(BMCR_SPEED100);
-		hp->sw_bmcr |= (BMCR_FULLDPLX);
 		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
 		return 0;
 	}
@@ -346,14 +338,20 @@
 	/* All we care about is making sure the bigmac tx_cfg has a
 	 * proper duplex setting.
 	 */
-	if(hp->timer_state == lupwait) {
+	if(hp->timer_state == arbwait) {
 		hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, DP83840_LPA);
-		if((hp->sw_lpa & LPA_100FULL) ||
-		   (!(hp->sw_lpa & LPA_100HALF) && (hp->sw_lpa & LPA_10FULL)))
+		if(!(hp->sw_lpa & (LPA_10HALF | LPA_10FULL | LPA_100HALF | LPA_100FULL)))
+			goto no_response;
+		if(hp->sw_lpa & LPA_100FULL)
+			full = 1;
+		else if(hp->sw_lpa & LPA_100HALF)
+			full = 0;
+		else if(hp->sw_lpa & LPA_10FULL)
 			full = 1;
 		else
 			full = 0;
 	} else {
+		/* Forcing a link mode. */
 		hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
 		if(hp->sw_bmcr & BMCR_FULLDPLX)
 			full = 1;
@@ -370,6 +368,8 @@
 		hp->bigmacregs->tx_cfg &= ~(BIGMAC_TXCFG_FULLDPLX);
 
 	return 0;
+no_response:
+	return 1;
 }
 
 static int happy_meal_init(struct happy_meal *hp, int from_irq);
@@ -390,11 +390,19 @@
 			/* Enter force mode. */
 	do_force_mode:
 			hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, DP83840_BMCR);
-			printk("%s: Auto-Negotiation timeout, trying force link "
-			       "mode BMCR=0x%04x.\n", hp->dev->name, hp->sw_bmcr);
-			hp->sw_bmcr &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
-			hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_ANENABLE);
+			printk("%s: Auto-Negotiation unsuccessful, trying force link mode\n",
+			       hp->dev->name);
+			hp->sw_bmcr = BMCR_SPEED100;
 			happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
+
+			/* OK, seems we need do disable the transceiver for the first
+			 * tick to make sure we get an accurate link state at the
+			 * second tick.
+			 */
+			hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
+			hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+
 			hp->timer_state = ltrywait;
 			hp->timer_ticks = 0;
 			restart_timer = 1;
@@ -409,6 +417,9 @@
 				if(ret) {
 					/* Ooops, something bad happened, go to force
 					 * mode.
+					 *
+					 * XXX Broken hubs which don't support 802.3u
+					 * XXX auto-negotiation make this happen as well.
 					 */
 					goto do_force_mode;
 				}
@@ -455,6 +466,21 @@
 		 * error recovery code for the most part.
 		 */
 		hp->sw_bmsr = happy_meal_tcvr_read(hp, tregs, DP83840_BMSR);
+		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
+		if(hp->timer_ticks == 1) {
+			/* Re-enable transceiver, we'll re-enable the transceiver next
+			 * tick, then check link state on the following tick. */
+			hp->sw_csconfig |= CSCONFIG_TCVDISAB;
+			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+			restart_timer = 1;
+			break;
+		}
+		if(hp->timer_ticks == 2) {
+			hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+			happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+			restart_timer = 1;
+			break;
+		}
 		if(hp->sw_bmsr & BMSR_LSTATUS) {
 			/* Force mode selection success. */
 			display_forced_link_mode(hp, tregs);
@@ -462,7 +488,7 @@
 			hp->timer_state = asleep;
 			restart_timer = 0;
 		} else {
-			if(hp->timer_ticks >= 3) { /* 6 seconds or so... */
+			if(hp->timer_ticks >= 4) { /* 6 seconds or so... */
 				int ret;
 
 				ret = try_next_permutation(hp, tregs);
@@ -483,6 +509,9 @@
 					}
 					return;
 				}
+				hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
+				hp->sw_csconfig |= CSCONFIG_TCVDISAB;
+				happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
 				hp->timer_ticks = 0;
 				restart_timer = 1;
 			} else {
@@ -1033,9 +1062,19 @@
 		 * XXX Should probably reset the DP83840 first
 		 * XXX as this is a gross fatal error...
 		 */
-		hp->sw_bmcr &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
-		hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_SPEED100);
+		hp->sw_bmcr = BMCR_SPEED100;
 		happy_meal_tcvr_write(hp, tregs, DP83840_BMCR, hp->sw_bmcr);
+
+		/* OK, seems we need do disable the transceiver for the first
+		 * tick to make sure we get an accurate link state at the
+		 * second tick.
+		 */
+		hp->sw_csconfig = happy_meal_tcvr_read(hp, tregs, DP83840_CSCONFIG);
+		printk("%s: CSCONFIG [%04x], disabling transceiver\n", hp->dev->name,
+		       hp->sw_csconfig);
+		hp->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+		happy_meal_tcvr_write(hp, tregs, DP83840_CSCONFIG, hp->sw_csconfig);
+
 		hp->timer_state = ltrywait;
 	} else {
 		hp->timer_state = arbwait;
@@ -1379,9 +1418,11 @@
 		/* Defer-timer expired.  Probably means the happy meal needed
 		 * to back off too much before it could transmit one frame.
 		 */
+#if 0		/* XXX This isn't worth reporting and is in fact a normal condition. */
 		printk("%s: Transmit defer timer expired, subnet congested?\n",
 		       hp->dev->name);
 		reset = 1;
+#endif
 	}
 
 	if(status & GREG_STAT_NORXD) {
@@ -2050,7 +2091,8 @@
 		return ENODEV;
 	}
 
-	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], sdev->num_registers);
+	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+			       sdev->num_registers, sdev);
 	hp->gregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
 				   sizeof(struct hmeal_gregs),
 				   "Happy Meal Global Regs",
@@ -2196,10 +2238,16 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_happy_dev) {
+		struct happy_meal *hp = root_happy_dev;
 		sunshine = root_happy_dev->next_module;
 
-		unregister_netdev(root_happy_dev->dev);
-		kfree(root_happy_dev->dev);
+		sparc_free_io(hp->gregs, sizeof(struct hmeal_gregs));
+		sparc_free_io(hp->etxregs, sizeof(struct hmeal_etxregs));
+		sparc_free_io(hp->erxregs, sizeof(struct hmeal_erxregs));
+		sparc_free_io(hp->bigmacregs, sizeof(struct hmeal_bigmacregs));
+		sparc_free_io(hp->tcvregs, sizeof(struct hmeal_tcvregs));
+		unregister_netdev(hp->dev);
+		kfree(hp->dev);
 		root_happy_dev = sunshine;
 	}
 }
diff -u --recursive --new-file v2.1.28/linux/drivers/net/sunhme.h linux/drivers/net/sunhme.h
--- v2.1.28/linux/drivers/net/sunhme.h	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/sunhme.h	Wed Mar  5 17:04:32 1997
@@ -370,7 +370,7 @@
 #define CSCONFIG_LED4           0x0002  /* Pin for full-dplx LED4      */
 #define CSCONFIG_LED1           0x0004  /* Pin for conn-status LED1    */
 #define CSCONFIG_RESV2          0x0008  /* Unused...                   */
-#define CSCONFIG_TXDISAB        0x0010  /* Turns off the transceiver   */
+#define CSCONFIG_TCVDISAB       0x0010  /* Turns off the transceiver   */
 #define CSCONFIG_DFBYPASS       0x0020  /* Bypass disconnect function  */
 #define CSCONFIG_GLFORCE        0x0040  /* Good link force for 100mbps */
 #define CSCONFIG_CLKTRISTATE    0x0080  /* Tristate 25m clock          */
@@ -528,6 +528,7 @@
 	unsigned short            sw_advertise;   /* SW copy of ADVERTISE              */
 	unsigned short            sw_lpa;         /* SW copy of LPA                    */
 	unsigned short            sw_expansion;   /* SW copy of EXPANSION              */
+	unsigned short            sw_csconfig;    /* SW copy of CSCONFIG               */
 	unsigned int              auto_speed;     /* Auto-nego link speed              */
         unsigned int              forced_speed;   /* Force mode link speed             */
 	unsigned int              poll_data;      /* MIF poll data                     */
@@ -540,7 +541,7 @@
 	enum happy_timer_state    timer_state;    /* State of the auto-neg timer.      */
 	unsigned int              timer_ticks;    /* Number of clicks at each state.   */
 
-	struct net_device_stats	 enet_stats;     /* Statistical counters              */
+	struct net_device_stats	  net_stats;      /* Statistical counters              */
 	struct linux_sbus_device *happy_sbus_dev; /* ;-)                               */
 	struct device            *dev;            /* Backpointer                       */
 	struct happy_meal        *next_module;
diff -u --recursive --new-file v2.1.28/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.28/linux/drivers/net/sunlance.c	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/sunlance.c	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.52 1997/01/25 23:29:56 ecd Exp $
+/* $Id: sunlance.c,v 1.55 1997/02/07 09:41:07 ecd Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -477,6 +477,8 @@
 				return 0;
 			}
 	    
+			lp->stats.rx_bytes += len;
+
 			skb->dev = dev;
 			skb_reserve (skb, 2);		/* 16 byte align */
 			skb_put (skb, len);		/* make room */
@@ -803,7 +805,7 @@
 
 	len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen;
 	
-	lp->stats.tx_bytes+=len;
+	lp->stats.tx_bytes += len;
 	
 	entry = lp->tx_new & TX_RING_MOD_MASK;
 	ib->btx_ring [entry].length = (-len) | 0xf000;
@@ -958,7 +960,7 @@
 
 	/* Get the IO region */
 	prom_apply_sbus_ranges (sdev->my_bus, &sdev->reg_addrs [0],
-				sdev->num_registers);
+				sdev->num_registers, sdev);
 	ll = sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0,
 			     sizeof (struct lance_regs), lancestr,
 			     sdev->reg_addrs[0].which_io, 0x0);
@@ -970,7 +972,8 @@
 	if (lebuffer){
 		prom_apply_sbus_ranges (lebuffer->my_bus,
 					&lebuffer->reg_addrs [0],
-					lebuffer->num_registers);
+					lebuffer->num_registers,
+					lebuffer);
 		lp->init_block = (void *)
 			sparc_alloc_io (lebuffer->reg_addrs [0].phys_addr, 0,
 				sizeof (struct lance_init_block), "lebuffer", 
diff -u --recursive --new-file v2.1.28/linux/drivers/net/sunqe.c linux/drivers/net/sunqe.c
--- v2.1.28/linux/drivers/net/sunqe.c	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/sunqe.c	Wed Mar  5 17:04:32 1997
@@ -777,7 +777,7 @@
 	/* Get it going. */
 	qep->qcregs->ctrl = CREG_CTRL_TWAKEUP;
 
-	qep->stats.tx_bytes+=skb->len;
+	qep->net_stats.tx_bytes+=skb->len;
 	
 	dev_kfree_skb(skb, FREE_WRITE);
 
@@ -980,11 +980,13 @@
 			qesdevs[i]->reg_addrs[j].phys_addr += qranges[k].ot_parent_base;
 		}
 
-		prom_apply_sbus_ranges(qesdevs[i]->my_bus, &qesdevs[i]->reg_addrs[0], 2);
+		prom_apply_sbus_ranges(qesdevs[i]->my_bus, &qesdevs[i]->reg_addrs[0],
+				       2, qesdevs[i]);
 	}
 
 	/* Now map in the registers, QEC globals first. */
-	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], sdev->num_registers);
+	prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+			       sdev->num_registers, sdev);
 	qecp->gregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
 				     sizeof(struct qe_globreg),
 				     "QEC Global Registers",
diff -u --recursive --new-file v2.1.28/linux/drivers/net/sunqe.h linux/drivers/net/sunqe.h
--- v2.1.28/linux/drivers/net/sunqe.h	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/sunqe.h	Wed Mar  5 17:04:32 1997
@@ -360,7 +360,7 @@
 
 	struct sunqec             *parent;
 
-	struct net_device_stats   enet_stats;   /* Statistical counters               */
+	struct net_device_stats   net_stats;   /* Statistical counters               */
 	struct linux_sbus_device  *qe_sbusdev;   /* QE's SBUS device struct            */
 	struct device             *dev;          /* QE's netdevice struct              */
 	int                        channel;      /* Who am I?                          */
diff -u --recursive --new-file v2.1.28/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.1.28/linux/drivers/net/wavelan.c	Sun Jan 19 05:47:25 1997
+++ linux/drivers/net/wavelan.c	Tue Mar  4 13:19:06 1997
@@ -1009,8 +1009,10 @@
   mmc_read(ioaddr, 0, (u_char *)&m, sizeof(m));
   mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
 
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
   /* Don't forget to update statistics */
   lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif	/* WIRELESS_EXT */
 
   printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
 #ifdef DEBUG_SHOW_UNUSED
@@ -1548,7 +1550,7 @@
     }
 
   /* Setting by channel (same as wfreqsel) */
-  /* Warning : each channel is 22MHz wide, so some of the channels
+  /* Warning : each channel is 11MHz wide, so some of the channels
    * will interfere... */
   if((frequency->e == 0) &&
      (frequency->m >= 0) && (frequency->m < BAND_NUM))
@@ -1931,6 +1933,23 @@
 	}
       break;
 
+    case SIOCSIWSENS:
+      /* Set the level threshold */
+      if(!suser())
+	return -EPERM;
+      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *) &psa.psa_thr_pre_set, 1);
+      mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+      break;
+
+    case SIOCGIWSENS:
+      /* Read the level threshold */
+      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *) &psa.psa_thr_pre_set, 1);
+      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
+      break;
+
     case SIOCGIWRANGE:
       /* Basic checking... */
       if(wrq->u.data.pointer != (caddr_t) 0)
@@ -1962,6 +1981,7 @@
 	  else
 	    range.num_channels = range.num_frequency = 0;
 
+	  range.sensitivity = 0x3F;
 	  range.max_qual.qual = MMR_SGNL_QUAL;
 	  range.max_qual.level = MMR_SIGNAL_LVL;
 	  range.max_qual.noise = MMR_SILENCE_LVL;
@@ -1980,8 +2000,6 @@
 	  {	/* cmd,		set_args,	get_args,	name */
 	    { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
 	    { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
-	    { SIOCSIPLTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setlevelthr" },
-	    { SIOCGIPLTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getlevelthr" },
 
 	    { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,	0, "sethisto" },
 	    { SIOCGIPHISTO, 0,	    IW_PRIV_TYPE_INT | 16, "gethisto" },
@@ -1994,7 +2012,7 @@
 	    break;
 
 	  /* Set the number of ioctl available */
-	  wrq->u.data.length = 6;
+	  wrq->u.data.length = 4;
 
 	  /* Copy structure to the user buffer */
 	  copy_to_user(wrq->u.data.pointer, (u_char *) priv,
@@ -2115,21 +2133,6 @@
       *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
       break;
 
-    case SIOCSIPLTHR:
-      if(!suser())
-	return -EPERM;
-      psa.psa_thr_pre_set = *(wrq->u.name) & 0x3F;
-      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
-	       (unsigned char *)&psa.psa_thr_pre_set, 1);
-      mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
-      break;
-
-    case SIOCGIPLTHR:
-      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
-	       (unsigned char *)&psa.psa_thr_pre_set, 1);
-      *(wrq->u.name) = psa.psa_thr_pre_set & 0x3F;
-      break;
-
 #ifdef HISTOGRAM
     case SIOCSIPHISTO:
       /* Verif if the user is root */
@@ -4016,9 +4019,11 @@
  * the initial value of dev->base_addr.
  * We follow the example in drivers/net/ne.c.)
  * (called in "Space.c")
+ * As this function is called outside the wavelan module, it should be
+ * declared extern, but it seem to cause troubles...
  */
- 
-int wavelan_probe(device *	dev)
+/* extern */ int
+wavelan_probe(device *	dev)
 {
   short		base_addr;
   mac_addr	mac;		/* Mac address (check wavelan existence) */
diff -u --recursive --new-file v2.1.28/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- v2.1.28/linux/drivers/net/wavelan.h	Thu Dec 19 07:10:25 1996
+++ linux/drivers/net/wavelan.h	Tue Mar  4 13:19:06 1997
@@ -225,7 +225,7 @@
   unsigned char	mmw_jam_time;		/* jamming time (after collision) */
   unsigned char	mmw_unused2[1];		/* unused */
   unsigned char	mmw_thr_pre_set;	/* level threshold preset */
-  /* Discard all packet with signal < this value (0) */
+  /* Discard all packet with signal < this value (4) */
   unsigned char	mmw_decay_prm;		/* decay parameters */
   unsigned char	mmw_decay_updat_prm;	/* decay update parameterz */
   unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
diff -u --recursive --new-file v2.1.28/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- v2.1.28/linux/drivers/net/wavelan.p.h	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/wavelan.p.h	Tue Mar  4 13:19:06 1997
@@ -139,11 +139,6 @@
  * the code
  * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
  * much needed informations on the Wavelan hardware.
- *
- * Yongguang Zhang <ygz@isl.hrl.hac.com> send me a patch for enabling
- * multicast in the old pcmcia driver. I tried to do the same (with
- * some minor changes) in this driver, but without any luck (I don't
- * know how to enable multicast in the chip...).
  */
 
 /* The original copyrights and litteratures mention others names and
@@ -173,6 +168,7 @@
  *	Robert Morris (rtm@das.harvard.edu),
  *	Jean Tourrilhes (jt@hplb.hpl.hp.com),
  *	Girish Welling (welling@paul.rutgers.edu),
+ *	Clark Woodworth <clark@hiway1.exit109.com>
  *	Yongguang Zhang <ygz@isl.hrl.hac.com>...
  *
  * Thanks go also to:
@@ -258,6 +254,13 @@
  *	- Update to wireless extensions changes
  *	- Silly bug in card initial configuration (psa_conf_status)
  *
+ * Changes made for release in 2.1.27 :
+ * ----------------------------------
+ *	- Small bug in debug code (probably not the last one...)
+ *	- Remove extern kerword for wavelan_probe()
+ *	- Level threshold is now a standard wireless extension (version 4 !)
+ *	- modules parameters types (new module interface)
+ *
  * Wishes & dreams :
  * ---------------
  *	- Encryption stuff
@@ -344,7 +347,7 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v12 (wireless extensions) 1/12/96\n";
+static const char	*version	= "wavelan.c : v15 (wireless extensions) 12/2/97\n";
 #endif
 
 /* Watchdog temporisation */
@@ -608,13 +611,13 @@
 };
 
 #ifdef	MODULE
-/* Name of the devices (memory allocation) */
-static char	devname[4][IFNAMSIZ] = { "", "", "", "" };
-
 /* Parameters set by insmod */
 static int	io[4]	= { 0, 0, 0, 0 };
 static int	irq[4]	= { 0, 0, 0, 0 };
-static char *	name[4] = { devname[0], devname[1], devname[2], devname[3] };
+static char	name[4][IFNAMSIZ] = { "", "", "", "" };
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM(name, "1-4c" __MODULE_STRING(IFNAMSIZ));
 #endif	/* MODULE */
 
 #endif	/* WAVELAN_P_H */
diff -u --recursive --new-file v2.1.28/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.28/linux/drivers/pci/pci.c	Tue Mar  4 10:25:23 1997
+++ linux/drivers/pci/pci.c	Tue Mar  4 13:24:05 1997
@@ -47,6 +47,11 @@
 	DEVICE( NCR,		NCR_53C820,	"53c820"),
 	DEVICE( NCR,		NCR_53C825,	"53c825"),
 	DEVICE( NCR,		NCR_53C815,	"53c815"),
+	DEVICE( NCR,		NCR_53C860,	"53c860"),
+	DEVICE( NCR,		NCR_53C896,	"53c896"),
+	DEVICE( NCR,		NCR_53C895,	"53c895"),
+	DEVICE( NCR,		NCR_53C885,	"53c885"),
+	DEVICE( NCR,		NCR_53C875,	"53c875"),
 	DEVICE( ATI,		ATI_68800,      "68800AX"),
 	DEVICE( ATI,		ATI_215CT222,   "215CT222"),
 	DEVICE( ATI,		ATI_210888CX,   "210888CX"),
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/Makefile linux/drivers/sbus/Makefile
--- v2.1.28/linux/drivers/sbus/Makefile	Fri Dec 13 01:37:32 1996
+++ linux/drivers/sbus/Makefile	Wed Mar  5 17:04:32 1997
@@ -8,8 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 SUB_DIRS     :=
-MOD_SUB_DIRS := $(SUB_DIRS) char
-ALL_SUB_DIRS := $(SUB_DIRS) char
+MOD_SUB_DIRS := $(SUB_DIRS) char audio
+ALL_SUB_DIRS := $(SUB_DIRS) char audio
 
 L_OBJS   := sbus.o dvma.o
 L_TARGET := sbus.a
@@ -19,6 +19,13 @@
 ifeq ($(CONFIG_SBUSCHAR),y)
 SUB_DIRS += char
 L_OBJS += char/sunchar.o
+endif
+
+# Audio devices for SBUS-based machines.
+#
+ifeq ($(CONFIG_SPARCAUDIO),y)
+SUB_DIRS += audio
+L_OBJS += audio/sparcaudio.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/Config.in linux/drivers/sbus/audio/Config.in
--- v2.1.28/linux/drivers/sbus/audio/Config.in	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/Config.in	Wed Mar  5 17:04:32 1997
@@ -2,10 +2,11 @@
 # Configuration script for sparcaudio subsystem
 #
 
-if [ "x$CONFIG_EXPERIMENTAL" = "xy" ]; then
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 
-  comment 'Linux/SPARC audio subsystem'
+  comment 'Linux/SPARC audio subsystem (EXPERIMENTAL)'
 
-  tristate 'Audio support' CONFIG_SPARCAUDIO
+  tristate 'Audio support (EXPERIMENTAL)' CONFIG_SPARCAUDIO
   dep_tristate '  AMD7930 Lowlevel Driver' CONFIG_SPARCAUDIO_AMD7930 $CONFIG_SPARCAUDIO
+  dep_tristate '  CS4231 Lowlevel Driver' CONFIG_SPARCAUDIO_CS4231 $CONFIG_SPARCAUDIO
 fi
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/Makefile linux/drivers/sbus/audio/Makefile
--- v2.1.28/linux/drivers/sbus/audio/Makefile	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/Makefile	Wed Mar  5 17:04:32 1997
@@ -15,6 +15,14 @@
 O_OBJS   :=
 M_OBJS   :=
 
+ifeq ($(CONFIG_SPARCAUDIO),y)
+M=y
+else
+  ifeq ($(CONFIG_SPARCAUDIO),m)
+  MM=y
+  endif
+endif
+
 ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
 M=y
 O_OBJS += amd7930.o
@@ -25,11 +33,21 @@
   endif
 endif
 
+ifeq ($(CONFIG_SPARCAUDIO_CS4231),y)
+M=y
+O_OBJS += cs4231.o
+else
+  ifeq ($(CONFIG_SPARCAUDIO_CS4231),m)
+  MM=y
+  M_OBJS += cs4231.o
+  endif
+endif
+
 ifdef M
-O_OBJS += audio.o
+OX_OBJS += audio.o
 else
   ifdef MM
-  M_OBJS += audio.o
+  MX_OBJS += audio.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/amd7930.c linux/drivers/sbus/audio/amd7930.c
--- v2.1.28/linux/drivers/sbus/audio/amd7930.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/amd7930.c	Wed Mar  5 17:04:32 1997
@@ -15,35 +15,42 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
+#include <linux/malloc.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/io.h>
+#include <asm/sbus.h>
+
 #include "audio.h"
 #include "amd7930.h"
 
-/*
- * Chip interface
- */
-struct mapreg {
-        u_short mr_x[8];
-        u_short mr_r[8];
-        u_short mr_gx;
-        u_short mr_gr;
-        u_short mr_ger;
-        u_short mr_stgr;
-        u_short mr_ftgr;
-        u_short mr_atgr;
-        u_char  mr_mmr1;
-        u_char  mr_mmr2;
-} map;
+#define MAX_DRIVERS 1
 
+/* Private information we store for each amd7930 chip. */
+struct amd7930_info {
+	/* Current buffer that the driver is playing. */
+	volatile __u8 * output_ptr;
+	volatile unsigned long output_count;
+
+	/* Current record buffer. */
+	volatile __u8 * input_ptr;
+	volatile unsigned long input_count;
+
+	/* Device registers information. */
+	struct amd7930 *regs;
+	unsigned long regs_size;
+	struct amd7930_map map;
+
+	/* Device interrupt information. */
+	int irq;
+	volatile int ints_on;
+};
 
-/* Write 16 bits of data from variable v to the data port of the audio chip */
-#define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
+/* Output a 16-bit quantity in the order that the amd7930 expects. */
+#define amd7930_out16(regs,v) ({ regs->dr = v & 0xFF; regs->dr = (v >> 8) & 0xFF; })
 
-/* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
 
 /*
  * gx, gr & stg gains.  this table must contain 256 elements with
@@ -51,7 +58,7 @@
  * elements match sun's gain curve (but with higher resolution):
  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
  */
-static const u_short gx_coeff[256] = {
+static __const__ __u16 gx_coeff[256] = {
 	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
@@ -86,10 +93,7 @@
 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
 };
 
-/*
- * second stage play gain.
- */
-static const u_short ger_coeff[] = {
+static __const__ __u16 ger_coeff[] = {
 	0x431f, /* 5. dB */
 	0x331f, /* 5.5 dB */
 	0x40dd, /* 6. dB */
@@ -111,262 +115,362 @@
 	0x2200, /* 15.9 dB */
 	0x000b, /* 16.9 dB */
 	0x000f  /* 18. dB */
-#define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
 };
-
-#if 0
-int
-amd7930_commit_settings(addr)
-	void *addr;
-{
-	register struct amd7930_softc *sc = addr;
-	register struct mapreg *map;
-	register volatile struct amd7930 *amd;
-	register int s, level;
-
-	DPRINTF(("sa_commit.\n"));
-
-	map = &sc->sc_map;
-	amd = sc->sc_au.au_amd;
-
-	map->mr_gx = gx_coeff[sc->sc_rlevel];
-	map->mr_stgr = gx_coeff[sc->sc_mlevel];
-
-	level = (sc->sc_plevel * (256 + NGER)) >> 8;
-	if (level >= 256) {
-		map->mr_ger = ger_coeff[level - 256];
-		map->mr_gr = gx_coeff[255];
-	} else {
-		map->mr_ger = ger_coeff[0];
-		map->mr_gr = gx_coeff[level];
-	}
-
-	if (sc->sc_out_port == SUNAUDIO_SPEAKER)
-		map->mr_mmr2 |= AMD_MMR2_LS;
-	else
-		map->mr_mmr2 &= ~AMD_MMR2_LS;
-
-	s = splaudio();
-
-	amd->cr = AMDR_MAP_MMR1;
-	amd->dr = map->mr_mmr1;
-	amd->cr = AMDR_MAP_GX;
-	WAMD16(amd, map->mr_gx);
-	amd->cr = AMDR_MAP_STG;
-	WAMD16(amd, map->mr_stgr);
-	amd->cr = AMDR_MAP_GR;
-	WAMD16(amd, map->mr_gr);
-	amd->cr = AMDR_MAP_GER;
-	WAMD16(amd, map->mr_ger);
-	amd->cr = AMDR_MAP_MMR2;
-	amd->dr = map->mr_mmr2;
-
-	splx(s);
-	return(0);
-}
-#endif
-
-static int amd7930_node, amd7930_irq, amd7930_regs_size, amd7930_ints_on = 0;
-static struct amd7930 *amd7930_regs = NULL;
-static __u8 * ptr;
-static size_t count;
+#define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
 
 /* Enable amd7930 interrupts atomically. */
-static __inline__ void amd7930_enable_ints(void)
+static __inline__ void amd7930_enable_ints(struct sparcaudio_driver *drv)
 {
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	register unsigned long flags;
 
-	if (amd7930_ints_on)
+	if (info->ints_on)
 		return;
 
 	save_and_cli(flags);
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE;
+	info->regs->cr = AMR_INIT;
+	info->regs->dr = AM_INIT_ACTIVE;
 	restore_flags(flags);
 
-	amd7930_ints_on = 1;
+	info->ints_on = 1;
 }
 
 /* Disable amd7930 interrupts atomically. */
-static __inline__ void amd7930_disable_ints(void)
+static __inline__ void amd7930_disable_ints(struct sparcaudio_driver *drv)
 {
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	register unsigned long flags;
 
-	if (!amd7930_ints_on)
+	if (!info->ints_on)
 		return;
 
 	save_and_cli(flags);
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS;
+	info->regs->cr = AMR_INIT;
+	info->regs->dr = AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS;
 	restore_flags(flags);
 
-	amd7930_ints_on = 0;
+	info->ints_on = 0;
 }  
 
+/* Commit the local copy of the MAP registers to the amd7930. */
+static void amd7930_commit_map(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+	struct amd7930      *regs = info->regs;
+	struct amd7930_map  *map  = &info->map;
+	unsigned long flags;
+
+	save_and_cli(flags);
+
+	regs->cr = AMR_MAP_GX;
+	amd7930_out16(regs, map->gx);
+
+	regs->cr = AMR_MAP_GR;
+	amd7930_out16(regs, map->gr);
+
+	regs->cr = AMR_MAP_STGR;
+	amd7930_out16(regs, map->stgr);
+
+	regs->cr = AMR_MAP_GER;
+	amd7930_out16(regs, map->ger);
+
+	regs->cr = AMR_MAP_MMR1;
+	regs->dr = map->mmr1;
+
+	regs->cr = AMR_MAP_MMR2;
+	regs->dr = map->mmr2;
 
-/* Audio interrupt handler. */
-static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+	restore_flags(flags);
+}
+
+/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
+static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
 {
+	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+	struct amd7930 *regs = info->regs;
 	__u8 dummy;
 
 	/* Clear the interrupt. */
-	dummy = amd7930_regs->ir;
+	dummy = regs->ir;
 
 	/* Send the next byte of outgoing data. */
-	if (ptr && count > 0) {
-		/* Send the next byte and advance the head pointer. */
-		amd7930_regs->bbtb = *ptr;
-		ptr++;
-		count--;
-
-		/* Empty buffer? Notify the midlevel driver. */
-		if (count == 0)
-			sparcaudio_output_done();
+	if (info->output_ptr && info->output_count > 0) {
+		/* Send the next byte and advance buffer pointer. */
+		regs->bbtb = *(info->output_ptr);
+		info->output_ptr++;
+		info->output_count--;
+
+		/* Done with the buffer? Notify the midlevel driver. */
+		if (info->output_count == 0) {
+			info->output_ptr = NULL;
+			info->output_count = 0;
+			sparcaudio_output_done(drv);
+		}
+	}
+
+	/* Read the next byte of incoming data. */
+	if (info->input_ptr && info->input_count > 0) {
+		/* Get the next byte and advance buffer pointer. */
+		*(info->input_ptr) = regs->bbrb;
+		info->input_ptr++;
+		info->input_count--;
+
+		/* Done with the buffer? Notify the midlevel driver. */
+		if (info->input_count == 0) {
+			info->input_ptr = NULL;
+			info->input_count = 0;
+			sparcaudio_input_done(drv);
+		}
 	}
 }
 
-static int amd7930_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+
+static int amd7930_open(struct inode * inode, struct file * file,
+			struct sparcaudio_driver *drv)
 {
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
 	int level;
 
 	/* Set the default audio parameters. */
-	map.mr_gx = gx_coeff[128];
-	map.mr_stgr = gx_coeff[0];
+	info->map.gx = gx_coeff[128];
+	info->map.stgr = gx_coeff[0];
 
-	level = (128 * (256 + NGER)) >> 8;
+	level = (128 * (256 + NR_GER_COEFFS)) >> 8;
 	if (level >= 256) {
-		map.mr_ger = ger_coeff[level-256];
-		map.mr_gr = gx_coeff[255];
+		info->map.ger = ger_coeff[level-256];
+		info->map.gr = gx_coeff[255];
 	} else {
-		map.mr_ger = ger_coeff[0];
-		map.mr_gr = gx_coeff[level];
+		info->map.ger = ger_coeff[0];
+		info->map.gr = gx_coeff[level];
 	}
 
-	map.mr_mmr2 |= AM_MAP_MMR2_LS;
+	info->map.mmr2 |= AM_MAP_MMR2_LS;
 
-	cli();
-
-	amd7930_regs->cr = AMR_MAP_MMR1;
-	amd7930_regs->dr = map.mr_mmr1;
-	amd7930_regs->cr = AMR_MAP_GX;
-	WAMD16(amd7930_regs,map.mr_gx);
-	amd7930_regs->cr = AMR_MAP_STG;
-	WAMD16(amd7930_regs,map.mr_stgr);
-	amd7930_regs->cr = AMR_MAP_GR;
-	WAMD16(amd7930_regs,map.mr_gr);
-	amd7930_regs->cr = AMR_MAP_GER;
-	WAMD16(amd7930_regs,map.mr_ger);
-	amd7930_regs->cr = AMR_MAP_MMR2;
-	amd7930_regs->dr = map.mr_mmr2;
-
-	sti();
+	amd7930_commit_map(drv);
 
 	MOD_INC_USE_COUNT;
 
 	return 0;
 }
 
-static void amd7930_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+static void amd7930_release(struct inode * inode, struct file * file,
+			    struct sparcaudio_driver *drv)
 {
-	amd7930_disable_ints();
+	amd7930_disable_ints(drv);
 	MOD_DEC_USE_COUNT;
 }
 
-static void amd7930_start_output(struct sparcaudio_driver *drv, __u8 * buffer, size_t the_count)
+static void amd7930_start_output(struct sparcaudio_driver *drv,
+				 __u8 * buffer, unsigned long count)
 {
-	count = the_count;
-	ptr = buffer;
-	amd7930_enable_ints();
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->output_ptr = buffer;
+	info->output_count = count;
+	amd7930_enable_ints(drv);
 }
 
 static void amd7930_stop_output(struct sparcaudio_driver *drv)
 {
-	amd7930_disable_ints();
-	ptr = NULL;
-	count = 0;
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->output_ptr = NULL;
+	info->output_count = 0;
+
+	if (!info->input_ptr)
+		amd7930_disable_ints(drv);
+}
+
+static void amd7930_start_input(struct sparcaudio_driver *drv,
+				__u8 * buffer, unsigned long count)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->input_ptr = buffer;
+	info->input_count = count;
+	amd7930_enable_ints(drv);
+}
+
+static void amd7930_stop_input(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	info->input_ptr = NULL;
+	info->input_count = 0;
+
+	if (!info->output_ptr)
+		amd7930_disable_ints(drv);
+}
+
+static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv,
+				 audio_device_t * audinfo)
+{
+	strncpy(audinfo->name, "amd7930", sizeof(audinfo->name) - 1);
+	strncpy(audinfo->version, "x", sizeof(audinfo->version) - 1);
+	strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1);
 }
 
 
+/*
+ *	Device detection and initialization.
+ */
+
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
 static struct sparcaudio_operations amd7930_ops = {
 	amd7930_open,
 	amd7930_release,
 	NULL,			/* amd7930_ioctl */
 	amd7930_start_output,
 	amd7930_stop_output,
+	amd7930_start_input,
+	amd7930_stop_input,
+	amd7930_sunaudio_getdev,
 };
 
-static struct sparcaudio_driver amd7930_drv = {
-	"amd7930",
-	&amd7930_ops,
-};
-
-/* Probe for the amd7930 chip and then attach the driver. */
-#ifdef MODULE
-int init_module(void)
-#else
-__initfunc(int amd7930_init(void))
-#endif
+/* Attach to an amd7930 chip given its PROM node. */
+static int amd7930_attach(struct sparcaudio_driver *drv, int node,
+			  struct linux_sbus *sbus, struct linux_sbus_device *sdev)
 {
-	struct linux_prom_registers regs[1];
+	struct linux_prom_registers regs;
 	struct linux_prom_irqs irq;
+	struct amd7930_info *info;
 	int err;
 
-#ifdef MODULE
-	register_symtab(0);
-#endif
-
-	/* Find the PROM "audio" node. */
-	amd7930_node = prom_getchild(prom_root_node);
-	amd7930_node = prom_searchsiblings(amd7930_node, "audio");
-	if (!amd7930_node)
-		return -EIO;
+	/* Allocate our private information structure. */
+	drv->private = kmalloc(sizeof(struct amd7930_info), GFP_KERNEL);
+	if (!drv->private)
+		return -ENOMEM;
+
+	/* Point at the information structure and initialize it. */
+	drv->ops = &amd7930_ops;
+	info = (struct amd7930_info *)drv->private;
+	info->output_ptr = info->input_ptr = NULL;
+	info->output_count = info->input_count = 0;
+	info->ints_on = 1; /* force disable below */
 
 	/* Map the registers into memory. */
-	prom_getproperty(amd7930_node, "reg", (char *)regs, sizeof(regs));
-	amd7930_regs_size = regs[0].reg_size;
-	amd7930_regs = sparc_alloc_io(regs[0].phys_addr, 0, regs[0].reg_size,
-				      "amd7930", regs[0].which_io, 0);
-	if (!amd7930_regs) {
+	prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
+	if (sbus && sdev)
+		prom_apply_sbus_ranges(sbus, &regs, 1, sdev);
+	info->regs_size = regs.reg_size;
+	info->regs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
+					   "amd7930", regs.which_io, 0);
+	if (!info->regs) {
 		printk(KERN_ERR "amd7930: could not allocate registers\n");
+		kfree(drv->private);
 		return -EIO;
 	}
 
 	/* Disable amd7930 interrupt generation. */
-	amd7930_disable_ints();
+	amd7930_disable_ints(drv);
 
 	/* Initialize the MUX unit to connect the MAP to the CPU. */
-	amd7930_regs->cr = AMR_MUX_1_4;
-	amd7930_regs->dr = (AM_MUX_CHANNEL_Bb << 4) | AM_MUX_CHANNEL_Ba;
-	amd7930_regs->dr = 0;
-	amd7930_regs->dr = 0;
-	amd7930_regs->dr = AM_MUX_MCR4_ENABLE_INTS;
+	info->regs->cr = AMR_MUX_1_4;
+	info->regs->dr = (AM_MUX_CHANNEL_Bb << 4) | AM_MUX_CHANNEL_Ba;
+	info->regs->dr = 0;
+	info->regs->dr = 0;
+	info->regs->dr = AM_MUX_MCR4_ENABLE_INTS;
 
 	/* Attach the interrupt handler to the audio interrupt. */
-	prom_getproperty(amd7930_node, "intr", (char *)&irq, sizeof(irq));
-	amd7930_irq = irq.pri;
-	request_irq(amd7930_irq, amd7930_interrupt, SA_INTERRUPT, "amd7930", NULL);
-	enable_irq(amd7930_irq);
-
-	memset(&map, 0, sizeof(map));
-	map.mr_mmr1 = AM_MAP_MMR1_GX | AM_MAP_MMR1_GER | AM_MAP_MMR1_GR | AM_MAP_MMR1_STG;
+	prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
+	info->irq = irq.pri;
+	request_irq(info->irq, amd7930_interrupt,
+		    SA_INTERRUPT, "amd7930", drv);
+	enable_irq(info->irq);
+
+	/* Initalize the local copy of the MAP registers. */
+	memset(&info->map, 0, sizeof(info->map));
+	info->map.mmr1 = AM_MAP_MMR1_GX | AM_MAP_MMR1_GER
+			 | AM_MAP_MMR1_GR | AM_MAP_MMR1_STG;
 
-	/* Register ourselves with the midlevel audio driver. */
-	err = register_sparcaudio_driver(&amd7930_drv);
+	/* Register the amd7930 with the midlevel audio driver. */
+	err = register_sparcaudio_driver(drv);
 	if (err < 0) {
-		/* XXX We should do something. Complain for now. */
-		printk(KERN_ERR "amd7930: really screwed now\n");
+		printk(KERN_ERR "amd7930: unable to register\n");
+		disable_irq(info->irq);
+		free_irq(info->irq, drv);
+		sparc_free_io(info->regs, info->regs_size);
+		kfree(drv->private);
 		return -EIO;
 	}
 
+	/* Announce the hardware to the user. */
+	printk(KERN_INFO "amd7930 at 0x%lx irq %d\n",
+		(unsigned long)info->regs, info->irq);
+
+	/* Success! */
 	return 0;
 }
 
 #ifdef MODULE
+/* Detach from an amd7930 chip given the device structure. */
+static void amd7930_detach(struct sparcaudio_driver *drv)
+{
+	struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+	unregister_sparcaudio_driver(drv);
+	amd7930_disable_ints(drv);
+	disable_irq(info->irq);
+	free_irq(info->irq, drv);
+	sparc_free_io(info->regs, info->regs_size);
+	kfree(drv->private);
+}
+#endif
+
+
+/* Probe for the amd7930 chip and then attach the driver. */
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int amd7930_init(void))
+#endif
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *sdev;
+	int node;
+
+#if 0
+#ifdef MODULE
+	register_symtab(0);
+#endif
+#endif
+
+	/* Try to find the sun4c "audio" node first. */
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "audio");
+	if (node && amd7930_attach(&drivers[0], node, NULL, NULL) == 0)
+		num_drivers = 1;
+	else
+		num_drivers = 0;
+
+	/* Probe each SBUS for amd7930 chips. */
+	for_all_sbusdev(sdev,bus) {
+		if (!strcmp(sdev->prom_name, "audio")) {
+			/* Don't go over the max number of drivers. */
+			if (num_drivers >= MAX_DRIVERS)
+				continue;
+
+			if (amd7930_attach(&drivers[num_drivers],
+					   sdev->prom_node, sdev->my_bus, sdev) == 0)
+				num_drivers++;
+		}
+	}
+
+	/* Only return success if we found some amd7930 chips. */
+	return (num_drivers > 0) ? 0 : -EIO;
+}
+
+#ifdef MODULE
 void cleanup_module(void)
 {
-	amd7930_disable_ints();
-	disable_irq(amd7930_irq);
-	free_irq(amd7930_irq, NULL);
-	sparc_free_io(amd7930_regs, amd7930_regs_size);
+	register int i;
+
+	for (i = 0; i < num_drivers; i++) {
+		amd7930_detach(&drivers[i]);
+		num_drivers--;
+	}
 }
 #endif
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/amd7930.h linux/drivers/sbus/audio/amd7930.h
--- v2.1.28/linux/drivers/sbus/audio/amd7930.h	Thu Dec 19 01:03:35 1996
+++ linux/drivers/sbus/audio/amd7930.h	Wed Mar  5 17:04:32 1997
@@ -16,7 +16,6 @@
 #include <linux/types.h>
 
 /* Register interface presented to the CPU by the amd7930. */
-
 struct amd7930
 {
 	__volatile__ __u8 cr;		/* Command Register (W) */
@@ -33,6 +32,22 @@
 	__volatile__ __u8 dsr2;		/* D-channel Status Register 2 (R) */
 };
 
+
+/* Indirect registers in the Main Audio Processor. */
+struct amd7930_map {
+	__u16	x[8];
+	__u16	r[8];
+	__u16	gx;
+	__u16	gr;
+	__u16	ger;
+	__u16	stgr;
+	__u16	ftgr;
+	__u16	atgr;
+	__u8	mmr1;
+	__u8	mmr2;
+};
+
+
 /* The amd7930 has "indirect registers" which are accessed by writing
  * the register number into the Command Register and then reading or
  * writing values from the Data Register as appropriate. We define the
@@ -84,7 +99,7 @@
 #define	AMR_MAP_GX			0x63
 #define	AMR_MAP_GR			0x64
 #define	AMR_MAP_GER			0x65
-#define	AMR_MAP_STG			0x66
+#define	AMR_MAP_STGR			0x66
 #define	AMR_MAP_FTGR_1_2		0x67
 #define	AMR_MAP_ATGR_1_2		0x68
 #define	AMR_MAP_MMR1			0x69
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/audio.c linux/drivers/sbus/audio/audio.c
--- v2.1.28/linux/drivers/sbus/audio/audio.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/audio.c	Wed Mar  5 17:04:32 1997
@@ -17,6 +17,7 @@
 #include <linux/tqueue.h>
 #include <linux/major.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>
 #include <linux/init.h>
 
 #include "audio.h"
@@ -26,15 +27,53 @@
  *	Low-level driver interface.
  */
 
-/* We only support one low-level audio driver. */
-static struct sparcaudio_driver *driver;
+/* We only support one low-level audio driver currently. */
+static struct sparcaudio_driver *driver = NULL;
 
 int register_sparcaudio_driver(struct sparcaudio_driver *drv)
 {
-	/* If a driver is already present, don't allow it to register. */
+	int i;
+
+	/* If a driver is already present, don't allow the register. */
 	if (driver)
 		return -EIO;
 
+	/* Ensure that the driver has a proper operations structure. */
+	if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output)
+		return -EINVAL;
+
+	/* Setup the circular queue of output buffers. */
+	drv->num_output_buffers = 32;
+	drv->output_front = 0;
+	drv->output_rear = 0;
+	drv->output_count = 0;
+	drv->output_active = 0;
+	drv->output_buffers = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL);
+	drv->output_sizes = kmalloc(32 * sizeof(size_t), GFP_KERNEL);
+	if (!drv->output_buffers || !drv->output_sizes) {
+		if (drv->output_buffers)
+			kfree(drv->output_buffers);
+		if (drv->output_sizes)
+			kfree(drv->output_sizes);
+		return -ENOMEM;
+	}
+
+	/* Allocate the pages for each output buffer. */
+	for (i = 0; i < drv->num_output_buffers; i++) {
+		drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
+		if (!drv->output_buffers[i]) {
+			int j;
+			for (j = 0; j < i; j++)
+				free_page((unsigned long) drv->output_buffers[j]);
+			kfree(drv->output_buffers);
+			kfree(drv->output_sizes);
+			return -ENOMEM;
+		}
+	}
+
+	/* Ensure that the driver is marked as not being open. */
+	drv->flags = 0;
+
 	MOD_INC_USE_COUNT;
 
 	driver = drv;
@@ -43,53 +82,76 @@
 
 int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
 {
+	int i;
+
 	/* Make sure that the current driver is unregistering. */
 	if (driver != drv)
 		return -EIO;
 
+	/* Deallocate the queue of output buffers. */
+	for (i = 0; i < driver->num_output_buffers; i++)
+		free_page((unsigned long) driver->output_buffers[i]);
+	kfree(driver->output_buffers);
+	kfree(driver->output_sizes);
+
 	MOD_DEC_USE_COUNT;
 
 	driver = NULL;
 	return 0;
 }
 
-static void sparcaudio_output_done_task(void * unused)
+static void sparcaudio_output_done_task(void * arg)
 {
+	struct sparcaudio_driver *drv = (struct sparcaudio_driver *)arg;
 	unsigned long flags;
 
+	printk(KERN_DEBUG "sparcaudio: next buffer (of=%d)\n",drv->output_front);
 	save_and_cli(flags);
-	printk(KERN_DEBUG "sparcaudio: next buffer\n");
-	driver->ops->start_output(driver, driver->output_buffers[driver->output_front],
-				  driver->output_sizes[driver->output_front]);
-	driver->output_active = 1;
+	drv->ops->start_output(drv,
+			       drv->output_buffers[drv->output_front],
+			       drv->output_sizes[drv->output_front]);
+	drv->output_active = 1;
 	restore_flags(flags);
 }
 
-static struct tq_struct sparcaudio_output_tqueue = {
-	0, 0, sparcaudio_output_done_task, 0 };
-
-void sparcaudio_output_done(void)
+void sparcaudio_output_done(struct sparcaudio_driver * drv)
 {
 	/* Point the queue after the "done" buffer. */
-	driver->output_front++;
-	driver->output_count--;
+	drv->output_front = (drv->output_front + 1) % drv->num_output_buffers;
+	drv->output_count--;
 
 	/* If the output queue is empty, shutdown the driver. */
-	if (driver->output_count == 0) {
+	if (drv->output_count == 0) {
 		/* Stop the lowlevel driver from outputing. */
-		printk(KERN_DEBUG "sparcaudio: lowlevel driver shutdown\n");
-		driver->ops->stop_output(driver);
-		driver->output_active = 0;
+		printk("sparcaudio: lowlevel driver shutdown\n");
+		drv->ops->stop_output(drv);
+		drv->output_active = 0;
+
+		/* Wake up any waiting writers or syncers and return. */
+		wake_up_interruptible(&drv->output_write_wait);
+		wake_up_interruptible(&drv->output_drain_wait);
 		return;
 	}
 
 	/* Otherwise, queue a task to give the driver the next buffer. */
-	queue_task(&sparcaudio_output_tqueue, &tq_immediate);
+	drv->tqueue.next = NULL;
+	drv->tqueue.sync = 0;
+	drv->tqueue.routine = sparcaudio_output_done_task;
+	drv->tqueue.data = drv;
+
+	queue_task(&drv->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
 
 	/* Wake up any tasks that are waiting. */
-	wake_up_interruptible(&driver->output_write_wait);
+	wake_up_interruptible(&drv->output_write_wait);
 }
 
+void sparcaudio_input_done(struct sparcaudio_driver * drv)
+{
+	/* XXX Implement! */
+}
+
+
 
 /*
  *	VFS layer interface
@@ -104,6 +166,7 @@
 static int sparcaudio_read(struct inode * inode, struct file * file,
 			   char *buf, int count)
 {
+	/* XXX Implement me! */
 	return -EINVAL;
 }
 
@@ -127,18 +190,20 @@
 				return bytes_written > 0 ? bytes_written : -EINTR;
 		}
 
-		/* Determine how much we can copy in this run. */
+		/* Determine how much we can copy in this iteration. */
 		bytes_to_copy = count;
 		if (bytes_to_copy > PAGE_SIZE)
 			bytes_to_copy = PAGE_SIZE;
 
-		err = verify_area(VERIFY_READ, buf, bytes_to_copy);
-		if (err)
-			return err;
+		copy_from_user_ret(driver->output_buffers[driver->output_rear],
+			       buf, bytes_to_copy, -EFAULT);
 
-		memcpy_fromfs(driver->output_buffers[driver->output_rear], buf, bytes_to_copy);
+		printk(KERN_DEBUG "Stuffing %d in %d\n",
+			 bytes_to_copy, driver->output_rear);
 
 		/* Update the queue pointers. */
+		buf += bytes_to_copy;
+		count -= bytes_to_copy;
 		bytes_written += bytes_to_copy;
 		driver->output_sizes[driver->output_rear] = bytes_to_copy;
 		driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers;
@@ -162,76 +227,117 @@
 static int sparcaudio_ioctl(struct inode * inode, struct file * file,
 			    unsigned int cmd, unsigned long arg)
 {
+	int retval = 0;
+
 	switch (cmd) {
+	case AUDIO_DRAIN:
+		if (driver->output_count > 0) {
+			interruptible_sleep_on(&driver->output_drain_wait);
+			retval = (current->signal & ~current->blocked) ? -EINTR : 0;
+		}
+		break;
+
+	case AUDIO_GETDEV:
+		if (driver->ops->sunaudio_getdev) {
+			audio_device_t tmp;
+
+			driver->ops->sunaudio_getdev(driver, &tmp);
+
+			copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT);
+		} else
+			retval = -EINVAL;
+		break;
+
 	default:
 		if (driver->ops->ioctl)
-			return driver->ops->ioctl(inode,file,cmd,arg,driver);
+			retval = driver->ops->ioctl(inode,file,cmd,arg,driver);
 		else
-			return -EINVAL;
+			retval = -EINVAL;
 	}
-	return 0;
+
+	return retval;
 }
 
 static int sparcaudio_open(struct inode * inode, struct file * file)
 {
-	unsigned int minor = MINOR(inode->i_rdev);
-	int i;
+	int err;
 
-	/* We only support minor #4 (/dev/audio right now. */
-	if (minor != 4)
-		return -ENXIO;
+	/* A low-level audio driver must exist. */
+	if (!driver)
+		return -ENODEV;
 
-	/* Make sure that the driver is not busy. */
-	if (driver->busy)
-		return -EBUSY;
-
-	/* Setup the queue of output buffers. */
-	driver->num_output_buffers = 32;
-	driver->output_front = 0;
-	driver->output_rear = 0;
-	driver->output_count = 0;
-	driver->output_active = 0;
-	driver->output_buffers = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL);
-	driver->output_sizes = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL);
-	if (!driver->output_buffers || !driver->output_sizes)
-		return -ENOMEM;
+	/* We only support minor #4 (/dev/audio) right now. */
+	if (MINOR(inode->i_rdev) != 4)
+		return -ENXIO;
 
-	/* Allocate space for the output buffers. */
-	for (i = 0; i < driver->num_output_buffers; i++) {
-		driver->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
-		if (!driver->output_buffers[i])
-			return -ENOMEM;
+	/* If the driver is busy, then wait to get through. */
+	retry_open:
+	if (file->f_mode & FMODE_READ && driver->flags & SDF_OPEN_READ) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EBUSY;
+
+		interruptible_sleep_on(&driver->open_wait);
+		if (current->signal & ~current->blocked)
+			return -EINTR;
+		goto retry_open;
+	}
+	if (file->f_mode & FMODE_WRITE && driver->flags & SDF_OPEN_WRITE) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EBUSY;
+
+		interruptible_sleep_on(&driver->open_wait);
+		if (current->signal & ~current->blocked)
+			return -EINTR;
+		goto retry_open;
 	}
 
-	/* Allow the low-level driver to initialize itself. */
-	if (driver->ops->open)
-		driver->ops->open(inode,file,driver);
-
+	/* Mark the driver as locked for read and/or write. */
+	if (file->f_mode & FMODE_READ)
+		driver->flags |= SDF_OPEN_READ;
+	if (file->f_mode & FMODE_WRITE) {
+		driver->output_front = 0;
+		driver->output_rear = 0;
+		driver->output_count = 0;
+		driver->output_active = 0;
+		driver->flags |= SDF_OPEN_WRITE;
+	}  
 
-	/* Mark the driver as busy. */
-	driver->busy = 1;
+	/* Allow the low-level driver to initialize itself. */
+	if (driver->ops->open) {
+		err = driver->ops->open(inode,file,driver);
+		if (err < 0)
+			return err;
+	}
 
 	MOD_INC_USE_COUNT;
 
-	/* Success return. */
+	/* Success! */
 	return 0;
 }
 
 static void sparcaudio_release(struct inode * inode, struct file * file)
 {
-	int i;
+	/* Wait for any output still in the queue to be played. */
+	if (driver->output_count > 0)
+		interruptible_sleep_on(&driver->output_drain_wait);
 
+	/* Force any output to be stopped. */
+	driver->ops->stop_output(driver);
+	driver->output_active = 0;
+
+	/* Let the low-level driver do any release processing. */
 	if (driver->ops->release)
 		driver->ops->release(inode,file,driver);
 
-	MOD_DEC_USE_COUNT;
+	if (file->f_mode & FMODE_READ)
+		driver->flags &= ~(SDF_OPEN_READ);
 
-	driver->busy = 0;
+	if (file->f_mode & FMODE_WRITE)
+		driver->flags &= ~(SDF_OPEN_WRITE);
 
-	for (i = 0; i < driver->num_output_buffers; i++)
-		kfree(driver->output_buffers[i]);
-	kfree(driver->output_buffers);
-	kfree(driver->output_sizes);
+	MOD_DEC_USE_COUNT;
+
+	wake_up_interruptible(&driver->open_wait);
 }
 
 static struct file_operations sparcaudio_fops = {
@@ -239,13 +345,17 @@
 	sparcaudio_read,
 	sparcaudio_write,
 	NULL,			/* sparcaudio_readdir */
-	NULL,			/* sparcaudio_poll */
+	NULL,			/* sparcaudio_select */
 	sparcaudio_ioctl,
 	NULL,			/* sparcaudio_mmap */
 	sparcaudio_open,
 	sparcaudio_release
 };
 
+EXPORT_SYMBOL(register_sparcaudio_driver);
+EXPORT_SYMBOL(unregister_sparcaudio_driver);
+EXPORT_SYMBOL(sparcaudio_output_done);
+EXPORT_SYMBOL(sparcaudio_input_done);
 
 #ifdef MODULE
 int init_module(void)
@@ -261,11 +371,16 @@
 	amd7930_init();
 #endif
 
+#ifdef CONFIG_SPARCAUDIO_CS4231
+	cs4231_init();
+#endif
+
 	return 0;
 }
 
 #ifdef MODULE
 void cleanup_module(void)
 {
+	unregister_chrdev(SOUND_MAJOR, "sparcaudio");
 }
 #endif
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/audio.h linux/drivers/sbus/audio/audio.h
--- v2.1.28/linux/drivers/sbus/audio/audio.h	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/audio.h	Wed Mar  5 17:04:32 1997
@@ -8,29 +8,246 @@
 #ifndef _AUDIO_H_
 #define _AUDIO_H_
 
+/*
+ *	SunOS/Solaris /dev/audio interface
+ */
+
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/ioctl.h>
+
+/*
+ * This structure contains state information for audio device IO streams.
+ */
+typedef struct audio_prinfo {
+	/*
+	 * The following values describe the audio data encoding.
+	 */
+	unsigned int sample_rate;	/* samples per second */
+	unsigned int channels;	/* number of interleaved channels */
+	unsigned int precision;	/* bit-width of each sample */
+	unsigned int encoding;	/* data encoding method */
+
+	/*
+	 * The following values control audio device configuration
+	 */
+	unsigned int gain;		/* gain level: 0 - 255 */
+	unsigned int port;		/* selected I/O port (see below) */
+	unsigned int avail_ports;	/* available I/O ports (see below) */
+	unsigned int _xxx[2];		/* Reserved for future use */
+
+	unsigned int buffer_size;	/* I/O buffer size */
+
+	/*
+	 * The following values describe driver state
+	 */
+	unsigned int samples;		/* number of samples converted */
+	unsigned int eof;		/* End Of File counter (play only) */
+
+	unsigned char	pause;		/* non-zero for pause, zero to resume */
+	unsigned char	error;		/* non-zero if overflow/underflow */
+	unsigned char	waiting;	/* non-zero if a process wants access */
+	unsigned char balance;	/* stereo channel balance */
+
+	unsigned short minordev;
+
+	/*
+	 * The following values are read-only state flags
+	 */
+	unsigned char open;		/* non-zero if open access permitted */
+	unsigned char active;		/* non-zero if I/O is active */
+} audio_prinfo_t;
+
+
+/*
+ * This structure describes the current state of the audio device.
+ */
+typedef struct audio_info {
+	/*
+	 * Per-stream information
+	 */
+	audio_prinfo_t play;	/* output status information */
+	audio_prinfo_t record;	/* input status information */
+
+	/*
+	 * Per-unit/channel information
+	 */
+	unsigned int monitor_gain;	/* input to output mix: 0 - 255 */
+	unsigned char output_muted;	/* non-zero if output is muted */
+	unsigned char _xxx[3];	/* Reserved for future use */
+	unsigned int _yyy[3];		/* Reserved for future use */
+} audio_info_t;
+
+
+/*
+ * Audio encoding types
+ */
+#define	AUDIO_ENCODING_NONE	(0)	/* no encoding assigned	*/
+#define	AUDIO_ENCODING_ULAW	(1)	/* u-law encoding	*/
+#define	AUDIO_ENCODING_ALAW	(2)	/* A-law encoding	*/
+#define	AUDIO_ENCODING_LINEAR	(3)	/* Linear PCM encoding	*/
+#define	AUDIO_ENCODING_DVI	(104)	/* DVI ADPCM		*/
+#define	AUDIO_ENCODING_LINEAR8	(105)	/* 8 bit UNSIGNED	*/
+
+/*
+ * These ranges apply to record, play, and monitor gain values
+ */
+#define	AUDIO_MIN_GAIN	(0)	/* minimum gain value */
+#define	AUDIO_MAX_GAIN	(255)	/* maximum gain value */
+
+/*
+ * These values apply to the balance field to adjust channel gain values
+ */
+#define	AUDIO_LEFT_BALANCE	(0)	/* left channel only	*/
+#define	AUDIO_MID_BALANCE	(32)	/* equal left/right channel */
+#define	AUDIO_RIGHT_BALANCE	(64)	/* right channel only	*/
+#define	AUDIO_BALANCE_SHIFT	(3)
+
+/*
+ * Generic minimum/maximum limits for number of channels, both modes
+ */
+#define	AUDIO_MIN_PLAY_CHANNELS	(1)
+#define	AUDIO_MAX_PLAY_CHANNELS	(4)
+#define	AUDIO_MIN_REC_CHANNELS	(1)
+#define	AUDIO_MAX_REC_CHANNELS	(4)
+
+/*
+ * Generic minimum/maximum limits for sample precision
+ */
+#define	AUDIO_MIN_PLAY_PRECISION	(8)
+#define	AUDIO_MAX_PLAY_PRECISION	(32)
+#define	AUDIO_MIN_REC_PRECISION		(8)
+#define	AUDIO_MAX_REC_PRECISION		(32)
+
+/*
+ * Define some convenient names for typical audio ports
+ */
+/*
+ * output ports (several may be enabled simultaneously)
+ */
+#define	AUDIO_SPEAKER		0x01	/* output to built-in speaker */
+#define	AUDIO_HEADPHONE		0x02	/* output to headphone jack */
+#define	AUDIO_LINE_OUT		0x04	/* output to line out	 */
+
+/*
+ * input ports (usually only one at a time)
+ */
+#define	AUDIO_MICROPHONE	0x01	/* input from microphone */
+#define	AUDIO_LINE_IN		0x02	/* input from line in	 */
+#define	AUDIO_CD		0x04	/* input from on-board CD inputs */
+#define	AUDIO_INTERNAL_CD_IN	AUDIO_CD	/* input from internal CDROM */
+
+
+/*
+ * This macro initializes an audio_info structure to 'harmless' values.
+ * Note that (~0) might not be a harmless value for a flag that was
+ * a signed int.
+ */
+#define	AUDIO_INITINFO(i)	{					\
+	unsigned int	*__x__;						\
+	for (__x__ = (unsigned int *)(i);				\
+	    (char *) __x__ < (((char *)(i)) + sizeof (audio_info_t));	\
+	    *__x__++ = ~0);						\
+}
+
+
+/*
+ * Parameter for the AUDIO_GETDEV ioctl to determine current
+ * audio devices.
+ */
+#define	MAX_AUDIO_DEV_LEN	(16)
+typedef struct audio_device {
+	char name[MAX_AUDIO_DEV_LEN];
+	char version[MAX_AUDIO_DEV_LEN];
+	char config[MAX_AUDIO_DEV_LEN];
+} audio_device_t;
+
+
+/*
+ * Ioctl calls for the audio device.
+ */
+
+/*
+ * AUDIO_GETINFO retrieves the current state of the audio device.
+ *
+ * AUDIO_SETINFO copies all fields of the audio_info structure whose
+ * values are not set to the initialized value (-1) to the device state.
+ * It performs an implicit AUDIO_GETINFO to return the new state of the
+ * device.  Note that the record.samples and play.samples fields are set
+ * to the last value before the AUDIO_SETINFO took effect.  This allows
+ * an application to reset the counters while atomically retrieving the
+ * last value.
+ *
+ * AUDIO_DRAIN suspends the calling process until the write buffers are
+ * empty.
+ *
+ * AUDIO_GETDEV returns a structure of type audio_device_t which contains
+ * three strings.  The string "name" is a short identifying string (for
+ * example, the SBus Fcode name string), the string "version" identifies
+ * the current version of the device, and the "config" string identifies
+ * the specific configuration of the audio stream.  All fields are
+ * device-dependent -- see the device specific manual pages for details.
+ */
+#define	AUDIO_GETINFO	_IOR('A', 1, audio_info_t)
+#define	AUDIO_SETINFO	_IOWR('A', 2, audio_info_t)
+#define	AUDIO_DRAIN	_IO('A', 3)
+#define	AUDIO_GETDEV	_IOR('A', 4, audio_device_t)
+
+/*
+ * The following ioctl sets the audio device into an internal loopback mode,
+ * if the hardware supports this.  The argument is TRUE to set loopback,
+ * FALSE to reset to normal operation.  If the hardware does not support
+ * internal loopback, the ioctl should fail with EINVAL.
+ */
+#define	AUDIO_DIAG_LOOPBACK	_IOW('A', 101, int)
+
+#ifdef notneeded
+/*
+ * Structure sent up as a M_PROTO message on trace streams
+ */
+typedef struct audtrace_hdr audtrace_hdr_t;
+struct audtrace_hdr {
+	unsigned int seq;		/* Sequence number (per-aud_stream) */
+	int type;		/* device-dependent */
+	struct timeval timestamp;
+	char _f[8];		/* filler */
+};
+#endif
+
+
+
+/*
+ *	Linux kernel internal implementation.
+ */
+
 #ifdef __KERNEL__
 
 #include <linux/types.h>
 #include <linux/fs.h>
+#include <linux/tqueue.h>
 #include <linux/wait.h>
 
-
-#define NR_SPARCAUDIO_DRIVERS 1
-
+#define	SDF_OPEN_WRITE	0x00000001
+#define	SDF_OPEN_READ	0x00000002
 
 struct sparcaudio_driver
 {
 	const char * name;
 	struct sparcaudio_operations *ops;
 	void *private;
+	unsigned long flags;
 
-	/* Nonzero if the driver is busy. */
-	int busy;
+	/* Processes blocked on open() sit here. */
+	struct wait_queue *open_wait;
+
+	/* Task queue for this driver's bottom half. */
+	struct tq_struct tqueue;
 
 	/* Support for a circular queue of output buffers. */
 	__u8 **output_buffers;
 	size_t *output_sizes;
-	int num_output_buffers, output_front, output_rear, output_count, output_active;
+	int num_output_buffers, output_front, output_rear;
+	int output_count, output_active;
 	struct wait_queue *output_write_wait, *output_drain_wait;
 };
 
@@ -42,33 +259,28 @@
 		     struct sparcaudio_driver *);
 
 	/* Ask driver to begin playing a buffer. */
-	void (*start_output)(struct sparcaudio_driver *, __u8 * buffer, size_t count);
+	void (*start_output)(struct sparcaudio_driver *, __u8 *, unsigned long);
 
 	/* Ask driver to stop playing a buffer. */
 	void (*stop_output)(struct sparcaudio_driver *);
 
-	/* Set and get the audio encoding method. */
-	int (*set_encoding)(int encoding);
-	int (*get_encoding)(void);
-
-	/* Set and get the audio sampling rate (samples per second). */
-	int (*set_sampling_rate)(int sampling_rate);
-	int (*get_sampling_rate)(void);
-
-	/* Set and get the audio output port. */
-	int (*set_output_port)(int output_port);
-	int (*get_output_port)(void);
-
-	/* Set and get the audio input port. */
-	int (*set_input_port)(int input_port);
-	int (*get_input_port)(void);
+	/* Ask driver to begin recording into a buffer. */
+	void (*start_input)(struct sparcaudio_driver *, __u8 *, unsigned long);
+
+	/* Ask driver to stop recording. */
+	void (*stop_input)(struct sparcaudio_driver *);
+
+	/* Return driver name/version to caller. (/dev/audio specific) */
+	void (*sunaudio_getdev)(struct sparcaudio_driver *, audio_device_t *);
 };
 
 extern int register_sparcaudio_driver(struct sparcaudio_driver *);
 extern int unregister_sparcaudio_driver(struct sparcaudio_driver *);
-extern void sparcaudio_output_done(void);
+extern void sparcaudio_output_done(struct sparcaudio_driver *);
+extern void sparcaudio_input_done(struct sparcaudio_driver *);
 extern int sparcaudio_init(void);
 extern int amd7930_init(void);
+extern int cs4231_init(void);
 
 #endif
 
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c
--- v2.1.28/linux/drivers/sbus/audio/cs4231.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/sbus/audio/cs4231.c	Wed Mar  5 17:04:32 1997
@@ -14,13 +14,15 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
+#include <linux/malloc.h>
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/io.h>
-#include <asm/auxio.h>
 #include <asm/delay.h>
+#include <asm/sbus.h>
+
 #include "audio.h"
 #include "cs4231.h"
 
@@ -32,219 +34,215 @@
 #define MIN(_a,_b)      ((_a)<(_b)?(_a):(_b))
 #endif
 
-static int cs4231_node, cs4231_irq, cs4231_is_revision_a, cs4231_ints_on = 0;
-static unsigned int cs4231_monitor_gain_value; 
-cs4231_regs_size
-
-static int cs4231_output_muted_value;
-
-static struct cs4231_stream_info cs4231_input;
-static struct cs4231_stream_info cs4231_output;
-
-static int cs4231_busy = 0, cs4231_need_init = 0;
+#define MAX_DRIVERS 1
+static struct sparcaudio_driver drivers[MAX_DRIVERS];
+static int num_drivers;
+
+static int cs4231_playintr(struct sparcaudio_driver *drv);
+static int cs4231_recintr(struct sparcaudio_driver *drv);
+static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value);
+static void cs4231_mute(struct sparcaudio_driver *drv);
+static void cs4231_pollinput(struct sparcaudio_driver *drv);
+static int cs4231_attach(struct sparcaudio_driver *drv, int node,
+                          struct linux_sbus *sbus);
+
+#define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000);
 
-static volatile struct cs4231_chip *cs4231_chip = NULL;
-
-static __u8 * ptr;
-static size_t count;
-
-#define CHIP_BUG udelay(100); cs4231_ready(); udelay(1000);
-
-static void cs4231_ready(void) 
+/* Disable mode change, let chip auto-calibrate */
+static void cs4231_ready(struct sparcaudio_driver *drv) 
 {
-  register unsigned int x = 0;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  unsigned int x = 0;
 
-  cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
-  while (cs4231_chip->pioregs.iar == IAR_NOT_READY && x <= CS_TIMEOUT) {
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  while (cs4231_chip->pioregs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) {
     x++;
   }
 
   x = 0;
-
-  cs4231_chip->pioregs.iar = 0x0b;
-
-  while (cs4231_chip->pioregs.idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) {
+  cs4231_chip->pioregs->iar = 0x0b;
+  while (cs4231_chip->pioregs->idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) {
     x++;
   }
 }
 
-/* Enable cs4231 interrupts atomically. */
-static __inline__ void cs4231_enable_ints(void)
-{
-	register unsigned long flags;
-
-	if (cs4231_ints_on)
-		return;
-
-	save_and_cli(flags);
-	/* do init here
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE;
-	*/
-	restore_flags(flags);
-
-	cs4231_ints_on = 1;
-}
-
-/* Disable cs4231 interrupts atomically. */
-static __inline__ void cs4231_disable_ints(void)
-{
-	register unsigned long flags;
-
-	if (!cs4231_ints_on)
-		return;
-
-	save_and_cli(flags);
-/*
-	amd7930_regs->cr = AMR_INIT;
-	amd7930_regs->dr = AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS;
-*/
-	restore_flags(flags);
-
-	cs4231_ints_on = 0;
-}  
-
-
 /* Audio interrupt handler. */
 static void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	__u8 dummy;
-
-        /* Clear the interrupt. */
-        dummy = cs4231_chip->dmaregs.dmacsr;
-
-        cs4231_chip->dmaregs.dmacsr = dummy;
-
+  struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  __u8 dummy;
+  int ic = 1;
+  
+  /* Clear the interrupt. */
+  dummy = cs4231_chip->dmaregs.dmacsr;
+  cs4231_chip->dmaregs.dmacsr = dummy;
+
+  /* now go through and figure out what gets to claim the interrupt */
+  if (dummy & CS_PLAY_INT) {
+    if (dummy & CS_XINT_PNVA) {
+      /* recalculate number of samples */
+      cs4231_playintr(drv);
+    }
+    ic = 0;
+  }
+  if (dummy & CS_CAPT_INT) {
+    if (dummy & CS_XINT_CNVA) {
+      /* recalculate number of samples */
+      cs4231_recintr(drv);
+    }
+    ic = 0;
+  }
+  if ((dummy & CS_XINT_CEMP) 
+      && (cs4231_chip->perchip_info.record.active == 0)) 
+    {
+      ic = 0;
+    }
+  if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) {
+    cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE);
+    cs4231_chip->pioregs->iar = 0x9;
+    cs4231_chip->pioregs->idr &= PEN_DISABLE;
+    
+    cs4231_mute(drv);
+    
+    /* recalculate number of samples */
+    /* cleanup DMA */
+    ic = 0;
+  }
+  if (dummy & CS_GENL_INT) {
+    ic = 0;
+  }
 }
 
-static unsigned int cs4231_output_muted(unsigned int value)
+/* Set output mute */
+static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   if (!value) {
-    cs4231_chip->pioregs.iar = 0x7;
-    cs4231_chip->pioregs.idr &= OUTCR_UNMUTE;
-    cs4231_chip->pioregs.iar = 0x6;
-    cs4231_chip->pioregs.idr &= OUTCR_UNMUTE;
-    cs4231_output_muted_value = 0;
+    cs4231_chip->pioregs->iar = 0x7;
+    cs4231_chip->pioregs->idr &= OUTCR_UNMUTE;
+    cs4231_chip->pioregs->iar = 0x6;
+    cs4231_chip->pioregs->idr &= OUTCR_UNMUTE;
+    cs4231_chip->perchip_info.output_muted = 0;
   } else {
-    cs4231_chip->pioregs.iar = 0x7;
-    cs4231_chip->pioregs.idr |= OUTCR_MUTE;
-    cs4231_chip->pioregs.iar = 0x6;
-    cs4231_chip->pioregs.idr |= OUTCR_MUTE;
-    cs4231_output_muted_value = 1;
+    cs4231_chip->pioregs->iar = 0x7;
+    cs4231_chip->pioregs->idr |= OUTCR_MUTE;
+    cs4231_chip->pioregs->iar = 0x6;
+    cs4231_chip->pioregs->idr |= OUTCR_MUTE;
+    cs4231_chip->perchip_info.output_muted = 1;
   }
-  return (cs4231_output_muted_value);
+  return /*(cs4231_chip->perchip_info.output_muted)*/;
 }
 
-static unsigned int cs4231_out_port(unsigned int value)
+/* Set chip "output" port */
+static unsigned int cs4231_out_port(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int r = 0;
 
   /* You can have any combo you want. Just don't tell anyone. */
 
-  cs4231_chip->pioregs.iar = 0x1a;
-  cs4231_chip->pioregs.idr |= MONO_IOCR_MUTE;
-  cs4231_chip->pioregs.iar = 0x0a;
-  cs4231_chip->pioregs.idr |= PINCR_LINE_MUTE;
-  cs4231_chip->pioregs.idr |= PINCR_HDPH_MUTE;
+  cs4231_chip->pioregs->iar = 0x1a;
+  cs4231_chip->pioregs->idr |= MONO_IOCR_MUTE;
+  cs4231_chip->pioregs->iar = 0x0a;
+  cs4231_chip->pioregs->idr |= PINCR_LINE_MUTE;
+  cs4231_chip->pioregs->idr |= PINCR_HDPH_MUTE;
 
   if (value & AUDIO_SPEAKER) {
-    cs4231_chip->pioregs.iar = 0x1a;
-    cs4231_chip->pioregs.idr &= ~MONO_IOCR_MUTE;
+    cs4231_chip->pioregs->iar = 0x1a;
+    cs4231_chip->pioregs->idr &= ~MONO_IOCR_MUTE;
     r |= AUDIO_SPEAKER;
   }
 
   if (value & AUDIO_HEADPHONE) {
-   cs4231_chip->pioregs.iar = 0x0a;
-   cs4231_chip->pioregs.idr &= ~PINCR_HDPH_MUTE;
+   cs4231_chip->pioregs->iar = 0x0a;
+   cs4231_chip->pioregs->idr &= ~PINCR_HDPH_MUTE;
    r |= AUDIO_HEADPHONE;
   }
 
   if (value & AUDIO_LINE_OUT) {
-    cs4231_chip->pioregs.iar = 0x0a;
-    cs4231_chip->pioregs.idr &= ~PINCR_LINE_MUTE;
+    cs4231_chip->pioregs->iar = 0x0a;
+    cs4231_chip->pioregs->idr &= ~PINCR_LINE_MUTE;
     r |= AUDIO_LINE_OUT;
   }
   
   return (r);
 }
 
-static unsigned int cs4231_in_port(unsigned int value)
+/* Set chip "input" port */
+static unsigned int cs4231_in_port(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int r = 0;
 
   /* The order of these seems to matter. Can't tell yet why. */
-
   if (value & AUDIO_INTERNAL_CD_IN) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = CDROM_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = CDROM_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_INTERNAL_CD_IN;
   }
   if ((value & AUDIO_LINE_IN)) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = LINE_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = LINE_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_LINE_IN;
   } else if (value & AUDIO_MICROPHONE) {
-    cs4231_chip->pioregs.iar = 0x1;
-    cs4231_chip->pioregs.idr = MIC_ENABLE(cs4231_chip->pioregs.idr);
-    cs4231_chip->pioregs.iar = 0x0;
-    cs4231_chip->pioregs.idr = MIC_ENABLE(cs4231_chip->pioregs.idr);
+    cs4231_chip->pioregs->iar = 0x1;
+    cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr);
+    cs4231_chip->pioregs->iar = 0x0;
+    cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr);
     r = AUDIO_MICROPHONE;
   }
 
   return (r);
 }
 
-static unsigned int cs4231_monitor_gain(unsigned int value)
+/* Set chip "monitor" gain */
+static unsigned int cs4231_monitor_gain(struct sparcaudio_driver *drv, unsigned int value)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   int a = 0;
 
-  a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) /
-                            (AUDIO_MAX_GAIN + 1));
+  a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
 
-  cs4231_chip->pioregs.iar = 0x0d;
+  cs4231_chip->pioregs->iar = 0x0d;
   if (a >= CS4231_MON_MAX_ATEN) 
-    cs4231_chip->pioregs.idr = LOOPB_OFF;
+    cs4231_chip->pioregs->idr = LOOPB_OFF;
   else 
-    cs4231_chip->pioregs.idr = ((a << 2) | LOOPB_ON);
-
+    cs4231_chip->pioregs->idr = ((a << 2) | LOOPB_ON);
 
-  if (value == AUDIO_MAX_GAIN)
-    return AUDIO_MAX_GAIN;
+  if (value == AUDIO_MAX_GAIN) return AUDIO_MAX_GAIN;
 
-  return ((CS4231_MAX_DEV_ATEN - a) * (AUDIO_MAX_GAIN + 1) /
-          (CS4231_MAX_DEV_ATEN + 1));
+  return ((CS4231_MAX_DEV_ATEN - a) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_DEV_ATEN + 1));
 }
 
-/* Set record gain */
-static unsigned int cs4231_record_gain(unsigned int value, unsigned char balance)
+/* Set chip record gain */
+static unsigned int cs4231_record_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int tmp = 0, r, l, ra, la;
   unsigned char old_gain;
 
-
   r = l = value;
 
   if (balance < AUDIO_MID_BALANCE) {
-    r = MAX(0, (int)(value -
-                     ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
+    r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
   } else if (balance > AUDIO_MID_BALANCE) {
-    l = MAX(0, (int)(value -
-                     ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
+    l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
   }
 
   la = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
   ra = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
   
-  cs4231_chip->pioregs.iar = 0x0;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = RECGAIN_SET(old_gain, la);
-  cs4231_chip->pioregs.iar = 0x1;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = RECGAIN_SET(old_gain, ra);
+  cs4231_chip->pioregs->iar = 0x0;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, la);
+  cs4231_chip->pioregs->iar = 0x1;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, ra);
   
   if (l == value) {
     (l == 0) ? (tmp = 0) : (tmp = ((la + 1) * AUDIO_MAX_GAIN) / (CS4231_MAX_GAIN + 1));
@@ -254,194 +252,374 @@
   return (tmp);
 }
 
-/* Set play gain */
-static unsigned int cs4231_play_gain(unsigned int value, unsigned char balance)
+/* Set chip play gain */
+static unsigned int cs4231_play_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance)
 {
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   unsigned int tmp = 0, r, l, ra, la;
   unsigned char old_gain;
 
   r = l = value;
   if (balance < AUDIO_MID_BALANCE) {
-    r = MAX(0, (int)(value -
-                     ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
+    r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT)));
   } else if (balance > AUDIO_MID_BALANCE) {
-    l = MAX(0, (int)(value -
-                     ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
+    l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT)));
   }
 
   if (l == 0) {
     la = CS4231_MAX_DEV_ATEN;
   } else {
-    la = CS4231_MAX_ATEN -
-      (l * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
+    la = CS4231_MAX_ATEN - (l * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
   }
   if (r == 0) {
     ra = CS4231_MAX_DEV_ATEN;
   } else {
-    ra = CS4231_MAX_ATEN -
-      (r * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
+    ra = CS4231_MAX_ATEN - (r * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1));
   }
   
-  cs4231_chip->pioregs.iar = 0x6;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = GAIN_SET(old_gain, la);
-  cs4231_chip->pioregs.iar = 0x7;
-  old_gain = cs4231_chip->pioregs.idr;
-  cs4231_chip->pioregs.idr = GAIN_SET(old_gain, ra);
+  cs4231_chip->pioregs->iar = 0x6;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = GAIN_SET(old_gain, la);
+  cs4231_chip->pioregs->iar = 0x7;
+  old_gain = cs4231_chip->pioregs->idr;
+  cs4231_chip->pioregs->idr = GAIN_SET(old_gain, ra);
   
   if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
     tmp = value;
   } else {
     if (l == value) {
-      tmp = ((CS4231_MAX_ATEN - la) *
-                 (AUDIO_MAX_GAIN + 1) /
-                 (CS4231_MAX_ATEN + 1));
+      tmp = ((CS4231_MAX_ATEN - la) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1));
     } else if (r == value) {
-      tmp = ((CS4231_MAX_ATEN - ra) *
-                 (AUDIO_MAX_GAIN + 1) /
-                 (CS4231_MAX_ATEN + 1));
+      tmp = ((CS4231_MAX_ATEN - ra) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1));
     }
   }
   return (tmp);
 }
 
 /* Reset the audio chip to a sane state. */
-static void cs4231_reset(void)
+static void cs4231_reset(struct sparcaudio_driver *drv)
 {
-        cs4231_chip->dmaregs.dmacsr = APC_RESET;
-        cs4231_chip->dmaregs.dmacsr = 0x00;
-        cs4231_chip->dmaregs.dmacsr |= APC_CODEC_PDN;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        udelay(20);
+  cs4231_chip->dmaregs.dmacsr = CS_CHIP_RESET;
+  cs4231_chip->dmaregs.dmacsr = 0x00;
+  cs4231_chip->dmaregs.dmacsr |= CS_CDC_RESET;
+  
+  udelay(100);
+  
+  cs4231_chip->dmaregs.dmacsr &= ~(CS_CDC_RESET);
+  cs4231_chip->pioregs->iar |= IAR_AUTOCAL_BEGIN;
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x0c;
+  cs4231_chip->pioregs->idr = MISC_IR_MODE2;
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+  cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+  cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */
+  
+  CHIP_BUG
+    
+  cs4231_chip->pioregs->iar = 0x19;
+  
+  /* see what we can turn on */
+  if (cs4231_chip->pioregs->idr & CS4231A)
+    cs4231_chip->status |= CS_STATUS_REV_A;
+  else
+    cs4231_chip->status &= ~CS_STATUS_REV_A;
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x10;
+  cs4231_chip->pioregs->idr = OLB_ENABLE;
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x11;
+  if (cs4231_chip->status & CS_STATUS_REV_A)
+    cs4231_chip->pioregs->idr = (HPF_ON | XTALE_ON);
+  else
+    cs4231_chip->pioregs->idr = (HPF_ON);
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1a;
+  cs4231_chip->pioregs->idr = 0x00;
+  
+  /* Now set things up for defaults */
+  cs4231_chip->perchip_info.play.port = cs4231_out_port(drv, AUDIO_SPEAKER);
+  cs4231_chip->perchip_info.record.port = cs4231_in_port(drv, AUDIO_MICROPHONE);
+  cs4231_chip->perchip_info.play.gain = cs4231_play_gain(drv, CS4231_DEFAULT_PLAYGAIN, AUDIO_MID_BALANCE);
+  cs4231_chip->perchip_info.record.gain = cs4231_record_gain(drv, CS4231_DEFAULT_RECGAIN, AUDIO_MID_BALANCE);
+  cs4231_chip->perchip_info.monitor_gain = cs4231_monitor_gain(drv, LOOPB_OFF);
+  
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  
+  cs4231_ready(drv);
+  
+  cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x09;
+  cs4231_chip->pioregs->idr &= ACAL_DISABLE;
+  cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END;
+  
+  cs4231_ready(drv);
 
-        cs4231_chip->dmaregs.dmacsr &= ~(APC_CODEC_PDN);
-        cs4231_chip->pioregs.iar |= IAR_AUTOCAL_BEGIN;
+  cs4231_output_muted(drv, 0);
+}
 
-        CHIP_BUG
+static void cs4231_mute(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x0c;
-        cs4231_chip->pioregs.idr = MISC_IR_MODE2;
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x08;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+  if (!(cs4231_chip->status & CS_STATUS_REV_A)) {
+    cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN;
+    udelay(100);
+    cs4231_chip->pioregs->iar = IAR_AUTOCAL_END;
+    CHIP_BUG
+  }
+}
 
-        CHIP_BUG
+/* Not yet useful */
+#if 0
+static int cs4231_len_to_sample(struct sparcaudio_driver *drv, int length, int direction)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int sample;
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1c;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+  if (/* number of channels == 2*/0) {
+    sample = (length/2);
+  } else {
+    sample = length;
+  }
+  if (/*encoding == AUDIO_ENCODING_LINEAR*/0) {
+    sample = sample/2;
+  }
+  return (sample);
+}
+#endif
 
-        CHIP_BUG
+static int cs4231_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{	
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_chip->pioregs.iar = 0x19;
+  /* Set the default audio parameters. */
+  
+  cs4231_chip->perchip_info.play.sample_rate = CS4231_RATE;
+  cs4231_chip->perchip_info.play.channels = CS4231_CHANNELS;
+  cs4231_chip->perchip_info.play.precision = CS4231_PRECISION;
+  cs4231_chip->perchip_info.play.encoding = AUDIO_ENCODING_ULAW;
+  
+  cs4231_chip->perchip_info.record.sample_rate = CS4231_RATE;
+  cs4231_chip->perchip_info.record.channels = CS4231_CHANNELS;
+  cs4231_chip->perchip_info.record.precision = CS4231_PRECISION;
+  cs4231_chip->perchip_info.record.encoding = AUDIO_ENCODING_ULAW;
+  
+  cs4231_ready(drv);
+  
+  cs4231_chip->status |= CS_STATUS_NEED_INIT;
+  
+  CHIP_BUG
+    
+  MOD_INC_USE_COUNT;
+  
+  return 0;
+}
 
-        if (cs4231_chip->pioregs.idr & CS4231A)
-                cs4231_is_revision_a = 1;
-        else
-                cs4231_is_revision_a = 0;
+static void cs4231_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+{
+  /* zero out any info about what data we have as well */
+  /* should insert init on close variable optionally calling cs4231_reset() */
+  MOD_DEC_USE_COUNT;
+}
+
+static int cs4231_playintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  /* Send the next byte of outgoing data. */
+#if 0
+  if (cs4231_chip->output_ptr && cs4231_chip->output_count > 0) {
+    cs4231_chip->dmaregs.dmapnva = dma_handle;
+    cs4231_chip->dmaregs.dmapnc = length; 
+    cs4231_chip->output_ptr++;
+    cs4231_chip->output_count--;
+    
+    /* Done with the buffer? Notify the midlevel driver. */
+    if (cs4231_chip->output_count == 0) {
+      cs4231_chip->output_ptr = NULL;
+      cs4231_chip->output_count = 0;
+      sparcaudio_output_done(drv);
+    }
+  }
+#endif
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x10;
-        cs4231_chip->pioregs.idr = (u_char)OLB_ENABLE;
+static void cs4231_recmute(int fmt)
+{
+  switch (fmt) {
+  case AUDIO_ENCODING_LINEAR:
+    /* Insert 0x00 from "here" to end of data stream */
+    break;
+  case AUDIO_ENCODING_ALAW:
+    /* Insert 0xd5 from "here" to end of data stream */
+    break;
+  case AUDIO_ENCODING_ULAW:
+    /* Insert 0xff from "here" to end of data stream */
+    break;
+  }
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x11;
-        if (cs4231_is_revision_a)
-                cs4231_chip->pioregs.idr = (HPF_ON | XTALE_ON);
-        else
-                cs4231_chip->pioregs.idr = (HPF_ON);
+static int cs4231_recintr(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  cs4231_recmute(cs4231_chip->perchip_info.record.encoding);
+
+  if (cs4231_chip->perchip_info.record.active == 0) {
+    cs4231_pollinput(drv);
+    cs4231_chip->pioregs->iar = 0x9;
+    cs4231_chip->pioregs->idr &= CEN_DISABLE;
+  }
+  /* Read the next byte of incoming data. */
+#if 0
+  if (cs4231_chip->input_ptr && cs4231_chip->input_count > 0) {
+    cs4231_chip->dmaregs.dmacnva = dma_handle;
+    cs4231_chip->dmaregs.dmacnc = length;
+    cs4231_chip->input_ptr++;
+    cs4231_chip->input_count--;
+    
+    /* Done with the buffer? Notify the midlevel driver. */
+    if (cs4231_chip->input_count == 0) {
+      cs4231_chip->input_ptr = NULL;
+      cs4231_chip->input_count = 0;
+      sparcaudio_input_done(drv);
+    }
+  }
+#endif
+}
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1a;
-        cs4231_chip->pioregs.idr = 0x00;
+static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_output.gain = cs4231_play_gain(CS4231_DEFAULT_PLAYGAIN,
-                                              AUDIO_MID_BALANCE);
-        cs4231_input.gain = cs4231_record_gain(CS4231_DEFAULT_RECGAIN,
-                                               AUDIO_MID_BALANCE);
+  if (cs4231_chip->perchip_info.play.active || (cs4231_chip->perchip_info.play.pause))
+    return;
 
-        cs4231_output.port = cs4231_out_port(AUDIO_SPEAKER);
-        cs4231_input.port = cs4231_in_port(AUDIO_MICROPHONE);
+  cs4231_ready(drv);
 
-        cs4231_monitor_gain_value = cs4231_monitor_gain(LOOPB_OFF);
+  if (cs4231_chip->status & CS_STATUS_NEED_INIT)
+    {
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
 
-        cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
+      CHIP_BUG
 
-        cs4231_ready();
+      cs4231_chip->status &= ~CS_STATUS_NEED_INIT;
+    }
 
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x09;
-        cs4231_chip->pioregs.idr &= ACAL_DISABLE;
-        cs4231_chip->pioregs.iar = (u_char)IAR_AUTOCAL_END;
+  if (!cs4231_chip->perchip_info.play.pause) 
+    {
+      /* init dma foo here */
+      cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_PLAY;
+      cs4231_chip->dmaregs.dmacsr &= ~CS_PPAUSE;
+      if (cs4231_playintr(drv)) {
+	cs4231_chip->dmaregs.dmacsr |= CS_PLAY_SETUP;
+	cs4231_chip->pioregs->iar = 0x9;
+	cs4231_chip->pioregs->idr |= PEN_ENABLE;
+      }
+    }
+  cs4231_chip->perchip_info.play.active = 1;
+}
 
-        cs4231_ready();
+static void cs4231_stop_output(struct sparcaudio_driver *drv)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-        cs4231_output_muted_value = cs4231_output_muted(0x0);
+  cs4231_chip->perchip_info.play.active = 0;
+  cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE);
 }
 
-static int cs4231_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
+static void cs4231_pollinput(struct sparcaudio_driver *drv)
 {
-	int level;
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+  int x = 0;
 
-        /* Set the default audio parameters. */
+  while (!(cs4231_chip->dmaregs.dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) {
+    x++;
+  }
+  cs4231_chip->dmaregs.dmacsr |= CS_XINT_CEMP;
+}
 
-        cs4231_output.sample_rate = CS4231_RATE;
-        cs4231_output.channels = CS4231_CHANNELS;
-        cs4231_output.precision = CS4231_PRECISION;
-        cs4231_output.encoding = AUDIO_ENCODING_ULAW;
-
-        cs4231_input.sample_rate = CS4231_RATE;
-        cs4231_input.channels = CS4231_CHANNELS;
-        cs4231_input.precision = CS4231_PRECISION;
-        cs4231_input.encoding = AUDIO_ENCODING_ULAW;
-
-        cs4231_ready();
-
-        cs4231_need_init = 1;
-#if 1
-        /* Arguably this should only happen once. I need to play around 
-         * on a Solaris box and see what happens
-         */
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x08;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
-        cs4231_chip->pioregs.iar = IAR_AUTOCAL_BEGIN | 0x1c;
-        cs4231_chip->pioregs.idr = DEFAULT_DATA_FMAT;
+static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count)
+{
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
 
-#endif
+  if (cs4231_chip->perchip_info.record.active || (cs4231_chip->perchip_info.record.pause))
+    return;
 
-        CHIP_BUG
+  cs4231_ready(drv);
 
-	MOD_INC_USE_COUNT;
+  if (cs4231_chip->status & CS_STATUS_NEED_INIT)
+    {
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
+      cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
+      cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT;
 
-	return 0;
-}
+      CHIP_BUG
 
-static void cs4231_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv)
-{
-	cs4231_disable_ints();
-	MOD_DEC_USE_COUNT;
+      cs4231_chip->status &= ~CS_STATUS_NEED_INIT;
+    }
+
+  if (!cs4231_chip->perchip_info.record.pause)
+    {
+      /* init dma foo here */
+      cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_CAPT;
+      cs4231_chip->dmaregs.dmacsr &= ~CS_CPAUSE;
+      cs4231_recintr(drv);
+      cs4231_chip->dmaregs.dmacsr |= CS_CAPT_SETUP;
+      cs4231_chip->pioregs->iar = 0x9;
+      cs4231_chip->pioregs->idr |= CEN_ENABLE;
+    }
+  cs4231_chip->perchip_info.record.active = 1;
 }
 
-static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, size_t the_count)
+static void cs4231_stop_input(struct sparcaudio_driver *drv)
 {
-	count = the_count;
-	ptr = buffer;
-	cs4231_enable_ints();
+  struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+  cs4231_chip->perchip_info.record.active = 0;
+  cs4231_chip->dmaregs.dmacsr |= (CS_CPAUSE);
+
+  cs4231_pollinput(drv);
+
+  /* need adjust the end pointer, process the input, and clean up the dma */
+
+  cs4231_chip->pioregs->iar = 0x09;
+  cs4231_chip->pioregs->idr &= CEN_DISABLE;
 }
 
-static void cs4231_stop_output(struct sparcaudio_driver *drv)
+static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
+                                 audio_device_t * audinfo)
 {
-	cs4231_disable_ints();
-	ptr = NULL;
-	count = 0;
+        strncpy(audinfo->name, "cs4231", sizeof(audinfo->name) - 1);
+        strncpy(audinfo->version, "x", sizeof(audinfo->version) - 1);
+        strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1);
 }
 
 
+/* The ioctl handler should be expected to identify itself and handle loopback
+   mode */
+/* There will also be a handler for getinfo and setinfo */
+
 static struct sparcaudio_operations cs4231_ops = {
 	cs4231_open,
 	cs4231_release,
 	NULL,			/* cs4231_ioctl */
 	cs4231_start_output,
 	cs4231_stop_output,
-};
-
-static struct sparcaudio_driver cs4231_drv = {
-	"cs4231",
-	&cs4231_ops,
+	cs4231_start_input,
+        cs4231_stop_input,
+	cs4231_audio_getdev,
 };
 
 /* Probe for the cs4231 chip and then attach the driver. */
@@ -451,68 +629,127 @@
 __initfunc(int cs4231_init(void))
 #endif
 {
-	struct linux_prom_registers regs[1];
-	struct linux_prom_irqs irq;
-	int err;
+  struct linux_sbus *bus;
+  struct linux_sbus_device *sdev;
+  int cs4231_node;
+  
+  /* Find the PROM CS4231 node. */
+  /* There's an easier way, and I should FIXME */
+  cs4231_node = prom_getchild(prom_root_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
+  cs4231_node = prom_getchild(cs4231_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
+  cs4231_node = prom_getchild(cs4231_node);
+  cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
+  
+  if (cs4231_node && cs4231_attach(&drivers[0], cs4231_node, NULL) == 0)
+    num_drivers = 1;
+  else
+    num_drivers = 0;
+  
+  /* Probe each SBUS for cs4231 chips. */
+  for_all_sbusdev(sdev,bus) {
+    if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
+      /* Don't go over the max number of drivers. */
+      if (num_drivers >= MAX_DRIVERS)
+	continue;
+      
+      if (cs4231_attach(&drivers[num_drivers],
+			sdev->prom_node, sdev->my_bus) == 0)
+	num_drivers++;
+    }
+  }
+  
+  /* Only return success if we found some cs4231 chips. */
+  return (num_drivers > 0) ? 0 : -EIO;
+}
 
-#ifdef MODULE
-	register_symtab(0);
+/* Attach to an cs4231 chip given its PROM node. */
+static int cs4231_attach(struct sparcaudio_driver *drv, int node,
+                          struct linux_sbus *sbus)
+{
+  struct linux_prom_registers regs;
+  struct linux_prom_irqs irq;
+  struct cs4231_chip *cs4231_chip;
+  int err;
+
+  /* Allocate our private information structure. */
+  drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
+  if (!drv->private)
+    return -ENOMEM;
+
+  /* Point at the information structure and initialize it. */
+  drv->ops = &cs4231_ops;
+  cs4231_chip = (struct cs4231_chip *)drv->private;
+#if 0
+  cs4231_chip->input_ptr = NULL;
+  cs4231_chip->input_count = 0;
+  cs4231_chip->output_ptr = NULL;
+  cs4231_chip->output_count = 0;
 #endif
 
-        /* Find the PROM CS4231 node. */
-        cs4231_node = prom_getchild(prom_root_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"iommu");
-        cs4231_node = prom_getchild(cs4231_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"sbus");
-        cs4231_node = prom_getchild(cs4231_node);
-        cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231");
-
-	if (!cs4231_node)
-		return -EIO;
-
-	/* XXX Add for_each_sbus() search as well for LX and friends. */
-	/* XXX Copy out for prom_apply_sbus_ranges. */
-
-	/* Map the registers into memory. */
-        prom_getproperty(cs4231_node, "reg", (char *)regs, sizeof(regs));
-	cs4231_regs_size = regs[0].reg_size;
-	cs4231_regs = sparc_alloc_io(regs[0].phys_addr, 0, regs[0].reg_size,
-				      "cs4231", regs[0].which_io, 0);
-	if (!cs4231_regs) {
-		printk(KERN_ERR "cs4231: could not allocate registers\n");
-		return -EIO;
-	}
-
-	/* Disable cs4231 interrupt generation. */
-	cs4231_disable_ints();
-
-        /* Reset the audio chip. */
-        cs4231_reset();
-
-	/* Attach the interrupt handler to the audio interrupt. */
-        prom_getproperty(cs4231_node, "intr", (char *)&irq, sizeof(irq));
-        cs4231_irq = irq.pri;
-        request_irq(cs4231_irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
-        enable_irq(cs4231_irq);
-
-	/* Register ourselves with the midlevel audio driver. */
-	err = register_sparcaudio_driver(&cs4231_drv);
-	if (err < 0) {
-		/* XXX We should do something. Complain for now. */
-		printk(KERN_ERR "cs4231: really screwed now\n");
-		return -EIO;
-	}
-
-	return 0;
+  /* Map the registers into memory. */
+  prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
+  if (sbus)
+    prom_apply_sbus_ranges(sbus, &regs, 1);
+  cs4231_chip->regs_size = regs.reg_size;
+  cs4231_chip->pioregs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size,
+				      "cs4231", regs.which_io, 0);
+  if (!cs4231_chip->pioregs) {
+    printk(KERN_ERR "cs4231: could not allocate registers\n");
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  /* Reset the audio chip. */
+  cs4231_reset(drv);
+
+  /* Attach the interrupt handler to the audio interrupt. */
+  prom_getproperty(node, "intr", (char *)&irq, sizeof(irq));
+  cs4231_chip->irq = irq.pri;
+  request_irq(cs4231_chip->irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL);
+  enable_irq(cs4231_chip->irq);
+
+  /* Register ourselves with the midlevel audio driver. */
+  err = register_sparcaudio_driver(drv);
+  if (err < 0) {
+    printk(KERN_ERR "cs4231: unable to register\n");
+    disable_irq(cs4231_chip->irq);
+    free_irq(cs4231_chip->irq, drv);
+    sparc_free_io(cs4231_chip->pioregs, cs4231_chip->regs_size);
+    kfree(drv->private);
+    return -EIO;
+  }
+
+  /* Announce the hardware to the user. */
+  printk(KERN_INFO "cs4231 at 0x%lx irq %d\n",
+	 (unsigned long)cs4231_chip->pioregs, cs4231_chip->irq);
+  
+  /* Success! */
+  return 0;
 }
 
 #ifdef MODULE
+/* Detach from an cs4231 chip given the device structure. */
+static void cs4231_detach(struct sparcaudio_driver *drv)
+{
+        struct cs4231_chip *info = (struct cs4231_chip *)drv->private;
+
+        unregister_sparcaudio_driver(drv);
+        disable_irq(info->irq);
+        free_irq(info->irq, drv);
+        sparc_free_io(info->pioregs, info->regs_size);
+        kfree(drv->private);
+}
+
 void cleanup_module(void)
 {
-	unregister_sparcaudio_driver(&cs4231_drv);
-	cs4231_disable_ints();
-	disable_irq(cs4231_irq);
-	free_irq(cs4231_irq, NULL);
-	sparc_free_io(cs4231_regs, cs4231_regs_size);
+        register int i;
+
+        for (i = 0; i < num_drivers; i++) {
+                cs4231_detach(&drivers[i]);
+                num_drivers--;
+        }
 }
 #endif
+
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/audio/cs4231.h linux/drivers/sbus/audio/cs4231.h
--- v2.1.28/linux/drivers/sbus/audio/cs4231.h	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/audio/cs4231.h	Wed Mar  5 17:04:32 1997
@@ -26,113 +26,205 @@
          u_long         dmapad[3];        
          u_long         dmacva;         /* Capture Virtual Address */
          u_long         dmacc;          /* Capture Count */
-         u_long         dmacnva;        /* Capture Next VAddress */
+         u_long         dmacnva;        /* Capture Next Virtual Address */
          u_long         dmacnc;         /* Capture Next Count */
          u_long         dmapva;         /* Playback Virtual Address */
          u_long         dmapc;          /* Playback Count */
-         u_long         dmapnva;        /* Playback Next VAddress */
+         u_long         dmapnva;        /* Playback Next Virtual Address */
          u_long         dmapnc;         /* Playback Next Count */
 };
 
 struct cs4231_chip {
-        struct cs4231_regs pioregs;
+        struct cs4231_regs *pioregs;
         struct cs4231_dma dmaregs;
-};
+	struct audio_info perchip_info;
+	int irq;
+	unsigned long regs_size;
+
+	/* Keep track of various info */
+	volatile unsigned int status;
 
-struct cs4231_stream_info {
-        unsigned int sample_rate;     /* samples per second */
-        unsigned int channels;        /* number of interleaved channels */
-        unsigned int precision;       /* bit-width of each sample */
-        unsigned int encoding;        /* data encoding method */
-        unsigned int gain;            /* gain level: 0 - 255 */
-        unsigned int port;
+        int dma;
+        int dma2;
 };
 
+/* Status bits */
+#define CS_STATUS_NEED_INIT 0x01
+#define CS_STATUS_INIT_ON_CLOSE 0x02
+#define CS_STATUS_REV_A 0x04
+
 #define CS_TIMEOUT      9000000
 
 #define GAIN_SET(var, gain)     ((var & ~(0x3f)) | gain)
 #define RECGAIN_SET(var, gain)  ((var & ~(0x1f)) | gain)
 
-#define IAR_AUTOCAL_BEGIN       0x40
-#define IAR_AUTOCAL_END         ~(0x40)
+#define IAR_AUTOCAL_BEGIN       0x40 /* IAR_MCE */
+#define IAR_AUTOCAL_END         ~(0x40) /* IAR_MCD */
 #define IAR_NOT_READY            0x80    /* 80h not ready CODEC state */
 
+/* Each register assumed mode 1 and 2 unless noted */
+
+/* 0 - Left Input Control */
+/* 1 - Right Input Control */
 #define MIC_ENABLE(var)         ((var & 0x2f) | 0x80)
 #define LINE_ENABLE(var)        (var & 0x2f)
 #define CDROM_ENABLE(var)       ((var & 0x2f) | 0x40)
+#define INPUTCR_AUX1            0x40
+
+/* 2 - Left Aux 1 Input Control */
+/* 3 - Right Aux 1 Input Control */
+/* 4 - Left Aux 2 Input Control */
+/* 5 - Right Aux 2 Input Control */
 
+/* 6 - Left Output Control */
+/* 7 - Right Output Control */
 #define OUTCR_MUTE              0x80
 #define OUTCR_UNMUTE            ~0x80
 
-/* 8 */
-#define DEFAULT_DATA_FMAT               0x20
-
-/* 10 */
+/* 8 - Playback Data Format (Mode 2) */
+#define CHANGE_DFR(var, val)            ((var & ~(0xF)) | val)
+#define CHANGE_ENCODING(var, val)       ((var & ~(0xe0)) | val)
+#define DEFAULT_DATA_FMAT               CS4231_DFR_ULAW
+#define CS4231_DFR_8000                 0x00
+#define CS4231_DFR_9600                 0x0e
+#define CS4231_DFR_11025                0x03
+#define CS4231_DFR_16000                0x02
+#define CS4231_DFR_18900                0x05
+#define CS4231_DFR_22050                0x07
+#define CS4231_DFR_32000                0x06
+#define CS4231_DFR_37800                0x09
+#define CS4231_DFR_44100                0x0b
+#define CS4231_DFR_48000                0x0c
+#define CS4231_DFR_LINEAR8              0x00
+#define CS4231_DFR_ULAW                 0x20
+#define CS4231_DFR_ALAW                 0x60
+#define CS4231_DFR_ADPCM                0xa0
+#define CS4231_DFR_LINEARBE             0xc0
+#define CS4231_STEREO_ON(val)           (val | 0x10)
+#define CS4231_MONO_ON(val)             (val & ~0x10)
+
+/* 9 - Interface Config. Register */
+#define CHIP_INACTIVE           0x08
+#define PEN_ENABLE              (0x01)
+#define PEN_DISABLE             (~0x01)
+#define CEN_ENABLE              (0x02)
+#define CEN_DISABLE             (~0x02)
+#define ACAL_DISABLE            (~0x08)
+#define ICR_AUTOCAL_INIT        0x01
+
+/* 10 - Pin Control Register */
+#define INTR_ON                 0x82
+#define INTR_OFF                0x80
 #define PINCR_LINE_MUTE         0x40
 #define PINCR_HDPH_MUTE         0x80
 
-/* 11 */
+/* 11 - Test/Initialization */
+#define DRQ_STAT                0x10
 #define AUTOCAL_IN_PROGRESS      0x20
 
-/* 12 */
+/* 12 - Misc Information */
 #define MISC_IR_MODE2           0x40
 
-/* 13 */
+/* 13 - Loopback Control */
 #define LOOPB_ON                0x01
 #define LOOPB_OFF               0x00
 
-/* 16 */
+/* 14 - Unused (mode 1) */
+/* 15 - Unused (mode 1) */
+
+/* 14 - Playback Upper (mode 2) */
+/* 15 - Playback Lower (mode 2) */
+
+/* The rest are mode 2 only */
+
+/* 16 - Alternate Feature 1 Enable */
 #define OLB_ENABLE              0x80
 
-/* 17 */
+/* 17 - Alternate Feature 2 Enable */
 #define HPF_ON                  0x01
 #define XTALE_ON                0x20
 
-#define MONO_IOCR_MUTE       0x40;
+/* 18 - Left Line Input Gain */
+/* 19 - Right Line Input Gain */
+
+/* 20 - Timer High */
+/* 21 - Timer Low */
+
+/* 22 - unused */
+/* 23 - unused */
 
-/* 30 */
+/* 24 - Alternate Feature Status */
+#define CS_PU                   0x01 /* Underrun */
+#define CS_PO                   0x20 /* Overrun */
+
+/* 25 - Version */
 #define CS4231A         0x20
+#define CS4231CDE       0x80
 
+/* 26 - Mono I/O Control */
+#define CHANGE_MONO_GAIN(val)   ((val & ~(0xFF)) | val)
+#define MONO_IOCR_MUTE       0x40
+
+/* 27 - Unused */
+
+/* 28 - Capture Data Format */
+/* see register 8 */
+
+/* 29 - Unused */
+
+/* 30 - Capture Upper */
+/* 31 - Capture Lower */
+
+/* Following are CSR register definitions for the Sparc */
+/* Also list "Solaris" equivs for now, not really useful tho */
+#define CS_INT_PENDING 0x800000 /* APC_IP */ /* Interrupt Pending */
+#define CS_PLAY_INT    0x400000 /* APC_PI */ /* Playback interrupt */
+#define CS_CAPT_INT    0x200000 /* APC_CI */ /* Capture interrupt */
+#define CS_GENL_INT    0x100000 /* APC_EI */ /* General interrupt */
+#define CS_XINT_ENA    0x80000  /* APC_IE */ /* General ext int. enable */
+#define CS_XINT_PLAY   0x40000  /* APC_PIE */ /* Playback ext intr */
+#define CS_XINT_CAPT   0x20000  /* APC_CIE */ /* Capture ext intr */
+#define CS_XINT_GENL   0x10000  /* APC_EIE */ /* Error ext intr */
+#define CS_XINT_EMPT   0x8000   /* APC_PMI */ /* Pipe empty interrupt */
+#define CS_XINT_PEMP   0x4000   /* APC_PM */ /* Play pipe empty */
+#define CS_XINT_PNVA   0x2000   /* APC_PD */ /* Playback NVA dirty */
+#define CS_XINT_PENA   0x1000   /* APC_PMIE */ /* play pipe empty Int enable */
+#define CS_XINT_COVF   0x800    /* APC_CM */ /* Cap data dropped on floor */
+#define CS_XINT_CNVA   0x400    /* APC_CD */ /* Capture NVA dirty */
+#define CS_XINT_CEMP   0x200    /* APC_CMI */ /* Capture pipe empty interrupt */
+#define CS_XINT_CENA   0x100    /* APC_CMIE */ /* Cap. pipe empty int enable */
+#define CS_PPAUSE      0x80     /* APC_PPAUSE */ /* Pause the play DMA */
+#define CS_CPAUSE      0x40     /* APC_CPAUSE */ /* Pause the capture DMA */
+#define CS_CDC_RESET   0x20     /* APC_CODEC_PDN */ /* CODEC RESET */
+#define PDMA_READY     0x08     /* PDMA_GO */
+#define CDMA_READY     0x04     /* CDMA_GO */
+#define CS_CHIP_RESET  0x01     /* APC_RESET */       /* Reset the chip */
 
-#define APC_CODEC_PDN   0x20            
-#define APC_RESET       0x01            
+#define CS_INIT_SETUP  (CDMA_READY | PDMA_READY | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_GENL | CS_INT_PENDING | CS_PLAY_INT | CS_CAPT_INT | CS_GENL_INT) 
 
-#define CS4231_DEFAULT_PLAYGAIN     (132)
-#define CS4231_DEFAULT_RECGAIN      (126)
+#define CS_PLAY_SETUP  (CS_GENL_INT | CS_PLAY_INT | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_EMPT | CS_XINT_GENL | CS_XINT_PENA | PDMA_READY)
+
+#define CS_CAPT_SETUP  (CS_GENL_INT | CS_CAPT_INT | CS_XINT_ENA | CS_XINT_CAPT | CS_XINT_CEMP | CS_XINT_GENL | CDMA_READY)
 
 #define CS4231_MIN_ATEN     (0)
 #define CS4231_MAX_ATEN     (31)
 #define CS4231_MAX_DEV_ATEN (63)
-#define CS4231_MIN_GAIN     (0)
-#define CS4231_MAX_GAIN     (15)
+
 #define CS4231_MON_MIN_ATEN         (0)
 #define CS4231_MON_MAX_ATEN         (63)
 
-#define CS4231_PRECISION    (8)             /* Bits per sample unit */
-#define CS4231_CHANNELS     (1)             /* Channels per sample frame */
+#define CS4231_DEFAULT_PLAYGAIN     (132)
+#define CS4231_DEFAULT_RECGAIN      (126)
 
-#define CS4231_RATE   (8000)          
+#define CS4231_MIN_GAIN     (0)
+#define CS4231_MAX_GAIN     (15)
 
-#define AUDIO_ENCODING_NONE     (0) /* no encoding assigned */
-#define AUDIO_ENCODING_ULAW     (1) /* u-law encoding */
-#define AUDIO_ENCODING_ALAW     (2) /* A-law encoding */
-#define AUDIO_ENCODING_LINEAR   (3) /* Linear PCM encoding */
-#define AUDIO_ENCODING_DVI      (104) /* DVI ADPCM */
-#define AUDIO_ENCODING_LINEAR8  (105) /* 8 bit UNSIGNED */
-
-#define AUDIO_LEFT_BALANCE      (0)
-#define AUDIO_MID_BALANCE       (32)
-#define AUDIO_RIGHT_BALANCE     (64)
-#define AUDIO_BALANCE_SHIFT     (3)
-
-#define AUDIO_SPEAKER           0x01
-#define AUDIO_HEADPHONE         0x02
-#define AUDIO_LINE_OUT          0x04
-
-#define AUDIO_MICROPHONE        0x01
-#define AUDIO_LINE_IN           0x02
-#define AUDIO_INTERNAL_CD_IN    0x04
+#define CS4231_PRECISION    (8)             /* # of bits/sample */
+#define CS4231_CHANNELS     (1)             /* channels/sample */
 
-#define AUDIO_MAX_GAIN  (255)
+#define CS4231_RATE   (8000)                /* default sample rate */
+/* Other rates supported are:
+ * 9600, 11025, 16000, 18900, 22050, 32000, 37800, 44100, 48000 
+ */
 
 #endif
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.1.28/linux/drivers/sbus/char/Config.in	Fri Dec 13 01:37:32 1996
+++ linux/drivers/sbus/char/Config.in	Wed Mar  5 17:04:32 1997
@@ -10,11 +10,7 @@
 	bool 'bwtwo support' SUN_FB_BWTWO
 	bool 'leo/zx support' SUN_FB_LEO
 	bool 'weitek P9X00 support' TADPOLE_FB_WEITEK
-	bool 'Fast 8 bit 1152x900  mode' SUN_FB_FAST_ONE
-	bool 'Fast mono  1152x900  mode' SUN_FB_FAST_TWO
-	bool 'Fast 8 bit 1280x1024 mode' SUN_FB_FAST_MONO
 	if [ "$TADPOLE_FB_WEITEK" = "n" ]; then
-		bool 'Generic AutoResolution mode' SUN_FB_GENERIC
 		fbs=$SUN_FB_CGSIX
 		fbs=$fbs$SUN_FB_TCX
 		fbs=$fbs$SUN_FB_CGTHREE
@@ -22,7 +18,6 @@
 		fbs=$fbs$SUN_FB_CGFOURTEEN
 		fbs=$fbs$SUN_FB_LEO
 		fbs=$fbs$TADPOLE_FB_WEITEK
-		fbs=$fbs$SUN_FB_GENERIC
 		if [ "$fbs" = "nnnnnnnn" ]; then
 			echo "Warning: You have excluded ALL FB Support"
 			echo "Notice:  Enabling Generic AutoResolution"
@@ -39,12 +34,9 @@
 	define_bool SUN_FB_BWTWO y
 	define_bool SUN_FB_LEO y
 	define_bool TADPOLE_FB_WEITEK y
-	define_bool SUN_FB_FAST_ONE y
-	define_bool SUN_FB_FAST_TWO y
-	define_bool SUN_FB_FAST_MONO y
-	define_bool SUN_FB_GENERIC y
 fi
 
 comment 'Misc Linux/SPARC drivers'
 tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO
 tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC
+tristate 'Bidirectional parallel port support' CONFIG_SUN_BPP
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.1.28/linux/drivers/sbus/char/Makefile	Fri Dec 13 01:37:32 1996
+++ linux/drivers/sbus/char/Makefile	Wed Mar  5 17:04:32 1997
@@ -65,5 +65,13 @@
   endif
 endif
 
+ifeq ($(CONFIG_SUN_BPP),y)
+O_OBJS += bpp.o
+else
+  ifeq ($(CONFIG_SUN_BPP),m)
+  M_OBJS += bpp.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c
--- v2.1.28/linux/drivers/sbus/char/bpp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sbus/char/bpp.c	Wed Mar  5 17:04:32 1997
@@ -0,0 +1,1093 @@
+/*
+ * drivers/sbus/char/bpp.c
+ *
+ * Copyright (c) 1995 Picture Elements
+ *      Stephen Williams (steve@icarus.com)
+ *      Gus Baldauf (gbaldauf@ix.netcom.com)
+ *
+ * Linux/SPARC port by Peter Zaitcev.
+ * Integration into SPARC tree by Tom Dyas.
+ */
+
+
+# include  <linux/kernel.h>
+# include  <linux/module.h>
+# include  <linux/version.h>
+# include  <linux/fs.h>
+# include  <linux/errno.h>
+# include  <linux/sched.h>
+# include  <linux/timer.h>
+# include  <linux/ioport.h>
+
+# include  <asm/uaccess.h>
+
+# if defined(__i386__)
+# include  <asm/io.h>
+# include  <asm/system.h>
+# include  <asm/segment.h>
+# endif
+
+# if defined(__sparc__)
+# include <linux/delay.h>         /* udelay() */
+
+# include <asm/oplib.h>           /* OpenProm Library */
+# include <asm/sbus.h>            /* struct linux_sbus *SBus_chain */
+# include <asm/io.h>              /* sparc_alloc_io() */
+# endif
+
+# include <asm/bpp.h>
+
+#define BPP_PROBE_CODE 0x55
+#define BPP_DELAY 100
+
+static const unsigned  BPP_MAJOR = 32;
+static const char* dev_name = "bpp";
+
+/* When switching from compatability to a mode where I can read, try
+   the following mode first. */
+
+/* const unsigned char DEFAULT_ECP = 0x10; */
+static const unsigned char DEFAULT_ECP = 0x30;
+static const unsigned char DEFAULT_NIBBLE = 0x00;
+
+/*
+ * These are 1284 time constraints, in units of jiffies.
+ */
+
+static const unsigned long TIME_PSetup = 1;
+static const unsigned long TIME_PResponse = 6;
+static const unsigned long TIME_IDLE_LIMIT = 2000;
+
+/*
+ * One instance per supported subdevice...
+ */
+# define BPP_NO 3
+
+enum IEEE_Mode { COMPATIBILITY, NIBBLE, ECP, ECP_RLE, EPP };
+
+struct inst {
+      unsigned present  : 1; /* True if the hardware exists */
+      unsigned enhanced : 1; /* True if the hardware in "enhanced" */
+      unsigned opened   : 1; /* True if the device is opened already */
+      unsigned run_flag : 1; /* True if waiting for a repeate byte */
+
+      unsigned char direction; /* 0 --> out, 0x20 --> IN */
+      unsigned char pp_state; /* State of host controlled pins. */
+      enum IEEE_Mode mode;
+
+      unsigned char run_length;
+      unsigned char repeat_byte;
+
+      /* These members manage timeouts for programmed delays */
+      struct wait_queue *wait_queue;
+      struct timer_list timer_list;
+};
+
+static struct inst instances[BPP_NO];
+
+#if defined(__i386__)
+
+const unsigned short base_addrs[BPP_NO] = { 0x278, 0x378, 0x3bc };
+
+/*
+ * These are for data access.
+ * Control lines accesses are hidden in set_bits() and get_bits().
+ * The exeption is the probe procedure, which is system-dependent.
+ */
+#define bpp_outb_p(data, base)  outb_p((data), (base))
+#define bpp_inb(base)  inb(base)
+#define bpp_inb_p(base)  inb_p(base)
+
+/*
+ * This method takes the pin values mask and sets the hardware pins to
+ * the requested value: 1 == high voltage, 0 == low voltage. This
+ * burries the annoying PC bit inversion and preserves the direction
+ * flag.
+ */
+static void set_pins(unsigned short pins, unsigned minor)
+{
+      unsigned char bits = instances[minor].direction;  /* == 0x20 */
+
+      if (! (pins & BPP_PP_nStrobe))   bits |= 1;
+      if (! (pins & BPP_PP_nAutoFd))   bits |= 2;
+      if (   pins & BPP_PP_nInit)      bits |= 4;
+      if (! (pins & BPP_PP_nSelectIn)) bits |= 8;
+
+      instances[minor].pp_state = bits;
+
+      outb_p(bits, base_addrs[minor]+2);
+}
+
+static unsigned short get_pins(unsigned minor)
+{
+      unsigned short bits = 0;
+
+      unsigned value = instances[minor].pp_state;
+      if (! (value & 0x01)) bits |= BPP_PP_nStrobe;
+      if (! (value & 0x02)) bits |= BPP_PP_nAutoFd;
+      if (value & 0x04)     bits |= BPP_PP_nInit;
+      if (! (value & 0x08)) bits |= BPP_PP_nSelectIn;
+
+      value = inb_p(base_addrs[minor]+1);
+      if (value & 0x08)     bits |= BPP_GP_nFault;
+      if (value & 0x10)     bits |= BPP_GP_Select;
+      if (value & 0x20)     bits |= BPP_GP_PError;
+      if (value & 0x40)     bits |= BPP_GP_nAck;
+      if (! (value & 0x80)) bits |= BPP_GP_Busy;
+
+      return bits;
+}
+
+#endif /* __i386__ */
+
+#if defined(__sparc__)
+
+/*
+ * Register block
+ */
+struct bpp_regs {
+      /* DMA registers */
+      __u32 p_csr;		/* DMA Control/Status Register */
+      __u32 p_addr;		/* Address Register */
+      __u32 p_bcnt;		/* Byte Count Register */
+      __u32 p_tst_csr;		/* Test Control/Status (DMA2 only) */
+      /* Parallel Port registers */
+      __u16 p_hcr;		/* Hardware Configuration Register */
+      __u16 p_ocr;		/* Operation Configuration Register */
+      __u8 p_dr;		/* Parallel Data Register */
+      __u8 p_tcr;		/* Transfer Control Register */
+      __u8 p_or;		/* Output Register */
+      __u8 p_ir;		/* Input Register */
+      __u16 p_icr;		/* Interrupt Control Register */
+};
+
+/* P_CSR.  Bits of type RW1 are cleared with writting '1'. */
+#define P_DEV_ID_MASK   0xf0000000      /* R   */
+#define P_DEV_ID_ZEBRA  0x40000000
+#define P_DEV_ID_L64854 0xa0000000      /*      == NCR 89C100+89C105. Pity. */
+#define P_NA_LOADED     0x08000000      /* R    NA wirtten but was not used */
+#define P_A_LOADED      0x04000000      /* R    */
+#define P_DMA_ON        0x02000000      /* R    DMA is not disabled */
+#define P_EN_NEXT       0x01000000      /* RW   */
+#define P_TCI_DIS       0x00800000      /* RW   TCI forbidden from interrupts */
+#define P_DIAG          0x00100000      /* RW   Disables draining and resetting
+                                                of P-FIFO on loading of P_ADDR*/
+#define P_BURST_SIZE    0x000c0000      /* RW   SBus burst size */
+#define P_BURST_8       0x00000000
+#define P_BURST_4       0x00040000
+#define P_BURST_1       0x00080000      /*      "No burst" write */
+#define P_TC            0x00004000      /* RW1  Term Count, can be cleared when
+                                           P_EN_NEXT=1 */
+#define P_EN_CNT        0x00002000      /* RW   */
+#define P_EN_DMA        0x00000200      /* RW   */
+#define P_WRITE         0x00000100      /* R    DMA dir, 1=to ram, 0=to port */
+#define P_RESET         0x00000080      /* RW   */
+#define P_SLAVE_ERR     0x00000040      /* RW1  Access size error */
+#define P_INVALIDATE    0x00000020      /* W    Drop P-FIFO */
+#define P_INT_EN        0x00000010      /* RW   OK to P_INT_PEND||P_ERR_PEND */
+#define P_DRAINING      0x0000000c      /* R    P-FIFO is draining to memory */
+#define P_ERR_PEND      0x00000002      /* R    */
+#define P_INT_PEND      0x00000001      /* R    */
+
+/* P_HCR. Time is in increments of SBus clock. */
+#define P_HCR_TEST      0x8000      /* Allows buried counters to be read */
+#define P_HCR_DSW       0x7f00      /* Data strobe width (in ticks) */
+#define P_HCR_DDS       0x007f      /* Data setup before strobe (in ticks) */
+
+/* P_OCR. */
+#define P_OCR_MEM_CLR   0x8000
+#define P_OCR_DATA_SRC  0x4000      /* )                  */
+#define P_OCR_DS_DSEL   0x2000      /* )  Bidirectional      */
+#define P_OCR_BUSY_DSEL 0x1000      /* )    selects            */
+#define P_OCR_ACK_DSEL  0x0800      /* )                  */
+#define P_OCR_EN_DIAG   0x0400
+#define P_OCR_BUSY_OP   0x0200      /* Busy operation */
+#define P_OCR_ACK_OP    0x0100      /* Ack operation */
+#define P_OCR_SRST      0x0080      /* Reset state machines. Not selfcleaning. */
+#define P_OCR_IDLE      0x0008      /* PP data transfer state machine is idle */
+#define P_OCR_V_ILCK    0x0002      /* Versatec faded. Zebra only. */
+#define P_OCR_EN_VER    0x0001      /* Enable Versatec (0 - enable). Zebra only. */
+
+/* P_TCR */
+#define P_TCR_DIR       0x08
+#define P_TCR_BUSY      0x04
+#define P_TCR_ACK       0x02
+#define P_TCR_DS        0x01        /* Strobe */
+
+/* P_OR */
+#define P_OR_V3         0x20        /* )                 */
+#define P_OR_V2         0x10        /* ) on Zebra only   */
+#define P_OR_V1         0x08        /* )                 */
+#define P_OR_INIT       0x04
+#define P_OR_AFXN       0x02        /* Auto Feed */
+#define P_OR_SLCT_IN    0x01
+
+/* P_IR */
+#define P_IR_PE         0x04
+#define P_IR_SLCT       0x02
+#define P_IR_ERR        0x01
+
+/* P_ICR */
+#define P_DS_IRQ        0x8000      /* RW1  */
+#define P_ACK_IRQ       0x4000      /* RW1  */
+#define P_BUSY_IRQ      0x2000      /* RW1  */
+#define P_PE_IRQ        0x1000      /* RW1  */
+#define P_SLCT_IRQ      0x0800      /* RW1  */
+#define P_ERR_IRQ       0x0400      /* RW1  */
+#define P_DS_IRQ_EN     0x0200      /* RW   Always on rising edge */
+#define P_ACK_IRQ_EN    0x0100      /* RW   Always on rising edge */
+#define P_BUSY_IRP      0x0080      /* RW   1= rising edge */
+#define P_BUSY_IRQ_EN   0x0040      /* RW   */
+#define P_PE_IRP        0x0020      /* RW   1= rising edge */
+#define P_PE_IRQ_EN     0x0010      /* RW   */
+#define P_SLCT_IRP      0x0008      /* RW   1= rising edge */
+#define P_SLCT_IRQ_EN   0x0004      /* RW   */
+#define P_ERR_IRP       0x0002      /* RW1  1= rising edge */
+#define P_ERR_IRQ_EN    0x0001      /* RW   */
+
+volatile struct bpp_regs *base_addrs[BPP_NO];
+
+static inline void bpp_outb_p(__u8 data, volatile struct bpp_regs *base){
+      base->p_dr = data;
+}
+
+#define bpp_inb_p(base)  bpp_inb(base)
+
+static inline __u8 bpp_inb(volatile struct bpp_regs *base){
+      return base->p_dr;
+}
+
+
+static void set_pins(unsigned short pins, unsigned minor)
+{
+      volatile struct bpp_regs *base = base_addrs[minor];
+      unsigned char bits_tcr = 0, bits_or = 0;
+
+      if (instances[minor].direction & 0x20) bits_tcr |= P_TCR_DIR;
+      if (   pins & BPP_PP_nStrobe)          bits_tcr |= P_TCR_DS;
+
+      if (   pins & BPP_PP_nAutoFd)          bits_or |= P_OR_AFXN;
+      if (! (pins & BPP_PP_nInit))           bits_or |= P_OR_INIT;
+      if (! (pins & BPP_PP_nSelectIn))       bits_or |= P_OR_SLCT_IN;
+
+      base->p_or = bits_or;
+      base->p_tcr = bits_tcr;
+}
+
+/*
+ * i386 people read output pins from a software image.
+ * We may get them back from hardware.
+ * Again, inversion of pins must he buried here.
+ */
+static unsigned short get_pins(unsigned minor)
+{
+      volatile struct bpp_regs *base = base_addrs[minor];
+      unsigned short bits = 0;
+      unsigned value_tcr = base->p_tcr;
+      unsigned value_ir = base->p_ir;
+      unsigned value_or = base->p_or;
+
+      if (value_tcr & P_TCR_DS)         bits |= BPP_PP_nStrobe;
+      if (value_or & P_OR_AFXN)         bits |= BPP_PP_nAutoFd;
+      if (! (value_or & P_OR_INIT))     bits |= BPP_PP_nInit;
+      if (! (value_or & P_OR_SLCT_IN))  bits |= BPP_PP_nSelectIn;
+
+      if (value_ir & P_IR_ERR)          bits |= BPP_GP_nFault;
+      if (! (value_ir & P_IR_SLCT))     bits |= BPP_GP_Select;
+      if (! (value_ir & P_IR_PE))       bits |= BPP_GP_PError;
+      if (! (value_tcr & P_TCR_ACK))    bits |= BPP_GP_nAck;
+      if (value_tcr & P_TCR_BUSY)       bits |= BPP_GP_Busy;
+
+      return bits;
+}
+
+#if 0
+/* P3 */
+static inline void bpp_snap(const char *msg, unsigned minor)
+{
+	volatile struct bpp_regs *r = base_addrs[minor];
+	printk("bpp.%s: c=%02x o=%02x i=%02x\n", msg, r->p_tcr, r->p_or, r->p_ir);
+}
+#endif
+
+#endif /* __sparc__ */
+
+/*
+ * This is TRUE if the module_init successfully loaded the module.
+ */
+#if 0
+static int loaded_flag = 0;
+#endif
+
+static void bpp_wake_up(unsigned long val)
+{ wake_up(&instances[val].wait_queue); }
+
+static void snooze(unsigned long snooze_time, unsigned minor)
+{
+      instances[minor].timer_list.expires = snooze_time+1;
+      instances[minor].timer_list.data    = minor;
+      add_timer(&instances[minor].timer_list);
+      sleep_on (&instances[minor].wait_queue);
+}
+
+static int wait_for(unsigned short set, unsigned short clr,
+               unsigned long delay, unsigned minor)
+{
+      unsigned short pins = get_pins(minor);
+
+      unsigned long extime = 0;
+
+      /*
+       * Try a real fast scan for the first jiffy, in case the device
+       * responds real good. The first while loop guesses an expire
+       * time accounting for possible wraparound of jiffies.
+       */
+      while (extime <= jiffies) extime = jiffies + 1;
+      while ( (jiffies < extime)
+              && (((pins & set) != set) || ((pins & clr) != 0)) ) {
+            pins = get_pins(minor);
+      }
+
+      delay -= 1;
+
+      /*
+       * If my delay expired or the pins are still not where I want
+       * them, then resort to using the timer and greatly reduce my
+       * sample rate. If the peripheral is going to be slow, this will
+       * give the CPU up to some more worthy process.
+       */
+      while ( delay && (((pins & set) != set) || ((pins & clr) != 0)) ) {
+
+            snooze(1, minor);
+            pins = get_pins(minor);
+            delay -= 1;
+      }
+
+      if (delay == 0) return -1;
+      else return pins;
+}
+
+/*
+ * Return ZERO(0) If the negotiation succeeds, an errno otherwise. An
+ * errno means something broke, and I do not yet know how to fix it.
+ */
+static int negotiate(unsigned char mode, unsigned minor)
+{
+      int rc;
+      unsigned short pins = get_pins(minor);
+      if (pins & BPP_PP_nSelectIn) return -EIO;
+
+
+        /* Event 0: Write the mode to the data lines */
+      bpp_outb_p(mode, base_addrs[minor]);
+
+      snooze(TIME_PSetup, minor);
+
+        /* Event 1: Strobe the mode code into the peripheral */
+      set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nInit, minor);
+
+        /* Wait for Event 2: Peripheral responds as a 1284 device. */
+      rc = wait_for(BPP_GP_PError|BPP_GP_Select|BPP_GP_nFault,
+                BPP_GP_nAck,
+                TIME_PResponse,
+                minor);
+
+      if (rc == -1) return -ETIMEDOUT;
+
+        /* Event 3: latch extensibility request */
+      set_pins(BPP_PP_nSelectIn|BPP_PP_nInit, minor);
+
+        /* ... quick nap while peripheral ponders the byte i'm sending...*/
+      snooze(1, minor);
+
+        /* Event 4: restore strobe, to ACK peripheral's response. */
+      set_pins(BPP_PP_nSelectIn|BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
+
+        /* Wait for Event 6: Peripheral latches response bits */
+      rc = wait_for(BPP_GP_nAck, 0, TIME_PSetup+TIME_PResponse, minor);
+      if (rc == -1) return -EIO;
+
+        /* A 1284 device cannot refuse nibble mode */
+      if (mode == DEFAULT_NIBBLE) return 0;
+
+      if (pins & BPP_GP_Select) return 0;
+
+      return -EPROTONOSUPPORT;
+}
+
+static int terminate(unsigned minor)
+{
+      int rc;
+
+        /* Event 22: Request termination of 1284 mode */
+      set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
+
+        /* Wait for Events 23 and 24: ACK termination request. */
+      rc = wait_for(BPP_GP_Busy|BPP_GP_nFault,
+                BPP_GP_nAck,
+                TIME_PSetup+TIME_PResponse,
+                minor);
+
+      instances[minor].direction = 0;
+      instances[minor].mode = COMPATIBILITY;
+
+      if (rc == -1) {
+          return -EIO;
+      }
+
+        /* Event 25: Handshake by lowering nAutoFd */
+      set_pins(BPP_PP_nStrobe|BPP_PP_nInit, minor);
+
+        /* Event 26: Peripheral wiggles lines... */
+
+        /* Event 27: Peripheral sets nAck HIGH to ack handshake */
+      rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
+      if (rc == -1) {
+          set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
+          return -EIO;
+      }
+
+        /* Event 28: Finish phase by raising nAutoFd */
+      set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
+
+      return 0;
+}
+
+
+/*
+ * Allow only one process to open the device at a time.
+ */
+static int open(struct inode *inode, struct file *f)
+{
+      unsigned minor = MINOR(inode->i_rdev);
+      if (minor >= BPP_NO) return -ENODEV;
+      if (! instances[minor].present) return -ENODEV;
+      if (instances[minor].opened) return -EBUSY;
+
+      instances[minor].opened = 1;
+
+      return 0;
+}
+
+/*
+ * When the process closes the device, this method is called to clean
+ * up and reset the hardware. Always leave the device in compatibility
+ * mode as this is a reasonable place to clean up from messes made by
+ * ioctls, or other mayhem.
+ */
+static void release(struct inode *inode, struct file *f)
+{
+      unsigned minor = MINOR(inode->i_rdev);
+      instances[minor].opened = 0;
+
+      if (instances[minor].mode != COMPATIBILITY)
+      terminate(minor);
+}
+
+static long read_nibble(unsigned minor, char *c, unsigned long cnt)
+{
+      unsigned long remaining = cnt;
+      long rc;
+
+      while (remaining > 0) {
+          unsigned char byte = 0;
+          int pins;
+
+          /* Event 7: request nibble */
+          set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor);
+
+          /* Wait for event 9: Peripher strobes first nibble */
+          pins = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor);
+          if (pins == -1) return -ETIMEDOUT;
+
+          /* Event 10: I handshake nibble */
+          set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor);
+          if (pins & BPP_GP_nFault) byte |= 0x01;
+          if (pins & BPP_GP_Select) byte |= 0x02;
+          if (pins & BPP_GP_PError) byte |= 0x04;
+          if (pins & BPP_GP_Busy)   byte |= 0x08;
+
+          /* Wait for event 11: Peripheral handshakes nibble */
+          rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
+
+          /* Event 7: request nibble */
+          set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor);
+
+          /* Wait for event 9: Peripher strobes first nibble */
+          pins = wait_for(0, BPP_GP_nAck, TIME_PResponse, minor);
+          if (rc == -1) return -ETIMEDOUT;
+
+          /* Event 10: I handshake nibble */
+          set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor);
+          if (pins & BPP_GP_nFault) byte |= 0x10;
+          if (pins & BPP_GP_Select) byte |= 0x20;
+          if (pins & BPP_GP_PError) byte |= 0x40;
+          if (pins & BPP_GP_Busy)   byte |= 0x80;
+
+          put_user_ret(byte, c, -EFAULT);
+          c += 1;
+          remaining -= 1;
+
+          /* Wait for event 11: Peripheral handshakes nibble */
+          rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
+          if (rc == -1) return -EIO;
+      }
+
+      return cnt - remaining;
+}
+
+static long read_ecp(unsigned minor, char *c, unsigned long cnt)
+{
+      unsigned long remaining;
+      long rc;
+
+        /* Turn ECP mode from forward to reverse if needed. */
+      if (! instances[minor].direction) {
+          unsigned short pins = get_pins(minor);
+
+            /* Event 38: Turn the bus around */
+          instances[minor].direction = 0x20;
+          pins &= ~BPP_PP_nAutoFd;
+          set_pins(pins, minor);
+
+            /* Event 39: Set pins for reverse mode. */
+          snooze(TIME_PSetup, minor);
+          set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor);
+
+            /* Wait for event 40: Peripheral ready to be strobed */
+          rc = wait_for(0, BPP_GP_PError, TIME_PResponse, minor);
+          if (rc == -1) return -ETIMEDOUT;
+      }
+
+      remaining = cnt;
+
+      while (remaining > 0) {
+
+            /* If there is a run length for a repeated byte, repeat */
+            /* that byte a few times. */
+          if (instances[minor].run_length && !instances[minor].run_flag) {
+
+              char buffer[128];
+              unsigned idx;
+              unsigned repeat = remaining < instances[minor].run_length
+                                     ? remaining
+                               : instances[minor].run_length;
+
+              for (idx = 0 ;  idx < repeat ;  idx += 1)
+                buffer[idx] = instances[minor].repeat_byte;
+
+              copy_to_user_ret(c, buffer, repeat, -EFAULT);
+              remaining -= repeat;
+              c += repeat;
+              instances[minor].run_length -= repeat;
+          }
+
+          if (remaining == 0) break;
+
+
+            /* Wait for Event 43: Data active on the bus. */
+          rc = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor);
+          if (rc == -1) break;
+
+          if (rc & BPP_GP_Busy) {
+                /* OK, this is data. read it in. */
+              unsigned char byte = bpp_inb(base_addrs[minor]);
+              put_user_ret(byte, c, -EFAULT);
+              c += 1;
+              remaining -= 1;
+
+              if (instances[minor].run_flag) {
+                  instances[minor].repeat_byte = byte;
+                  instances[minor].run_flag = 0;
+              }
+
+          } else {
+              unsigned char byte = bpp_inb(base_addrs[minor]);
+              if (byte & 0x80) {
+                  printk("bpp%d: "
+                         "Ignoring ECP channel %u from device.\n",
+                         minor, byte & 0x7f);
+              } else {
+                  instances[minor].run_length = byte;
+                  instances[minor].run_flag = 1;
+              }
+          }
+
+            /* Event 44: I got it. */
+          set_pins(BPP_PP_nStrobe|BPP_PP_nAutoFd|BPP_PP_nSelectIn, minor);
+
+            /* Wait for event 45: peripheral handshake */
+          rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
+          if (rc == -1) return -ETIMEDOUT;
+
+             /* Event 46: Finish handshake */
+          set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor);
+
+      }
+
+
+      return cnt - remaining;
+}
+
+static long read(struct inode *inode, struct file *f,
+		 char *c, unsigned long cnt)
+{
+      long rc;
+      const unsigned minor = MINOR(inode->i_rdev);
+      if (minor >= BPP_NO) return -ENODEV;
+      if (!instances[minor].present) return -ENODEV;
+
+      switch (instances[minor].mode) {
+
+        default:
+          if (instances[minor].mode != COMPATIBILITY)
+            terminate(minor);
+
+          if (instances[minor].enhanced) {
+              /* For now, do all reads with ECP-RLE mode */
+              unsigned short pins;
+
+              rc = negotiate(DEFAULT_ECP, minor);
+              if (rc < 0) break;
+
+              instances[minor].mode = ECP_RLE;
+
+              /* Event 30: set nAutoFd low to setup for ECP mode */
+              pins = get_pins(minor);
+              pins &= ~BPP_PP_nAutoFd;
+              set_pins(pins, minor);
+
+              /* Wait for Event 31: peripheral ready */
+              rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor);
+              if (rc == -1) return -ETIMEDOUT;
+
+              rc = read_ecp(minor, c, cnt);
+
+          } else {
+              rc = negotiate(DEFAULT_NIBBLE, minor);
+              if (rc < 0) break;
+
+              instances[minor].mode = NIBBLE;
+
+              rc = read_nibble(minor, c, cnt);
+          }
+          break;
+
+        case NIBBLE:
+          rc = read_nibble(minor, c, cnt);
+          break;
+
+        case ECP:
+        case ECP_RLE:
+          rc = read_ecp(minor, c, cnt);
+          break;
+
+      }
+
+
+      return rc;
+}
+
+/*
+ * Compatibility mode handshaking is a matter of writing data,
+ * strobing it, and waiting for the printer to stop being busy.
+ */
+static long write_compat(unsigned minor, const char *c, unsigned long cnt)
+{
+      long rc;
+      unsigned short pins = get_pins(minor);
+
+      unsigned long remaining = cnt;
+
+      while (remaining > 0) {
+            unsigned char byte;
+            c += 1;
+            get_user_ret(byte, c, -EFAULT);
+
+            rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor);
+            if (rc == -1) return -ETIMEDOUT;
+
+            bpp_outb_p(byte, base_addrs[minor]);
+            remaining -= 1;
+          /* snooze(1, minor); */
+
+          pins &= ~BPP_PP_nStrobe;
+          set_pins(pins, minor);
+
+          rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor);
+
+          pins |= BPP_PP_nStrobe;
+          set_pins(pins, minor);
+      }
+
+      return cnt - remaining;
+}
+
+/*
+ * Write data using ECP mode. Watch out that the port may be set up
+ * for reading. If so, turn the port around.
+ */
+static long write_ecp(unsigned minor, const char *c, unsigned long cnt)
+{
+      unsigned short pins = get_pins(minor);
+      unsigned long remaining = cnt;
+
+      if (instances[minor].direction) {
+          int rc;
+
+            /* Event 47 Request bus be turned around */
+          pins |= BPP_PP_nInit;
+          set_pins(pins, minor);
+
+            /* Wait for Event 49: Peripheral relinquished bus */
+          rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor);
+
+          pins |= BPP_PP_nAutoFd;
+          instances[minor].direction = 0;
+          set_pins(pins, minor);
+      }
+
+      while (remaining > 0) {
+          unsigned char byte;
+          int rc;
+
+          get_user_ret(byte, c, -EFAULT);
+
+          rc = wait_for(0, BPP_GP_Busy, TIME_PResponse, minor);
+          if (rc == -1) return -ETIMEDOUT;
+
+          c += 1;
+
+          bpp_outb_p(byte, base_addrs[minor]);
+
+          pins &= ~BPP_PP_nStrobe;
+          set_pins(pins, minor);
+
+          pins |= BPP_PP_nStrobe;
+          rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor);
+          if (rc == -1) return -EIO;
+
+          set_pins(pins, minor);
+      }
+
+      return cnt - remaining;
+}
+
+/*
+ * Write to the peripheral. Be sensitive of the current mode. If I'm
+ * in a mode that can be turned around (ECP) then just do
+ * that. Otherwise, terminate and do my writing in compat mode. This
+ * is the safest course as any device can handle it.
+ */
+static long write(struct inode *inode, struct file *f,
+		  const char *c, unsigned long cnt)
+{
+      long errno = 0;
+      unsigned minor = MINOR(inode->i_rdev);
+      if (minor >= BPP_NO) return -ENODEV;
+      if (!instances[minor].present) return -ENODEV;
+
+      switch (instances[minor].mode) {
+
+        case ECP:
+        case ECP_RLE:
+          errno = write_ecp(minor, c, cnt);
+          break;
+        case COMPATIBILITY:
+          errno = write_compat(minor, c, cnt);
+          break;
+        default:
+          terminate(minor);
+          errno = write_compat(minor, c, cnt);
+      }
+
+      return errno;
+}
+
+static int ioctl(struct inode *inode, struct file *f, unsigned int cmd,
+		 unsigned long arg)
+{
+      int errno = 0;
+
+      unsigned minor = MINOR(inode->i_rdev);
+      if (minor >= BPP_NO) return -ENODEV;
+      if (!instances[minor].present) return -ENODEV;
+
+
+      switch (cmd) {
+
+        case BPP_PUT_PINS:
+          set_pins(arg, minor);
+          break;
+
+        case BPP_GET_PINS:
+          errno = get_pins(minor);
+          break;
+
+        case BPP_PUT_DATA:
+          bpp_outb_p(arg, base_addrs[minor]);
+          break;
+
+        case BPP_GET_DATA:
+          errno = bpp_inb_p(base_addrs[minor]);
+          break;
+
+        case BPP_SET_INPUT:
+          if (arg)
+            if (instances[minor].enhanced) {
+                unsigned short bits = get_pins(minor);
+                instances[minor].direction = 0x20;
+                set_pins(bits, minor);
+            } else {
+                errno = -ENOTTY;
+            }
+          else {
+              unsigned short bits = get_pins(minor);
+              instances[minor].direction = 0x00;
+              set_pins(bits, minor);
+          }
+          break;
+
+        default:
+            errno = -EINVAL;
+      }
+
+      return errno;
+}
+
+static struct file_operations bpp_fops = {
+      0,
+      read, /* read */
+      write, /* write */
+      0,
+      0,
+      ioctl,
+      0, /* mmap */
+      open,
+      release,
+      0,
+      0,
+      0,
+      0
+};
+
+#if defined(__i386__)
+
+#define collectLptPorts()  {}
+
+static void probeLptPort(unsigned idx)
+{
+      unsigned int testvalue;
+      const unsigned short lpAddr = base_addrs[idx];
+
+      instances[idx].present = 0;
+      instances[idx].enhanced = 0;
+      instances[idx].direction = 0;
+      instances[idx].mode = COMPATIBILITY;
+      instances[idx].wait_queue = 0;
+      instances[idx].run_length = 0;
+      instances[idx].run_flag = 0;
+      init_timer(&instances[idx].timer_list);
+      instances[idx].timer_list.function = bpp_wake_up;
+      if (check_region(lpAddr,3)) return;
+
+      /*
+       * First, make sure the instance exists. Do this by writing to
+       * the data latch and reading the value back. If the port *is*
+       * present, test to see if it supports extended-mode
+       * operation. This will be required for IEEE1284 reverse
+       * transfers.
+       */
+
+      outb_p(BPP_PROBE_CODE, lpAddr);
+      for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
+            ;
+      testvalue = inb_p(lpAddr);
+      if (testvalue == BPP_PROBE_CODE) {
+            unsigned save;
+            instances[idx].present = 1;
+
+            request_region(lpAddr,3, dev_name);
+            save = inb_p(lpAddr+2);
+            for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
+                  ;
+            outb_p(save|0x20, lpAddr+2);
+            for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
+                  ;
+            outb_p(~BPP_PROBE_CODE, lpAddr);
+            for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
+                  ;
+            testvalue = inb_p(lpAddr);
+            if ((testvalue&0xff) == (0xff&~BPP_PROBE_CODE))
+                  instances[idx].enhanced = 0;
+            else
+                  instances[idx].enhanced = 1;
+            outb_p(save, lpAddr+2);
+      }
+
+      /*
+       * Leave the port in compat idle mode.
+       */
+      set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx);
+
+      printk("bpp%d: Port at 0x%03x: Enhanced mode %s\n", idx, base_addrs[idx],
+            instances[idx].enhanced? "SUPPORTED" : "UNAVAILABLE");
+}
+
+static inline void freeLptPort(int idx)
+{
+      release_region(base_addrs[idx], 3);
+}
+
+#endif
+
+#if defined(__sparc__)
+
+static volatile struct bpp_regs *map_bpp(struct linux_sbus_device *dev, int idx)
+{
+      volatile struct bpp_regs *regs;
+
+      /* Apply ranges to here, do not pollute Sbus devices list. */
+      struct linux_prom_registers areg;
+
+      /*
+       * PROM reports different numbers on Zebra and on DMA2.
+       * We need to figure out when to apply parent ranges.
+       * printk will show this on different machines.
+       */
+
+      areg = dev->reg_addrs[0];
+      printk("bpp%d.map_bpp: 0x%x.%p[0x%x] i=%d\n", idx,
+            areg.which_io, areg.phys_addr, areg.reg_size,
+            dev->irqs[0].pri);
+      /* IPC Zebra   1.fa200000[1c] i=2  */
+      /** prom_apply_sbus_ranges (&areg, 1); **/
+
+      regs = sparc_alloc_io (areg.phys_addr, 0,
+                                   sizeof(struct bpp_regs), "bpp",
+                                   areg.which_io, 0x0);
+
+      return regs;
+}
+
+static int collectLptPorts(void)
+{
+	struct linux_sbus *bus;
+	struct linux_sbus_device *dev;
+	int count;
+
+	count = 0;
+	for_all_sbusdev(dev, bus) {
+		if (strcmp(dev->prom_name, "SUNW,bpp") == 0) {
+			if (count >= BPP_NO) {
+				printk(KERN_NOTICE
+				       "bpp: More than %d bpp ports,"
+				       " rest is ignored\n", BPP_NO);
+				return count;
+			}
+			base_addrs[count] = map_bpp(dev, count);
+			count++;
+		}
+	}
+	return count;
+}
+
+static void probeLptPort(unsigned idx)
+{
+      volatile struct bpp_regs *rp = base_addrs[idx];
+      __u32 csr;
+      char *brand;
+
+      instances[idx].present = 0;
+      instances[idx].enhanced = 0;
+      instances[idx].direction = 0;
+      instances[idx].mode = COMPATIBILITY;
+      instances[idx].wait_queue = 0;
+      instances[idx].run_length = 0;
+      instances[idx].run_flag = 0;
+      init_timer(&instances[idx].timer_list);
+      instances[idx].timer_list.function = bpp_wake_up;
+
+      if (rp == 0) return;
+
+      instances[idx].present = 1;
+      instances[idx].enhanced = 1;   /* Sure */
+
+      if (((csr = rp->p_csr) & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) {
+            udelay(20);
+            csr = rp->p_csr;
+            if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) {
+                  printk("bpp%d: DRAINING still active (0x%08x)\n", idx, csr);
+            }
+      }
+      printk("bpp%d: reset with 0x%08x ..", idx, csr);
+      rp->p_csr = (csr | P_RESET) & ~P_INT_EN;
+      udelay(500);
+      rp->p_csr &= ~P_RESET;
+      printk(" done with csr=0x%08x ocr=0x%04x\n", rp->p_csr, rp->p_ocr);
+
+      switch (rp->p_csr & P_DEV_ID_MASK) {
+      case P_DEV_ID_ZEBRA:
+            brand = "Zebra";
+            break;
+      case P_DEV_ID_L64854:
+            brand = "DMA2";
+            break;
+      default:
+            brand = "Unknown";
+      }
+      printk("bpp%d: %s at 0x%p\n", idx, brand, rp);
+
+      /*
+       * Leave the port in compat idle mode.
+       */
+      set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx);
+
+      return;
+}
+
+static inline void freeLptPort(int idx)
+{
+      sparc_free_io ((void *)base_addrs[idx], sizeof(struct bpp_regs));
+}
+
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int bpp_init(void))
+#endif
+{
+	int rc;
+	unsigned idx;
+
+	rc = collectLptPorts();
+	if (rc == 0)
+		return -ENODEV;
+
+	rc = register_chrdev(BPP_MAJOR, dev_name, &bpp_fops);
+	if (rc < 0)
+		return rc;
+
+	for (idx = 0; idx < BPP_NO; idx += 1) {
+		instances[idx].opened = 0;
+		probeLptPort(idx);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	unsigned idx;
+
+	unregister_chrdev(BPP_MAJOR, dev_name);
+
+	for (idx = 0 ;  idx < BPP_NO ;  idx += 1) {
+		if (instances[idx].present)
+			freeLptPort(idx);
+	}
+}
+#endif
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/cgsix.c linux/drivers/sbus/char/cgsix.c
--- v2.1.28/linux/drivers/sbus/char/cgsix.c	Mon Dec 30 02:00:04 1996
+++ linux/drivers/sbus/char/cgsix.c	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: cgsix.c,v 1.21 1996/12/23 10:16:05 ecd Exp $
+/* $Id: cgsix.c,v 1.22 1997/02/02 02:12:41 ecd Exp $
  * cgsix.c: cgsix frame buffer driver
  *
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
@@ -553,7 +553,7 @@
 	
 static void cg6_blitc(unsigned short charattr, int xoff, int yoff)
 {
-	unsigned char attrib = charattr >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr);
 	unsigned char *p = &vga_font[((unsigned char)charattr) << 4];
 	GX_BLITC_START(attrib)
 	GX_BLITC_BODY1(xoff, yoff, gx->font=((*p++) << 24))
@@ -562,7 +562,7 @@
 
 static void cg6_setw(int xoff, int yoff, unsigned short c, int count)
 {
-	unsigned char attrib = c >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(c);
 	unsigned char *p = &vga_font[((unsigned char)c) << 4];
 	register unsigned char *q;
 	register uint l;
@@ -584,7 +584,7 @@
 
 static void cg6_cpyw(int xoff, int yoff, unsigned short *p, int count)
 {
-	unsigned char attrib = *p >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p);
 	unsigned char *p1, *p2, *p3, *p4;
 	GX_BLITC_START(attrib)
 	if (count >= 4) {
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/fb.h linux/drivers/sbus/char/fb.h
--- v2.1.28/linux/drivers/sbus/char/fb.h	Mon Dec 30 02:00:05 1996
+++ linux/drivers/sbus/char/fb.h	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: fb.h,v 1.20 1996/12/23 10:16:08 ecd Exp $
+/* $Id: fb.h,v 1.21 1997/02/02 02:12:43 ecd Exp $
  * fb.h: contains the definitions of the structures that various sun
  *       frame buffer can use to do console driver stuff.
  *
@@ -149,6 +149,12 @@
 } fbinfo_t;
 
 #define CM(i, j) [3*(i)+(j)]
+
+extern unsigned char reverse_color_table[];
+
+#define CHARATTR_TO_SUNCOLOR(attr) \
+	((reverse_color_table[(attr) >> 12] << 4) | \
+	  reverse_color_table[((attr) >> 8) & 0x0f])
 
 extern fbinfo_t *fbinfo;
 extern int fbinfos;
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/leo.c linux/drivers/sbus/char/leo.c
--- v2.1.28/linux/drivers/sbus/char/leo.c	Mon Dec 30 02:00:05 1996
+++ linux/drivers/sbus/char/leo.c	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: leo.c,v 1.10 1996/12/23 10:16:09 ecd Exp $
+/* $Id: leo.c,v 1.11 1997/02/02 02:12:44 ecd Exp $
  * leo.c: SUNW,leo 24/8bit frame buffer driver
  *
  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -637,7 +637,7 @@
 	
 static void leo_blitc(unsigned short charattr, int xoff, int yoff)
 {
-	unsigned char attrib = charattr >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(charattr);
 	unsigned char *p = &vga_font[((unsigned char)charattr) << 4];
 	u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff;
 	GX_BLITC_START(attrib, xoff, yoff, 1)
@@ -648,7 +648,7 @@
 
 static void leo_setw(int xoff, int yoff, unsigned short c, int count)
 {
-	unsigned char attrib = c >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(c);
 	unsigned char *p = &vga_font[((unsigned char)c) << 4];
 	register unsigned char *q;
 	u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff;
@@ -664,7 +664,7 @@
 
 static void leo_cpyw(int xoff, int yoff, unsigned short *p, int count)
 {
-	unsigned char attrib = *p >> 8;
+	unsigned char attrib = CHARATTR_TO_SUNCOLOR(*p);
 	register unsigned char *q;
 	u32 *u = ((u32 *)fbinfo[0].base) + (yoff << 11) + xoff;
 	GX_BLITC_START(attrib, xoff, yoff, count)
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/openprom.c linux/drivers/sbus/char/openprom.c
--- v2.1.28/linux/drivers/sbus/char/openprom.c	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/char/openprom.c	Wed Mar  5 17:04:32 1997
@@ -1,6 +1,6 @@
 /*
  * Linux/SPARC PROM Configuration Driver
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
+ * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
  * Copyright (C) 1996 Eddie C. Dost  (ecd@skynet.be)
  *
  * This character device driver allows user programs to access the
@@ -29,6 +29,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  */
 
+#define PROMLIB_INTERNAL
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -40,8 +42,8 @@
 #include <asm/oplib.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/openpromio.h>
 
-#include <linux/openpromio.h>
 
 /* Private data kept by the driver for each descriptor. */
 typedef struct openprom_private_data
@@ -214,9 +216,9 @@
 
 		save_and_cli(flags);
 		if (cmd == OPROMNEXT)
-			node = prom_nodeops->no_nextnode(node);
+			node = __prom_getsibling(node);
 		else
-			node = prom_nodeops->no_child(node);
+			node = __prom_getchild(node);
 		restore_flags(flags);
 
 		data->current_node = node;
@@ -447,9 +449,9 @@
 
 		save_and_cli(flags);
 		if (cmd == OPIOCGETNEXT)
-			node = prom_nodeops->no_nextnode(node);
+			node = __prom_getsibling(node);
 		else
-			node = prom_nodeops->no_child(node);
+			node = __prom_getchild(node);
 		restore_flags(flags);
 
 		__copy_to_user_ret((void *)arg, &node, sizeof(int), -EFAULT);
@@ -601,10 +603,8 @@
 }
 
 #ifdef MODULE
-
 void cleanup_module(void)
 {
 	misc_deregister(&openprom_dev);
 }
-
 #endif
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/suncons.c linux/drivers/sbus/char/suncons.c
--- v2.1.28/linux/drivers/sbus/char/suncons.c	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/char/suncons.c	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: suncons.c,v 1.44 1997/01/25 02:47:10 miguel Exp $
+/* $Id: suncons.c,v 1.50 1997/02/26 08:55:22 ecd Exp $
  *
  * suncons.c: Sun SparcStation console support.
  *
@@ -166,39 +166,52 @@
 
 #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 */
+/* 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_1152_128 (int cindex)
+color_fbuf_offset_1280_144 (int cindex)
 {
-	register int i = (cindex>>7);
-	/* (1152 * CHAR_HEIGHT) == 10010000000.0000 */
-	return skip_bytes + (i << 14) + (i << 11) + ((cindex & 127) << 3);
+	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_1280_144 (int cindex)
+color_fbuf_offset_1152_128 (int cindex)
 {
-	register int i = (cindex/144);
-	/* (1280 * CHAR_HEIGHT) == 10100000000.0000 */
-	return skip_bytes + (i << 14) + (i << 12) + ((cindex % 144) << 3);
+	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);
 }
 	 
@@ -345,10 +358,10 @@
 		under_cursor[1] = dst[1];
 		under_cursor[2] = dst[ints_per_line];
 		under_cursor[3] = dst[ints_per_line+1];
-		dst[0] = 0x0f0f0f0f;
-		dst[1] = 0x0f0f0f0f;
-		dst[ints_per_line] = 0x0f0f0f0f;
-		dst[ints_per_line+1] = 0x0f0f0f0f;
+		dst[0] = 0x00000000;
+		dst[1] = 0x00000000;
+		dst[ints_per_line] = 0x00000000;
+		dst[ints_per_line+1] = 0x00000000;
 		break;
 	}
 	default:
@@ -399,7 +412,7 @@
 		rects [1] = 0;
 		rects [2] = con_width;
 		rects [3] = con_height;
-		(*fbinfo[0].fill)(0, 1, rects);
+		(*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) {
@@ -411,7 +424,7 @@
 		(*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 * 84), linux_logo + 80 * i, 80);
+				memcpy (p + (cpu * 88), linux_logo + 80 * i, 80);
 			}
 		}
 	} else if (con_depth == 1) {
@@ -419,7 +432,7 @@
 			memcpy (p, linux_logo_bw + 10 * i, 10);
 	}
 	putconsxy(0, q);
-	ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 80 * (linux_num_cpus - 1);
+	ush = (unsigned short *) video_mem_base + video_num_columns * 2 + 20 + 11 * (linux_num_cpus - 1);
 
 	for (p = "Linux/SPARC version " UTS_RELEASE; *p; p++, ush++) {
 		*ush = (attr << 8) + *p;
@@ -584,9 +597,10 @@
 		rects [1] = 0;
 		rects [2] = con_width;
 		rects [3] = con_height;
-		(*fbinfo[0].fill)(0, 1, rects);
+		(*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) : 0,
+		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));
@@ -608,12 +622,13 @@
 		rects [1] = 0;
 		rects [2] = fbinfo[n].type.fb_width;
 		rects [3] = fbinfo[n].type.fb_height;
-		(*fbinfo[n].fill)(0, 1, rects);
+		(*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) : 0,
+		       (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);
 	}
@@ -642,14 +657,14 @@
 		rects [13] = con_height - y_margin;
 		rects [14] = con_width - x_margin;
 		rects [15] = con_height;
-		(*fbinfo[0].fill)(0, 4, rects);
+		(*fbinfo[0].fill)(reverse_color_table[0], 4, rects);
 	} else {
 		memset (con_fb_base, 
-			(con_depth == 1) ? ~(0) : 0,
+			(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) : 0,
+			(con_depth == 1) ? ~(0) : reverse_color_table[0],
 			skip_bytes - (x_margin<<1));
 		he = con_height - 2 * y_margin;
 		i = 2 * x_margin;
@@ -660,7 +675,7 @@
 		} else {
 			for (p = con_fb_base+skip_bytes-(x_margin<<1), h = 0;
 			     h <= he; p += chars_per_line, h++)
-				memset (p, 0, i);
+				memset (p, reverse_color_table[0], i);
 		}
 	}
 	if (fbinfo [0].switch_from_graph)
@@ -687,6 +702,18 @@
 {
 }
 
+/*
+ * 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
+};
+
+static unsigned char sparc_color_table[] = {
+	15, 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11
+};
+
 /* Call the frame buffer routine for setting the palette */
 void
 set_palette (void)
@@ -699,7 +726,7 @@
 	
 		/* First keep color_map with the palette colors */
 		for (i = 0; i < 16; i++){
-			j = color_table [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];
@@ -778,7 +805,8 @@
 }
 
 static char *known_cards [] __initdata = {
-	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx", "cgfourteen", "SUNW,leo", 0
+	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", "SUNW,tcx",
+	"cgfourteen", "SUNW,leo", 0
 };
 static char *v0_known_cards [] __initdata = {
 	"cgsix", "cgthree", "cgRDI", "cgthree+", "bwtwo", 0
@@ -799,12 +827,13 @@
 	int resx, resy;
 	int x_margin, y_margin;
 } scr_def [] = {
-	{ 1, 1152, 900,  8,  18 },
-	{ 8, 1152, 900,  64, 18 },
-	{ 8, 1152, 1024, 64, 80 },
 	{ 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 },
 };
 
@@ -823,8 +852,10 @@
 }
 
 __initfunc(static void
-sparc_framebuffer_setup(int primary, int con_node, int type, struct linux_sbus_device *sbdp, 
-			uint base, uint con_base, int prom_fb, int parent_node))
+	   sparc_framebuffer_setup(int primary, int con_node,
+				   int type, struct linux_sbus_device *sbdp, 
+				   uint base, uint con_base, int prom_fb,
+				   int parent_node))
 {
 	static int frame_buffers = 1;
 	int n, i;
@@ -835,7 +866,8 @@
 	if (primary)
 		n = 0;
 	else {
-		if (frame_buffers == FRAME_BUFFERS) return; /* Silently ignore */
+		if (frame_buffers == FRAME_BUFFERS)
+			return; /* Silently ignore */
 		n = frame_buffers++;
 	}
 	
@@ -934,21 +966,26 @@
 						2 * x_margin / con_depth;
 			ORIG_VIDEO_LINES = (con_height - 2 * y_margin) / 16;
 			switch (chars_per_line) {
-			case 1152:
-				if (ORIG_VIDEO_COLS == 128)
-					color_fbuf_offset =
-						color_fbuf_offset_1152_128;
-				break;
 			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 =
@@ -960,7 +997,8 @@
 
 		if (!scr_def [i].depth){
 			x_margin = y_margin = 0;
-			prom_printf ("console: unknown video resolution %dx%d, depth %d\n",
+			prom_printf ("console: unknown video resolution %dx%d,"
+				     " depth %d\n",
 			      	     con_width, con_height, con_depth);
 			prom_halt ();
 		}
@@ -1082,7 +1120,8 @@
 		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);
+			prom_apply_sbus_ranges (sbdp->my_bus, &sbdp->reg_addrs [0],
+						sbdp->num_registers, sbdp);
 
 			propl = prom_getproperty(con_node, "address", (char *) &con_base, 4);
 			if (propl != 4) con_base = 0;
@@ -1222,6 +1261,7 @@
 sun_blitc(uint charattr, unsigned long addr)
 {
 	unsigned int fgmask, bgmask;
+	unsigned char attrib;
 	int j, idx;
 	unsigned char *font_row;
 
@@ -1235,6 +1275,7 @@
   	/* 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){
@@ -1281,7 +1322,7 @@
 		"\n\t std	%0, [%1]" \
 		"\n\t std	%0, [%1 + %2]"
 
-			x1 = (charattr >> 12) & 0x0f;
+			x1 = attrib >> 4;
 			x1 |= x1 << 8;
 			x1 |= x1 << 16;
 			x3 = cpl << 1;
@@ -1303,7 +1344,7 @@
 				__asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8));
 			restore_flags (flags);
 #else
-			bgmask = (charattr >> 12) & 0x0f;
+			bgmask = attrib >> 4;
 			bgmask |= bgmask << 8;
 			bgmask |= bgmask << 16;
 			
@@ -1328,8 +1369,8 @@
 	                restore_flags(flags);
 #endif
 		} else /* non-space */ {
-			fgmask = (charattr >> 8) & 0x0f;
-			bgmask = (charattr >> 12) & 0x0f;
+			fgmask = attrib & 0x0f;
+			bgmask = attrib >> 4;
 			fgmask |= fgmask << 8;
 			fgmask |= fgmask << 16;
 			bgmask |= bgmask << 8;
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/sunmouse.c linux/drivers/sbus/char/sunmouse.c
--- v2.1.28/linux/drivers/sbus/char/sunmouse.c	Sun Jan 26 02:07:18 1997
+++ linux/drivers/sbus/char/sunmouse.c	Wed Mar  5 17:04:32 1997
@@ -134,6 +134,56 @@
 	wake_up_interruptible (&sunmouse.proc_list);
 }
 
+/* Auto baud rate "detection".  ;-) */
+static int mouse_bogon_bytes = 0;
+static int mouse_baud_changing = 0;	/* For reporting things to the user. */
+static int mouse_baud = 4800;		/* Initial rate set by zilog driver. */
+
+/* Change the baud rate after receiving too many "bogon bytes". */
+void sun_mouse_change_baud(void)
+{
+	extern void zs_change_mouse_baud(int newbaud);
+
+	if(mouse_baud == 1200)
+		mouse_baud = 4800;
+	else
+		mouse_baud = 1200;
+
+	zs_change_mouse_baud(mouse_baud);
+	mouse_baud_changing = 1;
+}
+
+void mouse_baud_detection(unsigned char c)
+{
+	static int wait_for_synchron = 1;
+	static int ctr = 0;
+
+	if(wait_for_synchron) {
+		if((c < 0x80) || (c > 0x87))
+			mouse_bogon_bytes++;
+		else {
+			ctr = 0;
+			wait_for_synchron = 0;
+		}
+	} else {
+		ctr++;
+		if(ctr >= 4) {
+			ctr = 0;
+			wait_for_synchron = 1;
+			if(mouse_baud_changing == 1) {
+				printk("sunmouse: Successfully adjusted to %d baud.\n",
+				       mouse_baud);
+				mouse_baud_changing = 0;
+			}
+		}
+	}
+	if(mouse_bogon_bytes > 12) {
+		sun_mouse_change_baud();
+		mouse_bogon_bytes = 0;
+		wait_for_synchron = 1;
+	}
+}
+
 /* The following is called from the zs driver when bytes are received on
  * the Mouse zs8530 channel.
  */
@@ -148,6 +198,8 @@
 	if(!sunmouse.active)
 		return;
 
+	mouse_baud_detection(byte);
+
 	if (!gen_events){
 		push_char (byte);
 		return;
@@ -419,6 +471,7 @@
 	sunmouse.delta_x = sunmouse.delta_y = 0;
 	sunmouse.button_state = 0x80;
 	sunmouse.proc_list = NULL;
+	sunmouse.byte = 69;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v2.1.28/linux/drivers/sbus/char/sunserial.c	Wed Jan  1 06:24:40 1997
+++ linux/drivers/sbus/char/sunserial.c	Wed Mar  5 17:04:32 1997
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.25 1996/12/30 07:50:26 davem Exp $
+/* $Id: sunserial.c,v 1.27 1997/03/04 19:33:55 davem Exp $
  * serial.c: Serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1736,7 +1736,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.25 $";
+	char *revision = "$Revision: 1.27 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2091,6 +2091,19 @@
 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;
@@ -2152,6 +2165,7 @@
 	
 	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;
@@ -2183,6 +2197,10 @@
 	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);
 
 	/*
@@ -2308,7 +2326,7 @@
 			write_zsreg(zs_soft[channel].zs_channel, R11,
 				    (TCBR | RCBR));
 
-			zs_soft[channel].zs_baud = 1200;
+			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,
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/dvma.c linux/drivers/sbus/dvma.c
--- v2.1.28/linux/drivers/sbus/dvma.c	Fri Dec 13 01:37:37 1996
+++ linux/drivers/sbus/dvma.c	Wed Mar  5 17:04:32 1997
@@ -76,9 +76,11 @@
 		 * routine only alloc 1 page, that was what the original code did
 		 */
 		if(hme) /* On HME cards, dvma lives with esp, 2 reg sets. */
-			prom_apply_sbus_ranges(sbus, dma->SBus_dev->reg_addrs, 0x2);
+			prom_apply_sbus_ranges(sbus, dma->SBus_dev->reg_addrs,
+					       0x2, dma->SBus_dev);
 		else    /* All others have only 1 reg set. */
-			prom_apply_sbus_ranges(sbus, dma->SBus_dev->reg_addrs, 0x1);
+			prom_apply_sbus_ranges(sbus, dma->SBus_dev->reg_addrs,
+					       0x1, dma->SBus_dev);
 		dma->regs = (struct sparc_dma_registers *)
 			sparc_alloc_io (dma->SBus_dev->reg_addrs[0].phys_addr, 0,
 					PAGE_SIZE, "dma",
diff -u --recursive --new-file v2.1.28/linux/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- v2.1.28/linux/drivers/sbus/sbus.c	Fri Dec 13 01:37:37 1996
+++ linux/drivers/sbus/sbus.c	Wed Mar  5 17:04:32 1997
@@ -12,6 +12,7 @@
 #include <asm/sbus.h>
 #include <asm/dma.h>
 #include <asm/oplib.h>
+#include <asm/bpp.h>
 
 /* This file has been written to be more dynamic and a bit cleaner,
  * but it still needs some spring cleaning.
@@ -59,6 +60,7 @@
 		panic("fill_sbus_device");
 	}
 	sbus_dev->num_registers = (len/sizeof(struct linux_prom_registers));
+	sbus_dev->ranges_applied = 0;
 
 	base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr;
 	if(base>=SUN_SBUS_BVADDR || sparc_cpu_model == sun4m) {
@@ -147,6 +149,9 @@
 #ifdef CONFIG_SUN_MOSTEK_RTC
 extern int rtc_init(void);
 #endif
+#ifdef CONFIG_SPARCAUDIO
+extern int sparcaudio_init(void);
+#endif
 
 __initfunc(static unsigned long 
 sbus_do_child_siblings(unsigned long memory_start, int start_node,
@@ -157,6 +162,7 @@
 	int this_node = start_node;
 
 	/* Child already filled in, just need to traverse siblings. */
+	child->child = 0;
 	while((this_node = prom_getsibling(this_node)) != 0) {
 		this_dev->next = (struct linux_sbus_device *) memory_start;
 		memory_start += sizeof(struct linux_sbus_device);
@@ -332,6 +338,12 @@
 #endif
 #ifdef CONFIG_SUN_MOSTEK_RTC
 	rtc_init();
+#endif
+#ifdef CONFIG_SPARCAUDIO
+	sparcaudio_init();
+#endif
+#ifdef CONFIG_SUN_BPP
+	bpp_init();
 #endif
 	return memory_start;
 }
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.28/linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Jan 13 22:21:41 1997
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Mar  4 13:22:49 1997
@@ -1,3 +1,58 @@
+Thu Feb 27 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c ncr53c8xx.h revision 1.18b
+	- 'On paper' support of the NCR53C895 Ultra-2 chip.
+	  (Clock quadrupler + 7 clock divisors)
+	- Load the main part of the script into the on-board RAM.
+	- 810A rev. 0x11 PCI problem fixed.
+	  This chip is now supported with all PCI features enabled and 
+	  16 dwords burst transfers. 
+	- Align on 32 boundary some internal structures.
+	  That fixes the 810A problem and allows cache line bursting when 
+	  moving the global header (64 bytes) from/to CCBs to/from NCB.
+	- Synchronous parameters calculation rewritten. The driver 
+	  now uses all available clock divisors and will be able to support 
+          clock frequencies that are not multiple of 40 Mhz if necessary.
+
+Sat Feb 8 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c - revision 1.17a
+	- IRQ mode set up from boot setup command.
+	  irqm:0    open drain (default)
+	  irqm:1    preserve initial setting (assumed from BIOS)
+	  irqm:2    totem pole
+	- DIFF mode set up from boot setup command.
+	  Suggested by Richard Waltham.
+	  diff:0    never set up diff mode (default)
+	  diff:1    set up diff mode according to initial setting (BIOS?)
+	  diff:2    always set up diff mode
+	  diff:3    set up diff mode if GPIO3 is zero (SYMBIOS boards)
+	- Change CONFIG option for LED support.
+          CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT allows LED support and 
+	  DIFF support for SYMBIOS boards and compatibles (clones?).
+	- Set 16 DWORD bursts for 810A rev. >= 0x12 since my SC200 with 
+	  such a chip have no problem with it (MB with Triton 2 HX).
+	  810A rev. 0x11 are set to 8 DWORD bursts since they may give
+	  problems with PCI read multiple and Triton 2 HX.
+	  Thanks to Stefan for this information.
+
+Sat Jan 25 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c - revision 1.17
+	- Controller LED support.
+	  Only works with LED pin wired to GPIO_FETCHN, so probably with 
+          all boards using SMDS BIOS.
+	  This option can be enabled only if CONFIG_EXPERIMENTAL is set.
+	- Assume clock doubler for 875 chip when clock frequency measurement 
+	  result is 40 MHz. May help when some old stuff as SDMS BIOS 3.0 
+	  or some old driver has broken the normal BIOS settings.
+	- Add wide negotiation control from boot setup command.
+	  May be usefull with systems using a 875 based board connected to 
+	  a wide device through a 50 pins to 68 pins converter.
+	- Add a "boot fail safe option" to the boot setup command line.
+	- Rewrite the "reset_command" routine.
+	  Low-level driver are responsible to keep the involved command 
+	  alive. The new code seems to behave correctly.
+	- Change some variables used by the script from u_long to u_int32.
+	- Remove some useless code.
+
 Sun Jan 12 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
 	* ncr53c8xx.c - revision 1.16e
 	- Add support of PCI burst length control from boot setup command.
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.28/linux/drivers/scsi/Config.in	Tue Mar  4 10:25:24 1997
+++ linux/drivers/scsi/Config.in	Tue Mar  4 13:22:49 1997
@@ -65,6 +65,9 @@
     if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then
       bool '  not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
     fi
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool '  assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    fi
   fi
 fi
 if [ "$CONFIG_MCA" = "y" ]; then
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.28/linux/drivers/scsi/Makefile	Tue Mar  4 10:25:24 1997
+++ linux/drivers/scsi/Makefile	Thu Mar  6 10:23:04 1997
@@ -266,10 +266,8 @@
 
 ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),y)
 L_OBJS += g_NCR5380.o
-EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
 else
   ifeq ($(CONFIG_SCSI_GENERIC_NCR5380),m)
-  EXTRA_CFLAGS = -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};"
   M_OBJS += g_NCR5380.o
   endif
 endif
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.28/linux/drivers/scsi/README.ncr53c8xx	Mon Jan 13 22:21:41 1997
+++ linux/drivers/scsi/README.ncr53c8xx	Tue Mar  4 13:22:49 1997
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-12 January 1997
+27 February 1997
 ===============================================================================
 
 1.  Introduction
@@ -91,12 +91,11 @@
 815        Y         N        N            Y             Y
 825        Y         Y        N            Y             Y
 825A       Y         Y        N            Y             Y
-860        N         N        Y(1)         Y             Y
-875        Y         Y        Y(1)         Y             Y
+860        N         N        Y            Y             Y
+875        Y         Y        Y            Y             Y
+895        Y         Y        Y(1)         Y             not yet 
 
-(1) Ultra (fast20 / fast40) SCSI data transfers are "in theory" supported 
-    by the driver, but not fully tested, since the maintainer does not have 
-    for the moment Ultra SCSI hard disks.
+(1) The 895 chip is supported 'on paper'.
 
 3. Summary of other supported features.
 
@@ -212,6 +211,7 @@
 860       0x6
 825A      0x3            >= 0x10
 875       0xf
+895       0xc
 
 The profiling information is updated upon completion of SCSI commands.
 A data structure is allocated and zeroed when the host adapter is
@@ -287,16 +287,21 @@
 
 Available commands:
 
-8.1 Set minimum synchronous period
+8.1 Set minimum synchronous period factor
 
-    setsync <target> <period>
+    setsync <target> <period factor>
 
     target:    target number
-    period:    minimum synchronous period in nano-seconds.
-               Maximum speed = 1000/(4*period) MB/second
+    period:    minimum synchronous period.
+               Maximum speed = 1000/(4*period factor) except for special
+               cases below.
 
     Specify a period of 255, to force asynchronous transfer mode.
 
+      10 means 25 nano-seconds synchronous period
+      11 means 30 nano-seconds synchronous period
+      12 means 50 nano-seconds synchronous period
+
 8.2 Set wide size
 
     setwide <target> <size>
@@ -403,6 +408,18 @@
     you can answer "y". Then, all SCSI devices will never disconnect the bus 
     even while performing long SCSI operations.
 
+CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+    Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 
+    bit as a flag indicating singled-ended/differential interface.
+    If all the boards of your system are genuine SYMBIOS boards or use
+    BIOS and drivers from SYMBIOS, you would want to enable this option.
+    This option must NOT be enabled if your system has at least one 53C8XX 
+    based scsi board with a vendor-specific BIOS.
+    For example, Tekram DC-390/U, DC-390/W and DC-390/F scsi controllers 
+    use a vendor-specific BIOS and are known to not use SYMBIOS compatible 
+    GPIO wiring. So, this option must not be enabled if your system has 
+    such a board installed.
+
 
 10. Boot setup commands
 
@@ -459,11 +476,14 @@
   If the driver is configured with a maximum of 4 queued commands, tags:4 is 
   the right argument to specify.
 
-Default synchronous negotiation frequency
-      sync:0     disabled (asynchronous transfer mode)
-      sync:#MHz  enabled up to #MHz.
-  #MHz > 10     Ultra SCSI
-  #MHz >  5     Fast  SCSI
+Default synchronous period factor
+      sync:255     disabled (asynchronous transfer mode)
+      sync:#factor
+  #factor = 10     Ultra-2 SCSI 40 Mega-transfers / second
+  #factor = 11     Ultra-2 SCSI 33 Mega-transfers / second
+  #factor < 25     Ultra   SCSI 20 Mega-transfers / second
+  #factor < 50     Fast    SCSI-2
+
   In all cases, the driver will use the minimum transfer period supported by 
   controllers according to NCR53C8XX chip type.
 
@@ -478,7 +498,7 @@
       verb:2     too much
 
 Debug mode
-      debug:0	clear debug flags
+      debug:0	 clear debug flags
       debug:#x   set debug flags
   #x is an integer value combining the following power-of-2 values:
   DEBUG_ALLOC       0x1
@@ -509,18 +529,71 @@
   and revision ids. By default the driver uses the maximum value supported 
   by the chip.
 
+LED support
+     led:1      enable  LED support
+     led:0      disable LED support
+  Donnot enable LED support if your scsi board does not use SDMS BIOS.
+  (See 'Configuration parameters')
+
+Max wide
+    wide:1      wide scsi enabled
+    wide:0      wide scsi disabled
+  Some scsi boards use a 875 (ultra wide) and only supply narrow connectors.
+  If you have connected a wide device with a 50 pins to 68 pins cable 
+  converter, any accepted wide negotiation will break further data transfers.
+  In such a case, using "wide:0" in the bootup command will be helpfull. 
+
+Differential mode
+    diff:0	never set up diff mode
+    diff:1	set up diff mode if BIOS set it
+    diff:2	always set up diff mode
+    diff:3	set diff mode if GPIO3 is not set
+
+IRQ mode
+    irqm:0	always open drain
+    irqm:1	same as initial settings (assumed BIOS settings)
+    irqm:2	always totem pole
+
+Boot fail safe
+    safe:y	load the following assumed fail safe initial setup
+
+  master parity			disabled		mpar:n
+  scsi parity			enabled			spar:y
+  disconnections		not allowed		disc:n
+  special features		disabled		specf:n
+  ultra scsi			disabled		ultra:n
+  force sync negotiation	disabled		fsn:n
+  verbosity level		2			verb:2
+  tagged command queuing	disabled		tags:0
+  synchronous negotiation	disabled		sync:255
+  debug flags			none			debug:0
+  burst length			from BIOS settings	burst:255
+  LED support			disabled		led:0
+  wide support			disabled		wide:0
+  settle time			10 seconds		settle:10
+  differential support		from BIOS settings	diff:1
+  irq mode			from BIOS settings	irqm:1
 
 10.3 Advised boot setup commands
 
 If the driver has been configured with default options, the equivalent 
 boot setup is:
-    ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:5,tags:0,verb:1
 
-For an installation diskette or a safe but not fast system, boot setup is:
-    ncr53c8xx=mpar:y,spar:y,disc:n,fsn:n,specf:n,ultra:n,sync:0,tags:0
+    ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,verb:2,tags:0\
+              sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
+
+For an installation diskette or a safe but not fast system,
+boot setup can be:
 
-My personnal system works flawlessly with the following setup:
-    ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:20,tags:8
+    ncr53c8xx=safe:y,mpar:y,disc:y
+    ncr53c8xx=safe:y,disc:y
+    ncr53c8xx=safe:y,mpar:y
+    ncr53c8xx=safe:y
+
+My personnal system works flawlessly with the following equivalent setup:
+
+    ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,verb:1,tags:8\
+              sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
 "ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
@@ -560,10 +633,10 @@
 	Maximum number of simultaneous tagged commands to a device.
 	Can be changed by "settags <target> <maxtags>"
 
-SCSI_NCR_SETUP_DEFAULT_SYNC     (default: 5)
-	Frequency in KHz the driver will use at boot time for synchronous 
+SCSI_NCR_SETUP_DEFAULT_SYNC     (default: 50)
+	Transfer period factor the driver will use at boot time for synchronous 
 	negotiation. 0 means asynchronous.
-	Can be changed by "setsync <target> <period>"
+	Can be changed by "setsync <target> <period factor>"
 
 SCSI_NCR_SETUP_DEFAULT_TAGS     (default: 4)
 	Default number of simultaneous tagged commands to a device.
@@ -635,18 +708,18 @@
 	Install.ncr53c8xx	: installation script
 
 	Patch-1.2.13.ncr53c8xx  : patch for linux-1.2.13
-	Patch-2.0.27.ncr53c8xx  : patch for linux-2.0.27
+	Patch-2.0.29.ncr53c8xx  : patch for linux-2.0.29
+
+You must untar the distribution with the following command:
 
-Prior to installing the driver, you must untar the distribution, as follow:
+	tar zxvf ncrBsd2Linux-1.18b-src.tar.gz
 
-	mkdir ncrB2L
-	cd ncrB2L
-	tar zxvf ncrBsd2Linux-1.16b-src.tar.gz
+The sub-directory ncr53c8xx-1.18b will be created. Change to this directory.
 
 
 12.2 Installation procedure
 
-This install script has been tested with linux-1.2.13 and 2.0.27.
+This install script has been tested with linux-1.2.13 and 2.0.29.
 
 This procedure copies the new driver files to the kernel tree and
 applies a patch to some files of the kernel tree.
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/dtc.c linux/drivers/scsi/dtc.c
--- v2.1.28/linux/drivers/scsi/dtc.c	Wed Nov  6 02:21:09 1996
+++ linux/drivers/scsi/dtc.c	Thu Mar  6 10:23:04 1997
@@ -87,6 +87,53 @@
 #include<linux/stat.h>
 #include<linux/string.h>
 
+
+#define DTC_PUBLIC_RELEASE 2
+
+/*#define DTCDEBUG 0x1*/
+#define DTCDEBUG_INIT	0x1
+#define DTCDEBUG_TRANSFER 0x2
+
+/*
+ * The DTC3180 & 3280 boards are memory mapped.
+ * 
+ */
+
+/*
+ */ 
+/* Offset from DTC_5380_OFFSET */
+#define DTC_CONTROL_REG		0x100	/* rw */
+#define D_CR_ACCESS		0x80	/* ro set=can access 3280 registers */
+#define CSR_DIR_READ		0x40	/* rw direction, 1 = read 0 = write */
+
+#define CSR_RESET              0x80    /* wo  Resets 53c400 */
+#define CSR_5380_REG           0x80    /* ro  5380 registers can be accessed */
+#define CSR_TRANS_DIR          0x40    /* rw  Data transfer direction */
+#define CSR_SCSI_BUFF_INTR     0x20    /* rw  Enable int on transfer ready */
+#define CSR_5380_INTR          0x10    /* rw  Enable 5380 interrupts */
+#define CSR_SHARED_INTR        0x08    /* rw  Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY   0x04    /* ro  Host buffer not ready */
+#define CSR_SCSI_BUF_RDY       0x02    /* ro  SCSI buffer ready */
+#define CSR_GATED_5380_IRQ     0x01    /* ro  Last block xferred */
+#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
+
+
+#define DTC_BLK_CNT		0x101   /* rw 
+					 * # of 128-byte blocks to transfer */
+
+
+#define D_CR_ACCESS             0x80    /* ro set=can access 3280 registers */
+
+#define DTC_SWITCH_REG		0x3982	/* ro - DIP switches */
+#define DTC_RESUME_XFER		0x3982	/* wo - resume data xfer 
+					   * after disconnect/reconnect*/
+
+#define DTC_5380_OFFSET		0x3880	/* 8 registers here, see NCR5380.h */
+
+/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
+#define DTC_DATA_BUF		0x3900  /* rw 128 bytes long */
+
+
 struct proc_dir_entry proc_scsi_dtc = {
    PROC_SCSI_T128, 7, "dtc3x80",
 	S_IFDIR | S_IRUGO | S_IXUGO, 2
@@ -178,7 +225,7 @@
       else
 	 for (; !base && (current_base < NO_BASES); ++current_base) {
 #if (DTCDEBUG & DTCDEBUG_INIT)
-	 printk("scsi : probing address %08x\n", bases[current_base].address);
+	 printk("scsi-dtc : probing address %08x\n", bases[current_base].address);
 #endif
 	 for (sig = 0; sig < NO_SIGNATURES; ++sig)
 	    if (!bases[current_base].noauto && 
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/dtc.h linux/drivers/scsi/dtc.h
--- v2.1.28/linux/drivers/scsi/dtc.h	Wed Nov  6 02:21:09 1996
+++ linux/drivers/scsi/dtc.h	Thu Mar  6 10:23:04 1997
@@ -28,52 +28,6 @@
 #ifndef DTC3280_H
 #define DTC3280_H
 
-#define DTC_PUBLIC_RELEASE 2
-
-/*#define DTCDEBUG 0x1*/
-#define DTCDEBUG_INIT	0x1
-#define DTCDEBUG_TRANSFER 0x2
-
-/*
- * The DTC3180 & 3280 boards are memory mapped.
- * 
- */
-
-/*
- */ 
-/* Offset from DTC_5380_OFFSET */
-#define DTC_CONTROL_REG		0x100	/* rw */
-#define D_CR_ACCESS		0x80	/* ro set=can access 3280 registers */
-#define CSR_DIR_READ		0x40	/* rw direction, 1 = read 0 = write */
-
-#define CSR_RESET              0x80    /* wo  Resets 53c400 */
-#define CSR_5380_REG           0x80    /* ro  5380 registers can be accessed */
-#define CSR_TRANS_DIR          0x40    /* rw  Data transfer direction */
-#define CSR_SCSI_BUFF_INTR     0x20    /* rw  Enable int on transfer ready */
-#define CSR_5380_INTR          0x10    /* rw  Enable 5380 interrupts */
-#define CSR_SHARED_INTR        0x08    /* rw  Interrupt sharing */
-#define CSR_HOST_BUF_NOT_RDY   0x04    /* ro  Host buffer not ready */
-#define CSR_SCSI_BUF_RDY       0x02    /* ro  SCSI buffer ready */
-#define CSR_GATED_5380_IRQ     0x01    /* ro  Last block xferred */
-#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
-
-
-#define DTC_BLK_CNT		0x101   /* rw 
-					 * # of 128-byte blocks to transfer */
-
-
-#define D_CR_ACCESS             0x80    /* ro set=can access 3280 registers */
-
-#define DTC_SWITCH_REG		0x3982	/* ro - DIP switches */
-#define DTC_RESUME_XFER		0x3982	/* wo - resume data xfer 
-					   * after disconnect/reconnect*/
-
-#define DTC_5380_OFFSET		0x3880	/* 8 registers here, see NCR5380.h */
-
-/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
-#define DTC_DATA_BUF		0x3900  /* rw 128 bytes long */
-
-
 #ifndef ASM
 int dtc_abort(Scsi_Cmnd *);
 int dtc_biosparam(Disk *, kdev_t, int*);
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c
--- v2.1.28/linux/drivers/scsi/esp.c	Fri Dec 13 01:37:37 1996
+++ linux/drivers/scsi/esp.c	Wed Mar  5 17:04:33 1997
@@ -750,7 +750,8 @@
 			/* Map in the ESP registers from I/O space */
 			if(!hme) {
 				prom_apply_sbus_ranges(esp->edev->my_bus, 
-						       esp->edev->reg_addrs, 1);
+						       esp->edev->reg_addrs,
+						       1, esp->edev);
 
 				esp->eregs = eregs = (struct Sparc_ESP_regs *)
 				sparc_alloc_io(esp->edev->reg_addrs[0].phys_addr, 0,
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.28/linux/drivers/scsi/ncr53c8xx.c	Mon Jan 13 22:21:41 1997
+++ linux/drivers/scsi/ncr53c8xx.c	Tue Mar  4 13:22:49 1997
@@ -40,7 +40,7 @@
 */
 
 /*
-**	12 January 1997, version 1.16e
+**	27 February 1997, version 1.18b
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -57,6 +57,7 @@
 **		53C825		(Wide, ~53C820 with on board rom BIOS)
 **		53C860		(Narrow fast 20, BIOS required)
 **		53C875		(Wide fast 40 with on board rom BIOS)
+**		53C895		(Ultra2 80 MB/s with on board rom BIOS)
 **
 **	Other features:
 **		Memory mapped IO (linux-1.3.X and above only)
@@ -151,24 +152,6 @@
 #endif
 
 /*
-**    The maximal synchronous frequency in kHz.
-**    (0=asynchronous)
-*/
-
-#ifndef SCSI_NCR_MAX_SYNC
-#define SCSI_NCR_MAX_SYNC   (10000)
-#endif
-
-/*
-**    The maximal bus with (in log2 byte)
-**    (0=8 bit, 1=16 bit)
-*/
-
-#ifndef SCSI_NCR_MAX_WIDE
-#define SCSI_NCR_MAX_WIDE   (1)
-#endif
-
-/*
 **    The maximum number of tags per logic unit.
 **    Used only for disk devices that support tags.
 */
@@ -352,18 +335,66 @@
 **	I notice that kmalloc() returns NULL during host attach under
 **	Linux 1.2.13. But this ncr driver is reliable enough to
 **	accomodate with this joke.
-**/
+**
+**	kmalloc() only ensure 8 bytes boundary alignment.
+**	The NCR need better alignment for cache line bursting.
+**	The global header is moved betewen the NCB and CCBs and need 
+**	origin and destination addresses to have same lower four bits.
+**
+**	We use 32 boundary alignment for NCB and CCBs and offset multiple 
+**	of 32 for global header fields. That's too much but at least enough.
+*/
+
+#define ALIGN_SIZE(shift)	(1UL << shift)
+#define ALIGN_MASK(shift)	(~(ALIGN_SIZE(shift)-1))
+
+#define NCB_ALIGN_SHIFT		5
+#define CCB_ALIGN_SHIFT		5
+#define LCB_ALIGN_SHIFT		5
+#define SCR_ALIGN_SHIFT		5
+
+#define NCB_ALIGN_SIZE		ALIGN_SIZE(NCB_ALIGN_SHIFT)
+#define NCB_ALIGN_MASK		ALIGN_MASK(NCB_ALIGN_SHIFT)
+#define CCB_ALIGN_SIZE		ALIGN_SIZE(CCB_ALIGN_SHIFT)
+#define CCB_ALIGN_MASK		ALIGN_MASK(CCB_ALIGN_SHIFT)
+#define SCR_ALIGN_SIZE		ALIGN_SIZE(SCR_ALIGN_SHIFT)
+#define SCR_ALIGN_MASK		ALIGN_MASK(SCR_ALIGN_SHIFT)
+
+static void *m_alloc(int size, int a_shift)
+{
+	u_long addr;
+	void *ptr;
+	u_long a_size, a_mask;
+
+	if (a_shift < 3)
+		a_shift = 3;
+
+	a_size	= ALIGN_SIZE(a_shift);
+	a_mask	= ALIGN_MASK(a_shift);
+
+	ptr = (void *) kmalloc(size + a_size, GFP_ATOMIC);
+	if (ptr) {
+		addr	= (((u_long) ptr) + a_size) & a_mask;
+		*((void **) (addr - sizeof(void *))) = ptr;
+		ptr	= (void *) addr;
+	}
 
-static void *m_alloc(int size)
-{
-	void *ptr = (void *) kmalloc(size, GFP_ATOMIC);
-	if (((unsigned long) ptr) & 3)
-		panic("ncr53c8xx: kmalloc returns misaligned address %lx\n", (unsigned long) ptr);
 	return ptr;
 }
 
-static inline void m_free(void *ptr, int size)
-	{ kfree(ptr); }
+#ifdef MODULE
+static void m_free(void *ptr, int size)
+{
+	u_long addr;
+
+	if (ptr) {
+		addr	= (u_long) ptr;
+		ptr	= *((void **) (addr - sizeof(void *)));
+
+		kfree(ptr);
+	}
+}
+#endif
 
 /*
 **	Transfer direction
@@ -431,19 +462,27 @@
 **	This structure is initialized from linux config options.
 **	It can be overridden at boot-up by the boot command line.
 */
-static struct {
+struct ncr_driver_setup {
 	unsigned master_parity	: 1;
 	unsigned scsi_parity	: 1;
 	unsigned disconnection	: 1;
 	unsigned special_features : 1;
-	unsigned ultra_scsi	: 1;
+	unsigned ultra_scsi	: 2;
 	unsigned force_sync_nego: 1;
-	unsigned verbose	: 8;
-	unsigned default_tags;
-	unsigned default_sync;
-	unsigned debug;
-	unsigned burst_max;
-} driver_setup = SCSI_NCR_DRIVER_SETUP;
+	u_char	verbose;
+	u_char	default_tags;
+	u_short	default_sync;
+	u_short	debug;
+	u_char	burst_max;
+	u_char	led_pin;
+	u_char	max_wide;
+	u_char	settle_time;
+	u_char	diff_support;
+	u_char	irqm;
+};
+
+static struct ncr_driver_setup driver_setup	= SCSI_NCR_DRIVER_SETUP;
+static struct ncr_driver_setup driver_safe_setup= SCSI_NCR_DRIVER_SAFE_SETUP;
 
 /*
 **	Other Linux definitions
@@ -719,7 +758,6 @@
 */
 
 #define CCB_MAGIC	(0xf2691ad2)
-#define	MAX_TAGS	(16)		/* hard limit */
 
 /*==========================================================
 **
@@ -1019,9 +1057,9 @@
 	**	the last transfer command.
 	*/
 
-	u_long		savep;
-	u_long		lastp;
-	u_long		goalp;
+	u_int32		savep;
+	u_int32		lastp;
+	u_int32		goalp;
 
 	/*
 	**	The virtual address of the ccb
@@ -1152,6 +1190,14 @@
 
 struct ccb {
 	/*
+	**	This field forces 32 bytes alignement for phys.header,
+	**	in order to use cache line bursting when copying it 
+	**	to the ncb.
+	*/
+
+	struct link		filler[2];
+
+	/*
 	**	during reselection the ncr jumps to this point.
 	**	If a "SIMPLE_TAG" message was received,
 	**	then SFBR is set to the tag.
@@ -1278,6 +1324,16 @@
 */
 
 struct ncb {
+	/*
+	**	The global header.
+	**	Accessible to both the host and the
+	**	script-processor.
+	**	Is 32 bytes aligned since ncb is, in order to 
+	**	allow cache line bursting when copying it from or 
+	**	to ccbs.
+	*/
+	struct head     header;
+
 	/*-----------------------------------------------
 	**	Specific Linux fields
 	**-----------------------------------------------
@@ -1289,6 +1345,7 @@
 	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
 					/* that we can't put into the squeue */
 	u_char	release_stage;		/* Synchronisation stage on release  */
+	u_char	resetting;		/* Reset in progess		     */
 
 	/*-----------------------------------------------
 	**	Added field to support differences
@@ -1310,13 +1367,16 @@
 	u_char	sv_ctest3;
 	u_char	sv_ctest4;
 	u_char	sv_ctest5;
+	u_char	sv_gpcntl;
+	u_char	sv_stest2;
 
 	u_char	rv_dmode;
 	u_char	rv_dcntl;
 	u_char	rv_ctest3;
 	u_char	rv_ctest4;
 	u_char	rv_ctest5;
-	u_char	uf_doubler;
+	u_char	rv_stest2;
+	u_char	multiplier;
 
 	/*-----------------------------------------------
 	**	Scripts ..
@@ -1342,6 +1402,9 @@
 	vm_offset_t     vaddr;
 	vm_offset_t     paddr;
 
+	vm_offset_t     vaddr2;
+	vm_offset_t     paddr2;
+
 	/*
 	**	pointer to the chip's registers.
 	*/
@@ -1349,14 +1412,22 @@
 	struct ncr_reg* reg;
 
 	/*
-	**	A copy of the script, relocated for this ncb.
+	**	A copy of the scripts, relocated for this ncb.
+	*/
+	struct script	*script0;
+	struct scripth	*scripth0;
+
+	/*
+	**	Scripts instance virtual address.
 	*/
 	struct script	*script;
+	struct scripth	*scripth;
 
 	/*
-	**	Physical address of this instance of ncb->script
+	**	Scripts instance physical address.
 	*/
 	u_long		p_script;
+	u_long		p_scripth;
 
 	/*
 	**	The SCSI address of the host adapter.
@@ -1374,6 +1445,12 @@
 	*/
 	u_char		maxoffs;
 
+	/*
+	**	controller scsi clock frequency and available divisors
+	*/
+	u_long		clock_khz;
+	int		clock_divn;
+
 	/*-----------------------------------------------
 	**	Link to the generic SCSI driver
 	**-----------------------------------------------
@@ -1398,7 +1475,7 @@
 	/*
 	**	Start queue.
 	*/
-	u_long		squeue [MAX_START];
+	u_int32		squeue [MAX_START];
 	u_short		squeueput;
 	u_short		actccbs;
 
@@ -1427,19 +1504,12 @@
 	u_long		disc_ref;
 
 	/*
-	**	The global header.
-	**	Accessible to both the host and the
-	**	script-processor.
-	*/
-	struct head     header;
-
-	/*
 	**	The global control block.
 	**	It's used only during the configuration phase.
 	**	A target control block will be created
 	**	after the first successful transfer.
 	*/
-	struct ccb      ccb;
+	struct ccb      *ccb;
 
 	/*
 	**	message buffers.
@@ -1449,7 +1519,7 @@
 	*/
 	u_char		msgout[8];
 	u_char		msgin [8];
-	u_long		lastmsg;
+	u_int32		lastmsg;
 
 	/*
 	**	Buffer for STATUS_IN phase.
@@ -1477,7 +1547,8 @@
 	u_short		irq;
 };
 
-#define NCB_SCRIPT_PHYS(np,lbl)	(np->p_script + offsetof (struct script, lbl))
+#define NCB_SCRIPT_PHYS(np,lbl)	 (np->p_script  + offsetof (struct script, lbl))
+#define NCB_SCRIPTH_PHYS(np,lbl) (np->p_scripth + offsetof (struct scripth, lbl))
 
 /*==========================================================
 **
@@ -1500,12 +1571,15 @@
 **----------------------------------------------------------
 */
 
+/*
+**	Script fragments which are loaded into the on-board RAM 
+**	of 825A, 875 and 895 chips.
+*/
 struct script {
 	ncrcmd	start		[  7];
 	ncrcmd	start0		[  2];
 	ncrcmd	start1		[  3];
 	ncrcmd  startpos	[  1];
-	ncrcmd  tryloop		[MAX_START*5+2];
 	ncrcmd  trysel		[  8];
 	ncrcmd	skip		[  8];
 	ncrcmd	skip2		[  3];
@@ -1523,14 +1597,6 @@
 	ncrcmd  status		[ 27];
 	ncrcmd  msg_in		[ 26];
 	ncrcmd  msg_bad		[  6];
-	ncrcmd  msg_parity	[  6];
-	ncrcmd	msg_reject	[  8];
-	ncrcmd	msg_ign_residue	[ 32];
-	ncrcmd  msg_extended	[ 18];
-	ncrcmd  msg_ext_2	[ 18];
-	ncrcmd	msg_wdtr	[ 27];
-	ncrcmd  msg_ext_3	[ 18];
-	ncrcmd	msg_sdtr	[ 27];
 	ncrcmd  complete	[ 13];
 	ncrcmd	cleanup		[ 12];
 	ncrcmd	cleanup0	[ 11];
@@ -1542,6 +1608,30 @@
 	ncrcmd  disconnect1	[ 23];
 	ncrcmd	msg_out		[  9];
 	ncrcmd	msg_out_done	[  7];
+	ncrcmd  badgetcc	[  6];
+	ncrcmd	reselect	[  8];
+	ncrcmd	reselect1	[  8];
+	ncrcmd	reselect2	[  8];
+	ncrcmd	resel_tmp	[  5];
+	ncrcmd  resel_lun	[ 18];
+	ncrcmd	resel_tag	[ 24];
+	ncrcmd  data_io		[  2];	/* MUST be just before data_in */
+	ncrcmd  data_in		[MAX_SCATTER * 4 + 7];
+};
+
+/*
+**	Script fragments which stay in main memory for all chips.
+*/
+struct scripth {
+	ncrcmd  tryloop		[MAX_START*5+2];
+	ncrcmd  msg_parity	[  6];
+	ncrcmd	msg_reject	[  8];
+	ncrcmd	msg_ign_residue	[ 32];
+	ncrcmd  msg_extended	[ 18];
+	ncrcmd  msg_ext_2	[ 18];
+	ncrcmd	msg_wdtr	[ 27];
+	ncrcmd  msg_ext_3	[ 18];
+	ncrcmd	msg_sdtr	[ 27];
 	ncrcmd	msg_out_abort	[ 10];
 	ncrcmd  getcc		[  4];
 	ncrcmd  getcc1		[  5];
@@ -1551,14 +1641,6 @@
 	ncrcmd	getcc2		[ 14];
 #endif
 	ncrcmd	getcc3		[ 10];
-	ncrcmd  badgetcc	[  6];
-	ncrcmd	reselect	[ 12];
-	ncrcmd	reselect2	[  6];
-	ncrcmd	resel_tmp	[  5];
-	ncrcmd  resel_lun	[ 18];
-	ncrcmd	resel_tag	[ 24];
-	ncrcmd  data_io		[  2];	/* MUST be just before data_in */
-	ncrcmd  data_in		[MAX_SCATTER * 4 + 7];
 	ncrcmd  data_out	[MAX_SCATTER * 4 + 7];
 	ncrcmd	aborttag	[  4];
 	ncrcmd	abort		[ 22];
@@ -1579,7 +1661,8 @@
 static	void	ncr_complete	(ncb_p np, ccb_p cp);
 static	void	ncr_exception	(ncb_p np);
 static	void	ncr_free_ccb	(ncb_p np, ccb_p cp, u_long t, u_long l);
-static	void	ncr_getclock	(ncb_p np);
+static	void	ncr_getclock	(ncb_p np, int mult);
+static	void	ncr_selectclock	(ncb_p np, u_char scntl3);
 static	void	ncr_save_bios_setting	(ncb_p np);
 static	ccb_p	ncr_get_ccb	(ncb_p np, u_long t,u_long l);
 static	void	ncr_init	(ncb_p np, char * msg, u_long code);
@@ -1596,10 +1679,11 @@
 #endif
 
 static	void	ncr_script_copy_and_bind
-				(struct script * script, ncb_p np);
-static  void    ncr_script_fill (struct script * scr);
+				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
+static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
 static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
 static	void	ncr_setmaxtags	(ncb_p np, tcb_p tp, u_long usrtags);
+static	int	ncr_getsync	(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p);
 static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static	void	ncr_settags     (tcb_p tp, lcb_p lp);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide);
@@ -1617,11 +1701,12 @@
 		       int irq, int bus, u_char device_fn);
 
 static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
 static void process_waiting_list(ncb_p np, int sts);
 
+#define remove_from_waiting_list(np, cmd) \
+		retrieve_from_waiting_list(1, (np), (cmd))
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
-#define abort_waiting_list(np) process_waiting_list((np), DID_ABORT)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
 /*==========================================================
@@ -1687,10 +1772,12 @@
 #define	RELOC_LABEL	0x50000000
 #define	RELOC_REGISTER	0x60000000
 #define	RELOC_KVAR	0x70000000
+#define	RELOC_LABELH	0x80000000
 #define	RELOC_MASK	0xf0000000
 
 #define	NADDR(label)	(RELOC_SOFTC | offsetof(struct ncb, label))
 #define PADDR(label)    (RELOC_LABEL | offsetof(struct script, label))
+#define PADDRH(label)   (RELOC_LABELH | offsetof(struct scripth, label))
 #define	RADDR(label)	(RELOC_REGISTER | REG(label))
 #define	FADDR(label,ofs)(RELOC_REGISTER | ((REG(label))+(ofs)))
 #define	KVAR(which)	(RELOC_KVAR | (which))
@@ -1747,33 +1834,7 @@
 	*/
 	SCR_JUMP,
 }/*-------------------------< STARTPOS >--------------------*/,{
-		PADDR(tryloop),
-}/*-------------------------< TRYLOOP >---------------------*/,{
-/*
-**	Load an entry of the start queue into dsa
-**	and try to start it by jumping to TRYSEL.
-**
-**	Because the size depends on the
-**	#define MAX_START parameter, it is filled
-**	in at runtime.
-**
-**-----------------------------------------------------------
-**
-**  ##===========< I=0; i<MAX_START >===========
-**  ||	SCR_COPY (4),
-**  ||		NADDR (squeue[i]),
-**  ||		RADDR (dsa),
-**  ||	SCR_CALL,
-**  ||		PADDR (trysel),
-**  ##==========================================
-**
-**	SCR_JUMP,
-**		PADDR(tryloop),
-**
-**-----------------------------------------------------------
-*/
-0
-
+		PADDRH(tryloop),
 }/*-------------------------< TRYSEL >----------------------*/,{
 	/*
 	**	Now:
@@ -2011,7 +2072,7 @@
 	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
 		PADDR (msg_in),
 	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDR (msg_reject),
+		PADDRH (msg_reject),
 	/*
 	**	normal processing
 	*/
@@ -2228,7 +2289,7 @@
 	SCR_FROM_REG (socl),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
+		PADDRH (msg_parity),
 	SCR_FROM_REG (scratcha),
 		0,
 	/*
@@ -2243,13 +2304,13 @@
 	SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)),
 		PADDR (disconnect),
 	SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)),
-		PADDR (msg_extended),
+		PADDRH (msg_extended),
 	SCR_JUMP ^ IFTRUE (DATA (M_NOOP)),
 		PADDR (clrack),
 	SCR_JUMP ^ IFTRUE (DATA (M_REJECT)),
-		PADDR (msg_reject),
+		PADDRH (msg_reject),
 	SCR_JUMP ^ IFTRUE (DATA (M_IGN_RESIDUE)),
-		PADDR (msg_ign_residue),
+		PADDRH (msg_ign_residue),
 	/*
 	**	Rest of the messages left as
 	**	an exercise ...
@@ -2268,196 +2329,624 @@
 	SCR_JUMP,
 		PADDR (setmsg),
 
-}/*-------------------------< MSG_PARITY >---------------*/,{
+}/*-------------------------< COMPLETE >-----------------*/,{
 	/*
-	**	count it
+	**	Complete message.
+	**
+	**	If it's not the get condition code,
+	**	copy TEMP register to LASTP in header.
 	*/
-	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+	SCR_FROM_REG (SS_REG),
 		0,
-	/*
-	**	send a "message parity error" message.
+/*<<<*/	SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
+		12,
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (header.lastp),
+/*>>>*/	/*
+	**	When we terminate the cycle by clearing ACK,
+	**	the target may disconnect immediately.
+	**
+	**	We don't want to be told of an
+	**	"unexpected disconnect",
+	**	so we disable this feature.
 	*/
-	SCR_LOAD_REG (scratcha, M_PARITY),
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
 		0,
-	SCR_JUMP,
-		PADDR (setmsg),
-}/*-------------------------< MSG_REJECT >---------------*/,{
 	/*
-	**	If a negotiation was in progress,
-	**	negotiation failed.
+	**	Terminate cycle ...
 	*/
-	SCR_FROM_REG (HS_REG),
+	SCR_CLR (SCR_ACK|SCR_ATN),
 		0,
-	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
-		SIR_NEGO_FAILED,
 	/*
-	**	else make host log this message
+	**	... and wait for the disconnect.
 	*/
-	SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
-		SIR_REJECT_RECEIVED,
-	SCR_JUMP,
-		PADDR (clrack),
-
-}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+	SCR_WAIT_DISC,
+		0,
+}/*-------------------------< CLEANUP >-------------------*/,{
 	/*
-	**	Terminate cycle
+	**      dsa:    Pointer to ccb
+	**	      or xxxxxxFF (no ccb)
+	**
+	**      HS_REG:   Host-Status (<>0!)
 	*/
-	SCR_CLR (SCR_ACK),
+	SCR_FROM_REG (dsa),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
+	SCR_JUMP ^ IFTRUE (DATA (0xff)),
+		PADDR (signal),
 	/*
-	**	get residue size.
+	**      dsa is valid.
+	**	save the status registers
 	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[1]),
+	SCR_COPY (4),
+		RADDR (scr0),
+		NADDR (header.status),
 	/*
-	**	Check for message parity error.
+	**	and copy back the header to the ccb.
 	*/
-	SCR_TO_REG (scratcha),
+	SCR_COPY (4),
+		RADDR (dsa),
+		PADDR (cleanup0),
+	SCR_COPY (sizeof (struct head)),
+		NADDR (header),
+}/*-------------------------< CLEANUP0 >--------------------*/,{
 		0,
-	SCR_FROM_REG (socl),
+
+	/*
+	**	If command resulted in "check condition"
+	**	status and is not yet completed,
+	**	try to get the condition code.
+	*/
+	SCR_FROM_REG (HS_REG),
 		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
+/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
+		16,
+	SCR_FROM_REG (SS_REG),
 		0,
+	SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
+		PADDRH(getcc2),
 	/*
-	**	Size is 0 .. ignore message.
+	**	And make the DSA register invalid.
 	*/
-	SCR_JUMP ^ IFTRUE (DATA (0)),
-		PADDR (clrack),
+/*>>>*/	SCR_LOAD_REG (dsa, 0xff), /* invalid */
+		0,
+}/*-------------------------< SIGNAL >----------------------*/,{
 	/*
-	**	Size is not 1 .. have to interrupt.
+	**	if status = queue full,
+	**	reinsert in startqueue and stall queue.
 	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (1)),
-		40,
+	SCR_FROM_REG (SS_REG),
+		0,
+	SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
+		SIR_STALL_QUEUE,
 	/*
-	**	Check for residue byte in swide register
+	**	if job completed ...
 	*/
-	SCR_FROM_REG (scntl2),
+	SCR_FROM_REG (HS_REG),
 		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
-		16,
 	/*
-	**	There IS data in the swide register.
-	**	Discard it.
+	**	... signal completion to the host
 	*/
-	SCR_REG_REG (scntl2, SCR_OR, WSR),
+	SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
 		0,
+	/*
+	**	Auf zu neuen Schandtaten!
+	*/
+	SCR_JUMP,
+		PADDR(start),
+
+}/*-------------------------< SAVE_DP >------------------*/,{
+	/*
+	**	SAVE_DP message:
+	**	Copy TEMP register to SAVEP in header.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (header.savep),
 	SCR_JUMP,
 		PADDR (clrack),
+}/*-------------------------< RESTORE_DP >---------------*/,{
 	/*
-	**	Load again the size to the sfbr register.
+	**	RESTORE_DP message:
+	**	Copy SAVEP in header to TEMP register.
 	*/
-/*>>>*/	SCR_FROM_REG (scratcha),
-		0,
-/*>>>*/	SCR_INT,
-		SIR_IGN_RESIDUE,
+	SCR_COPY (4),
+		NADDR (header.savep),
+		RADDR (temp),
 	SCR_JUMP,
 		PADDR (clrack),
 
-}/*-------------------------< MSG_EXTENDED >-------------*/,{
+}/*-------------------------< DISCONNECT >---------------*/,{
 	/*
-	**	Terminate cycle
+	**	If QUIRK_AUTOSAVE is set,
+	**	do an "save pointer" operation.
 	*/
-	SCR_CLR (SCR_ACK),
+	SCR_FROM_REG (QU_REG),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
+		12,
+	/*
+	**	like SAVE_DP message:
+	**	Copy TEMP register to SAVEP in header.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (header.savep),
+/*>>>*/	/*
+	**	Check if temp==savep or temp==goalp:
+	**	if not, log a missing save pointer message.
+	**	In fact, it's a comparison mod 256.
+	**
+	**	Hmmm, I hadn't thought that I would be urged to
+	**	write this kind of ugly self modifying code.
+	**
+	**	It's unbelievable, but the ncr53c8xx isn't able
+	**	to subtract one register from another.
+	*/
+	SCR_FROM_REG (temp),
+		0,
+	/*
+	**	You are not expected to understand this ..
+	**
+	**	CAUTION: only little endian architectures supported! XXX
+	*/
+	SCR_COPY (1),
+		NADDR (header.savep),
+		PADDR (disconnect0),
+}/*-------------------------< DISCONNECT0 >--------------*/,{
+/*<<<*/	SCR_JUMPR ^ IFTRUE (DATA (1)),
+		20,
+	/*
+	**	neither this
+	*/
+	SCR_COPY (1),
+		NADDR (header.goalp),
+		PADDR (disconnect1),
+}/*-------------------------< DISCONNECT1 >--------------*/,{
+	SCR_INT ^ IFFALSE (DATA (1)),
+		SIR_MISSING_SAVE,
+/*>>>*/
+
+	/*
+	**	DISCONNECTing  ...
+	**
+	**	disable the "unexpected disconnect" feature,
+	**	and remove the ACK signal.
+	*/
+	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
+		0,
+	SCR_CLR (SCR_ACK|SCR_ATN),
+		0,
+	/*
+	**	Wait for the disconnect.
+	*/
+	SCR_WAIT_DISC,
+		0,
+	/*
+	**	Profiling:
+	**	Set a time stamp,
+	**	and count the disconnects.
+	*/
+	SCR_COPY (sizeof (u_long)),
+		KVAR(SCRIPT_KVAR_JIFFIES),
+		NADDR (header.stamp.disconnect),
+	SCR_COPY (4),
+		NADDR (disc_phys),
+		RADDR (temp),
+	SCR_REG_REG (temp, SCR_ADD, 0x01),
+		0,
+	SCR_COPY (4),
+		RADDR (temp),
+		NADDR (disc_phys),
+	/*
+	**	Status is: DISCONNECTED.
+	*/
+	SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+		0,
+	SCR_JUMP,
+		PADDR (cleanup),
+
+}/*-------------------------< MSG_OUT >-------------------*/,{
+	/*
+	**	The target requests a message.
+	*/
+	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
+		NADDR (msgout),
+	SCR_COPY (1),
+		RADDR (sfbr),
+		NADDR (lastmsg),
+	/*
+	**	If it was no ABORT message ...
+	*/
+	SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
+		PADDRH (msg_out_abort),
+	/*
+	**	... wait for the next phase
+	**	if it's a message out, send it again, ...
+	*/
+	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
+		PADDR (msg_out),
+}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+	/*
+	**	... else clear the message ...
+	*/
+	SCR_LOAD_REG (scratcha, M_NOOP),
+		0,
+	SCR_COPY (4),
+		RADDR (scratcha),
+		NADDR (msgout),
+	/*
+	**	... and process the next phase
+	*/
+	SCR_JUMP,
 		PADDR (dispatch),
+}/*------------------------< BADGETCC >---------------------*/,{
 	/*
-	**	get length.
+	**	If SIGP was set, clear it and try again.
+	*/
+	SCR_FROM_REG (ctest2),
+		0,
+	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+		PADDRH (getcc2),
+	SCR_INT,
+		SIR_SENSE_FAILED,
+}/*-------------------------< RESELECT >--------------------*/,{
+	/*
+	**	This NOP will be patched with LED OFF
+	**	SCR_REG_REG (gpreg, SCR_OR, 0x01)
+	*/
+	SCR_JUMP ^ IFFALSE (0),
+		0,
+	/*
+	**	make the DSA invalid.
+	*/
+	SCR_LOAD_REG (dsa, 0xff),
+		0,
+	SCR_CLR (SCR_TRG),
+		0,
+	/*
+	**	Sleep waiting for a reselection.
+	**	If SIGP is set, special treatment.
+	**
+	**	Zu allem bereit ..
+	*/
+	SCR_WAIT_RESEL,
+		PADDR(reselect2),
+}/*-------------------------< RESELECT1 >--------------------*/,{
+	/*
+	**	This NOP will be patched with LED ON
+	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+	*/
+	SCR_JUMP ^ IFFALSE (0),
+		0,
+	/*
+	**	... zu nichts zu gebrauchen ?
+	**
+	**      load the target id into the SFBR
+	**	and jump to the control block.
+	**
+	**	Look at the declarations of
+	**	- struct ncb
+	**	- struct tcb
+	**	- struct lcb
+	**	- struct ccb
+	**	to understand what's going on.
+	*/
+	SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
+		0,
+	SCR_TO_REG (ctest0),
+		0,
+	SCR_JUMP,
+		NADDR (jump_tcb),
+}/*-------------------------< RESELECT2 >-------------------*/,{
+	/*
+	**	This NOP will be patched with LED ON
+	**	SCR_REG_REG (gpreg, SCR_AND, 0xfe)
+	*/
+	SCR_JUMP ^ IFFALSE (0),
+		0,
+	/*
+	**	If it's not connected :(
+	**	-> interrupted by SIGP bit.
+	**	Jump to start.
+	*/
+	SCR_FROM_REG (ctest2),
+		0,
+	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
+		PADDR (start),
+	SCR_JUMP,
+		PADDR (reselect),
+
+}/*-------------------------< RESEL_TMP >-------------------*/,{
+	/*
+	**	The return address in TEMP
+	**	is in fact the data structure address,
+	**	so copy it to the DSA register.
+	*/
+	SCR_COPY (4),
+		RADDR (temp),
+		RADDR (dsa),
+	SCR_JUMP,
+		PADDR (prepare),
+
+}/*-------------------------< RESEL_LUN >-------------------*/,{
+	/*
+	**	come back to this point
+	**	to get an IDENTIFY message
+	**	Wait for a msg_in phase.
+	*/
+/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		48,
+	/*
+	**	message phase
+	**	It's not a sony, it's a trick:
+	**	read the data without acknowledging it.
+	*/
+	SCR_FROM_REG (sbdl),
+		0,
+/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
+		32,
+	/*
+	**	It WAS an Identify message.
+	**	get it and ack it!
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[1]),
+		NADDR (msgin),
+	SCR_CLR (SCR_ACK),
+		0,
 	/*
-	**	Check for message parity error.
+	**	Mask out the lun.
 	*/
-	SCR_TO_REG (scratcha),
+	SCR_REG_REG (sfbr, SCR_AND, 0x07),
 		0,
-	SCR_FROM_REG (socl),
+	SCR_RETURN,
 		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
+	/*
+	**	No message phase or no IDENTIFY message:
+	**	return 0.
+	*/
+/*>>>*/	SCR_LOAD_SFBR (0),
 		0,
+	SCR_RETURN,
+		0,
+
+}/*-------------------------< RESEL_TAG >-------------------*/,{
 	/*
+	**	come back to this point
+	**	to get a SIMPLE_TAG message
+	**	Wait for a MSG_IN phase.
 	*/
-	SCR_JUMP ^ IFTRUE (DATA (3)),
-		PADDR (msg_ext_3),
-	SCR_JUMP ^ IFFALSE (DATA (2)),
-		PADDR (msg_bad),
-}/*-------------------------< MSG_EXT_2 >----------------*/,{
+/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		64,
+	/*
+	**	message phase
+	**	It's a trick - read the data
+	**	without acknowledging it.
+	*/
+	SCR_FROM_REG (sbdl),
+		0,
+/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
+		48,
+	/*
+	**	It WAS a SIMPLE_TAG message.
+	**	get it and ack it!
+	*/
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin),
 	SCR_CLR (SCR_ACK),
 		0,
-	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		PADDR (dispatch),
 	/*
-	**	get extended message code.
+	**	Wait for the second byte (the tag)
+	*/
+/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		24,
+	/*
+	**	Get it and ack it!
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[2]),
+		NADDR (msgin),
+	SCR_CLR (SCR_ACK|SCR_CARRY),
+		0,
+	SCR_RETURN,
+		0,
 	/*
-	**	Check for message parity error.
+	**	No message phase or no SIMPLE_TAG message
+	**	or no second byte: return 0.
 	*/
-	SCR_TO_REG (scratcha),
+/*>>>*/	SCR_LOAD_SFBR (0),
 		0,
-	SCR_FROM_REG (socl),
+	SCR_SET (SCR_CARRY),
 		0,
-	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
-	SCR_FROM_REG (scratcha),
+	SCR_RETURN,
 		0,
-	SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
-		PADDR (msg_wdtr),
+
+}/*-------------------------< DATA_IO >--------------------*/,{
+/*
+**	Because Linux does not provide xfer data direction 
+**	to low-level scsi drivers, we must trust the target 
+**	for actual data direction when we cannot guess it.
+**	The programmed interrupt patches savep, lastp, goalp,
+**	etc.., and restarts the scsi script at data_out.
+*/
+	SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
+		SIR_DATA_IO_IS_OUT,
+
+}/*-------------------------< DATA_IN >--------------------*/,{
+/*
+**	Because the size depends on the
+**	#define MAX_SCATTER parameter,
+**	it is filled in at runtime.
+**
+**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**		PADDR (no_data),
+**	SCR_COPY (sizeof (u_long)),
+**		KVAR(SCRIPT_KVAR_JIFFIES),
+**		NADDR (header.stamp.data),
+**	SCR_MOVE_TBL ^ SCR_DATA_IN,
+**		offsetof (struct dsb, data[ 0]),
+**
+**  ##===========< i=1; i<MAX_SCATTER >=========
+**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**  ||		PADDR (checkatn),
+**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
+**  ||		offsetof (struct dsb, data[ i]),
+**  ##==========================================
+**
+**	SCR_CALL,
+**		PADDR (checkatn),
+**	SCR_JUMP,
+**		PADDR (no_data),
+*/
+0
+}/*--------------------------------------------------------*/
+};
+
+static	struct scripth scripth0 = {
+/*-------------------------< TRYLOOP >---------------------*/{
+/*
+**	Load an entry of the start queue into dsa
+**	and try to start it by jumping to TRYSEL.
+**
+**	Because the size depends on the
+**	#define MAX_START parameter, it is filled
+**	in at runtime.
+**
+**-----------------------------------------------------------
+**
+**  ##===========< I=0; i<MAX_START >===========
+**  ||	SCR_COPY (4),
+**  ||		NADDR (squeue[i]),
+**  ||		RADDR (dsa),
+**  ||	SCR_CALL,
+**  ||		PADDR (trysel),
+**  ##==========================================
+**
+**	SCR_JUMP,
+**		PADDRH(tryloop),
+**
+**-----------------------------------------------------------
+*/
+0
+},/*-------------------------< MSG_PARITY >---------------*/{
 	/*
-	**	unknown extended message
+	**	count it
 	*/
+	SCR_REG_REG (PS_REG, SCR_ADD, 0x01),
+		0,
+	/*
+	**	send a "message parity error" message.
+	*/
+	SCR_LOAD_REG (scratcha, M_PARITY),
+		0,
 	SCR_JUMP,
-		PADDR (msg_bad)
-}/*-------------------------< MSG_WDTR >-----------------*/,{
+		PADDR (setmsg),
+}/*-------------------------< MSG_REJECT >---------------*/,{
+	/*
+	**	If a negotiation was in progress,
+	**	negotiation failed.
+	*/
+	SCR_FROM_REG (HS_REG),
+		0,
+	SCR_INT ^ IFTRUE (DATA (HS_NEGOTIATE)),
+		SIR_NEGO_FAILED,
+	/*
+	**	else make host log this message
+	*/
+	SCR_INT ^ IFFALSE (DATA (HS_NEGOTIATE)),
+		SIR_REJECT_RECEIVED,
+	SCR_JUMP,
+		PADDR (clrack),
+
+}/*-------------------------< MSG_IGN_RESIDUE >----------*/,{
+	/*
+	**	Terminate cycle
+	*/
 	SCR_CLR (SCR_ACK),
 		0,
 	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
 		PADDR (dispatch),
 	/*
-	**	get data bus width
+	**	get residue size.
 	*/
 	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin[3]),
+		NADDR (msgin[1]),
+	/*
+	**	Check for message parity error.
+	*/
+	SCR_TO_REG (scratcha),
+		0,
 	SCR_FROM_REG (socl),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
+		PADDRH (msg_parity),
+	SCR_FROM_REG (scratcha),
+		0,
 	/*
-	**	let the host do the real work.
+	**	Size is 0 .. ignore message.
 	*/
-	SCR_INT,
-		SIR_NEGO_WIDE,
+	SCR_JUMP ^ IFTRUE (DATA (0)),
+		PADDR (clrack),
 	/*
-	**	let the target fetch our answer.
+	**	Size is not 1 .. have to interrupt.
 	*/
-	SCR_SET (SCR_ATN),
+/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (1)),
+		40,
+	/*
+	**	Check for residue byte in swide register
+	*/
+	SCR_FROM_REG (scntl2),
 		0,
-	SCR_CLR (SCR_ACK),
+/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (WSR, WSR)),
+		16,
+	/*
+	**	There IS data in the swide register.
+	**	Discard it.
+	*/
+	SCR_REG_REG (scntl2, SCR_OR, WSR),
 		0,
-
-	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
-		SIR_NEGO_PROTO,
+	SCR_JUMP,
+		PADDR (clrack),
 	/*
-	**	Send the M_X_WIDE_REQ
+	**	Load again the size to the sfbr register.
 	*/
-	SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
-		NADDR (msgout),
-	SCR_CLR (SCR_ATN),
+/*>>>*/	SCR_FROM_REG (scratcha),
 		0,
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
+/*>>>*/	SCR_INT,
+		SIR_IGN_RESIDUE,
 	SCR_JUMP,
-		PADDR (msg_out_done),
+		PADDR (clrack),
 
-}/*-------------------------< MSG_EXT_3 >----------------*/,{
+}/*-------------------------< MSG_EXTENDED >-------------*/,{
+	/*
+	**	Terminate cycle
+	*/
+	SCR_CLR (SCR_ACK),
+		0,
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
+	/*
+	**	get length.
+	*/
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin[1]),
+	/*
+	**	Check for message parity error.
+	*/
+	SCR_TO_REG (scratcha),
+		0,
+	SCR_FROM_REG (socl),
+		0,
+	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+		PADDRH (msg_parity),
+	SCR_FROM_REG (scratcha),
+		0,
+	/*
+	*/
+	SCR_JUMP ^ IFTRUE (DATA (3)),
+		PADDRH (msg_ext_3),
+	SCR_JUMP ^ IFFALSE (DATA (2)),
+		PADDR (msg_bad),
+}/*-------------------------< MSG_EXT_2 >----------------*/,{
 	SCR_CLR (SCR_ACK),
 		0,
 	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
@@ -2475,36 +2964,35 @@
 	SCR_FROM_REG (socl),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
+		PADDRH (msg_parity),
 	SCR_FROM_REG (scratcha),
 		0,
-	SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
-		PADDR (msg_sdtr),
+	SCR_JUMP ^ IFTRUE (DATA (M_X_WIDE_REQ)),
+		PADDRH (msg_wdtr),
 	/*
 	**	unknown extended message
 	*/
 	SCR_JUMP,
 		PADDR (msg_bad)
-
-}/*-------------------------< MSG_SDTR >-----------------*/,{
+}/*-------------------------< MSG_WDTR >-----------------*/,{
 	SCR_CLR (SCR_ACK),
 		0,
 	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
 		PADDR (dispatch),
 	/*
-	**	get period and offset
+	**	get data bus width
 	*/
-	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
 		NADDR (msgin[3]),
 	SCR_FROM_REG (socl),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
-		PADDR (msg_parity),
+		PADDRH (msg_parity),
 	/*
 	**	let the host do the real work.
 	*/
 	SCR_INT,
-		SIR_NEGO_SYNC,
+		SIR_NEGO_WIDE,
 	/*
 	**	let the target fetch our answer.
 	*/
@@ -2516,9 +3004,9 @@
 	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
 		SIR_NEGO_PROTO,
 	/*
-	**	Send the M_X_SYNC_REQ
+	**	Send the M_X_WIDE_REQ
 	*/
-	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+	SCR_MOVE_ABS (4) ^ SCR_MSG_OUT,
 		NADDR (msgout),
 	SCR_CLR (SCR_ATN),
 		0,
@@ -2528,257 +3016,77 @@
 	SCR_JUMP,
 		PADDR (msg_out_done),
 
-}/*-------------------------< COMPLETE >-----------------*/,{
-	/*
-	**	Complete message.
-	**
-	**	If it's not the get condition code,
-	**	copy TEMP register to LASTP in header.
-	*/
-	SCR_FROM_REG (SS_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFTRUE (MASK (S_SENSE, S_SENSE)),
-		12,
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.lastp),
-/*>>>*/	/*
-	**	When we terminate the cycle by clearing ACK,
-	**	the target may disconnect immediately.
-	**
-	**	We don't want to be told of an
-	**	"unexpected disconnect",
-	**	so we disable this feature.
-	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	/*
-	**	Terminate cycle ...
-	*/
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	/*
-	**	... and wait for the disconnect.
-	*/
-	SCR_WAIT_DISC,
-		0,
-}/*-------------------------< CLEANUP >-------------------*/,{
-	/*
-	**      dsa:    Pointer to ccb
-	**	      or xxxxxxFF (no ccb)
-	**
-	**      HS_REG:   Host-Status (<>0!)
-	*/
-	SCR_FROM_REG (dsa),
-		0,
-	SCR_JUMP ^ IFTRUE (DATA (0xff)),
-		PADDR (signal),
-	/*
-	**      dsa is valid.
-	**	save the status registers
-	*/
-	SCR_COPY (4),
-		RADDR (scr0),
-		NADDR (header.status),
-	/*
-	**	and copy back the header to the ccb.
-	*/
-	SCR_COPY (4),
-		RADDR (dsa),
-		PADDR (cleanup0),
-	SCR_COPY (sizeof (struct head)),
-		NADDR (header),
-}/*-------------------------< CLEANUP0 >--------------------*/,{
-		0,
-
-	/*
-	**	If command resulted in "check condition"
-	**	status and is not yet completed,
-	**	try to get the condition code.
-	*/
-	SCR_FROM_REG (HS_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (0, HS_DONEMASK)),
-		16,
-	SCR_FROM_REG (SS_REG),
+}/*-------------------------< MSG_EXT_3 >----------------*/,{
+	SCR_CLR (SCR_ACK),
 		0,
-	SCR_JUMP ^ IFTRUE (DATA (S_CHECK_COND)),
-		PADDR(getcc2),
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
 	/*
-	**	And make the DSA register invalid.
+	**	get extended message code.
 	*/
-/*>>>*/	SCR_LOAD_REG (dsa, 0xff), /* invalid */
-		0,
-}/*-------------------------< SIGNAL >----------------------*/,{
+	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
+		NADDR (msgin[2]),
 	/*
-	**	if status = queue full,
-	**	reinsert in startqueue and stall queue.
+	**	Check for message parity error.
 	*/
-	SCR_FROM_REG (SS_REG),
+	SCR_TO_REG (scratcha),
 		0,
-	SCR_INT ^ IFTRUE (DATA (S_QUEUE_FULL)),
-		SIR_STALL_QUEUE,
-	/*
-	**	if job completed ...
-	*/
-	SCR_FROM_REG (HS_REG),
+	SCR_FROM_REG (socl),
 		0,
-	/*
-	**	... signal completion to the host
-	*/
-	SCR_INT_FLY ^ IFFALSE (MASK (0, HS_DONEMASK)),
+	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+		PADDRH (msg_parity),
+	SCR_FROM_REG (scratcha),
 		0,
+	SCR_JUMP ^ IFTRUE (DATA (M_X_SYNC_REQ)),
+		PADDRH (msg_sdtr),
 	/*
-	**	Auf zu neuen Schandtaten!
-	*/
-	SCR_JUMP,
-		PADDR(start),
-
-}/*-------------------------< SAVE_DP >------------------*/,{
-	/*
-	**	SAVE_DP message:
-	**	Copy TEMP register to SAVEP in header.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.savep),
-	SCR_JUMP,
-		PADDR (clrack),
-}/*-------------------------< RESTORE_DP >---------------*/,{
-	/*
-	**	RESTORE_DP message:
-	**	Copy SAVEP in header to TEMP register.
+	**	unknown extended message
 	*/
-	SCR_COPY (4),
-		NADDR (header.savep),
-		RADDR (temp),
 	SCR_JUMP,
-		PADDR (clrack),
-
-}/*-------------------------< DISCONNECT >---------------*/,{
-	/*
-	**	If QUIRK_AUTOSAVE is set,
-	**	do an "save pointer" operation.
-	*/
-	SCR_FROM_REG (QU_REG),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (QUIRK_AUTOSAVE, QUIRK_AUTOSAVE)),
-		12,
-	/*
-	**	like SAVE_DP message:
-	**	Copy TEMP register to SAVEP in header.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (header.savep),
-/*>>>*/	/*
-	**	Check if temp==savep or temp==goalp:
-	**	if not, log a missing save pointer message.
-	**	In fact, it's a comparison mod 256.
-	**
-	**	Hmmm, I hadn't thought that I would be urged to
-	**	write this kind of ugly self modifying code.
-	**
-	**	It's unbelievable, but the ncr53c8xx isn't able
-	**	to subtract one register from another.
-	*/
-	SCR_FROM_REG (temp),
-		0,
-	/*
-	**	You are not expected to understand this ..
-	**
-	**	CAUTION: only little endian architectures supported! XXX
-	*/
-	SCR_COPY (1),
-		NADDR (header.savep),
-		PADDR (disconnect0),
-}/*-------------------------< DISCONNECT0 >--------------*/,{
-/*<<<*/	SCR_JUMPR ^ IFTRUE (DATA (1)),
-		20,
-	/*
-	**	neither this
-	*/
-	SCR_COPY (1),
-		NADDR (header.goalp),
-		PADDR (disconnect1),
-}/*-------------------------< DISCONNECT1 >--------------*/,{
-	SCR_INT ^ IFFALSE (DATA (1)),
-		SIR_MISSING_SAVE,
-/*>>>*/
+		PADDR (msg_bad)
 
-	/*
-	**	DISCONNECTing  ...
-	**
-	**	disable the "unexpected disconnect" feature,
-	**	and remove the ACK signal.
-	*/
-	SCR_REG_REG (scntl2, SCR_AND, 0x7f),
-		0,
-	SCR_CLR (SCR_ACK|SCR_ATN),
-		0,
-	/*
-	**	Wait for the disconnect.
-	*/
-	SCR_WAIT_DISC,
-		0,
-	/*
-	**	Profiling:
-	**	Set a time stamp,
-	**	and count the disconnects.
-	*/
-	SCR_COPY (sizeof (u_long)),
-		KVAR(SCRIPT_KVAR_JIFFIES),
-		NADDR (header.stamp.disconnect),
-	SCR_COPY (4),
-		NADDR (disc_phys),
-		RADDR (temp),
-	SCR_REG_REG (temp, SCR_ADD, 0x01),
+}/*-------------------------< MSG_SDTR >-----------------*/,{
+	SCR_CLR (SCR_ACK),
 		0,
-	SCR_COPY (4),
-		RADDR (temp),
-		NADDR (disc_phys),
+	SCR_JUMP ^ IFFALSE (WHEN (SCR_MSG_IN)),
+		PADDR (dispatch),
 	/*
-	**	Status is: DISCONNECTED.
+	**	get period and offset
 	*/
-	SCR_LOAD_REG (HS_REG, HS_DISCONNECT),
+	SCR_MOVE_ABS (2) ^ SCR_MSG_IN,
+		NADDR (msgin[3]),
+	SCR_FROM_REG (socl),
 		0,
-	SCR_JUMP,
-		PADDR (cleanup),
-
-}/*-------------------------< MSG_OUT >-------------------*/,{
-	/*
-	**	The target requests a message.
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_OUT,
-		NADDR (msgout),
-	SCR_COPY (1),
-		RADDR (sfbr),
-		NADDR (lastmsg),
-	/*
-	**	If it was no ABORT message ...
-	*/
-	SCR_JUMP ^ IFTRUE (DATA (M_ABORT)),
-		PADDR (msg_out_abort),
+	SCR_JUMP ^ IFTRUE (MASK (CATN, CATN)),
+		PADDRH (msg_parity),
 	/*
-	**	... wait for the next phase
-	**	if it's a message out, send it again, ...
+	**	let the host do the real work.
 	*/
-	SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_OUT)),
-		PADDR (msg_out),
-}/*-------------------------< MSG_OUT_DONE >--------------*/,{
+	SCR_INT,
+		SIR_NEGO_SYNC,
 	/*
-	**	... else clear the message ...
+	**	let the target fetch our answer.
 	*/
-	SCR_LOAD_REG (scratcha, M_NOOP),
+	SCR_SET (SCR_ATN),
 		0,
-	SCR_COPY (4),
-		RADDR (scratcha),
-		NADDR (msgout),
+	SCR_CLR (SCR_ACK),
+		0,
+
+	SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)),
+		SIR_NEGO_PROTO,
 	/*
-	**	... and process the next phase
+	**	Send the M_X_SYNC_REQ
 	*/
+	SCR_MOVE_ABS (5) ^ SCR_MSG_OUT,
+		NADDR (msgout),
+	SCR_CLR (SCR_ATN),
+		0,
+	SCR_COPY (1),
+		RADDR (sfbr),
+		NADDR (lastmsg),
 	SCR_JUMP,
-		PADDR (dispatch),
+		PADDR (msg_out_done),
+
 }/*-------------------------< MSG_OUT_ABORT >-------------*/,{
 	/*
 	**	After ABORT message,
@@ -2811,7 +3119,7 @@
 	*/
 	SCR_COPY (4),
 		RADDR (dsa),
-		PADDR (getcc1),
+		PADDRH (getcc1),
 	/*
 	**	... then we do the actual copy.
 	*/
@@ -2877,7 +3185,7 @@
 	SCR_FROM_REG (QU_REG),
 		0,
 	SCR_JUMP ^ IFTRUE (MASK (QUIRK_NOMSG, QUIRK_NOMSG)),
-		PADDR(getcc3),
+		PADDRH(getcc3),
 	/*
 	**	Then try to connect to the target.
 	**	If we are reselected, special treatment
@@ -2941,205 +3249,6 @@
 	SCR_JUMP,
 		PADDR (prepare2),
 
-}/*------------------------< BADGETCC >---------------------*/,{
-	/*
-	**	If SIGP was set, clear it and try again.
-	*/
-	SCR_FROM_REG (ctest2),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-		PADDR (getcc2),
-	SCR_INT,
-		SIR_SENSE_FAILED,
-}/*-------------------------< RESELECT >--------------------*/,{
-	/*
-	**	make the DSA invalid.
-	*/
-	SCR_LOAD_REG (dsa, 0xff),
-		0,
-	SCR_CLR (SCR_TRG),
-		0,
-	/*
-	**	Sleep waiting for a reselection.
-	**	If SIGP is set, special treatment.
-	**
-	**	Zu allem bereit ..
-	*/
-	SCR_WAIT_RESEL,
-		PADDR(reselect2),
-	/*
-	**	... zu nichts zu gebrauchen ?
-	**
-	**      load the target id into the SFBR
-	**	and jump to the control block.
-	**
-	**	Look at the declarations of
-	**	- struct ncb
-	**	- struct tcb
-	**	- struct lcb
-	**	- struct ccb
-	**	to understand what's going on.
-	*/
-	SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
-		0,
-	SCR_TO_REG (ctest0),
-		0,
-	SCR_JUMP,
-		NADDR (jump_tcb),
-}/*-------------------------< RESELECT2 >-------------------*/,{
-	/*
-	**	If it's not connected :(
-	**	-> interrupted by SIGP bit.
-	**	Jump to start.
-	*/
-	SCR_FROM_REG (ctest2),
-		0,
-	SCR_JUMP ^ IFTRUE (MASK (CSIGP,CSIGP)),
-		PADDR (start),
-	SCR_JUMP,
-		PADDR (reselect),
-
-}/*-------------------------< RESEL_TMP >-------------------*/,{
-	/*
-	**	The return address in TEMP
-	**	is in fact the data structure address,
-	**	so copy it to the DSA register.
-	*/
-	SCR_COPY (4),
-		RADDR (temp),
-		RADDR (dsa),
-	SCR_JUMP,
-		PADDR (prepare),
-
-}/*-------------------------< RESEL_LUN >-------------------*/,{
-	/*
-	**	come back to this point
-	**	to get an IDENTIFY message
-	**	Wait for a msg_in phase.
-	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		48,
-	/*
-	**	message phase
-	**	It's not a sony, it's a trick:
-	**	read the data without acknowledging it.
-	*/
-	SCR_FROM_REG (sbdl),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (MASK (M_IDENTIFY, 0x98)),
-		32,
-	/*
-	**	It WAS an Identify message.
-	**	get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK),
-		0,
-	/*
-	**	Mask out the lun.
-	*/
-	SCR_REG_REG (sfbr, SCR_AND, 0x07),
-		0,
-	SCR_RETURN,
-		0,
-	/*
-	**	No message phase or no IDENTIFY message:
-	**	return 0.
-	*/
-/*>>>*/	SCR_LOAD_SFBR (0),
-		0,
-	SCR_RETURN,
-		0,
-
-}/*-------------------------< RESEL_TAG >-------------------*/,{
-	/*
-	**	come back to this point
-	**	to get a SIMPLE_TAG message
-	**	Wait for a MSG_IN phase.
-	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		64,
-	/*
-	**	message phase
-	**	It's a trick - read the data
-	**	without acknowledging it.
-	*/
-	SCR_FROM_REG (sbdl),
-		0,
-/*<<<*/	SCR_JUMPR ^ IFFALSE (DATA (M_SIMPLE_TAG)),
-		48,
-	/*
-	**	It WAS a SIMPLE_TAG message.
-	**	get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK),
-		0,
-	/*
-	**	Wait for the second byte (the tag)
-	*/
-/*<<<*/	SCR_JUMPR ^ IFFALSE (WHEN (SCR_MSG_IN)),
-		24,
-	/*
-	**	Get it and ack it!
-	*/
-	SCR_MOVE_ABS (1) ^ SCR_MSG_IN,
-		NADDR (msgin),
-	SCR_CLR (SCR_ACK|SCR_CARRY),
-		0,
-	SCR_RETURN,
-		0,
-	/*
-	**	No message phase or no SIMPLE_TAG message
-	**	or no second byte: return 0.
-	*/
-/*>>>*/	SCR_LOAD_SFBR (0),
-		0,
-	SCR_SET (SCR_CARRY),
-		0,
-	SCR_RETURN,
-		0,
-
-}/*-------------------------< DATA_IO >--------------------*/,{
-/*
-**	Because Linux does not provide xfer data direction 
-**	to low-level scsi drivers, we must trust the target 
-**	for actual data direction when we cannot guess it.
-**	The programmed interrupt patches savep, lastp, goalp,
-**	etc.., and restarts the scsi script at data_out.
-*/
-	SCR_INT ^ IFTRUE (WHEN (SCR_DATA_OUT)),
-		SIR_DATA_IO_IS_OUT,
-
-}/*-------------------------< DATA_IN >--------------------*/,{
-/*
-**	Because the size depends on the
-**	#define MAX_SCATTER parameter,
-**	it is filled in at runtime.
-**
-**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**		PADDR (no_data),
-**	SCR_COPY (sizeof (u_long)),
-**		KVAR(SCRIPT_KVAR_JIFFIES),
-**		NADDR (header.stamp.data),
-**	SCR_MOVE_TBL ^ SCR_DATA_IN,
-**		offsetof (struct dsb, data[ 0]),
-**
-**  ##===========< i=1; i<MAX_SCATTER >=========
-**  ||	SCR_CALL ^ IFFALSE (WHEN (SCR_DATA_IN)),
-**  ||		PADDR (checkatn),
-**  ||	SCR_MOVE_TBL ^ SCR_DATA_IN,
-**  ||		offsetof (struct dsb, data[ i]),
-**  ##==========================================
-**
-**	SCR_CALL,
-**		PADDR (checkatn),
-**	SCR_JUMP,
-**		PADDR (no_data),
-*/
-0
 }/*-------------------------< DATA_OUT >-------------------*/,{
 /*
 **	Because the size depends on the
@@ -3169,7 +3278,6 @@
 **---------------------------------------------------------
 */
 0	/* was (u_long)&ident ? */
-
 }/*-------------------------< ABORTTAG >-------------------*/,{
 	/*
 	**      Abort a bad reselection.
@@ -3243,12 +3351,12 @@
 **==========================================================
 */
 
-void ncr_script_fill (struct script * scr)
+void ncr_script_fill (struct script * scr, struct scripth * scrh)
 {
 	int	i;
 	ncrcmd	*p;
 
-	p = scr->tryloop;
+	p = scrh->tryloop;
 	for (i=0; i<MAX_START; i++) {
 		*p++ =SCR_COPY (4);
 		*p++ =NADDR (squeue[i]);
@@ -3257,9 +3365,9 @@
 		*p++ =PADDR (trysel);
 	};
 	*p++ =SCR_JUMP;
-	*p++ =PADDR(tryloop);
+	*p++ =PADDRH(tryloop);
 
-	assert ((u_long)p == (u_long)&scr->tryloop + sizeof (scr->tryloop));
+	assert ((u_long)p == (u_long)&scrh->tryloop + sizeof (scrh->tryloop));
 
 	p = scr->data_in;
 
@@ -3285,7 +3393,7 @@
 
 	assert ((u_long)p == (u_long)&scr->data_in + sizeof (scr->data_in));
 
-	p = scr->data_out;
+	p = scrh->data_out;
 
 	*p++ =SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT));
 	*p++ =PADDR (no_data);
@@ -3307,7 +3415,7 @@
 	*p++ =SCR_JUMP;
 	*p++ =PADDR (no_data);
 
-	assert ((u_long)p == (u_long)&scr->data_out + sizeof (scr->data_out));
+	assert ((u_long)p == (u_long)&scrh->data_out + sizeof (scrh->data_out));
 }
 
 /*==========================================================
@@ -3319,19 +3427,14 @@
 **==========================================================
 */
 
-static void ncr_script_copy_and_bind (struct script *script, ncb_p np)
+static void ncr_script_copy_and_bind (ncb_p np, ncrcmd *src, ncrcmd *dst, int len)
 {
 	ncrcmd  opcode, new, old, tmp1, tmp2;
-	ncrcmd	*src, *dst, *start, *end;
+	ncrcmd	*start, *end;
 	int relocs;
 
-	np->p_script = vtophys(np->script);
-
-	src = script->start;
-	dst = np->script->start;
-
 	start = src;
-	end = src + (sizeof (struct script) / 4);
+	end = src + len/4;
 
 	while (src < end) {
 
@@ -3418,6 +3521,9 @@
 				case RELOC_LABEL:
 					new = (old & ~RELOC_MASK) + np->p_script;
 					break;
+				case RELOC_LABELH:
+					new = (old & ~RELOC_MASK) + np->p_scripth;
+					break;
 				case RELOC_SOFTC:
 					new = (old & ~RELOC_MASK) + vtophys(np);
 					break;
@@ -3459,10 +3565,6 @@
 **==========================================================
 */
 
-#define	MIN_ASYNC_PD	40
-#define	MIN_SYNC_PD	20
-
-
 /*
 **	Linux host data structure
 **
@@ -3472,12 +3574,22 @@
 */
 
 struct host_data {
-     struct ncb ncb_data;
+     struct ncb *ncb;
+
+     char ncb_align[NCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     struct ncb _ncb_data;
+
+     char ccb_align[CCB_ALIGN_SIZE-1];	/* Filler for alignment */
+     struct ccb _ccb_data;
+
+     char scr_align[SCR_ALIGN_SIZE-1];	/* Filler for alignment */
      struct script script_data;
+
+     struct scripth scripth_data;
 };
 
 /*
-**	Print something which allow to retreive the controler type, unit,
+**	Print something which allow to retrieve the controler type, unit,
 **	target, lun concerned by a kernel message.
 */
 
@@ -3487,7 +3599,7 @@
 static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
 	struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
-	ncb_p np                    = &host_data->ncb_data;
+	ncb_p np                    = host_data->ncb;
 	if (np) PRINT_LUN(np, cmd->target, cmd->lun);
 }
 
@@ -3530,13 +3642,25 @@
 	instance->irq = irq;
 	host_data = (struct host_data *) instance->hostdata;
 
-	np        = &host_data->ncb_data;
+	/*
+	**	Align np and first ccb to 32 boundary for cache line 
+	**	bursting when copying the global header.
+	*/
+	np        = (ncb_p) (((u_long) &host_data->_ncb_data) & NCB_ALIGN_MASK);
+	host_data->ncb = np;
 	bzero (np, sizeof (*np));
+
+	np->ccb   = (ccb_p) (((u_long) &host_data->_ccb_data) & CCB_ALIGN_MASK);
+	bzero (np->ccb, sizeof (*np->ccb));
+
 	np->unit = unit;
 	np->chip = chip;
 	np->device_id	= device_id;
 	np->revision_id	= revision_id;
-	np->script = &host_data->script_data;
+
+	np->script0  =
+	(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
+	np->scripth0 = &host_data->scripth_data;
 
 	/*
 	**    Initialize timer structure
@@ -3562,7 +3686,7 @@
 #endif
 	}
 	else
-		if (bootverbose)
+		if (bootverbose > 1)
 			printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
 
 	/*
@@ -3595,7 +3719,10 @@
 	np->maxwide = 0;
 	np->rv_scntl3 = 0x13;	/* default: 40MHz clock */
 	np->ns_sync   = 25;
+	np->clock_khz	= 40000;
+	np->clock_divn	= 4;
 	np->maxoffs   = 8;
+	np->multiplier = 1;
 
 	/*
 	**	Get the frequency of the chip's clock.
@@ -3609,21 +3736,67 @@
 	case PCI_DEVICE_ID_NCR_53C860:
 		if (driver_setup.ultra_scsi) {
 			np->rv_scntl3 = 0x15;
+			np->clock_khz = 80000;
 			np->ns_sync   = 12;
 		}
 		else
 			np->rv_scntl3 = 0x35;	/* always assume 80MHz clock for 860 */
+		np->clock_divn	= 5;
+		break;
+	case PCI_DEVICE_ID_NCR_53C875:
+		np->maxwide = 1;
+		if (driver_setup.special_features)
+			np->maxoffs = 16;
+		np->clock_divn	= 5;
+		ncr_getclock(np, revision_id >= 2 ? 2 : 1);
+		break;
+	case PCI_DEVICE_ID_NCR_53C895:
+		np->maxwide = 1;
+		if (driver_setup.special_features)
+			np->maxoffs = 31;
+		np->clock_divn	= 7;
+		ncr_getclock(np, 4);
 		break;
+	}
+
+	/*
+	**	Get on-board RAM bus address when supported
+	*/
+	switch (device_id) {
+	case PCI_DEVICE_ID_NCR_53C825:
+		if (revision_id < 0x10)
+			break;
 	case PCI_DEVICE_ID_NCR_53C875:
-		np->maxwide = 1;
-		if (driver_setup.special_features)
-			np->maxoffs = 16;
-		ncr_getclock(np);
+	case PCI_DEVICE_ID_NCR_53C895:
+		if (driver_setup.special_features) {
+			OUTONB(nc_ctest2, 0x8);
+			np->paddr2 = INL(nc_scr0);
+			OUTOFFB(nc_ctest2, 0x8);
+		}
 		break;
 	}
 
+	if (bootverbose && np->paddr2)
+		printf ("%s: on-board RAM at 0x%lx\n", ncr_name(np), np->paddr2);
+
 	if (bootverbose && np->ns_sync < 25)
-		printf ("%s: Ultra SCSI support enabled\n", ncr_name(np));
+		printf ("%s: Ultra%s SCSI support enabled\n", ncr_name(np),
+			np->ns_sync < 12 ? "-2": "");
+
+#ifndef NCR_IOMAPPED
+	if (np->paddr2 && sizeof(struct script) <= 4096) {
+		np->vaddr2 = remap_pci_mem((u_long) np->paddr2, (u_long) 4096);
+		if (!np->vaddr2) {
+			printf("%s: can't map memory mapped IO region\n", ncr_name(np));
+#ifdef NCR_MEMORYMAPPED
+			goto attach_error;
+#endif
+		}
+		else
+			if (bootverbose > 1)
+				printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+	}
+#endif /* !defined NCR_IOMAPPED */
 
 	/*
 	**	Fill Linux host instance structure
@@ -3646,16 +3819,24 @@
 	/*
 	**	Patch script to physical addresses
 	*/
-	ncr_script_fill (&script0);
-	ncr_script_copy_and_bind (&script0, np);
-	np->ccb.p_ccb		= vtophys (&np->ccb);
+	ncr_script_fill (&script0, &scripth0);
+
+	np->scripth	= np->scripth0;
+	np->p_scripth	= vtophys(np->scripth);
+
+	np->script	= (np->vaddr2) ? (struct script *) np->vaddr2 : np->script0;
+	np->p_script	= (np->vaddr2) ? np->paddr2 : vtophys(np->script0);
+
+	ncr_script_copy_and_bind (np, (ncrcmd *) &script0, (ncrcmd *) np->script0, sizeof(struct script));
+	ncr_script_copy_and_bind (np, (ncrcmd *) &scripth0, (ncrcmd *) np->scripth0, sizeof(struct scripth));
+	np->ccb->p_ccb		= vtophys (np->ccb);
 
 	/*
 	**	init data structure
 	*/
 
 	np->jump_tcb.l_cmd	= SCR_JUMP;
-	np->jump_tcb.l_paddr	= NCB_SCRIPT_PHYS (np, abort);
+	np->jump_tcb.l_paddr	= NCB_SCRIPTH_PHYS (np, abort);
 
 #if !defined(NCR_IOMAPPED) && !defined(NCR_MEMORYMAPPED)
 retry_chip_init:
@@ -3701,7 +3882,7 @@
 	*/
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 #   ifdef SCSI_NCR_SHARE_IRQ
-	if (bootverbose)
+	if (bootverbose > 1)
 		printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
 		        ncr_name(np), irq, (u_long) np);
 	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
@@ -3750,14 +3931,11 @@
 	**	The middle-level SCSI driver does not
 	**	wait devices to settle.
 	*/
-#ifdef SCSI_NCR_SETTLE_TIME
-#if    SCSI_NCR_SETTLE_TIME > 2
-	printf("%s: waiting for scsi devices to settle...\n", ncr_name(np));
-#endif
-#if    SCSI_NCR_SETTLE_TIME > 0
-	DELAY(SCSI_NCR_SETTLE_TIME*1000000);
-#endif
-#endif
+	if (driver_setup.settle_time > 2)
+		printf("%s: waiting %d seconds for scsi devices to settle...\n",
+			ncr_name(np), driver_setup.settle_time);
+	if (driver_setup.settle_time)
+		DELAY(1000000UL * driver_setup.settle_time);
 
 	/*
 	**	Now let the generic SCSI driver
@@ -3794,6 +3972,10 @@
 		printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 		unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
 	}
+	if (np->vaddr2) {
+		printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+		unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
+	}
 #endif
 	if (np->port) {
 		printf("%s: releasing IO region %x[%d]\n", ncr_name(np), np->port, 128);
@@ -3859,7 +4041,7 @@
         struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
 	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = &host_data->ncb_data;
+	ncb_p np                      = host_data->ncb;
 	tcb_p tp                      = &np->target[cmd->target];
 
 	ccb_p cp;
@@ -3867,7 +4049,7 @@
 
 	int	segments;
 	u_char	qidx, nego, idmsg, *msgptr;
-	u_long  msglen, msglen2;
+	u_int  msglen, msglen2;
 	u_long	flags;
 	int	xfer_direction;
 
@@ -4000,7 +4182,7 @@
 		*/
 
 		if (!nego && !tp->period) {
-			if (SCSI_NCR_MAX_SYNC 
+			if ( 1
 #if defined (CDROM_ASYNC)
 			    && ((tp->inqdata[0] & 0x1f) != 5)
 #endif
@@ -4051,7 +4233,7 @@
 
 	idmsg = M_IDENTIFY | cmd->lun;
 
-	if (cp != &np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
+	if (cp != np->ccb && ((np->disc && !(tp->usrflag & UF_NODISC)) || cp->tag))
 		idmsg |= 0x40;
 
 	msgptr = cp->scsi_smsg;
@@ -4187,7 +4369,7 @@
 	     cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
 	     break;
 	case XferOut:
-	     cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_out);
+	     cp->phys.header.savep = NCB_SCRIPTH_PHYS (np, data_out);
 	     cp->phys.header.goalp = cp->phys.header.savep +20 +segments*16;
 	     break;
 	case XferNone:
@@ -4295,7 +4477,7 @@
 		printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
 		np->squeueput,
 		(unsigned)(np->script->startpos[0]- 
-			   (NCB_SCRIPT_PHYS (np, tryloop))));
+			   (NCB_SCRIPTH_PHYS (np, tryloop))));
 
 	/*
 	**	Script processor may be waiting for reselect.
@@ -4329,18 +4511,67 @@
         struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
 	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = &host_data->ncb_data;
+	ncb_p np                      = host_data->ncb;
+	ccb_p cp;
 	u_long flags;
+	int found;
 
 	save_flags(flags); cli();
-
-	reset_waiting_list(np);
-
-	OUTB (nc_scntl1, CRST);
+/*
+ * Return immediately from recursive call
+ */
+	if (np->resetting) {
+		restore_flags(flags);
+		return SCSI_RESET_PUNT;
+	}
+	++np->resetting;
+/*
+ * Perform chip reset, SCSI reset, wait 2 seconds
+ */
+	OUTB (nc_istat, SRST);
 	DELAY (1000);
+	OUTB (nc_istat, 0);
+	OUTB (nc_scntl1, CRST);
+	DELAY (100);
+	OUTB (nc_scntl1, 0);
+	DELAY (2000000);
+/*
+ * First, look in the wakeup list
+ */
+	for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
+		/*
+		**	look for the ccb of this command.
+		*/
+		if (cp->host_status == HS_IDLE) continue;
+		if (cp->cmd == cmd) {
+			found = 1;
+			break;
+		}
+	}
+/*
+ * Then, look in the waiting list
+ */
+	if (!found && retrieve_from_waiting_list(0, np, cmd))
+		found = 1;
+/*
+ * Reinitialise the NCR and wake-up pending commands
+ */
 	ncr_init(np, "scsi bus reset", HS_RESET);
-
 	np->disc = 1;
+/*
+ * Complete awaiting commands with DID_RESET
+ */
+	reset_waiting_list(np);
+/*
+ * If the involved command was not in a driver queue, complete it 
+ * with DID_RESET, in order to keep it alive.
+ */
+	if (!found && cmd && cmd->scsi_done) {
+		cmd->result = ScsiResult(DID_RESET, 0);
+		cmd->scsi_done(cmd);
+	}
+
+	--np->resetting;
 
 	restore_flags(flags);
 
@@ -4361,7 +4592,7 @@
         struct Scsi_Host   *host      = cmd->host;
 /*	Scsi_Device        *device    = cmd->device; */
 	struct host_data   *host_data = (struct host_data *) host->hostdata;
-	ncb_p np                      = &host_data->ncb_data;
+	ncb_p np                      = host_data->ncb;
 	ccb_p cp;
 	u_long flags;
 	int found;
@@ -4381,7 +4612,7 @@
 /*
  * Then, look in the wakeup list
  */
-	for (found=0, cp=&np->ccb; cp; cp=cp->link_ccb) {
+	for (found=0, cp=np->ccb; cp; cp=cp->link_ccb) {
 		/*
 		**	look for the ccb of this command.
 		*/
@@ -4502,17 +4733,10 @@
 	OUTB(nc_ctest3,	np->sv_ctest3);
 	OUTB(nc_ctest4,	np->sv_ctest4);
 	OUTB(nc_ctest5,	np->sv_ctest5);
+	OUTB(nc_gpcntl,	np->sv_gpcntl);
+	OUTB(nc_stest2,	np->sv_stest2);
 
-	if (np->uf_doubler) {
-		OUTB(nc_stest1, DBLEN);		/* Enable clock doubler	*/
-		DELAY(10);
-		OUTB(nc_stest3, 0x20);		/* Halt the scsi clock	*/
-		OUTB(nc_scntl3,	np->sv_scntl3);
-		OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler	*/
-		OUTB(nc_stest3, 0x00);		/* Restart scsi clock 	*/
-	}
-	else
-		OUTB(nc_scntl3,	np->sv_scntl3);
+	ncr_selectclock(np, np->sv_scntl3);
 
 	/*
 	**	Release Memory mapped IO region and IO mapped region
@@ -4520,9 +4744,13 @@
 
 #ifndef NCR_IOMAPPED
 #ifdef DEBUG
-	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->reg, 128);
+	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr, 128);
 #endif
 	unmap_pci_mem((vm_offset_t) np->vaddr, (u_long) 128);
+#ifdef DEBUG
+	printf("%s: releasing memory mapped IO region %lx[%d]\n", ncr_name(np), (u_long) np->vaddr2, 4096);
+#endif
+	unmap_pci_mem((vm_offset_t) np->vaddr2, (u_long) 4096);
 #endif
 
 #ifdef DEBUG
@@ -4534,8 +4762,8 @@
 	**	Free allocated ccb(s)
 	*/
 
-	while ((cp=np->ccb.link_ccb) != NULL) {
-		np->ccb.link_ccb = cp->link_ccb;
+	while ((cp=np->ccb->link_ccb) != NULL) {
+		np->ccb->link_ccb = cp->link_ccb;
 		if (cp->host_status) {
 		printf("%s: shall free an active ccb (host_status=%d)\n",
 			ncr_name(np), cp->host_status);
@@ -4889,7 +5117,7 @@
 	**	complete all jobs that are not IDLE.
 	*/
 
-	ccb_p cp = &np->ccb;
+	ccb_p cp = np->ccb;
 	while (cp) {
 		switch (cp->host_status) {
 
@@ -4918,7 +5146,7 @@
 /*===============================================================
 **
 **	NCR chips allow burst lengths of 2, 4, 8, 16, 32, 64, 128 
-**	transfers. 32,64,128 are only supported by 875 chips.
+**	transfers. 32,64,128 are only supported by 875 and 895 chips.
 **	We use log base 2 (burst length) as internal code, with 
 **	value 0 meaning "burst disabled".
 **
@@ -4995,8 +5223,8 @@
 	*/
 
 	np->squeueput = 0;
-	np->script->startpos[0] = NCB_SCRIPT_PHYS (np, tryloop);
-	np->script->start0  [0] = SCR_INT ^ IFFALSE (0);
+	np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
+	np->script0->start0  [0] = SCR_INT ^ IFFALSE (0);
 
 	/*
 	**	Wakeup all pending jobs.
@@ -5043,10 +5271,8 @@
 /**	NCR53C810A or NCR53C860		**/
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) ||
 	    ChipDevice == PCI_DEVICE_ID_NCR_53C860) {
-		if (!driver_setup.special_features)
-			burst_max	= burst_max < 4 ? burst_max : 4;
-		else {
-			burst_max	= burst_max < 4 ? burst_max : 4;
+		burst_max	= burst_max < 4 ? burst_max : 4;
+		if (driver_setup.special_features) {
 			np->rv_dmode	= BOF | ERMP | ERL;
 						/* burst op-code fetch, read multiple */
 						/* read line */
@@ -5056,9 +5282,10 @@
 		}
 	}
 	else
-/**	NCR53C825A or NCR53C875		**/
+/**	NCR53C825A or NCR53C875 or NCR53C895		**/
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
-	    ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
+	    ChipDevice == PCI_DEVICE_ID_NCR_53C875 ||
+	    ChipDevice == PCI_DEVICE_ID_NCR_53C895) {
 		if (!driver_setup.special_features)
 			burst_max	= burst_max < 4 ? burst_max : 4;
 		else {
@@ -5069,7 +5296,7 @@
 			np->rv_dcntl	= PFEN | CLSE;
 						/* prefetch, cache line size */
 			np->rv_ctest3	= WRIE;	/* write and invalidate */
-			np->rv_ctest5	= 0x20;	/* dma fifo 536 (0x20) */
+			np->rv_ctest5	= 0x20;	/* large dma fifo (0x20) */
 		}
 	}
 /**	OTHERS				**/
@@ -5078,26 +5305,60 @@
 	}
 #endif /* SCSI_NCR_TRUST_BIOS_SETTING */
 
+#if 0
+	/*
+	 *	Do not enable read-multiple for 810A revision 0x11
+	 */
+	if (np->device_id == PCI_DEVICE_ID_NCR_53C810 && np->revision_id == 0x11)
+		np->rv_dmode &= (~ERMP);
+#endif
+
 	/*
 	 *	Prepare initial io register bits for burst length
 	 */
 	ncr_init_burst(np, burst_max);
 
-	if (bootverbose > 1) {
-		printf ("%s: initial value of dmode/ctest4/ctest5 = 0x%02x/0x%02x/0x%02x\n",
-			ncr_name(np), np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
+	/*
+	**	Set differential mode.
+	*/
+	switch(driver_setup.diff_support) {
+	case 3:
+		if (INB(nc_gpreg) & 0x08)
+			break;
+	case 2:
+		np->rv_stest2	|= 0x20;
+		break;
+	case 1:
+		np->rv_stest2	|= (np->sv_stest2 & 0x20);
+		break;
+	default:
+		break;
 	}
-	if (bootverbose) {
-		printf ("%s: final value of dmode/ctest4/ctest5 = 0x%02x/0x%02x/0x%02x\n",
-			ncr_name(np), np->rv_dmode, np->rv_ctest4, np->rv_ctest5);
+
+	/*
+	**	Set irq mode.
+	*/
+	switch(driver_setup.irqm) {
+	case 2:
+		np->rv_dcntl	|= IRQM;
+		break;
+	case 1:
+		np->rv_stest2	|= (np->sv_dcntl & IRQM);
+		break;
+	default:
+		break;
 	}
 
-#if 0
-	printf("%s: bios: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x, ctest5=0x%02x\n",
-		ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
-	printf("%s: used: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x, ctest5=0x%02x\n",
-		ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
-#endif
+	if (bootverbose > 1) {
+		printf ("%s: initial value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
+			ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+	}
+	if (bootverbose > 1) {
+		printf ("%s: final value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
+			ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
+		if (np->rv_stest2 & 0x20)
+			printf ("%s: setting up differential mode\n", ncr_name(np));
+	}
 
 	OUTB (nc_istat,  0x00   );      /*  Remove Reset, abort ...	     */
 	if (driver_setup.scsi_parity)
@@ -5107,18 +5368,8 @@
 
 	OUTB (nc_scntl1, 0x00	);	/*  odd parity, and remove CRST!!    */
 
-	if (np->uf_doubler) {
-		if (bootverbose >= 2)
-			printf ("%s: enabling clock doubler\n", ncr_name(np));
-		OUTB(nc_stest1, DBLEN);		/* Enable clock doubler	*/
-		DELAY(10);
-		OUTB(nc_stest3, 0x20);		/* Halt the scsi clock	*/
-		OUTB(nc_scntl3, 0x05);		/* Safe timing for now	*/
-		OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler	*/
-		OUTB(nc_stest3, 0x00);		/* Restart scsi clock 	*/
-	}
+	ncr_selectclock(np, np->rv_scntl3);
 
-	OUTB (nc_scntl3, np->rv_scntl3);/*  timing prescaler		     */
 	OUTB (nc_scid  , RRE|np->myaddr);/*  host adapter SCSI address       */
 	OUTW (nc_respid, 1ul<<np->myaddr);/*  id to respond to		     */
 	OUTB (nc_istat , SIGP	);	/*  Signal Process		     */
@@ -5135,7 +5386,7 @@
 	else
 		OUTB (nc_ctest4, 0x00|np->rv_ctest4);	/*  disable master parity checking   */
 
-	OUTB (nc_stest2, EXT    );	/*  Extended Sreq/Sack filtering     */
+	OUTB (nc_stest2, EXT|np->rv_stest2);	/*  Extended Sreq/Sack filtering     */
 	OUTB (nc_stest3, TE     );	/*  TolerANT enable		     */
 	OUTB (nc_stime0, 0x0d	);	/*  HTH = disable  STO = 0.4 sec.    */
 					/*  0.25 sec recommended for scsi 1  */
@@ -5145,17 +5396,15 @@
 	**	Have to renegotiate synch mode.
 	*/
 
-	usrsync = 255;
-
-	if (driver_setup.default_sync && SCSI_NCR_MAX_SYNC) {
-		u_long period;
-		period =1000000/driver_setup.default_sync; /* ns = 10e6 / kHz */
-		if (period <= 11 * 50) {
-			if (period < 4 * np->ns_sync)
+	usrsync = driver_setup.default_sync;
+	if (usrsync != 255) {
+		if (4 * usrsync <= 11 * 50) {
+			if (usrsync < np->ns_sync) {
 				usrsync = np->ns_sync;
-			else
-				usrsync = period / 4;
-		};
+			}
+		}
+		else
+			usrsync = 255;
 	};
 
 	/*
@@ -5163,7 +5412,7 @@
 	**	Have to renegotiate wide mode.
 	*/
 
-	usrwide = (SCSI_NCR_MAX_WIDE);
+	usrwide = driver_setup.max_wide;
 	if (usrwide > np->maxwide) usrwide=np->maxwide;
 
 	/*
@@ -5189,6 +5438,27 @@
 	}
 
 	/*
+	**    Enable GPIO0 pin for writing.
+	**    Patch the script for LED support.
+	*/
+
+	if (driver_setup.led_pin & (~np->sv_gpcntl) & 0x01) {
+		OUTOFFB (nc_gpcntl, 0x01);
+		np->script0->reselect[0]  = SCR_REG_REG(gpreg, SCR_OR,  0x01);
+		np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+		np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+	}
+
+	/*
+	**    Upload the script into on-board RAM
+	*/
+	if (np->vaddr2) {
+		if (bootverbose)
+			printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
+		bcopy(np->script0, np->script, sizeof(struct script));
+	}
+
+	/*
 	**      enable ints
 	*/
 
@@ -5218,7 +5488,10 @@
 
 	u_long minsync = tp->usrsync;
 
-	if (driver_setup.ultra_scsi) {
+	if	(driver_setup.ultra_scsi >= 2) {
+		if (minsync < 10) minsync=10;
+	}
+	else if	(driver_setup.ultra_scsi == 1) {
 		if (minsync < 12) minsync=12;
 	}
 	else {
@@ -5264,6 +5537,92 @@
 
 /*==========================================================
 **
+**	Get clock factor and sync divisor.
+**
+**==========================================================
+*/
+
+#define SCSI_NCR_USE_ALL_DIVISORS
+
+/*
+**	NCR chip clock divisor table.
+**	Multiplied by 2x2000000 in order to avoid useless operations in 
+**	the code that gets clock factor and sync divisor from sync factor.
+*/
+#define _2M 2000000
+static u_long ncr_div2_2M[] = {2*_2M, 3*_2M, 4*_2M, 6*_2M, 8*_2M, 12*_2M, 16*_2M};
+
+/*
+**	Get clock factor and sync divisor for a given sync factor period.
+**	Returns the clock factor, scntl3 and resulting period.
+*/
+static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
+{
+	u_long	clk	= np->clock_khz;	/* Clock in kHz		*/
+	int	idiv	= np->clock_divn;	/* # divisors supported	*/
+	u_long	fak, per, per_clk;
+
+	/*
+	**	Compute the synchronous period in nano-seconds
+	*/
+	if	(fac <= 10)	per = 25;
+	else if	(fac == 11)	per = 30;
+	else if	(fac == 12)	per = 50;
+	else			per = 4 * fac;
+
+	/*
+	**	Find the greatest divisor that allows an input speed 
+	**	faster than the period.
+	*/
+	per_clk = per * clk;
+	while (--idiv >= 0) {
+#ifndef SCSI_NCR_USE_ALL_DIVISORS
+		if (idiv & 1) continue;
+#endif
+		if (ncr_div2_2M[idiv] <= per_clk) break;
+	}
+	if (idiv < 0) idiv = 0;		/* Should never happen */
+
+	/*
+	**	Calculate the lowest clock factor that allows an output 
+	**	speed not faster than the period.
+	*/
+	fak = (4 * per_clk - 1) / ncr_div2_2M[idiv] + 1;
+	per = (fak * ncr_div2_2M[idiv]) / (4 * clk);
+
+#ifdef SCSI_NCR_USE_ALL_DIVISORS
+	/*
+	**	Try the next divisor and choose the one that give 
+	**	the fastest output speed.
+	*/
+	if (idiv >= 1 && fak < 8) {
+		u_long fak2, per2;
+		fak2 = (4 * per_clk - 1) / ncr_div2_2M[idiv-1] + 1;
+		per2 = (fak2 * ncr_div2_2M[idiv-1]) / (4 * clk);
+		if (per2 < per && fak2 <= 8) {
+			fak = fak2;
+			per = per2;
+			--idiv;
+		}
+	}
+#endif
+	if (fak < 4) fak = 4;		/* Should never happen */
+
+	/*
+	**	Compute and return sync parameters for the ncr
+	*/
+	*fakp		= fak - 4;
+	*scntl3p	= ((idiv+1) << 4) + (per < 100 ? 0x80 : 0);
+
+#ifdef DEBUG
+printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
+#endif
+
+	return per;
+}
+
+/*==========================================================
+**
 **	Switch sync mode for current job and it's target
 **
 **==========================================================
@@ -5274,7 +5633,7 @@
 	Scsi_Cmnd *cmd;
 	tcb_p tp;
 	u_char target = INB (nc_ctest0) & 0x0f;
-	u_char p2;
+	u_char idiv;
 
 	assert (cp);
 	if (!cp) return;
@@ -5292,18 +5651,16 @@
 
 	/*
 	**	Deduce the value of controller sync period from scntl3.
-	**	p2 is twice this value since we do integer calculations.
-	*	(12.5 ns would give inaccurate results)
 	*/
-	p2 = (scntl3 & 0x07) == 0x05 ? 25 : 50;
-	switch(scntl3 & 0x70) {
-	case 0x50:	p2 *= 4;	break;
-	case 0x30:	p2 *= 2;	break;
-	default:			break;
-	}
-
-	tp->period= sxfer&0x1f ? (((sxfer>>5)+4) * p2)/2 : 0xffff;
+	idiv = ((scntl3 >> 4) & 0x7);
+	if ((sxfer & 0x1f) && idiv)
+		tp->period = (((sxfer>>5)+4)*ncr_div2_2M[idiv-1])/(4*np->clock_khz);
+	else
+		tp->period = 0xffff;
 
+	/*
+	**	 Stop there if sync parameters are unchanged
+	*/
 	if (tp->sval == sxfer && tp->wval == scntl3) return;
 	tp->sval = sxfer;
 	tp->wval = scntl3;
@@ -5315,16 +5672,29 @@
 	if (sxfer & 0x01f) {
 		unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
 		unsigned mb10 = (f10 + tp->period/2) / tp->period;
+		char *msg;
+
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
-		if (tp->period <= 200) OUTB (nc_stest2, 0);
-		printf ("%s%d.%d MB/s (%d ns, offset %d)\n",
-			tp->widedone > 1    ?
-			(tp->period  < 100  ? "ULTRA WIDE SCSI " :
-			(tp->period  < 200  ? "FAST WIDE SCSI-2 ":"WIDE ")) :
-			(tp->period  < 100  ? "ULTRA SCSI " :
-			(tp->period  < 200  ? "FAST SCSI-2 ":"")),
+		if (tp->period <= 200) OUTOFFB (nc_stest2, EXT);
+
+		/*
+		**	Bells and whistles   ;-)
+		*/
+		msg = "";
+		if (tp->widedone > 1) {
+			if	(tp->period < 50)  msg = "ULTRA-2 WIDE SCSI ";
+			else if	(tp->period < 100) msg = "ULTRA WIDE SCSI ";
+			else if	(tp->period < 200) msg = "FAST WIDE SCSI-2 ";
+		}
+		else {
+			if	(tp->period < 50)  msg = "ULTRA-2 SCSI ";
+			else if	(tp->period < 100) msg = "ULTRA SCSI ";
+			else if	(tp->period < 200) msg = "FAST SCSI-2 ";
+		}
+
+		printf ("%s%d.%d MB/s (%d ns, offset %d)\n", msg,
 			mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
 	} else  printf ("asynchronous.\n");
 
@@ -5339,7 +5709,7 @@
 	/*
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = &np->ccb; cp; cp = cp->link_ccb) {
+	for (cp = np->ccb; cp; cp = cp->link_ccb) {
 		if (!cp->cmd) continue;
 		if (cp->cmd->target != target) continue;
 		cp->sync_status = sxfer;
@@ -5395,7 +5765,7 @@
 	/*
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = &np->ccb; cp; cp = cp->link_ccb) {
+	for (cp = np->ccb; cp; cp = cp->link_ccb) {
 		if (!cp->cmd) continue;
 		if (cp->cmd->target != target) continue;
 		cp->wide_status = scntl3;
@@ -5627,6 +5997,7 @@
 		t = (thistime - np->heartbeat) / HZ;
 
 		if (t<2) np->latetime=0; else np->latetime++;
+#if 0
 		if (np->latetime>5) {
 			/*
 			**      If there are no requests, the script
@@ -5637,6 +6008,7 @@
 
 			OUTB (nc_istat, SIGP);
 		}
+#endif
 
 		/*----------------------------------------------------
 		**
@@ -5645,7 +6017,7 @@
 		**----------------------------------------------------
 		*/
 
-		for (cp=&np->ccb; cp; cp=cp->link_ccb) {
+		for (cp=np->ccb; cp; cp=cp->link_ccb) {
 			/*
 			**	look for timed out ccbs.
 			*/
@@ -6077,7 +6449,7 @@
 	*/
 
 	dsa = INL (nc_dsa);
-	cp = &np->ccb;
+	cp = np->ccb;
 	while (cp && (CCB_PHYS (cp, phys) != dsa))
 		cp = cp->link_ccb;
 
@@ -6091,7 +6463,7 @@
 	*/
 
 	scratcha = INL (nc_scratcha);
-	diff = scratcha - NCB_SCRIPT_PHYS (np, tryloop);
+	diff = scratcha - NCB_SCRIPTH_PHYS (np, tryloop);
 
 /*	assert ((diff <= MAX_START * 20) && !(diff % 20));*/
 
@@ -6122,59 +6494,79 @@
 {
 	u_int32	dbc;
 	u_int32	rest;
-	u_int32	dsa;
 	u_int32	dsp;
+	u_int32	dsa;
 	u_int32	nxtdsp;
 	u_int32	*vdsp;
 	u_int32	oadr, olen;
 	u_int32	*tblp;
         ncrcmd *newcmd;
-	u_char	cmd, sbcl, ss0, ss2, ctest5;
-	u_short	delta;
+	u_char	cmd, sbcl;
 	ccb_p	cp;
 
-	dsp = INL (nc_dsp);
-	dsa = INL (nc_dsa);
-	dbc = INL (nc_dbc);
-	ss0 = INB (nc_sstat0);
-	ss2 = INB (nc_sstat2);
-	sbcl= INB (nc_sbcl);
-
-	if (driver_setup.special_features)
-		ctest5 = INB (nc_ctest5);
-	else
-		ctest5 = 0;
-
-	cmd = dbc >> 24;
-	rest= dbc & 0xffffff;
+	dsp	= INL (nc_dsp);
+	dbc	= INL (nc_dbc);
+	sbcl	= INB (nc_sbcl);
 
-	if (ctest5 & 0x20)
-		delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
-	else
-		delta=(INB (nc_dfifo) - rest) & 0x7f;
+	cmd	= dbc >> 24;
+	rest	= dbc & 0xffffff;
 
 	/*
-	**	The data in the dma fifo has not been transfered to
-	**	the target -> add the amount to the rest
-	**	and clear the data.
-	**	Check the sstat2 register in case of wide transfer.
+	**	Take into account dma fifo and various buffers and latches,
+	**	only if the interrupted phase in an OUTPUT phase.
 	*/
 
-	if (! (INB(nc_dstat) & DFE)) rest += delta;
-	if (ss0 & OLF) rest++;
-	if (ss0 & ORF) rest++;
-	if (INB(nc_scntl3) & EWS) {
-		if (ss2 & OLF1) rest++;
-		if (ss2 & ORF1) rest++;
-	};
-	OUTONB (nc_ctest3, CLF );	/* clear dma fifo  */
-	OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
+	if ((cmd & 1) == 0) {
+		u_char	ctest5, ss0, ss2;
+		u_short	delta;
+
+		if (!(INB(nc_dstat) & DFE)) {
+			ctest5 = (np->rv_ctest5 & 0x20) ? INB (nc_ctest5) : 0;
+			if (ctest5 & 0x20)
+				delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
+			else
+				delta=(INB (nc_dfifo) - rest) & 0x7f;
+		} else {
+			delta = 0;
+		}
+
+		/*
+		**	The data in the dma fifo has not been transfered to
+		**	the target -> add the amount to the rest
+		**	and clear the data.
+		**	Check the sstat2 register in case of wide transfer.
+		*/
+
+		rest += delta;
+		ss0  = INB (nc_sstat0);
+		if (ss0 & OLF) rest++;
+		if (ss0 & ORF) rest++;
+		if (INB(nc_scntl3) & EWS) {
+			ss2 = INB (nc_sstat2);
+			if (ss2 & OLF1) rest++;
+			if (ss2 & ORF1) rest++;
+		};
+
+		OUTONB (nc_ctest3, CLF );	/* clear dma fifo  */
+		OUTB (nc_stest3, TE|CSF);	/* clear scsi fifo */
+
+		if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+			printf ("P%x%x RL=%d D=%d SS0=%x ", cmd&7, sbcl&7,
+				(unsigned) rest, (unsigned) delta, ss0);
+
+	} else	{
+		if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE))
+			printf ("P%x%x RL=%d ", cmd&7, sbcl&7, rest);
+		if (!(INB(nc_dstat) & DFE))
+			printf("INPUT phase mismatch with DMA fifo not empty, P%x%x RL=%d\n",
+				cmd&7, sbcl&7, rest);
+	}
 
 	/*
 	**	locate matching cp
 	*/
 	dsa = INL (nc_dsa);
-	cp = &np->ccb;
+	cp  = np->ccb;
 	while (cp && (CCB_PHYS (cp, phys) != dsa))
 		cp = cp->link_ccb;
 
@@ -6200,19 +6592,18 @@
 	} else if (dsp == vtophys (&cp->patch[6])) {
 		vdsp = &cp->patch[4];
 		nxtdsp = vdsp[3];
-	} else {
+	} else if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
 		vdsp = (u_int32 *) ((char*)np->script - np->p_script + dsp -8);
 		nxtdsp = dsp;
+	} else {
+		vdsp = (u_int32 *) ((char*)np->scripth - np->p_scripth + dsp -8);
+		nxtdsp = dsp;
 	};
 
 	/*
 	**	log the information
 	*/
-	if (DEBUG_FLAGS & (DEBUG_TINY|DEBUG_PHASE)) {
-		printf ("P%x%x ",cmd&7, sbcl&7);
-		printf ("RL=%d D=%d SS0=%x ",
-			(unsigned) rest, (unsigned) delta, ss0);
-	};
+
 	if (DEBUG_FLAGS & DEBUG_PHASE) {
 		printf ("\nCP=%p CP2=%p DSP=%x NXT=%x VDSP=%p CMD=%x ",
 			cp, np->header.cp,
@@ -6349,7 +6740,7 @@
 		**	lookup the ccb
 		*/
 		dsa = INL (nc_dsa);
-		cp = &np->ccb;
+		cp = np->ccb;
 		while (cp && (CCB_PHYS (cp, phys) != dsa))
 			cp = cp->link_ccb;
 
@@ -6369,7 +6760,7 @@
 **	We have to patch the script context with DATA OUT context and 
 **	restart processing at data out script address.
 */
-		cp->phys.header.savep	= NCB_SCRIPT_PHYS (np, data_out);
+		cp->phys.header.savep	= NCB_SCRIPTH_PHYS (np, data_out);
 		cp->phys.header.goalp	= cp->phys.header.savep +20 +cp->segments*16;
 		cp->phys.header.lastp	= cp->phys.header.savep;
 		np->header.savep	= cp->phys.header.savep;
@@ -6415,7 +6806,7 @@
 			if (DEBUG_FLAGS & DEBUG_RESTART)
 				printf ("+ restart job ..\n");
 			OUTL (nc_dsa, CCB_PHYS (cp, phys));
-			OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, getcc));
+			OUTL (nc_dsp, NCB_SCRIPTH_PHYS (np, getcc));
 			return;
 		};
 
@@ -6602,38 +6993,11 @@
 
 		/*
 		**	Check against controller limits.
-		**	--------------------------------
-		**	per <  25  fast20
-		**	per <  50  fast
-		**	per < 100  slow
-		**	Use a value p2 twice the controller limit in order to 
-		**	not do wrong integer calculation for 80 MHz clock.
-		**	(12.5x2 = 25ns).
-		**	Compute scntl3&0xf0 sync clock divisor for 50 ns period 
-		**	from the async pre-scaler.
-		**	Ajust it according to actual controller sync period.
-		**	- 0x40 divides it by 4  -> 50/4 = 12.5ns
-		**	- 0x20 divides it by 2  -> 50/2 = 25 ns
-		**	Similar stuff is used in ncr_getclock().
 		*/
 		fak	= 7;
 		scntl3	= 0;
 		if (ofs != 0) {
-			u_char p2;
-
-			p2	= 100;
-			scntl3	= (np->rv_scntl3 & 0x07) << 4;
-
-			if (per < 25) {
-				p2	= 25;
-				scntl3	= (scntl3 - 0x40) | 0x80;
-			}
-			else if (per < 50) {
-				p2	= 50;
-				scntl3	= scntl3 - 0x20;
-			}
-
-			fak = (8 * per - 1) / p2 - 3;
+			(void) ncr_getsync(np, per, &fak, &scntl3);
 			if (fak > 7) {
 				chg = 1;
 				ofs = 0;
@@ -6942,7 +7306,7 @@
 		/*
 		**	Look for a disconnected job.
 		*/
-		cp = &np->ccb;
+		cp = np->ccb;
 		while (cp && cp->host_status != HS_DISCONNECT)
 			cp = cp->link_ccb;
 
@@ -7020,7 +7384,7 @@
 	if ((!cp) && lp && lp->actccbs > 0)
 		return ((ccb_p) 0);
 
-	if (!cp) cp = &np->ccb;
+	if (!cp) cp = np->ccb;
 
 	/*
 	**	Wait until available.
@@ -7072,7 +7436,7 @@
 	cp -> host_status = HS_IDLE;
 	cp -> magic = 0;
 #if 0
-	if (cp == &np->ccb)
+	if (cp == np->ccb)
 		wakeup ((caddr_t) cp);
 #endif
 }
@@ -7123,7 +7487,7 @@
 		tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
 
 		tp->jump_lcb.l_cmd   = (SCR_JUMP);
-		tp->jump_lcb.l_paddr = NCB_SCRIPT_PHYS (np, abort);
+		tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
 		np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
 	}
 
@@ -7135,7 +7499,7 @@
 		/*
 		**	Allocate a lcb
 		*/
-		lp = (lcb_p) m_alloc (sizeof (struct lcb));
+		lp = (lcb_p) m_alloc (sizeof (struct lcb), LCB_ALIGN_SHIFT);
 		if (!lp) return;
 
 		if (DEBUG_FLAGS & DEBUG_ALLOC) {
@@ -7154,7 +7518,7 @@
 		lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
 
 		lp->jump_ccb.l_cmd   = (SCR_JUMP);
-		lp->jump_ccb.l_paddr = NCB_SCRIPT_PHYS (np, aborttag);
+		lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
 
 		lp->actlink = 1;
 
@@ -7186,7 +7550,7 @@
 	/*
 	**	Allocate a ccb
 	*/
-	cp = (ccb_p) m_alloc (sizeof (struct ccb));
+	cp = (ccb_p) m_alloc (sizeof (struct ccb), CCB_ALIGN_SHIFT);
 	if (!cp)
 		return;
 
@@ -7224,8 +7588,8 @@
 	/*
 	**	Chain into wakeup list
 	*/
-	cp->link_ccb      = np->ccb.link_ccb;
-	np->ccb.link_ccb  = cp;
+	cp->link_ccb      = np->ccb->link_ccb;
+	np->ccb->link_ccb = cp;
 
 	/*
 	**	Chain into CCB list
@@ -7401,7 +7765,7 @@
 	/*
 	**	init
 	*/
-	pc  = NCB_SCRIPT_PHYS (np, snooptest);
+	pc  = NCB_SCRIPTH_PHYS (np, snooptest);
 	host_wr = 1;
 	ncr_wr  = 2;
 	/*
@@ -7445,11 +7809,11 @@
 	/*
 	**	Check termination position.
 	*/
-	if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) {
+	if (pc != NCB_SCRIPTH_PHYS (np, snoopend)+8) {
 		printf ("CACHE TEST FAILED: script execution failed.\n");
 		printf ("start=%08lx, pc=%08lx, end=%08lx\n", 
-			(u_long) NCB_SCRIPT_PHYS (np, snooptest), pc,
-			(u_long) NCB_SCRIPT_PHYS (np, snoopend) +8);
+			(u_long) NCB_SCRIPTH_PHYS (np, snooptest), pc,
+			(u_long) NCB_SCRIPTH_PHYS (np, snoopend) +8);
 		return (0x40);
 	};
 	/*
@@ -7482,29 +7846,14 @@
 **==========================================================
 */
 
-
 #ifdef SCSI_NCR_PROFILE
 
-#if 0
-/*
-**	Compute the difference in milliseconds.
-*/
-
-static	int ncr_delta (u_long from, u_long to)
-{
-	if (!from) return (-1);
-	if (!to) return (-2);
-	return ((to  - from) * 1000 / HZ );
-}
-#else
-
 /*
 **	Compute the difference in jiffies ticks.
 */
 
 #define ncr_delta(from, to) \
 	( ((to) && (from))? (to) - (from) : -1 )
-#endif
 
 #define PROFILE  cp->phys.header.stamp
 static	void ncb_profile (ncb_p np, ccb_p cp)
@@ -7634,10 +7983,6 @@
 **----------------------------------------------------------
 */
 
-#ifndef NCR_CLOCK
-#	define NCR_CLOCK 40
-#endif /* NCR_CLOCK */
-
 /*
  *	calculate NCR SCSI clock frequency (in KHz)
  */
@@ -7687,67 +8032,114 @@
 	return ms ? ((1 << gen) * 4340) / ms : 0;
 }
 
-static void ncr_getclock (ncb_p np)
+/*
+ *	Select NCR SCSI clock frequency
+ */
+static void ncr_selectclock(ncb_p np, u_char scntl3)
+{
+	if (np->multiplier < 2) {
+		OUTB(nc_scntl3,	scntl3);
+		return;
+	}
+
+	if (bootverbose >= 2)
+		printf ("%s: enabling clock multiplier\n", ncr_name(np));
+
+	OUTB(nc_stest1, DBLEN);	   /* Enable clock multiplier		  */
+	if (np->multiplier > 2) {  /* Poll bit 5 of stest4 for quadrupler */
+		int i = 20;
+		while (!(INB(nc_stest4) & 0x20) && --i > 0)
+			DELAY(20);
+		if (!i)
+			printf("%s: the chip cannot lock the frequency\n", ncr_name(np));
+	} else			/* Wait 20 micro-seconds for doubler	*/
+		DELAY(20);
+	OUTB(nc_stest3, 0x20);		/* Halt the scsi clock		*/
+	OUTB(nc_scntl3,	scntl3);
+	OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock multiplier	*/
+	OUTB(nc_stest3, 0x00);		/* Restart scsi clock 		*/
+}
+
+
+/*
+ *	Get/probe NCR SCSI clock frequency
+ */
+static void ncr_getclock (ncb_p np, int mult)
 {
 	unsigned char scntl3 = INB(nc_scntl3);
 	unsigned char stest1 = INB(nc_stest1);
+	unsigned f1;
+
+	np->multiplier = 1;
+	f1 = 40000;
 
 	/*
-	**	Always false, except for 875 with clock doubler selected
-	**	If true, disable clock doubler and assume 40 MHz clock.
+	**	True with 875 or 895 with clock multiplier selected
 	*/
-	if ((stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
-		if (driver_setup.ultra_scsi) {
-			if (bootverbose >= 2)
-				printf ("%s: clock doubler found\n", ncr_name(np));
-			np->uf_doubler	= 1;
-			scntl3		= 5;
-		}
-		else {
+	if (mult > 1 && (stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+		if (bootverbose >= 2)
+			printf ("%s: clock multiplier found\n", ncr_name(np));
+		np->multiplier = mult;
+	}
+
+	/*
+	**	If multiplier not found or scntl3 not 7,5,3,
+	**	reset chip and get frequency from general purpose timer.
+	**	Otherwise trust scntl3 BIOS setting.
+	*/
+	if (np->multiplier != mult || (scntl3 & 7) < 3 || !(scntl3 & 1)) {
+		unsigned f2;
+
+		OUTB(nc_istat, SRST); DELAY(5); OUTB(nc_istat, 0);
+
+		(void) ncrgetfreq (np, 11);	/* throw away first result */
+		f1 = ncrgetfreq (np, 11);
+		f2 = ncrgetfreq (np, 11);
+
+		if (bootverbose)
+			printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
+
+		if (f1 > f2) f1 = f2;		/* trust lower result	*/
+
+		if	(f1 <	45000)		f1 =  40000;
+		else if (f1 <	55000)		f1 =  50000;
+		else				f1 =  80000;
+
+		if (f1 < 80000 && mult > 1) {
 			if (bootverbose >= 2)
-				printf ("%s: disabling clock doubler\n", ncr_name(np));
-			OUTB(nc_stest1, 0);
-			np->sv_scntl3	= 3;	/* Fix scntl3 for next insmod */
-			scntl3		= 3;
+				printf ("%s: clock multiplier assumed\n", ncr_name(np));
+			np->multiplier	= mult;
 		}
 	} else {
-		if ((scntl3 & 7) == 0) {
-			unsigned f1, f2;
-			/* throw away first result */
-			(void) ncrgetfreq (np, 11);
-			f1 = ncrgetfreq (np, 11);
-			f2 = ncrgetfreq (np, 11);
-
-			if (bootverbose)
-				printf ("%s: NCR clock is %uKHz, %uKHz\n", ncr_name(np), f1, f2);
-			if (f1 > f2) f1 = f2;	/* trust lower result	*/
-			if (f1 > 45000) {
-				scntl3 = 5;	/* >45Mhz: assume 80MHz	*/
-			} else {
-				scntl3 = 3;	/* <45Mhz: assume 40MHz	*/
-			}
-		}
+		if	((scntl3 & 7) == 3)	f1 =  40000;
+		else if	((scntl3 & 7) == 5)	f1 =  80000;
+		else 				f1 = 160000;
+
+		f1 /= np->multiplier;
 	}
 
 	/*
-	**	Assume 40 Mhz clock if no dependable value supplied by BIOS.
+	**	Compute controller synchronous parameters.
 	*/
-	if ((scntl3 & 7) < 3) {
-		scntl3 = 3;
-	}
+	f1		*= np->multiplier;
+	np->clock_khz	= f1;
+	np->ns_sync	= 25;
 
-	if (driver_setup.ultra_scsi && ((scntl3 & 0x7) == 0x5)) {
-		np->ns_sync   = 12;
-		np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x40 + (scntl3 & 0x7);
+	if	(f1 >= 160000) {
+		if (driver_setup.ultra_scsi) np->ns_sync = 10;
+		np->rv_scntl3 = 7;
+	}
+	else if	(f1 >= 80000) {
+		if (driver_setup.ultra_scsi) np->ns_sync = 12;
+		np->rv_scntl3 = 5;
 	}
 	else {
-		np->ns_sync   = 25;
-		np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+		np->rv_scntl3 = 3;
 	}
 
-	if (bootverbose) {
+	if (bootverbose > 1) {
 		printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
-			ncr_name(np), INB(nc_scntl3), np->rv_scntl3);
+			ncr_name(np), scntl3, np->rv_scntl3);
 	}
 }
 
@@ -7768,8 +8160,10 @@
 **	CTEST4  0x80
 **		0x80	burst disabled
 **	CTEST5  0x24
-**		0x20	dma fifo 536		(875 only)
-**		0x04	burst len 32/64/128	(875 only)
+**		0x20	large dma fifo 		(875 and 895 only)
+**		0x04	burst len 32/64/128	(875 and 895 only)
+**	GPCNTL	    	general purpose control register
+**	STEST2	0x20	differential mode
 */
 
 static void ncr_save_bios_setting(ncb_p np)
@@ -7780,6 +8174,8 @@
 	np->sv_ctest3	= INB(nc_ctest3) & 0x01;
 	np->sv_ctest4	= INB(nc_ctest4) & 0x80;
 	np->sv_ctest5	= INB(nc_ctest5) & 0x24;
+	np->sv_gpcntl	= INB(nc_gpcntl);
+	np->sv_stest2	= INB(nc_stest2) & 0x20;
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -7855,13 +8251,26 @@
 			driver_setup.default_tags	= val;
 			}
 		else if	(!strncmp(cur, "sync:", 5))
-			driver_setup.default_sync	= val * 1000;
+			driver_setup.default_sync	= val;
 		else if	(!strncmp(cur, "verb:", 5))
 			driver_setup.verbose	= val;
 		else if	(!strncmp(cur, "debug:", 6))
 			driver_setup.debug	= val;
 		else if	(!strncmp(cur, "burst:", 6))
 			driver_setup.burst_max	= val;
+		else if	(!strncmp(cur, "led:", 4))
+			driver_setup.led_pin	= val;
+		else if	(!strncmp(cur, "wide:", 5))
+			driver_setup.max_wide	= val? 1:0;
+		else if	(!strncmp(cur, "settle:", 7))
+			driver_setup.settle_time= val;
+		else if	(!strncmp(cur, "diff:", 5))
+			driver_setup.diff_support= val;
+		else if	(!strncmp(cur, "irqm:", 5))
+			driver_setup.irqm	= val;
+
+		else if	(!strncmp(cur, "safe:", 5) && val)
+			memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
 
 		if ((cur = strchr(cur, ',')) != NULL)
 			++cur;
@@ -7916,19 +8325,24 @@
 
 #define YesNo(y)	y ? 'y' : 'n'
     if (bootverbose >= 2) {
-         printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d\n",
-			 YesNo(driver_setup.disconnection),
-			 YesNo(driver_setup.special_features),
-			 YesNo(driver_setup.ultra_scsi),
-			 driver_setup.default_tags,
-			 driver_setup.default_sync/1000,
-			 driver_setup.burst_max);
-         printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x\n",
-			 YesNo(driver_setup.master_parity),
-			 YesNo(driver_setup.scsi_parity),
-			 YesNo(driver_setup.force_sync_nego),
-			 driver_setup.verbose,
-			 driver_setup.debug);
+         printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+			YesNo(driver_setup.disconnection),
+			YesNo(driver_setup.special_features),
+			YesNo(driver_setup.ultra_scsi),
+			driver_setup.default_tags,
+			driver_setup.default_sync,
+			driver_setup.burst_max,
+			YesNo(driver_setup.max_wide),
+			driver_setup.diff_support);
+         printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x,led:%c,settle:%d,irqm:%d\n",
+			YesNo(driver_setup.master_parity),
+			YesNo(driver_setup.scsi_parity),
+			YesNo(driver_setup.force_sync_nego),
+			driver_setup.verbose,
+			driver_setup.debug,
+			YesNo(driver_setup.led_pin),
+			driver_setup.settle_time,
+			driver_setup.irqm);
    }
 #undef YesNo
 
@@ -8053,13 +8467,6 @@
 		 "            match expected 0x%04x\n",
 		  (unsigned int) device_id, (unsigned int) expected_id );
     
-     if (max_revision != -1 && revision > max_revision) 
-	  printk("ncr53c8xx: warning : revision %d is greater than expected.\n",
-		 (int) revision);
-     else if (min_revision != -1 && revision < min_revision)
-	  printk("ncr53c8xx: warning : revision %d is lower than expected.\n",
-		 (int) revision);
-
      if (io_port && check_region (io_port, 128)) {
 	  printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
 		 (int) io_port, (int) (io_port + 127));
@@ -8142,10 +8549,10 @@
 	       host_data = (struct host_data *) host->hostdata;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
 #   ifdef SCSI_NCR_SHARE_IRQ
-               if (dev_id == &host_data->ncb_data)
+               if (dev_id == host_data->ncb)
 #   endif
 #endif
-	       ncr_intr(&host_data->ncb_data);
+	       ncr_intr(host_data->ncb);
 	  }
      }
 }
@@ -8169,9 +8576,7 @@
 int ncr53c8xx_reset(Scsi_Cmnd *cmd)
 #endif
 {
-#ifdef DEBUG
-printk("ncr53c8xx_reset : reset call\n");
-#endif
+	printk("ncr53c8xx_reset: command pid %lu\n", cmd->pid);
 	return ncr_reset_bus(cmd);
 }
 
@@ -8181,7 +8586,7 @@
 
 int ncr53c8xx_abort(Scsi_Cmnd *cmd)
 {
-printk("ncr53c8xx_abort : abort call\n");
+	printk("ncr53c8xx_abort: command pid %lu\n", cmd->pid);
 	return ncr_abort_command(cmd);
 }
 
@@ -8196,7 +8601,7 @@
      for (host = first_host; host; host = host->next) {
 	  if (host->hostt == the_template) {
 	       host_data = (struct host_data *) host->hostdata;
-	       ncr_detach(&host_data->ncb_data, host->irq);
+	       ncr_detach(host_data->ncb, host->irq);
 	  }
      }
 
@@ -8236,17 +8641,19 @@
 	}
 }
 
-static Scsi_Cmnd *remove_from_waiting_list(ncb_p np, Scsi_Cmnd *cmd)
+static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd)
 {
 	Scsi_Cmnd *wcmd;
 
 	if (!(wcmd = np->waiting_list)) return 0;
 	while (wcmd->next_wcmd) {
 		if (cmd == (Scsi_Cmnd *) wcmd->next_wcmd) {
-			wcmd->next_wcmd = cmd->next_wcmd;
-			cmd->next_wcmd = 0;
+			if (to_remove) {
+				wcmd->next_wcmd = cmd->next_wcmd;
+				cmd->next_wcmd = 0;
+			}
 #ifdef DEBUG_WAITING_LIST
-	printf("%s: cmd %lx removed from waiting list\n", ncr_name(np), (u_long) cmd);
+	printf("%s: cmd %lx retrieved from waiting list\n", ncr_name(np), (u_long) cmd);
 #endif
 			return cmd;
 		}
@@ -8583,11 +8990,7 @@
 **	Copy formatted profile information into the input buffer.
 */
 
-#if 0
-#define to_ms(t) (t)
-#else
 #define to_ms(t) ((t) * 1000 / HZ)
-#endif
 
 static int ncr_host_info(ncb_p np, char *ptr, off_t offset, int len)
 {
@@ -8650,7 +9053,7 @@
 	for (host = first_host; host; host = host->next) {
 		if (host->hostt == the_template && host->host_no == hostno) {
 			host_data = (struct host_data *) host->hostdata;
-			ncb = &host_data->ncb_data;
+			ncb = host_data->ncb;
 			break;
 		}
 	}
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.28/linux/drivers/scsi/ncr53c8xx.h	Mon Jan 13 22:21:41 1997
+++ linux/drivers/scsi/ncr53c8xx.h	Wed Mar  5 10:42:49 1997
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.16e"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.18b"
  
 /*
 **	If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined,
@@ -65,7 +65,7 @@
 **		0x80	burst disabled
 **	CTEST5  0x24	(825a and 875 only)
 **		0x04	burst 128
-**		0x80	dma fifo 536
+**		0x80	large dma fifo
 **
 **	If SCSI_NCR_TRUST_BIOS_SETTING is defined, the driver will use the 
 **	initial value of corresponding bit fields, assuming they have been 
@@ -146,18 +146,12 @@
 */
 
 /*
- * For Ultra SCSI support option, use special features and allow 20Mhz 
+ * For Ultra2 SCSI support option, use special features and allow 40Mhz 
  * synchronous data transfers.
  */
-#if	1 /* CONFIG_SCSI_NCR53C8XX_ULTRA_SUPPORT */
 #define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(1)
-#define SCSI_NCR_SETUP_ULTRA_SCSI		(1)
-#define SCSI_NCR_MAX_SYNC (20000)
-#else
-#define	SCSI_NCR_SETUP_SPECIAL_FEATURES		(0)
-#define SCSI_NCR_SETUP_ULTRA_SCSI		(0)
-#define SCSI_NCR_MAX_SYNC (10000)
-#endif
+#define SCSI_NCR_SETUP_ULTRA_SCSI		(2)
+#define SCSI_NCR_MAX_SYNC			(40)
 
 /*
  * Allow tags from 2 to 12, default 4
@@ -193,20 +187,24 @@
 
 /*
  * Sync transfer frequency at startup.
- * Allow from 5Mhz to 20Mhz default 10 Mhz.
+ * Allow from 5Mhz to 40Mhz default 10 Mhz.
  */
-#ifdef	CONFIG_SCSI_NCR53C8XX_SYNC
-#if	CONFIG_SCSI_NCR53C8XX_SYNC == 0
-#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(0)
-#elif	CONFIG_SCSI_NCR53C8XX_SYNC*1000 < 5000
-#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(5000)
-#elif	CONFIG_SCSI_NCR53C8XX_SYNC*1000 > SCSI_NCR_MAX_SYNC
+#ifndef	CONFIG_SCSI_NCR53C8XX_SYNC
+#define	CONFIG_SCSI_NCR53C8XX_SYNC	(5)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC > SCSI_NCR_MAX_SYNC
 #define	SCSI_NCR_SETUP_DEFAULT_SYNC	SCSI_NCR_MAX_SYNC
-#else
-#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(CONFIG_SCSI_NCR53C8XX_SYNC * 1000)
 #endif
+
+#if	CONFIG_SCSI_NCR53C8XX_SYNC == 0
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(255)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 5
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(50)
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 20
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(250/(CONFIG_SCSI_NCR53C8XX_SYNC))
+#elif	CONFIG_SCSI_NCR53C8XX_SYNC <= 33
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(11)
 #else
-#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(10000)
+#define	SCSI_NCR_SETUP_DEFAULT_SYNC	(10)
 #endif
 
 /*
@@ -214,7 +212,7 @@
  */
 #ifdef	CONFIG_SCSI_FORCE_ASYNCHRONOUS
 #undef	SCSI_NCR_SETUP_DEFAULT_SYNC
-#define SCSI_NCR_SETUP_DEFAULT_SYNC	(0)
+#define SCSI_NCR_SETUP_DEFAULT_SYNC	(255)
 #endif
 
 /*
@@ -254,6 +252,22 @@
 #endif
 
 /*
+ * Vendor specific stuff
+ */
+#ifdef CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
+#define SCSI_NCR_SETUP_LED_PIN		(1)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT	(3)
+#else
+#define SCSI_NCR_SETUP_LED_PIN		(0)
+#define SCSI_NCR_SETUP_DIFF_SUPPORT	(0)
+#endif
+
+/*
+ * Settle time after reset at boot-up
+ */
+#define SCSI_NCR_SETUP_SETTLE_TIME	(2)
+
+/*
 **	Other parameters not configurable with "make config"
 **	Avoid to change these constants, unless you know what you are doing.
 */
@@ -262,7 +276,6 @@
 #define SCSI_NCR_MAX_SCATTER	(128)
 #define SCSI_NCR_MAX_TARGET	(16)
 #define SCSI_NCR_MAX_HOST	(2)
-#define SCSI_NCR_SETTLE_TIME	(2)
 #define SCSI_NCR_TIMEOUT_ALERT	(3*HZ)
 
 #define SCSI_NCR_CAN_QUEUE	(7*SCSI_NCR_MAX_TAGS)
@@ -293,9 +306,38 @@
 	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
 	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
 	0x00,					\
-	7					\
+	7,					\
+	SCSI_NCR_SETUP_LED_PIN,			\
+	1,					\
+	SCSI_NCR_SETUP_SETTLE_TIME,		\
+	SCSI_NCR_SETUP_DIFF_SUPPORT,		\
+	0					\
 }
 
+/*
+**	Boot fail safe setup.
+**	Override initial setup from boot command line:
+**	ncr53c8xx=safe:y
+*/
+#define SCSI_NCR_DRIVER_SAFE_SETUP		\
+{						\
+	0,					\
+	1,					\
+	0,					\
+	0,					\
+	0,					\
+	0,					\
+	2,					\
+	0,					\
+	255,					\
+	0x00,					\
+	255,					\
+	0,					\
+	0,					\
+	10,					\
+	1,					\
+	1					\
+}
 
 /*
 **	Define Scsi_Host_Template parameters
@@ -670,7 +712,8 @@
 	#define   CSF    0x02	/* c: clear scsi fifo */
 
 /*50*/  u_short   nc_sidl;	/* Lowlevel: latched from scsi data */
-/*52*/  u_short   nc_52_;
+/*52*/  u_char    nc_stest4;
+/*53*/	u_char    nc_53_;
 /*54*/  u_short   nc_sodl;	/* Lowlevel: data out to scsi data  */
 /*56*/  u_short   nc_56_;
 /*58*/  u_short   nc_sbdl;	/* Lowlevel: data from scsi data    */
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/qlogicpti.c linux/drivers/scsi/qlogicpti.c
--- v2.1.28/linux/drivers/scsi/qlogicpti.c	Fri Dec 13 01:37:37 1996
+++ linux/drivers/scsi/qlogicpti.c	Wed Mar  5 17:04:33 1997
@@ -618,7 +618,8 @@
 
 			/* Setup the reg property for this device. */
 			prom_apply_sbus_ranges(qpti->qdev->my_bus,
-					       qpti->qdev->reg_addrs, 1);
+					       qpti->qdev->reg_addrs,
+					       1, qpti->qdev);
 
 			/* Map in Qlogic,ISP regs and the PTI status reg. */
 			qpti->qregs = qregs = (struct qlogicpti_regs *)
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.28/linux/drivers/scsi/scsi.c	Fri Feb  7 04:02:08 1997
+++ linux/drivers/scsi/scsi.c	Mon Mar 10 12:35:39 1997
@@ -282,6 +282,7 @@
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
 {"CANON","IPUBJD","*", BLIST_SPARSELUN},
 {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 /*
  * Must be at end of list...
  */
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v2.1.28/linux/drivers/scsi/t128.c	Tue Apr  9 04:25:38 1996
+++ linux/drivers/scsi/t128.c	Thu Mar  6 10:23:04 1997
@@ -214,13 +214,15 @@
 	else 
 	    for (; !base && (current_base < NO_BASES); ++current_base) {
 #if (TDEBUG & TDEBUG_INIT)
-    printk("scsi : probing address %08x\n", (unsigned int) bases[current_base].address);
+    printk("scsi-t128 : probing address %08x\n", (unsigned int) bases[current_base].address);
 #endif
 		for (sig = 0; sig < NO_SIGNATURES; ++sig) 
-		    if (!bases[current_base].noauto && !memcmp 
-			(bases[current_base].address + signatures[sig].offset, 
-			signatures[sig].string, strlen(signatures[sig].string))) {
-			base = bases[current_base].address;
+		    if (!bases[current_base].noauto && 
+			check_signature(bases[current_base].address +
+					signatures[sig].offset,
+					signatures[sig].string,
+					strlen(signatures[sig].string))) {
+		      base = bases[current_base].address;
 #if (TDEBUG & TDEBUG_INIT)
 			printk("scsi-t128 : detected board.\n");
 #endif
diff -u --recursive --new-file v2.1.28/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.1.28/linux/drivers/scsi/wd7000.c	Sun Feb  2 05:34:32 1997
+++ linux/drivers/scsi/wd7000.c	Thu Mar  6 10:23:04 1997
@@ -1300,8 +1300,9 @@
 		        break;
 
 		if ((i == pass) &&
-		    !memcmp ((void *) (wd7000_biosaddr[biosaddr_ptr] +
-		             signatures[sig_ptr].ofs), signatures[sig_ptr].sig,
+		    check_signature(wd7000_biosaddr[biosaddr_ptr] +
+		             signatures[sig_ptr].ofs,
+			     signatures[sig_ptr].sig,
 		             signatures[sig_ptr].len))
 		    goto bios_matched;
 	    }
diff -u --recursive --new-file v2.1.28/linux/fs/proc/Makefile linux/fs/proc/Makefile
--- v2.1.28/linux/fs/proc/Makefile	Tue Mar  4 10:25:26 1997
+++ linux/fs/proc/Makefile	Fri Mar  7 12:51:44 1997
@@ -9,7 +9,7 @@
 
 O_TARGET := proc.o
 O_OBJS   := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
-		kmsg.o net.o scsi.o proc_tty.o
+		kmsg.o scsi.o proc_tty.o
 OX_OBJS  := procfs_syms.o
 M_OBJS   := 
 
diff -u --recursive --new-file v2.1.28/linux/fs/proc/generic.c linux/fs/proc/generic.c
--- v2.1.28/linux/fs/proc/generic.c	Tue Mar  4 10:25:26 1997
+++ linux/fs/proc/generic.c	Fri Mar  7 12:51:44 1997
@@ -41,7 +41,7 @@
  * proc files can do almost nothing..
  */
 struct inode_operations proc_file_inode_operations = {
-    &proc_file_operations,  /* default scsi directory file-ops */
+    &proc_file_operations,  /* default proc file-ops */
     NULL,	    /* create	   */
     NULL,	    /* lookup	   */
     NULL,	    /* link	   */
@@ -60,6 +60,30 @@
     NULL	    /* permission  */
 };
 
+/*
+ * compatibility to replace fs/proc/net.c
+ */
+struct inode_operations proc_net_inode_operations = {
+	&proc_file_operations,	/* default net file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+
 #ifndef MIN
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
@@ -72,7 +96,8 @@
 {
 	char 	*page;
 	int	retval=0;
-	int	n;
+	int	eof=0;
+	int	n, count;
 	char	*start;
 	struct proc_dir_entry * dp;
 
@@ -82,10 +107,11 @@
 	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 		return -ENOMEM;
 
-	while (nbytes > 0)
+	while ((nbytes > 0) && !eof)
 	{
-		n = MIN(PROC_BLOCK_SIZE, nbytes);
+		count = MIN(PROC_BLOCK_SIZE, nbytes);
 
+		start = NULL;
 		if (dp->get_info) {
 			/*
 			 * Handle backwards compatibility with the old net
@@ -94,14 +120,27 @@
 			 * XXX What gives with the file->f_flags & O_ACCMODE
 			 * test?  Seems stupid to me....
 			 */
-			n = dp->get_info(page, &start, file->f_pos, n,
+			n = dp->get_info(page, &start, file->f_pos, count,
 				 (file->f_flags & O_ACCMODE) == O_RDWR);
+			if (n < count)
+				eof = 1;
 		} else if (dp->read_proc) {
 			n = dp->read_proc(page, &start, file->f_pos,
-					  n, dp->data);
+					  count, &eof, dp->data);
 		} else
 			break;
 			
+		if (!start) {
+			/*
+			 * For proc files that are less than 4k
+			 */
+			start = page + file->f_pos;
+			n -= file->f_pos;
+			if (n <= 0)
+				break;
+			if (n > count)
+				n = count;
+		}
 		if (n == 0)
 			break;	/* End of file */
 		if (n < 0) {
@@ -131,13 +170,10 @@
 		const char * buffer, unsigned long count)
 {
 	struct proc_dir_entry * dp;
-	char 	*page;
 	
 	if (count < 0)
 		return -EINVAL;
 	dp = (struct proc_dir_entry *) inode->u.generic_ip;
-	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
-		return -ENOMEM;
 
 	if (!dp->write_proc)
 		return -EIO;
@@ -146,7 +182,6 @@
 }
 
 
-
 static long long proc_file_lseek(struct inode * inode, struct file * file, 
 				 long long offset, int orig)
 {
@@ -164,10 +199,50 @@
     }
 }
 
+/*
+ * This function parses a name such as "tty/driver/serial", and
+ * returns the struct proc_dir_entry for "/proc/tty/driver", and
+ * returns "serial" in residual.
+ */
+static int xlate_proc_name(const char *name,
+			   struct proc_dir_entry **ret, const char **residual)
+{
+	const char     		*cp = name, *next;
+	struct proc_dir_entry	*de;
+	int			len;
+
+	de = &proc_root;
+	while (1) {
+		next = strchr(cp, '/');
+		if (!next)
+			break;
+
+		len = next - cp;
+		for (de = de->subdir; de ; de = de->next) {
+			if (proc_match(len, cp, de))
+				break;
+		}
+		if (!de)
+			return -ENOENT;
+		cp += len + 1;
+	}
+	*residual = cp;
+	*ret = de;
+	return 0;
+}
+
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 					 struct proc_dir_entry *parent)
 {
 	struct proc_dir_entry *ent;
+	const char *fn;
+
+	if (parent)
+		fn = name;
+	else {
+		if (xlate_proc_name(name, &parent, &fn))
+			return NULL;
+	}
 
 	ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
 	if (!ent)
@@ -179,17 +254,37 @@
 	else if (mode == 0)
 		mode = S_IFREG | S_IRUGO;
 	
-	ent->name = name;
-	ent->namelen = strlen(ent->name);
+	ent->name = fn;
+	ent->namelen = strlen(fn);
 	ent->mode = mode;
 	if (S_ISDIR(mode)) 
 		ent->nlink = 2;
 	else
 		ent->nlink = 1;
 
-	if (parent)
-		proc_register(parent, ent);
+	proc_register(parent, ent);
 	
 	return ent;
 }
 
+void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *de;
+	const char *fn;
+	int len;
+
+	if (parent)
+		fn = name;
+	else 
+		if (xlate_proc_name(name, &parent, &fn))
+			return;
+	len = strlen(fn);
+
+	for (de = parent->subdir; de ; de = de->next) {
+		if (proc_match(len, fn, de))
+			break;
+	}
+	if (de)
+		proc_unregister(parent, de->low_ino);
+	kfree(de);
+}
diff -u --recursive --new-file v2.1.28/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.28/linux/fs/proc/inode.c	Tue Mar  4 10:25:26 1997
+++ linux/fs/proc/inode.c	Fri Mar  7 12:51:44 1997
@@ -121,7 +121,6 @@
 struct super_block *proc_read_super(struct super_block *s,void *data, 
 				    int silent)
 {
-	proc_root_init();
 	lock_super(s);
 	s->s_blocksize = 1024;
 	s->s_blocksize_bits = 10;
diff -u --recursive --new-file v2.1.28/linux/fs/proc/proc_tty.c linux/fs/proc/proc_tty.c
--- v2.1.28/linux/fs/proc/proc_tty.c	Tue Mar  4 10:25:26 1997
+++ linux/fs/proc/proc_tty.c	Fri Mar  7 12:51:44 1997
@@ -19,20 +19,20 @@
 
 
 static int tty_drivers_read_proc(char *page, char **start, off_t off,
-				 int count, void *data);
+				 int count, int *eof, void *data);
 static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
-				int count, void *data);
+				int count, int *eof, void *data);
 
 /*
  * The /proc/tty directory inodes...
  */
-static struct proc_dir_entry *proc_tty, *proc_tty_ldisc, *proc_tty_driver;
+static struct proc_dir_entry *proc_tty_ldisc, *proc_tty_driver;
 
 /*
  * This is the handler for /proc/tty/drivers
  */
 static int tty_drivers_read_proc(char *page, char **start, off_t off,
-				 int count, void *data)
+				 int count, int *eof, void *data)
 {
 	int	len = 0;
 	off_t	begin = 0;
@@ -87,6 +87,8 @@
 			len = 0;
 		}
 	}
+	if (!p)
+		*eof = 1;
 	if (off >= len+begin)
 		return 0;
 	*start = page + (begin-off);
@@ -97,7 +99,7 @@
  * This is the handler for /proc/tty/ldiscs
  */
 static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
-				int count, void *data)
+				int count, int *eof, void *data)
 {
 	int	i;
 	int	len = 0;
@@ -115,6 +117,8 @@
 			len = 0;
 		}
 	}
+	if (i >= NR_LDISCS)
+		*eof = 1;
 	if (off >= len+begin)
 		return 0;
 	*start = page + (begin-off);
@@ -168,16 +172,16 @@
 {
 	struct proc_dir_entry *ent;
 	
-	proc_tty = create_proc_entry("tty", S_IFDIR, &proc_root);
-	if (!proc_tty)
+	ent = create_proc_entry("tty", S_IFDIR, 0);
+	if (!ent)
 		return;
-	proc_tty_ldisc = create_proc_entry("ldisc", S_IFDIR, proc_tty);
-	proc_tty_driver = create_proc_entry("driver", S_IFDIR, proc_tty);
+	proc_tty_ldisc = create_proc_entry("tty/ldisc", S_IFDIR, 0);
+	proc_tty_driver = create_proc_entry("tty/driver", S_IFDIR, 0);
 
-	ent = create_proc_entry("ldiscs", 0, proc_tty);
+	ent = create_proc_entry("tty/ldiscs", 0, 0);
 	ent->read_proc = tty_ldiscs_read_proc;
 
-	ent = create_proc_entry("drivers", 0, proc_tty);
+	ent = create_proc_entry("tty/drivers", 0, 0);
 	ent->read_proc = tty_drivers_read_proc;
 }
 
diff -u --recursive --new-file v2.1.28/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v2.1.28/linux/fs/proc/procfs_syms.c	Sun Feb  2 05:18:43 1997
+++ linux/fs/proc/procfs_syms.c	Fri Mar  7 12:51:44 1997
@@ -13,7 +13,6 @@
 extern struct inode_operations proc_scsi_inode_operations;
 
 EXPORT_SYMBOL(proc_register);
-EXPORT_SYMBOL(proc_register_dynamic);
 EXPORT_SYMBOL(proc_unregister);
 EXPORT_SYMBOL(proc_root);
 EXPORT_SYMBOL(proc_get_inode);
diff -u --recursive --new-file v2.1.28/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.28/linux/fs/proc/root.c	Tue Mar  4 10:25:26 1997
+++ linux/fs/proc/root.c	Fri Mar  7 12:51:44 1997
@@ -125,22 +125,7 @@
 	&proc_root, NULL
 };
 
-struct proc_dir_entry proc_net = {
-	PROC_NET, 3, "net",
-	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
-	0, &proc_dir_inode_operations,
-	NULL, NULL,
-	NULL,
-	NULL, NULL	
-};
-
-struct proc_dir_entry proc_scsi = {
-	PROC_SCSI, 4, "scsi",
-	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
-	0, &proc_dir_inode_operations,
-	NULL, NULL,
-	NULL, &proc_root, NULL
-};
+struct proc_dir_entry *proc_net, *proc_scsi;
 
 #ifdef CONFIG_MCA
 struct proc_dir_entry proc_mca = {
@@ -358,18 +343,6 @@
 	return -EINVAL;
 }	
 
-int proc_register_dynamic(struct proc_dir_entry * dir,
-			  struct proc_dir_entry * dp)
-{
-	/*
-	 * Make sure we use a dynamically allocated inode.
-	 * In the future, all procedures should just call
-	 * proc_register....
-	 */
-	dp->low_ino = 0;
-	return proc_register(dir, dp);
-}
-
 /*
  * /proc/self:
  */
@@ -567,11 +540,6 @@
 
 void proc_root_init(void)
 {
-	static int done = 0;
-
-	if (done)
-		return;
-	done = 1;
 	proc_base_init();
 	proc_register(&proc_root, &proc_root_loadavg);
 	proc_register(&proc_root, &proc_root_uptime);
@@ -586,8 +554,8 @@
 #endif
 	proc_register(&proc_root, &proc_root_cpuinfo);
 	proc_register(&proc_root, &proc_root_self);
-	proc_register(&proc_root, &proc_net);
-	proc_register(&proc_root, &proc_scsi);
+	proc_net = create_proc_entry("net", S_IFDIR, 0);
+	proc_scsi = create_proc_entry("scsi", S_IFDIR, 0);
 	proc_register(&proc_root, &proc_sys_root);
 #ifdef CONFIG_MCA
 	proc_register(&proc_root, &proc_mca);
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/asm_offsets.h linux/include/asm-sparc/asm_offsets.h
--- v2.1.28/linux/include/asm-sparc/asm_offsets.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/asm_offsets.h	Wed Mar  5 17:04:33 1997
@@ -0,0 +1,258 @@
+/* Automatically generated. Do not edit. */
+#ifndef __ASM_OFFSETS_H__
+#define __ASM_OFFSETS_H__
+
+#define AOFF_task_state	0x00000000
+#define ASIZ_task_state	0x00000004
+#define AOFF_task_counter	0x00000004
+#define ASIZ_task_counter	0x00000004
+#define AOFF_task_priority	0x00000008
+#define ASIZ_task_priority	0x00000004
+#define AOFF_task_signal	0x0000000c
+#define ASIZ_task_signal	0x00000004
+#define AOFF_task_blocked	0x00000010
+#define ASIZ_task_blocked	0x00000004
+#define AOFF_task_flags	0x00000014
+#define ASIZ_task_flags	0x00000004
+#define AOFF_task_errno	0x00000018
+#define ASIZ_task_errno	0x00000004
+#define AOFF_task_debugreg	0x0000001c
+#define ASIZ_task_debugreg	0x00000020
+#define AOFF_task_exec_domain	0x0000003c
+#define ASIZ_task_exec_domain	0x00000004
+#define AOFF_task_binfmt	0x00000040
+#define ASIZ_task_binfmt	0x00000004
+#define AOFF_task_next_task	0x00000044
+#define ASIZ_task_next_task	0x00000004
+#define AOFF_task_prev_task	0x00000048
+#define ASIZ_task_prev_task	0x00000004
+#define AOFF_task_next_run	0x0000004c
+#define ASIZ_task_next_run	0x00000004
+#define AOFF_task_prev_run	0x00000050
+#define ASIZ_task_prev_run	0x00000004
+#define AOFF_task_saved_kernel_stack	0x00000054
+#define ASIZ_task_saved_kernel_stack	0x00000004
+#define AOFF_task_kernel_stack_page	0x00000058
+#define ASIZ_task_kernel_stack_page	0x00000004
+#define AOFF_task_exit_code	0x0000005c
+#define ASIZ_task_exit_code	0x00000004
+#define AOFF_task_exit_signal	0x00000060
+#define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_personality	0x00000064
+#define ASIZ_task_personality	0x00000004
+#define AOFF_task_pid	0x0000006c
+#define ASIZ_task_pid	0x00000004
+#define AOFF_task_pgrp	0x00000070
+#define ASIZ_task_pgrp	0x00000004
+#define AOFF_task_tty_old_pgrp	0x00000074
+#define ASIZ_task_tty_old_pgrp	0x00000004
+#define AOFF_task_session	0x00000078
+#define ASIZ_task_session	0x00000004
+#define AOFF_task_leader	0x0000007c
+#define ASIZ_task_leader	0x00000004
+#define AOFF_task_ngroups	0x00000080
+#define ASIZ_task_ngroups	0x00000004
+#define AOFF_task_groups	0x00000084
+#define ASIZ_task_groups	0x00000040
+#define AOFF_task_p_opptr	0x000000c4
+#define ASIZ_task_p_opptr	0x00000004
+#define AOFF_task_p_pptr	0x000000c8
+#define ASIZ_task_p_pptr	0x00000004
+#define AOFF_task_p_cptr	0x000000cc
+#define ASIZ_task_p_cptr	0x00000004
+#define AOFF_task_p_ysptr	0x000000d0
+#define ASIZ_task_p_ysptr	0x00000004
+#define AOFF_task_p_osptr	0x000000d4
+#define ASIZ_task_p_osptr	0x00000004
+#define AOFF_task_wait_chldexit	0x000000d8
+#define ASIZ_task_wait_chldexit	0x00000004
+#define AOFF_task_uid	0x000000dc
+#define ASIZ_task_uid	0x00000002
+#define AOFF_task_euid	0x000000de
+#define ASIZ_task_euid	0x00000002
+#define AOFF_task_suid	0x000000e0
+#define ASIZ_task_suid	0x00000002
+#define AOFF_task_fsuid	0x000000e2
+#define ASIZ_task_fsuid	0x00000002
+#define AOFF_task_gid	0x000000e4
+#define ASIZ_task_gid	0x00000002
+#define AOFF_task_egid	0x000000e6
+#define ASIZ_task_egid	0x00000002
+#define AOFF_task_sgid	0x000000e8
+#define ASIZ_task_sgid	0x00000002
+#define AOFF_task_fsgid	0x000000ea
+#define ASIZ_task_fsgid	0x00000002
+#define AOFF_task_timeout	0x000000ec
+#define ASIZ_task_timeout	0x00000004
+#define AOFF_task_policy	0x000000f0
+#define ASIZ_task_policy	0x00000004
+#define AOFF_task_rt_priority	0x000000f4
+#define ASIZ_task_rt_priority	0x00000004
+#define AOFF_task_it_real_value	0x000000f8
+#define ASIZ_task_it_real_value	0x00000004
+#define AOFF_task_it_prof_value	0x000000fc
+#define ASIZ_task_it_prof_value	0x00000004
+#define AOFF_task_it_virt_value	0x00000100
+#define ASIZ_task_it_virt_value	0x00000004
+#define AOFF_task_it_real_incr	0x00000104
+#define ASIZ_task_it_real_incr	0x00000004
+#define AOFF_task_it_prof_incr	0x00000108
+#define ASIZ_task_it_prof_incr	0x00000004
+#define AOFF_task_it_virt_incr	0x0000010c
+#define ASIZ_task_it_virt_incr	0x00000004
+#define AOFF_task_real_timer	0x00000110
+#define ASIZ_task_real_timer	0x00000014
+#define AOFF_task_utime	0x00000124
+#define ASIZ_task_utime	0x00000004
+#define AOFF_task_stime	0x00000128
+#define ASIZ_task_stime	0x00000004
+#define AOFF_task_cutime	0x0000012c
+#define ASIZ_task_cutime	0x00000004
+#define AOFF_task_cstime	0x00000130
+#define ASIZ_task_cstime	0x00000004
+#define AOFF_task_start_time	0x00000134
+#define ASIZ_task_start_time	0x00000004
+#define AOFF_task_min_flt	0x00000138
+#define ASIZ_task_min_flt	0x00000004
+#define AOFF_task_maj_flt	0x0000013c
+#define ASIZ_task_maj_flt	0x00000004
+#define AOFF_task_nswap	0x00000140
+#define ASIZ_task_nswap	0x00000004
+#define AOFF_task_cmin_flt	0x00000144
+#define ASIZ_task_cmin_flt	0x00000004
+#define AOFF_task_cmaj_flt	0x00000148
+#define ASIZ_task_cmaj_flt	0x00000004
+#define AOFF_task_cnswap	0x0000014c
+#define ASIZ_task_cnswap	0x00000004
+#define AOFF_task_swap_address	0x00000154
+#define ASIZ_task_swap_address	0x00000004
+#define AOFF_task_old_maj_flt	0x00000158
+#define ASIZ_task_old_maj_flt	0x00000004
+#define AOFF_task_dec_flt	0x0000015c
+#define ASIZ_task_dec_flt	0x00000004
+#define AOFF_task_swap_cnt	0x00000160
+#define ASIZ_task_swap_cnt	0x00000004
+#define AOFF_task_rlim	0x00000164
+#define ASIZ_task_rlim	0x00000050
+#define AOFF_task_used_math	0x000001b4
+#define ASIZ_task_used_math	0x00000002
+#define AOFF_task_comm	0x000001b6
+#define ASIZ_task_comm	0x00000010
+#define AOFF_task_link_count	0x000001c8
+#define ASIZ_task_link_count	0x00000004
+#define AOFF_task_tty	0x000001cc
+#define ASIZ_task_tty	0x00000004
+#define AOFF_task_semundo	0x000001d0
+#define ASIZ_task_semundo	0x00000004
+#define AOFF_task_semsleeping	0x000001d4
+#define ASIZ_task_semsleeping	0x00000004
+#define AOFF_task_ldt	0x000001d8
+#define ASIZ_task_ldt	0x00000004
+#define AOFF_task_tss	0x000001e0
+#define ASIZ_task_tss	0x000003a0
+#define AOFF_task_fs	0x00000580
+#define ASIZ_task_fs	0x00000004
+#define AOFF_task_files	0x00000584
+#define ASIZ_task_files	0x00000004
+#define AOFF_task_mm	0x00000588
+#define ASIZ_task_mm	0x00000004
+#define AOFF_task_sig	0x0000058c
+#define ASIZ_task_sig	0x00000004
+#define AOFF_task_processor	0x00000590
+#define ASIZ_task_processor	0x00000004
+#define AOFF_task_last_processor	0x00000594
+#define ASIZ_task_last_processor	0x00000004
+#define AOFF_task_lock_depth	0x00000598
+#define ASIZ_task_lock_depth	0x00000004
+#define AOFF_mm_count	0x00000000
+#define ASIZ_mm_count	0x00000004
+#define AOFF_mm_pgd	0x00000004
+#define ASIZ_mm_pgd	0x00000004
+#define AOFF_mm_context	0x00000008
+#define ASIZ_mm_context	0x00000004
+#define AOFF_mm_start_code	0x0000000c
+#define ASIZ_mm_start_code	0x00000004
+#define AOFF_mm_end_code	0x00000010
+#define ASIZ_mm_end_code	0x00000004
+#define AOFF_mm_start_data	0x00000014
+#define ASIZ_mm_start_data	0x00000004
+#define AOFF_mm_end_data	0x00000018
+#define ASIZ_mm_end_data	0x00000004
+#define AOFF_mm_start_brk	0x0000001c
+#define ASIZ_mm_start_brk	0x00000004
+#define AOFF_mm_brk	0x00000020
+#define ASIZ_mm_brk	0x00000004
+#define AOFF_mm_start_stack	0x00000024
+#define ASIZ_mm_start_stack	0x00000004
+#define AOFF_mm_start_mmap	0x00000028
+#define ASIZ_mm_start_mmap	0x00000004
+#define AOFF_mm_arg_start	0x0000002c
+#define ASIZ_mm_arg_start	0x00000004
+#define AOFF_mm_arg_end	0x00000030
+#define ASIZ_mm_arg_end	0x00000004
+#define AOFF_mm_env_start	0x00000034
+#define ASIZ_mm_env_start	0x00000004
+#define AOFF_mm_env_end	0x00000038
+#define ASIZ_mm_env_end	0x00000004
+#define AOFF_mm_rss	0x0000003c
+#define ASIZ_mm_rss	0x00000004
+#define AOFF_mm_total_vm	0x00000040
+#define ASIZ_mm_total_vm	0x00000004
+#define AOFF_mm_locked_vm	0x00000044
+#define ASIZ_mm_locked_vm	0x00000004
+#define AOFF_mm_def_flags	0x00000048
+#define ASIZ_mm_def_flags	0x00000004
+#define AOFF_mm_mmap	0x0000004c
+#define ASIZ_mm_mmap	0x00000004
+#define AOFF_mm_mmap_avl	0x00000050
+#define ASIZ_mm_mmap_avl	0x00000004
+#define AOFF_mm_mmap_sem	0x00000054
+#define ASIZ_mm_mmap_sem	0x0000000c
+#define AOFF_thread_uwinmask	0x00000000
+#define ASIZ_thread_uwinmask	0x00000004
+#define AOFF_thread_kregs	0x00000004
+#define ASIZ_thread_kregs	0x00000004
+#define AOFF_thread_sig_address	0x00000008
+#define ASIZ_thread_sig_address	0x00000004
+#define AOFF_thread_sig_desc	0x0000000c
+#define ASIZ_thread_sig_desc	0x00000004
+#define AOFF_thread_ksp	0x00000010
+#define ASIZ_thread_ksp	0x00000004
+#define AOFF_thread_kpc	0x00000014
+#define ASIZ_thread_kpc	0x00000004
+#define AOFF_thread_kpsr	0x00000018
+#define ASIZ_thread_kpsr	0x00000004
+#define AOFF_thread_kwim	0x0000001c
+#define ASIZ_thread_kwim	0x00000004
+#define AOFF_thread_fork_kpsr	0x00000020
+#define ASIZ_thread_fork_kpsr	0x00000004
+#define AOFF_thread_fork_kwim	0x00000024
+#define ASIZ_thread_fork_kwim	0x00000004
+#define AOFF_thread_reg_window	0x00000028
+#define ASIZ_thread_reg_window	0x00000200
+#define AOFF_thread_rwbuf_stkptrs	0x00000228
+#define ASIZ_thread_rwbuf_stkptrs	0x00000020
+#define AOFF_thread_w_saved	0x00000248
+#define ASIZ_thread_w_saved	0x00000004
+#define AOFF_thread_float_regs	0x00000250
+#define ASIZ_thread_float_regs	0x00000080
+#define AOFF_thread_fsr	0x000002d0
+#define ASIZ_thread_fsr	0x00000004
+#define AOFF_thread_fpqdepth	0x000002d4
+#define ASIZ_thread_fpqdepth	0x00000004
+#define AOFF_thread_fpqueue	0x000002d8
+#define ASIZ_thread_fpqueue	0x00000080
+#define AOFF_thread_sstk_info	0x00000358
+#define ASIZ_thread_sstk_info	0x00000008
+#define AOFF_thread_flags	0x00000360
+#define ASIZ_thread_flags	0x00000004
+#define AOFF_thread_ex	0x00000368
+#define ASIZ_thread_ex	0x00000010
+#define AOFF_thread_current_ds	0x00000378
+#define ASIZ_thread_current_ds	0x00000004
+#define AOFF_thread_core_exec	0x0000037c
+#define ASIZ_thread_core_exec	0x00000020
+#define AOFF_thread_new_signal	0x0000039c
+#define ASIZ_thread_new_signal	0x00000004
+
+#endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/auxio.h linux/include/asm-sparc/auxio.h
--- v2.1.28/linux/include/asm-sparc/auxio.h	Fri Dec 13 01:37:39 1996
+++ linux/include/asm-sparc/auxio.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: auxio.h,v 1.15 1996/12/06 00:37:11 davem Exp $
+/* $Id: auxio.h,v 1.16 1997/01/31 23:26:05 tdyas Exp $
  * auxio.h:  Definitions and code for the Auxiliary I/O register.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -68,5 +68,14 @@
 	restore_flags(flags);
 }
 #endif /* !(__ASSEMBLY__) */
+
+
+/* AUXIO2 (Power Off Control) */
+extern __volatile__ unsigned char * auxio_power_register;
+
+#define	AUXIO_POWER_DETECT_FAILURE	32
+#define	AUXIO_POWER_CLEAR_FAILURE	2
+#define	AUXIO_POWER_OFF			1
+
 
 #endif /* !(_SPARC_AUXIO_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/bpp.h linux/include/asm-sparc/bpp.h
--- v2.1.28/linux/include/asm-sparc/bpp.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc/bpp.h	Wed Mar  5 17:04:33 1997
@@ -0,0 +1,82 @@
+#ifndef _SPARC_BPP_H
+#define _SPARC_BPP_H
+
+/*
+ * Copyright (c) 1995 Picture Elements
+ *	Stephen Williams
+ *	Gus Baldauf
+ *
+ * Linux/SPARC port by Peter Zaitcev.
+ * Integration into SPARC tree by Tom Dyas.
+ */
+
+#include  <linux/ioctl.h>
+
+/*
+ * This is a driver that supports IEEE Std 1284-1994 communications
+ * with compliant or compatible devices. It will use whatever features
+ * the device supports, prefering those that are typically faster.
+ *
+ * When the device is opened, it is left in COMPATABILITY mode, and
+ * writes work like any printer device. The driver only attempt to
+ * negotiate 1284 modes when needed so that plugs can be pulled,
+ * switch boxes switched, etc., without disrupting things. It will
+ * also leave the device in compatibility mode when closed.
+ */
+
+
+
+/*
+ * This driver also supplies ioctls to manually manipulate the
+ * pins. This is great for testing devices, or writing code to deal
+ * with bizzarro-mode of the ACME Special TurboThingy Plus.
+ *
+ * NOTE: These ioctl currently do not interact well with
+ * read/write. Caveat emptor.
+ *
+ * PUT_PINS allows us to assign the sense of all the pins, including
+ * the data pins if being driven by the host. The GET_PINS returns the
+ * pins that the peripheral drives, including data if appropriate.
+ */
+
+# define BPP_PUT_PINS _IOW('B', 1, int)
+# define BPP_GET_PINS _IOR('B', 2, void)
+# define BPP_PUT_DATA _IOW('B', 3, int)
+# define BPP_GET_DATA _IOR('B', 4, void)
+
+/*
+ * Set the data bus to input mode. Disengage the data bin driver and
+ * be prepared to read values from the peripheral. If the arg is 0,
+ * then revert the bus to output mode.
+ */
+# define BPP_SET_INPUT _IOW('B', 5, int)
+
+/*
+ * These bits apply to the PUT operation...
+ */
+# define BPP_PP_nStrobe   0x0001
+# define BPP_PP_nAutoFd   0x0002
+# define BPP_PP_nInit     0x0004
+# define BPP_PP_nSelectIn 0x0008
+
+/*
+ * These apply to the GET operation, which also reads the current value
+ * of the previously put values. A bit mask of these will be returned
+ * as a bit mask in the return code of the ioctl().
+ */
+# define BPP_GP_nAck   0x0100
+# define BPP_GP_Busy   0x0200
+# define BPP_GP_PError 0x0400
+# define BPP_GP_Select 0x0800
+# define BPP_GP_nFault 0x1000
+
+
+/*
+ * Prototype for the initialization routine.
+ */
+
+#ifdef __KERNEL__
+extern int bpp_init(void);
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/checksum.h linux/include/asm-sparc/checksum.h
--- v2.1.28/linux/include/asm-sparc/checksum.h	Sun Nov 10 13:28:33 1996
+++ linux/include/asm-sparc/checksum.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: checksum.h,v 1.22 1996/11/10 21:28:25 davem Exp $ */
+/* $Id: checksum.h,v 1.25 1997/02/19 15:51:19 jj Exp $ */
 #ifndef __SPARC_CHECKSUM_H
 #define __SPARC_CHECKSUM_H
 
@@ -8,12 +8,16 @@
  *  Copyright(C) 1995 Miguel de Icaza
  *  Copyright(C) 1996 David S. Miller
  *  Copyright(C) 1996 Eddie C. Dost
+ *  Copyright(C) 1997 Jakub Jelinek
  *
  * derived from:
  *	Alpha checksum c-code
  *      ix86 inline assembly
  *      RFC1071 Computing the Internet Checksum
  */
+ 
+#include <asm/uaccess.h>
+#include <asm/cprefix.h>
 
 /* computes the checksum of a memory block at buff, length len,
  * and adds in "sum" (32-bit)
@@ -34,11 +38,84 @@
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
-extern unsigned int csum_partial_copy(char *src, char *dst, int len, int sum);
 
+/* FIXME: Remove these two macros ASAP */
+#define csum_partial_copy(src, dst, len, sum) \
+ 		       csum_partial_copy_nocheck(src,dst,len,sum)
 #define csum_partial_copy_fromuser(s, d, l, w)  \
-                       csum_partial_copy((char *) (s), (d), (l), (w))
+                         csum_partial_copy((char *) (s), (d), (l), (w))
+  
+extern __inline__ unsigned int 
+csum_partial_copy_nocheck (const char *src, char *dst, int len, 
+			   unsigned int sum)
+{
+	register unsigned int ret asm("o0") = (unsigned int)src;
+	register char *d asm("o1") = dst;
+	register int l asm("g1") = len;
+	
+	__asm__ __volatile__ ("
+		call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+		 mov %4, %%g7
+	" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (sum) :
+	"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+	return ret;
+}
 
+extern __inline__ unsigned int 
+csum_partial_copy_from_user(const char *src, char *dst, int len, 
+			    unsigned int sum, int *err)
+  {
+	if (!access_ok (VERIFY_READ, src, len)) {
+		*err = -EFAULT;
+		memset (dst, 0, len);
+		return sum;
+	} else {
+		register unsigned int ret asm("o0") = (unsigned int)src;
+		register char *d asm("o1") = dst;
+		register int l asm("g1") = len;
+		register unsigned int s asm("g7") = sum;
+
+		__asm__ __volatile__ ("
+		.section __ex_table,#alloc
+		.align 4
+		.word 1f,2
+		.previous
+1:
+		call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+		 st %5, [%%sp + 64]
+		" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+		"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+		return ret;
+	}
+  }
+  
+extern __inline__ unsigned int 
+csum_partial_copy_to_user(const char *src, char *dst, int len, 
+			  unsigned int sum, int *err)
+{
+	if (!access_ok (VERIFY_WRITE, dst, len)) {
+		*err = -EFAULT;
+		return sum;
+	} else {
+		register unsigned int ret asm("o0") = (unsigned int)src;
+		register char *d asm("o1") = dst;
+		register int l asm("g1") = len;
+		register unsigned int s asm("g7") = sum;
+
+		__asm__ __volatile__ ("
+		.section __ex_table,#alloc
+		.align 4
+		.word 1f,1
+		.previous
+1:
+		call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+		 st %5, [%%sp + 64]
+		" : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+		"o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+		return ret;
+	}
+}
+  
 /* ihl is always 5 or greater, almost always is 5, and iph is word aligned
  * the majority of the time.
  */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/cprefix.h linux/include/asm-sparc/cprefix.h
--- v2.1.28/linux/include/asm-sparc/cprefix.h	Sat Nov  9 00:29:15 1996
+++ linux/include/asm-sparc/cprefix.h	Wed Mar  5 17:04:33 1997
@@ -11,8 +11,10 @@
 
 #if defined(__svr4__) || defined(__ELF__)
 #define C_LABEL_PREFIX
+#define C_LABEL_STR(name) #name
 #else
 #define C_LABEL_PREFIX _
+#define C_LABEL_STR(name) "_" #name
 #endif
 
 #define CONCAT(a, b) CONCAT2(a, b)
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/fcntl.h linux/include/asm-sparc/fcntl.h
--- v2.1.28/linux/include/asm-sparc/fcntl.h	Sat Nov  9 00:29:24 1996
+++ linux/include/asm-sparc/fcntl.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: fcntl.h,v 1.8 1996/10/27 08:55:26 davem Exp $ */
+/* $Id: fcntl.h,v 1.9 1997/02/04 07:29:20 davem Exp $ */
 #ifndef _SPARC_FCNTL_H
 #define _SPARC_FCNTL_H
 
@@ -8,7 +8,6 @@
 #define O_WRONLY	0x0001
 #define O_RDWR		0x0002
 #define O_ACCMODE	0x0003
-#define O_NDELAY	0x0004
 #define O_APPEND	0x0008
 #define FASYNC		0x0040	/* fcntl, for BSD compatibility */
 #define O_CREAT		0x0200	/* not fcntl */
@@ -16,6 +15,7 @@
 #define O_EXCL		0x0800	/* not fcntl */
 #define O_SYNC		0x2000
 #define O_NONBLOCK	0x4000
+#define O_NDELAY	(0x0004 | O_NONBLOCK)
 #define O_NOCTTY	0x8000	/* not fcntl */
 
 #define F_DUPFD		0	/* dup */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- v2.1.28/linux/include/asm-sparc/oplib.h	Sat Nov  9 00:29:49 1996
+++ linux/include/asm-sparc/oplib.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.12 1996/10/31 06:29:13 davem Exp $
+/* $Id: oplib.h,v 1.13 1997/01/31 00:16:52 tdyas Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -208,7 +208,19 @@
 extern void prom_putsegment(int context, unsigned long virt_addr,
 			    int physical_segment);
 
+
 /* PROM device tree traversal functions... */
+
+#ifdef PROMLIB_INTERNAL
+
+/* Internal version of prom_getchild. */
+extern int __prom_getchild(int parent_node);
+
+/* Internal version of prom_getsibling. */
+extern int __prom_getsibling(int node);
+
+#endif
+
 
 /* Get the child node of the given node, or zero if no child exists. */
 extern int prom_getchild(int parent_node);
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h
--- v2.1.28/linux/include/asm-sparc/processor.h	Mon Dec 30 01:59:59 1996
+++ linux/include/asm-sparc/processor.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.54 1996/12/24 09:19:49 davem Exp $
+/* $Id: processor.h,v 1.57 1997/03/04 16:27:22 jj Exp $
  * include/asm-sparc/processor.h
  *
  * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
@@ -35,12 +35,9 @@
  */
 #define TASK_SIZE	(page_offset)
 
-/* Ok this is hot.  Sparc exception save area. */
-struct exception_struct {
-	unsigned long count;   /* Exception count */
-	unsigned long pc;      /* Callers PC for copy/clear user */
-	unsigned long expc;    /* Where to jump when exception signaled */
-	unsigned long address; /* Saved user base address for transfer */
+struct fpq {
+	unsigned long *insn_addr;
+	unsigned long insn;
 };
 
 /* The Sparc processor specific thread struct. */
@@ -71,16 +68,12 @@
 	unsigned long w_saved;
 
 	/* Floating point regs */
-	unsigned long   float_regs[64] __attribute__ ((aligned (8)));
+	unsigned long   float_regs[32] __attribute__ ((aligned (8)));
 	unsigned long   fsr;
 	unsigned long   fpqdepth;
-	struct fpq {
-		unsigned long *insn_addr;
-		unsigned long insn;
-	} fpqueue[16];
+	struct fpq	fpqueue[16];
 	struct sigstack sstk_info;
 	unsigned long flags;
-	struct exception_struct ex __attribute__ ((aligned (8)));
 	int current_ds;
 	struct exec core_exec;     /* just what it says. */
 	int new_signal;
@@ -104,15 +97,13 @@
 /* w_saved */ \
    0, \
 /* FPU regs */   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
-                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
-                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
 /* FPU status, FPU qdepth, FPU queue */ \
    0,          0,  { { 0, 0, }, }, \
 /* sstk_info */ \
 { 0, 0, }, \
-/* flags,              ex,       current_ds, */ \
-   SPARC_FLAG_KTHREAD, { 0, }, USER_DS, \
+/* flags,              current_ds, */ \
+   SPARC_FLAG_KTHREAD, USER_DS, \
 /* core_exec */ \
 { 0, }, \
 /* new_signal */ \
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/ptrace.h linux/include/asm-sparc/ptrace.h
--- v2.1.28/linux/include/asm-sparc/ptrace.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/ptrace.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: ptrace.h,v 1.24 1997/01/06 06:53:23 davem Exp $ */
+/* $Id: ptrace.h,v 1.25 1997/03/04 16:27:25 jj Exp $ */
 #ifndef _SPARC_PTRACE_H
 #define _SPARC_PTRACE_H
 
@@ -73,36 +73,7 @@
 #define REGWIN_SZ         0x40
 #endif
 
-/* First generic task_struct offsets. sizeof(task_struct)=1568 */
-#define TASK_STATE        0x000
-#define TASK_PRIORITY     0x008
-#define TASK_SIGNAL       0x00c
-#define TASK_BLOCKED      0x010
-#define TASK_FLAGS        0x014
-#define TASK_SAVED_KSTACK 0x054
-#define TASK_KSTACK_PG    0x058
-#define TASK_LOCK_DEPTH   0x618
-
-/* Thread stuff. */
-#define THREAD_UMASK      0x1e0
-#define THREAD_SADDR      0x1e8
-#define THREAD_SDESC      0x1ec
-#define THREAD_KSP        0x1f0
-#define THREAD_KPC        0x1f4
-#define THREAD_KPSR       0x1f8
-#define THREAD_KWIM       0x1fc
-#define THREAD_FORK_KPSR  0x200
-#define THREAD_FORK_KWIM  0x204
-#define THREAD_REG_WINDOW 0x208
-#define THREAD_STACK_PTRS 0x408
-#define THREAD_W_SAVED    0x428
-#define THREAD_FLOAT_REGS 0x430
-#define THREAD_FSR        0x530
-#define THREAD_SIGSTK     0x5b8
-#define THREAD_FLAGS      0x5c0
-#define THREAD_DS         0x5d8
-#define THREAD_MM         0x608
-#define THREAD_MM_CTX     0x008
+#include <asm/asm_offsets.h>
 
 /* These are for pt_regs. */
 #define PT_PSR    0x0
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/sbus.h linux/include/asm-sparc/sbus.h
--- v2.1.28/linux/include/asm-sparc/sbus.h	Sat Nov  9 11:40:09 1996
+++ linux/include/asm-sparc/sbus.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.11 1996/10/31 06:29:12 davem Exp $
+/* $Id: sbus.h,v 1.12 1997/02/04 07:29:21 davem Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -51,7 +51,7 @@
   char linux_name[64];                 /* Name used internally by Linux */
 
   struct linux_prom_registers reg_addrs[PROMREG_MAX];
-  int num_registers;
+  int num_registers, ranges_applied;
 
   struct linux_prom_irqs irqs[PROMINTR_MAX];
   int num_irqs;
@@ -96,6 +96,7 @@
 
 /* Apply promlib probed SBUS ranges to registers. */
 extern void prom_apply_sbus_ranges(struct linux_sbus *sbus, 
-				   struct linux_prom_registers *sbusregs, int nregs);
+				   struct linux_prom_registers *sbusregs,
+				   int nregs, struct linux_sbus_device *sdev);
 
 #endif /* !(_SPARC_SBUS_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/sigcontext.h linux/include/asm-sparc/sigcontext.h
--- v2.1.28/linux/include/asm-sparc/sigcontext.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/sigcontext.h	Wed Mar  5 17:04:33 1997
@@ -1,6 +1,6 @@
-/* $Id: sigcontext.h,v 1.11 1997/01/19 22:32:07 ecd Exp $ */
-#ifndef _ASMsparc_SIGCONTEXT_H
-#define _ASMsparc_SIGCONTEXT_H
+/* $Id: sigcontext.h,v 1.12 1997/03/03 16:51:52 jj Exp $ */
+#ifndef __SPARC_SIGCONTEXT_H
+#define __SPARC_SIGCONTEXT_H
 
 #include <asm/ptrace.h>
 
@@ -43,7 +43,7 @@
 } __siginfo_t;
 
 typedef struct {
-	unsigned   long si_float_regs [64];
+	unsigned   long si_float_regs [32];
 	unsigned   long si_fsr;
 	unsigned   long si_fpqdepth;
 	struct {
@@ -54,4 +54,4 @@
 
 #endif /* !(__ASSEMBLY__) */
 
-#endif /* !(_ASMsparc_SIGCONTEXT_H) */
+#endif /* !(__SPARC_SIGCONTEXT_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/smp_lock.h linux/include/asm-sparc/smp_lock.h
--- v2.1.28/linux/include/asm-sparc/smp_lock.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/smp_lock.h	Wed Mar  5 17:04:33 1997
@@ -46,7 +46,7 @@
 	mov	%%o7, %%g4
 	call	___lock_kernel
 	 ld	[%%g6 + %0], %%g2
-"	: : "i" (TASK_LOCK_DEPTH), "r" (klip), "r" (proc)
+"	: : "i" (AOFF_task_lock_depth), "r" (klip), "r" (proc)
 	: "g2", "g3", "g4", "g7", "memory");
 }
 
@@ -59,7 +59,7 @@
 	mov	%%o7, %%g4
 	call	___unlock_kernel
 	 ld	[%%g6 + %0], %%g2
-"	: : "i" (TASK_LOCK_DEPTH), "r" (klip)
+"	: : "i" (AOFF_task_lock_depth), "r" (klip)
 	: "g2", "g3", "g4", "memory");
 }
 
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/string.h linux/include/asm-sparc/string.h
--- v2.1.28/linux/include/asm-sparc/string.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/string.h	Wed Mar  5 17:04:33 1997
@@ -1,8 +1,9 @@
-/* $Id: string.h,v 1.28 1997/01/15 16:01:54 jj Exp $
+/* $Id: string.h,v 1.30 1997/03/03 17:11:12 jj Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #ifndef __SPARC_STRING_H__
@@ -10,22 +11,26 @@
 
 /* Really, userland/ksyms should not see any of this stuff. */
 
-#if defined(__KERNEL__) && !defined(EXPORT_SYMTAB)
+#ifdef __KERNEL__
+
+extern void __memmove(void *,const void *,__kernel_size_t);
+extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t);
+extern __kernel_size_t __memset(void *,int,__kernel_size_t);
+
+#ifndef EXPORT_SYMTAB
 
 /* First the mem*() things. */
 #define __HAVE_ARCH_BCOPY
 #define __HAVE_ARCH_MEMMOVE
-extern void __memmove(void *,const void *,__kernel_size_t);
-
 #undef memmove
 #define memmove(_to, _from, _n) \
 ({ \
-	__memmove(_to, _from, _n); \
-	_to; \
+	void *_t = (_to); \
+	__memmove(_t, (_from), (_n)); \
+	_t; \
 })
 
 #define __HAVE_ARCH_MEMCPY
-extern void __memcpy(void *,const void *,__kernel_size_t);
 
 extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n)
 {
@@ -59,12 +64,11 @@
  __nonconstant_memcpy((t),(f),(n)))
 
 #define __HAVE_ARCH_MEMSET
-extern void *__memset(void *,int,__kernel_size_t);
 
-extern inline void *__constant_c_and_count_memset(void *s, char c, size_t count)
+extern inline void *__constant_c_and_count_memset(void *s, char c, __kernel_size_t count)
 {
 	extern void *bzero_1page(void *);
-	extern void *__bzero(void *, size_t);
+	extern __kernel_size_t __bzero(void *, __kernel_size_t);
 
 	if(!c) {
 		if(count == 4096)
@@ -77,9 +81,9 @@
 	return s;
 }
 
-extern inline void *__constant_c_memset(void *s, char c, size_t count)
+extern inline void *__constant_c_memset(void *s, char c, __kernel_size_t count)
 {
-	extern void *__bzero(void *, size_t);
+	extern __kernel_size_t __bzero(void *, __kernel_size_t);
 
 	if(!c)
 		__bzero(s, count);
@@ -88,12 +92,18 @@
 	return s;
 }
 
+extern inline void *__nonconstant_memset(void *s, char c, __kernel_size_t count)
+{
+	__memset(s, c, count);
+	return s;
+}
+
 #undef memset
 #define memset(s, c, count) \
 (__builtin_constant_p(c) ? (__builtin_constant_p(count) ? \
                             __constant_c_and_count_memset((s), (c), (count)) : \
                             __constant_c_memset((s), (c), (count))) \
-                         : __memset((s), (c), (count)))
+                          : __nonconstant_memset((s), (c), (count)))
 
 #define __HAVE_ARCH_MEMSCAN
 
@@ -181,7 +191,9 @@
 (__builtin_constant_p(__arg2) ?	\
  __constant_strncmp(__arg0, __arg1, __arg2) : \
  __strncmp(__arg0, __arg1, __arg2))
+ 
+#endif /* EXPORT_SYMTAB */
 
-#endif /* (__KERNEL__) && !(_KSYMS_INTERNEL) */
+#endif /* __KERNEL__ */
 
 #endif /* !(__SPARC_STRING_H__) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/termbits.h linux/include/asm-sparc/termbits.h
--- v2.1.28/linux/include/asm-sparc/termbits.h	Sat Nov  9 11:39:37 1996
+++ linux/include/asm-sparc/termbits.h	Wed Mar  5 17:04:33 1997
@@ -152,6 +152,7 @@
 #define  B230400 0x00010003
 #define  B460800 0x00010004
 #define CIBAUD	  0x000f0000  /* input baud rate (not used) */
+#define CMSPAR	  010000000000		/* mark or space (stick) parity */
 #define CRTSCTS	  0x80000000  /* flow control */
 
 /* c_lflag bits */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h
--- v2.1.28/linux/include/asm-sparc/termios.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/termios.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: termios.h,v 1.24 1996/12/31 22:35:45 davem Exp $ */
+/* $Id: termios.h,v 1.25 1997/03/04 19:36:18 davem Exp $ */
 #ifndef _SPARC_TERMIOS_H
 #define _SPARC_TERMIOS_H
 
@@ -88,7 +88,7 @@
  * Translate a "termio" structure into a "termios". Ugh.
  */
 #define user_termio_to_kernel_termios(termios, termio) \
-do { \
+({ \
 	unsigned short tmp; \
 	get_user(tmp, &(termio)->c_iflag); \
 	(termios)->c_iflag = (0xffff0000 & ((termios)->c_iflag)) | tmp; \
@@ -99,7 +99,8 @@
 	get_user(tmp, &(termio)->c_lflag); \
 	(termios)->c_lflag = (0xffff0000 & ((termios)->c_lflag)) | tmp; \
 	copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \
-} while(0)
+	0; \
+})
 
 /*
  * Translate a "termios" structure into a "termio". Ugh.
@@ -107,7 +108,7 @@
  * Note the "fun" _VMIN overloading.
  */
 #define kernel_termios_to_user_termio(termio, termios) \
-do { \
+({ \
 	put_user((termios)->c_iflag, &(termio)->c_iflag); \
 	put_user((termios)->c_oflag, &(termio)->c_oflag); \
 	put_user((termios)->c_cflag, &(termio)->c_cflag); \
@@ -118,10 +119,11 @@
 		put_user((termios)->c_cc[VMIN], &(termio)->c_cc[_VMIN]); \
 		put_user((termios)->c_cc[VTIME], &(termio)->c_cc[_VTIME]); \
 	} \
-} while(0)
+	0; \
+})
 
 #define user_termios_to_kernel_termios(k, u) \
-do { \
+({ \
 	get_user((k)->c_iflag, &(u)->c_iflag); \
 	get_user((k)->c_oflag, &(u)->c_oflag); \
 	get_user((k)->c_cflag, &(u)->c_cflag); \
@@ -135,10 +137,11 @@
 		get_user((k)->c_cc[VMIN],  &(u)->c_cc[_VMIN]); \
 		get_user((k)->c_cc[VTIME], &(u)->c_cc[_VTIME]); \
 	} \
-} while(0)
+	0; \
+})
 
 #define kernel_termios_to_user_termios(u, k) \
-do { \
+({ \
 	put_user((k)->c_iflag, &(u)->c_iflag); \
 	put_user((k)->c_oflag, &(u)->c_oflag); \
 	put_user((k)->c_cflag, &(u)->c_cflag); \
@@ -152,7 +155,8 @@
 		put_user((k)->c_cc[VEOF], &(u)->c_cc[VEOF]); \
 		put_user((k)->c_cc[VEOL], &(u)->c_cc[VEOL]); \
 	} \
-} while(0)
+	0; \
+})
 
 #endif	/* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/uaccess.h linux/include/asm-sparc/uaccess.h
--- v2.1.28/linux/include/asm-sparc/uaccess.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/uaccess.h	Wed Mar  5 17:04:33 1997
@@ -1,11 +1,12 @@
-/* $Id: uaccess.h,v 1.10 1997/01/16 14:19:03 davem Exp $ */
+/* $Id: uaccess.h,v 1.11 1997/02/06 18:57:10 jj Exp $
+ * uaccess.h: User space memore access functions.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
-/*
- * User space memory access functions
- */
-
 #ifdef __KERNEL__
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -288,14 +289,14 @@
 
 extern int __get_user_bad(void);
 
-extern int __copy_user(unsigned long to, unsigned long from, int size);
+extern __kernel_size_t __copy_user(void *to, void *from, __kernel_size_t size);
 
 #define copy_to_user(to,from,n) ({ \
-unsigned long __copy_to = (unsigned long) (to); \
-unsigned long __copy_size = (unsigned long) (n); \
-unsigned long __copy_res; \
-if(__copy_size && __access_ok(__copy_to, __copy_size)) { \
-__copy_res = __copy_user(__copy_to, (unsigned long) (from), __copy_size); \
+void *__copy_to = (void *) (to); \
+__kernel_size_t __copy_size = (__kernel_size_t) (n); \
+__kernel_size_t __copy_res; \
+if(__copy_size && __access_ok((unsigned long)__copy_to, __copy_size)) { \
+__copy_res = __copy_user(__copy_to, (void *) (from), __copy_size); \
 } else __copy_res = __copy_size; \
 __copy_res; })
 
@@ -305,8 +306,8 @@
 })
 
 #define __copy_to_user(to,from,n)		\
-	__copy_user((unsigned long)(to),	\
-		    (unsigned long)(from), n)
+	__copy_user((void *)(to),		\
+		    (void *)(from), n)
 
 #define __copy_to_user_ret(to,from,n,retval) ({ \
 if (__copy_to_user(to,from,n)) \
@@ -314,11 +315,11 @@
 })
 
 #define copy_from_user(to,from,n) ({ \
-unsigned long __copy_from = (unsigned long) (from); \
-unsigned long __copy_size = (unsigned long) (n); \
-unsigned long __copy_res; \
-if(__copy_size && __access_ok(__copy_from, __copy_size)) { \
-__copy_res = __copy_user((unsigned long) (to), __copy_from, __copy_size); \
+void *__copy_from = (void *) (from); \
+__kernel_size_t __copy_size = (__kernel_size_t) (n); \
+__kernel_size_t __copy_res; \
+if(__copy_size && __access_ok((unsigned long)__copy_from, __copy_size)) { \
+__copy_res = __copy_user((void *) (to), __copy_from, __copy_size); \
 } else __copy_res = __copy_size; \
 __copy_res; })
 
@@ -328,21 +329,37 @@
 })
 
 #define __copy_from_user(to,from,n)		\
-	__copy_user((unsigned long)(to),	\
-		    (unsigned long)(from), n)
+	__copy_user((void *)(to),		\
+		    (void *)(from), n)
 
 #define __copy_from_user_ret(to,from,n,retval) ({ \
 if (__copy_from_user(to,from,n)) \
 	return retval; \
 })
 
-extern int __clear_user(unsigned long addr, int size);
+extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
+{
+  __kernel_size_t ret;
+  __asm__ __volatile__ ("
+	.section __ex_table,#alloc
+	.align 4
+	.word 1f,3
+	.previous
+1:
+	mov %2, %%o1
+	call __bzero
+	 mov %1, %%o0
+	mov %%o0, %0 
+	" : "=r" (ret) : "r" (addr), "r" (size) :
+	"o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+  return ret;
+}
 
 #define clear_user(addr,n) ({ \
-unsigned long __clear_addr = (unsigned long) (addr); \
-int __clear_size = (int) (n); \
-int __clear_res; \
-if(__clear_size && __access_ok(__clear_addr, __clear_size)) { \
+void *__clear_addr = (void *) (addr); \
+__kernel_size_t __clear_size = (__kernel_size_t) (n); \
+__kernel_size_t __clear_res; \
+if(__clear_size && __access_ok((unsigned long)__clear_addr, __clear_size)) { \
 __clear_res = __clear_user(__clear_addr, __clear_size); \
 } else __clear_res = __clear_size; \
 __clear_res; })
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h
--- v2.1.28/linux/include/asm-sparc/unistd.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc/unistd.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: unistd.h,v 1.28 1997/01/26 07:12:06 davem Exp $ */
+/* $Id: unistd.h,v 1.29 1997/02/04 07:14:17 miguel Exp $ */
 #ifndef _SPARC_UNISTD_H
 #define _SPARC_UNISTD_H
 
@@ -479,15 +479,5 @@
 }
 
 #endif /* __KERNEL_SYSCALLS__ */
-
-/* sysconf options, for SunOS compatibility */
-#define   _SC_ARG_MAX             1
-#define   _SC_CHILD_MAX           2
-#define   _SC_CLK_TCK             3
-#define   _SC_NGROUPS_MAX         4
-#define   _SC_OPEN_MAX            5
-#define   _SC_JOB_CONTROL         6
-#define   _SC_SAVED_IDS           7
-#define   _SC_VERSION             8
 
 #endif /* _SPARC_UNISTD_H */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc/winmacro.h linux/include/asm-sparc/winmacro.h
--- v2.1.28/linux/include/asm-sparc/winmacro.h	Sat Nov  9 00:30:28 1996
+++ linux/include/asm-sparc/winmacro.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: winmacro.h,v 1.17 1996/09/19 20:27:44 davem Exp $
+/* $Id: winmacro.h,v 1.18 1997/03/04 16:27:27 jj Exp $
  * winmacro.h: Window loading-unloading macros.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -98,18 +98,18 @@
         STORE_PT_INS(base_reg)
 
 #define SAVE_BOLIXED_USER_STACK(cur_reg, scratch) \
-        ld       [%cur_reg + THREAD_W_SAVED], %scratch; \
+        ld       [%cur_reg + AOFF_task_tss + AOFF_thread_w_saved], %scratch; \
         sll      %scratch, 2, %scratch; \
         add      %scratch, %cur_reg, %scratch; \
-        st       %sp, [%scratch + THREAD_STACK_PTRS]; \
+        st       %sp, [%scratch + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]; \
         sub      %scratch, %cur_reg, %scratch; \
         sll      %scratch, 4, %scratch; \
         add      %scratch, %cur_reg, %scratch; \
-        STORE_WINDOW(scratch + THREAD_REG_WINDOW); \
+        STORE_WINDOW(scratch + AOFF_task_tss + AOFF_thread_reg_window); \
         sub      %scratch, %cur_reg, %scratch; \
         srl      %scratch, 6, %scratch; \
         add      %scratch, 1, %scratch; \
-        st       %scratch, [%cur_reg + THREAD_W_SAVED];
+        st       %scratch, [%cur_reg + AOFF_task_tss + AOFF_thread_w_saved];
 
 #ifdef __SMP__
 #define LOAD_CURRENT(dest_reg, idreg) \
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/asm_offsets.h linux/include/asm-sparc64/asm_offsets.h
--- v2.1.28/linux/include/asm-sparc64/asm_offsets.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/asm_offsets.h	Wed Mar  5 17:04:33 1997
@@ -0,0 +1,246 @@
+/* Automatically generated. Do not edit. */
+#ifndef __ASM_OFFSETS_H__
+#define __ASM_OFFSETS_H__
+
+#define AOFF_task_state	0x00000000
+#define ASIZ_task_state	0x00000008
+#define AOFF_task_counter	0x00000008
+#define ASIZ_task_counter	0x00000008
+#define AOFF_task_priority	0x00000010
+#define ASIZ_task_priority	0x00000008
+#define AOFF_task_signal	0x00000018
+#define ASIZ_task_signal	0x00000008
+#define AOFF_task_blocked	0x00000020
+#define ASIZ_task_blocked	0x00000008
+#define AOFF_task_flags	0x00000028
+#define ASIZ_task_flags	0x00000008
+#define AOFF_task_errno	0x00000030
+#define ASIZ_task_errno	0x00000004
+#define AOFF_task_debugreg	0x00000038
+#define ASIZ_task_debugreg	0x00000040
+#define AOFF_task_exec_domain	0x00000078
+#define ASIZ_task_exec_domain	0x00000008
+#define AOFF_task_binfmt	0x00000080
+#define ASIZ_task_binfmt	0x00000008
+#define AOFF_task_next_task	0x00000088
+#define ASIZ_task_next_task	0x00000008
+#define AOFF_task_prev_task	0x00000090
+#define ASIZ_task_prev_task	0x00000008
+#define AOFF_task_next_run	0x00000098
+#define ASIZ_task_next_run	0x00000008
+#define AOFF_task_prev_run	0x000000a0
+#define ASIZ_task_prev_run	0x00000008
+#define AOFF_task_saved_kernel_stack	0x000000a8
+#define ASIZ_task_saved_kernel_stack	0x00000008
+#define AOFF_task_kernel_stack_page	0x000000b0
+#define ASIZ_task_kernel_stack_page	0x00000008
+#define AOFF_task_exit_code	0x000000b8
+#define ASIZ_task_exit_code	0x00000004
+#define AOFF_task_exit_signal	0x000000bc
+#define ASIZ_task_exit_signal	0x00000004
+#define AOFF_task_personality	0x000000c0
+#define ASIZ_task_personality	0x00000008
+#define AOFF_task_pid	0x000000cc
+#define ASIZ_task_pid	0x00000004
+#define AOFF_task_pgrp	0x000000d0
+#define ASIZ_task_pgrp	0x00000004
+#define AOFF_task_tty_old_pgrp	0x000000d4
+#define ASIZ_task_tty_old_pgrp	0x00000004
+#define AOFF_task_session	0x000000d8
+#define ASIZ_task_session	0x00000004
+#define AOFF_task_leader	0x000000dc
+#define ASIZ_task_leader	0x00000004
+#define AOFF_task_ngroups	0x000000e0
+#define ASIZ_task_ngroups	0x00000004
+#define AOFF_task_groups	0x000000e4
+#define ASIZ_task_groups	0x00000040
+#define AOFF_task_p_opptr	0x00000128
+#define ASIZ_task_p_opptr	0x00000008
+#define AOFF_task_p_pptr	0x00000130
+#define ASIZ_task_p_pptr	0x00000008
+#define AOFF_task_p_cptr	0x00000138
+#define ASIZ_task_p_cptr	0x00000008
+#define AOFF_task_p_ysptr	0x00000140
+#define ASIZ_task_p_ysptr	0x00000008
+#define AOFF_task_p_osptr	0x00000148
+#define ASIZ_task_p_osptr	0x00000008
+#define AOFF_task_wait_chldexit	0x00000150
+#define ASIZ_task_wait_chldexit	0x00000008
+#define AOFF_task_uid	0x00000158
+#define ASIZ_task_uid	0x00000002
+#define AOFF_task_euid	0x0000015a
+#define ASIZ_task_euid	0x00000002
+#define AOFF_task_suid	0x0000015c
+#define ASIZ_task_suid	0x00000002
+#define AOFF_task_fsuid	0x0000015e
+#define ASIZ_task_fsuid	0x00000002
+#define AOFF_task_gid	0x00000160
+#define ASIZ_task_gid	0x00000002
+#define AOFF_task_egid	0x00000162
+#define ASIZ_task_egid	0x00000002
+#define AOFF_task_sgid	0x00000164
+#define ASIZ_task_sgid	0x00000002
+#define AOFF_task_fsgid	0x00000166
+#define ASIZ_task_fsgid	0x00000002
+#define AOFF_task_timeout	0x00000168
+#define ASIZ_task_timeout	0x00000008
+#define AOFF_task_policy	0x00000170
+#define ASIZ_task_policy	0x00000008
+#define AOFF_task_rt_priority	0x00000178
+#define ASIZ_task_rt_priority	0x00000008
+#define AOFF_task_it_real_value	0x00000180
+#define ASIZ_task_it_real_value	0x00000008
+#define AOFF_task_it_prof_value	0x00000188
+#define ASIZ_task_it_prof_value	0x00000008
+#define AOFF_task_it_virt_value	0x00000190
+#define ASIZ_task_it_virt_value	0x00000008
+#define AOFF_task_it_real_incr	0x00000198
+#define ASIZ_task_it_real_incr	0x00000008
+#define AOFF_task_it_prof_incr	0x000001a0
+#define ASIZ_task_it_prof_incr	0x00000008
+#define AOFF_task_it_virt_incr	0x000001a8
+#define ASIZ_task_it_virt_incr	0x00000008
+#define AOFF_task_real_timer	0x000001b0
+#define ASIZ_task_real_timer	0x00000028
+#define AOFF_task_utime	0x000001d8
+#define ASIZ_task_utime	0x00000008
+#define AOFF_task_stime	0x000001e0
+#define ASIZ_task_stime	0x00000008
+#define AOFF_task_cutime	0x000001e8
+#define ASIZ_task_cutime	0x00000008
+#define AOFF_task_cstime	0x000001f0
+#define ASIZ_task_cstime	0x00000008
+#define AOFF_task_start_time	0x000001f8
+#define ASIZ_task_start_time	0x00000008
+#define AOFF_task_min_flt	0x00000200
+#define ASIZ_task_min_flt	0x00000008
+#define AOFF_task_maj_flt	0x00000208
+#define ASIZ_task_maj_flt	0x00000008
+#define AOFF_task_nswap	0x00000210
+#define ASIZ_task_nswap	0x00000008
+#define AOFF_task_cmin_flt	0x00000218
+#define ASIZ_task_cmin_flt	0x00000008
+#define AOFF_task_cmaj_flt	0x00000220
+#define ASIZ_task_cmaj_flt	0x00000008
+#define AOFF_task_cnswap	0x00000228
+#define ASIZ_task_cnswap	0x00000008
+#define AOFF_task_swap_address	0x00000238
+#define ASIZ_task_swap_address	0x00000008
+#define AOFF_task_old_maj_flt	0x00000240
+#define ASIZ_task_old_maj_flt	0x00000008
+#define AOFF_task_dec_flt	0x00000248
+#define ASIZ_task_dec_flt	0x00000008
+#define AOFF_task_swap_cnt	0x00000250
+#define ASIZ_task_swap_cnt	0x00000008
+#define AOFF_task_rlim	0x00000258
+#define ASIZ_task_rlim	0x000000a0
+#define AOFF_task_used_math	0x000002f8
+#define ASIZ_task_used_math	0x00000002
+#define AOFF_task_comm	0x000002fa
+#define ASIZ_task_comm	0x00000010
+#define AOFF_task_link_count	0x0000030c
+#define ASIZ_task_link_count	0x00000004
+#define AOFF_task_tty	0x00000310
+#define ASIZ_task_tty	0x00000008
+#define AOFF_task_semundo	0x00000318
+#define ASIZ_task_semundo	0x00000008
+#define AOFF_task_semsleeping	0x00000320
+#define ASIZ_task_semsleeping	0x00000008
+#define AOFF_task_ldt	0x00000328
+#define ASIZ_task_ldt	0x00000008
+#define AOFF_task_tss	0x00000340
+#define ASIZ_task_tss	0x00000700
+#define AOFF_task_fs	0x00000a40
+#define ASIZ_task_fs	0x00000008
+#define AOFF_task_files	0x00000a48
+#define ASIZ_task_files	0x00000008
+#define AOFF_task_mm	0x00000a50
+#define ASIZ_task_mm	0x00000008
+#define AOFF_task_sig	0x00000a58
+#define ASIZ_task_sig	0x00000008
+#define AOFF_task_processor	0x00000a60
+#define ASIZ_task_processor	0x00000004
+#define AOFF_task_last_processor	0x00000a64
+#define ASIZ_task_last_processor	0x00000004
+#define AOFF_task_lock_depth	0x00000a68
+#define ASIZ_task_lock_depth	0x00000004
+#define AOFF_mm_count	0x00000000
+#define ASIZ_mm_count	0x00000004
+#define AOFF_mm_pgd	0x00000008
+#define ASIZ_mm_pgd	0x00000008
+#define AOFF_mm_context	0x00000010
+#define ASIZ_mm_context	0x00000008
+#define AOFF_mm_start_code	0x00000018
+#define ASIZ_mm_start_code	0x00000008
+#define AOFF_mm_end_code	0x00000020
+#define ASIZ_mm_end_code	0x00000008
+#define AOFF_mm_start_data	0x00000028
+#define ASIZ_mm_start_data	0x00000008
+#define AOFF_mm_end_data	0x00000030
+#define ASIZ_mm_end_data	0x00000008
+#define AOFF_mm_start_brk	0x00000038
+#define ASIZ_mm_start_brk	0x00000008
+#define AOFF_mm_brk	0x00000040
+#define ASIZ_mm_brk	0x00000008
+#define AOFF_mm_start_stack	0x00000048
+#define ASIZ_mm_start_stack	0x00000008
+#define AOFF_mm_start_mmap	0x00000050
+#define ASIZ_mm_start_mmap	0x00000008
+#define AOFF_mm_arg_start	0x00000058
+#define ASIZ_mm_arg_start	0x00000008
+#define AOFF_mm_arg_end	0x00000060
+#define ASIZ_mm_arg_end	0x00000008
+#define AOFF_mm_env_start	0x00000068
+#define ASIZ_mm_env_start	0x00000008
+#define AOFF_mm_env_end	0x00000070
+#define ASIZ_mm_env_end	0x00000008
+#define AOFF_mm_rss	0x00000078
+#define ASIZ_mm_rss	0x00000008
+#define AOFF_mm_total_vm	0x00000080
+#define ASIZ_mm_total_vm	0x00000008
+#define AOFF_mm_locked_vm	0x00000088
+#define ASIZ_mm_locked_vm	0x00000008
+#define AOFF_mm_def_flags	0x00000090
+#define ASIZ_mm_def_flags	0x00000008
+#define AOFF_mm_mmap	0x00000098
+#define ASIZ_mm_mmap	0x00000008
+#define AOFF_mm_mmap_avl	0x000000a0
+#define ASIZ_mm_mmap_avl	0x00000008
+#define AOFF_mm_mmap_sem	0x000000a8
+#define ASIZ_mm_mmap_sem	0x00000010
+#define AOFF_thread_float_regs	0x00000000
+#define ASIZ_thread_float_regs	0x00000100
+#define AOFF_thread_fsr	0x00000100
+#define ASIZ_thread_fsr	0x00000008
+#define AOFF_thread_fpqdepth	0x00000108
+#define ASIZ_thread_fpqdepth	0x00000008
+#define AOFF_thread_fpqueue	0x00000110
+#define ASIZ_thread_fpqueue	0x00000100
+#define AOFF_thread_user_globals	0x00000210
+#define ASIZ_thread_user_globals	0x00000040
+#define AOFF_thread_ksp	0x00000250
+#define ASIZ_thread_ksp	0x00000008
+#define AOFF_thread_kpc	0x00000258
+#define ASIZ_thread_kpc	0x00000008
+#define AOFF_thread_reg_window	0x00000260
+#define ASIZ_thread_reg_window	0x00000400
+#define AOFF_thread_rwbuf_stkptrs	0x00000660
+#define ASIZ_thread_rwbuf_stkptrs	0x00000040
+#define AOFF_thread_w_saved	0x000006a0
+#define ASIZ_thread_w_saved	0x00000008
+#define AOFF_thread_flags	0x000006a8
+#define ASIZ_thread_flags	0x00000008
+#define AOFF_thread_sig_address	0x000006b0
+#define ASIZ_thread_sig_address	0x00000008
+#define AOFF_thread_sig_desc	0x000006b8
+#define ASIZ_thread_sig_desc	0x00000008
+#define AOFF_thread_sstk_info	0x000006c0
+#define ASIZ_thread_sstk_info	0x00000010
+#define AOFF_thread_current_ds	0x000006d0
+#define ASIZ_thread_current_ds	0x00000004
+#define AOFF_thread_new_signal	0x000006d4
+#define ASIZ_thread_new_signal	0x00000004
+#define AOFF_thread_core_exec	0x000006d8
+#define ASIZ_thread_core_exec	0x00000020
+
+#endif /* __ASM_OFFSETS_H__ */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/head.h linux/include/asm-sparc64/head.h
--- v2.1.28/linux/include/asm-sparc64/head.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/head.h	Wed Mar  5 17:04:33 1997
@@ -1,13 +1,9 @@
-/* $Id: head.h,v 1.2 1996/12/28 18:39:49 davem Exp $ */
+/* $Id: head.h,v 1.4 1997/02/25 20:00:32 jj Exp $ */
 #ifndef _SPARC64_HEAD_H
 #define _SPARC64_HEAD_H
 
 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop;
 
-#define BTRAP(lvl)
-
-#define BTRAPTL1(lvl)
-
 #define CLEAN_WINDOW							\
 	clr	%o0;	clr	%o1;	clr	%o2;	clr	%o3;	\
 	clr	%o4;	clr	%o5;	clr	%o6;	clr	%o7;	\
@@ -17,24 +13,194 @@
 	wrpr %g1, 0x0, %cleanwin;	retry;				\
 	nop;		nop;		nop;		nop;
 
-#define TRAP(routine)			\
-	b	etrap;			\
-	 rd	%pc, %g7;		\
-	call	routine;		\
-	 add	%sp, REGWIN_SZ, %o0;	\
-	b	rtrap;			\
-	 subcc	%g0, %o0, %g0;		\
-	nop;				\
+#define TRAP(routine)					\
+	b	etrap;					\
+	 rd	%pc, %g7;				\
+	call	routine;				\
+	 add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
+	b	rtrap;					\
+	 subcc	%g0, %o0, %g0;				\
+	nop;						\
 	nop;
 
-#define TRAP_IRQ(routine, level)	\
-	b	etrap;			\
-	 rd	%pc, %g7;		\
-	add	%sp, REGWIN_SZ, %o0;	\
-	call	routine;		\
-	 mov	level, %o1;		\
-	b	rtrap;			\
-	 subcc	%g0, %o0, %g0;		\
+#define TRAP_ARG(routine, arg)				\
+	b	etrap;					\
+	 rd	%pc, %g7;				\
+	add	%sp, STACK_BIAS + REGWIN_SZ, %o0;	\
+	call	routine;				\
+	 mov	arg, %o1;				\
+	b	rtrap;					\
+	 subcc	%g0, %o0, %g0;				\
 	nop;
+
+/* FIXME: Write these actually */	
+#define SUNOS_SYSCALL_TRAP TRAP(sunos_syscall)
+#define	LINUX_32BIT_SYSCALL_TRAP TRAP(linux32_syscall)
+#define LINUX_64BIT_SYSCALL_TRAP TRAP(linux64_syscall)
+#define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall)
+#define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall)
+#define BREAKPOINT_TRAP TRAP(breakpoint_trap)
+#define GETCC_TRAP TRAP(getcc)
+#define SETCC_TRAP TRAP(setcc)
+#define INDIRECT_SOLARIS_SYSCALL(tlvl) TRAP_ARG(indirect_syscall, tlvl)
+
+#define TRAP_IRQ(routine, level) TRAP_ARG(routine, level)
+
+#define BTRAP(lvl) TRAP_ARG(bad_trap, lvl)
+
+#define BTRAPTL1(lvl) TRAP_ARG(bad_trap_tl1, lvl)
+
+#define FLUSH_WINDOW_TRAP					\
+	flushw;							\
+	done; nop; nop; nop; nop; nop; nop;
+	        
+
+/* Normal kernel spill */
+#define SPILL_0_NORMAL					\
+	stx	%l0, [%sp + STACK_BIAS + 0x00];		\
+	stx	%l1, [%sp + STACK_BIAS + 0x08];		\
+	stx	%l2, [%sp + STACK_BIAS + 0x10];		\
+	stx	%l3, [%sp + STACK_BIAS + 0x18];		\
+	stx	%l4, [%sp + STACK_BIAS + 0x20];		\
+	stx	%l5, [%sp + STACK_BIAS + 0x28];		\
+	stx	%l6, [%sp + STACK_BIAS + 0x30];		\
+	stx	%l7, [%sp + STACK_BIAS + 0x38];		\
+	stx	%i0, [%sp + STACK_BIAS + 0x40];		\
+	stx	%i1, [%sp + STACK_BIAS + 0x48];		\
+	stx	%i2, [%sp + STACK_BIAS + 0x50];		\
+	stx	%i3, [%sp + STACK_BIAS + 0x58];		\
+	stx	%i4, [%sp + STACK_BIAS + 0x60];		\
+	stx	%i5, [%sp + STACK_BIAS + 0x68];		\
+	stx	%i6, [%sp + STACK_BIAS + 0x70];		\
+	stx	%i7, [%sp + STACK_BIAS + 0x78];		\
+	saved; retry; nop; nop; nop; nop; nop; nop;	\
+	nop; nop; nop; nop; nop; nop; nop; nop;
+
+/* Normal 64bit spill */
+#define SPILL_1_NORMAL					\
+	wr	%g0, ASI_AIUP, %asi;			\
+	stxa	%l0, [%sp + STACK_BIAS + 0x00] %asi;	\
+	stxa	%l1, [%sp + STACK_BIAS + 0x08] %asi;	\
+	stxa	%l2, [%sp + STACK_BIAS + 0x10] %asi;	\
+	stxa	%l3, [%sp + STACK_BIAS + 0x18] %asi;	\
+	stxa	%l4, [%sp + STACK_BIAS + 0x20] %asi;	\
+	stxa	%l5, [%sp + STACK_BIAS + 0x28] %asi;	\
+	stxa	%l6, [%sp + STACK_BIAS + 0x30] %asi;	\
+	stxa	%l7, [%sp + STACK_BIAS + 0x38] %asi;	\
+	stxa	%i0, [%sp + STACK_BIAS + 0x40] %asi;	\
+	stxa	%i1, [%sp + STACK_BIAS + 0x48] %asi;	\
+	stxa	%i2, [%sp + STACK_BIAS + 0x50] %asi;	\
+	stxa	%i3, [%sp + STACK_BIAS + 0x58] %asi;	\
+	stxa	%i4, [%sp + STACK_BIAS + 0x60] %asi;	\
+	stxa	%i5, [%sp + STACK_BIAS + 0x68] %asi;	\
+	stxa	%i6, [%sp + STACK_BIAS + 0x70] %asi;	\
+	stxa	%i7, [%sp + STACK_BIAS + 0x78] %asi;	\
+	saved; retry; nop; nop; nop; nop; nop; nop;	\
+	nop; nop; nop; nop; nop; nop; nop;
+
+/* Normal 32bit spill */
+#define SPILL_2_NORMAL					\
+	wr	%g0, ASI_AIUP, %asi;			\
+	srl	%sp, 0, %sp;				\
+	stda	%l0, [%sp + 0x00] %asi;			\
+	stda	%l2, [%sp + 0x10] %asi;			\
+	stda	%l4, [%sp + 0x20] %asi;			\
+	stda	%l6, [%sp + 0x30] %asi;			\
+	stda	%i0, [%sp + 0x40] %asi;			\
+	stda	%i2, [%sp + 0x50] %asi;			\
+	stda	%i4, [%sp + 0x60] %asi;			\
+	stda	%i6, [%sp + 0x70] %asi;			\
+	saved; retry; nop; nop; nop; nop; nop;		\
+	nop; nop; nop; nop; nop; nop; nop; nop;		\
+	nop; nop; nop; nop; nop; nop; nop; nop;
+
+#define SPILL_3_NORMAL SPILL_0_NORMAL
+#define SPILL_4_NORMAL SPILL_0_NORMAL
+#define SPILL_5_NORMAL SPILL_0_NORMAL
+#define SPILL_6_NORMAL SPILL_0_NORMAL
+#define SPILL_7_NORMAL SPILL_0_NORMAL
+
+#define SPILL_0_OTHER SPILL_0_NORMAL
+#define SPILL_1_OTHER SPILL_1_NORMAL
+#define SPILL_2_OTHER SPILL_2_NORMAL
+#define SPILL_3_OTHER SPILL_3_NORMAL
+#define SPILL_4_OTHER SPILL_4_NORMAL
+#define SPILL_5_OTHER SPILL_5_NORMAL
+#define SPILL_6_OTHER SPILL_6_NORMAL
+#define SPILL_7_OTHER SPILL_7_NORMAL
+
+/* Normal kernel fill */
+#define FILL_0_NORMAL					\
+	ldx	[%sp + STACK_BIAS + 0x00], %l0;		\
+	ldx	[%sp + STACK_BIAS + 0x08], %l1;		\
+	ldx	[%sp + STACK_BIAS + 0x10], %l2;		\
+	ldx	[%sp + STACK_BIAS + 0x18], %l3;		\
+	ldx	[%sp + STACK_BIAS + 0x20], %l4;		\
+	ldx	[%sp + STACK_BIAS + 0x28], %l5;		\
+	ldx	[%sp + STACK_BIAS + 0x30], %l6;		\
+	ldx	[%sp + STACK_BIAS + 0x38], %l7;		\
+	ldx	[%sp + STACK_BIAS + 0x40], %i0;		\
+	ldx	[%sp + STACK_BIAS + 0x48], %i1;		\
+	ldx	[%sp + STACK_BIAS + 0x50], %i2;		\
+	ldx	[%sp + STACK_BIAS + 0x58], %i3;		\
+	ldx	[%sp + STACK_BIAS + 0x60], %i4;		\
+	ldx	[%sp + STACK_BIAS + 0x68], %i5;		\
+	ldx	[%sp + STACK_BIAS + 0x70], %i6;		\
+	ldx	[%sp + STACK_BIAS + 0x78], %i7;		\
+	saved; retry; nop; nop; nop; nop; nop; nop;	\
+	nop; nop; nop; nop; nop; nop; nop; nop;
+
+/* Normal 64bit fill */
+#define FILL_1_NORMAL					\
+	wr	%g0, ASI_AIUP, %asi;			\
+	ldxa	[%sp + STACK_BIAS + 0x00] %asi, %l0;	\
+	ldxa	[%sp + STACK_BIAS + 0x08] %asi, %l1;	\
+	ldxa	[%sp + STACK_BIAS + 0x10] %asi, %l2;	\
+	ldxa	[%sp + STACK_BIAS + 0x18] %asi, %l3;	\
+	ldxa	[%sp + STACK_BIAS + 0x20] %asi, %l4;	\
+	ldxa	[%sp + STACK_BIAS + 0x28] %asi, %l5;	\
+	ldxa	[%sp + STACK_BIAS + 0x30] %asi, %l6;	\
+	ldxa	[%sp + STACK_BIAS + 0x38] %asi, %l7;	\
+	ldxa	[%sp + STACK_BIAS + 0x40] %asi, %i0;	\
+	ldxa	[%sp + STACK_BIAS + 0x48] %asi, %i1;	\
+	ldxa	[%sp + STACK_BIAS + 0x50] %asi, %i2;	\
+	ldxa	[%sp + STACK_BIAS + 0x58] %asi, %i3;	\
+	ldxa	[%sp + STACK_BIAS + 0x60] %asi, %i4;	\
+	ldxa	[%sp + STACK_BIAS + 0x68] %asi, %i5;	\
+	ldxa	[%sp + STACK_BIAS + 0x70] %asi, %i6;	\
+	ldxa	[%sp + STACK_BIAS + 0x78] %asi, %i7;	\
+	saved; retry; nop; nop; nop; nop; nop; nop;	\
+	nop; nop; nop; nop; nop; nop; nop;
+
+/* Normal 32bit fill */
+#define FILL_2_NORMAL					\
+	wr	%g0, ASI_AIUP, %asi;			\
+	srl	%sp, 0, %sp;				\
+	ldda	[%sp + 0x00] %asi, %l0;			\
+	ldda	[%sp + 0x10] %asi, %l2;			\
+	ldda	[%sp + 0x20] %asi, %l4;			\
+	ldda	[%sp + 0x30] %asi, %l6;			\
+	ldda	[%sp + 0x40] %asi, %i0;			\
+	ldda	[%sp + 0x50] %asi, %i2;			\
+	ldda	[%sp + 0x60] %asi, %i4;			\
+	ldda	[%sp + 0x70] %asi, %i6;			\
+	saved; retry; nop; nop; nop; nop; nop;		\
+	nop; nop; nop; nop; nop; nop; nop; nop;		\
+	nop; nop; nop; nop; nop; nop; nop; nop;
+
+#define FILL_3_NORMAL FILL_0_NORMAL
+#define FILL_4_NORMAL FILL_0_NORMAL
+#define FILL_5_NORMAL FILL_0_NORMAL
+#define FILL_6_NORMAL FILL_0_NORMAL
+#define FILL_7_NORMAL FILL_0_NORMAL
+
+#define FILL_0_OTHER FILL_0_NORMAL
+#define FILL_1_OTHER FILL_1_NORMAL
+#define FILL_2_OTHER FILL_2_NORMAL
+#define FILL_3_OTHER FILL_3_NORMAL
+#define FILL_4_OTHER FILL_4_NORMAL
+#define FILL_5_OTHER FILL_5_NORMAL
+#define FILL_6_OTHER FILL_6_NORMAL
+#define FILL_7_OTHER FILL_7_NORMAL
 
 #endif /* !(_SPARC64_HEAD_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- v2.1.28/linux/include/asm-sparc64/io.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/io.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.1 1996/12/26 13:25:21 davem Exp $ */
+/* $Id: io.h,v 1.3 1997/03/03 16:51:53 jj Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -7,48 +7,18 @@
 #include <asm/page.h>      /* IO address mapping routines need this */
 #include <asm/system.h>
 
-extern void sun4c_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
-extern void srmmu_mapioaddr(unsigned long, unsigned long, int bus_type, int rdonly);
+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);
 
-extern __inline__ void mapioaddr(unsigned long physaddr, unsigned long virt_addr,
-				 int bus, int rdonly)
+extern __inline__ void mapioaddr (unsigned long physaddr, unsigned long virt_addr, int bus, int rdonly)
 {
-	switch(sparc_cpu_model) {
-	case sun4c:
-		sun4c_mapioaddr(physaddr, virt_addr, bus, rdonly);
-		break;
-	case sun4m:
-	case sun4d:
-	case sun4e:
-		srmmu_mapioaddr(physaddr, virt_addr, bus, rdonly);
-		break;
-	default:
-		printk("mapioaddr: Trying to map IO space for unsupported machine.\n");
-		printk("mapioaddr: sparc_cpu_model = %d\n", sparc_cpu_model);
-		printk("mapioaddr: Halting...\n");
-		halt();
-	};
+	sparc_ultra_mapioaddr (physaddr, virt_addr, bus, rdonly);
 	return;
 }
 
-extern void srmmu_unmapioaddr(unsigned long virt);
-extern void sun4c_unmapioaddr(unsigned long virt);
-
 extern __inline__ void unmapioaddr(unsigned long virt_addr)
 {
-	switch(sparc_cpu_model) {
-	case sun4c:
-		sun4c_unmapioaddr(virt_addr);
-		break;
-	case sun4m:
-	case sun4d:
-	case sun4e:
-		srmmu_unmapioaddr(virt_addr);
-		break;
-	default:
-		printk("unmapioaddr: sparc_cpu_model = %d, halt...\n", sparc_cpu_model);
-		halt();
-	};
+	sparc_ultra_unmapioaddr (virt_addr);
 	return;
 }
 
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/openprom.h linux/include/asm-sparc64/openprom.h
--- v2.1.28/linux/include/asm-sparc64/openprom.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/openprom.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: openprom.h,v 1.1 1996/12/26 14:22:32 davem Exp $ */
+/* $Id: openprom.h,v 1.2 1997/02/25 12:40:41 jj Exp $ */
 #ifndef __SPARC64_OPENPROM_H
 #define __SPARC64_OPENPROM_H
 
@@ -51,7 +51,7 @@
 
 struct linux_mlist_v0 {
 	struct linux_mlist_v0 *theres_more;
-	char *start_adr;
+	unsigned start_adr;
 	unsigned num_bytes;
 };
 
@@ -187,8 +187,13 @@
 
 struct linux_prom_registers {
 	int which_io;         /* is this in OBIO space? */
-	char *phys_addr;      /* The physical address of this register */
+	unsigned phys_addr;   /* The physical address of this register */
 	int reg_size;         /* How many bytes does this register take up? */
+};
+
+struct linux_prom64_registers {
+	long phys_addr;
+	long reg_size;
 };
 
 struct linux_prom_irqs {
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/oplib.h linux/include/asm-sparc64/oplib.h
--- v2.1.28/linux/include/asm-sparc64/oplib.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/oplib.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.2 1996/12/27 08:49:07 jj Exp $
+/* $Id: oplib.h,v 1.4 1997/02/25 20:00:34 jj Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -29,6 +29,23 @@
  */
 extern int prom_root_node;
 
+/* /chosen node of the prom device tree, this stays constant after
+ * initialization is complete.
+ */
+extern int prom_chosen_node;
+
+struct linux_mlist_p1275 {
+	struct linux_mlist_p1275 *theres_more;
+	unsigned long start_adr;
+	unsigned long num_bytes;
+};
+
+struct linux_mem_p1275 {
+	struct linux_mlist_p1275 **p1275_totphys;
+	struct linux_mlist_p1275 **p1275_prommap;
+	struct linux_mlist_p1275 **p1275_available; /* What we can use */
+};
+
 /* The functions... */
 
 /* You must call prom_init() before using any of the library services,
@@ -72,7 +89,7 @@
  * These lists are returned pre-sorted, this should make your life easier
  * since the prom itself is way too lazy to do such nice things.
  */
-extern struct linux_mem_v0 *prom_meminfo(void);
+extern struct linux_mem_p1275 *prom_meminfo(void);
 
 /* Miscellaneous routines, don't really fit in any category per se. */
 
@@ -252,17 +269,18 @@
 extern void prom_adjust_ranges(struct linux_prom_ranges *cranges, int ncranges,
 			       struct linux_prom_ranges *pranges, int npranges);
 
-/* Apply promlib probed OBIO ranges to registers. */
-extern void prom_apply_obio_ranges(struct linux_prom_registers *obioregs, int nregs);
-
 /* Apply ranges of any prom node (and optionally parent node as well) to registers. */
 extern void prom_apply_generic_ranges(int node, int parent, 
 				      struct linux_prom_registers *sbusregs, int nregs);
 				      
-extern long (*prom_command)(char *, long, ...);
+extern long p1275_cmd (char *, long, ...);
 				   
 
+#if 0
 #define P1275_SIZE(x) ((((long)((x) / 32)) << 32) | (x))
+#else
+#define P1275_SIZE(x) x
+#endif
 
 /* We support at most 16 input and 1 output argument */
 #define P1275_ARG_NUMBER		0
@@ -270,10 +288,11 @@
 #define P1275_ARG_OUT_BUF		2
 #define P1275_ARG_OUT_32B		3
 #define P1275_ARG_IN_FUNCTION		4
+#define P1275_ARG_IN_BUF		5
 
 #define P1275_IN(x) ((x) & 0xf)
 #define P1275_OUT(x) (((x) << 4) & 0xf0)
 #define P1275_INOUT(i,o) (P1275_IN(i)|P1275_OUT(o))
-#define P1275_ARG(n,x) ((x) << ((n)*3 + 8)
+#define P1275_ARG(n,x) ((x) << ((n)*3 + 8))
 
 #endif /* !(__SPARC64_OPLIB_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h
--- v2.1.28/linux/include/asm-sparc64/page.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/page.h	Wed Mar  5 17:04:33 1997
@@ -1,16 +1,16 @@
-/* $Id: page.h,v 1.4 1996/12/28 18:39:51 davem Exp $ */
+/* $Id: page.h,v 1.5 1997/02/25 20:00:36 jj Exp $ */
 
 #ifndef _SPARC64_PAGE_H
 #define _SPARC64_PAGE_H
 
 #define PAGE_SHIFT   13
 
-#ifndef __ASSEMBLY__
+#ifdef __KERNEL__
 
-#define PAGE_SIZE    (1UL << PAGE_SHIFT)
+#define PAGE_SIZE    (1 << PAGE_SHIFT)
 #define PAGE_MASK    (~(PAGE_SIZE-1))
 
-#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
 
 #define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE)
 #define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
@@ -73,12 +73,12 @@
 
 #endif /* !(__ASSEMBLY__) */
 
-#define TASK_UNMAPPED_BASE	0x0000000070000000UL
+#define TASK_UNMAPPED_BASE	0x0000000070000000
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
-#define PAGE_OFFSET		0xFFFFF80000000000UL
+#define PAGE_OFFSET		0xFFFFF80000000000
 #define __pa(x)			((unsigned long)(x) - PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long) (x) + PAGE_OFFSET))
 #define MAP_NR(addr)		(__pa(addr) >> PAGE_SHIFT)
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/pgtable.h linux/include/asm-sparc64/pgtable.h
--- v2.1.28/linux/include/asm-sparc64/pgtable.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/pgtable.h	Wed Mar  5 17:04:33 1997
@@ -1,4 +1,4 @@
-/* $Id: pgtable.h,v 1.8 1996/12/28 18:39:52 davem Exp $
+/* $Id: pgtable.h,v 1.10 1997/03/03 16:51:54 jj Exp $
  * pgtable.h: SpitFire page table operations.
  *
  * Copyright 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -11,6 +11,9 @@
  * the SpitFire page tables.
  */
 
+#ifndef __ASSEMBLY__
+#include <linux/mm.h>
+#endif
 #include <asm/spitfire.h>
 #include <asm/asi.h>
 #include <asm/mmu_context.h>
@@ -48,32 +51,32 @@
 #endif /* !(__ASSEMBLY__) */
 
 /* SpitFire TTE bits. */
-#define _PAGE_VALID	0x8000000000000000UL	/* Valid TTE                          */
-#define _PAGE_R		0x8000000000000000UL	/* Used to keep ref bit up to date    */
-#define _PAGE_SZ4MB	0x6000000000000000UL	/* 4MB Page                           */
-#define _PAGE_SZ512K	0x4000000000000000UL	/* 512K Page                          */
-#define _PAGE_SZ64K	0x2000000000000000UL	/* 64K Page                           */
-#define _PAGE_SZ8K	0x0000000000000000UL	/* 8K Page                            */
-#define _PAGE_NFO	0x1000000000000000UL	/* No Fault Only                      */
-#define _PAGE_IE	0x0800000000000000UL	/* Invert Endianness                  */
-#define _PAGE_SOFT2	0x07FC000000000000UL	/* Second set of software bits        */
-#define _PAGE_DIAG	0x0003FE0000000000UL	/* Diagnostic TTE bits                */
-#define _PAGE_PADDR	0x000001FFFFFFE000UL	/* Physical Address bits [40:13]      */
-#define _PAGE_SOFT	0x0000000000001F80UL	/* First set of software bits         */
-#define _PAGE_L		0x0000000000000040UL	/* Locked TTE                         */
-#define _PAGE_CP	0x0000000000000020UL	/* Cacheable in Physical Cache        */
-#define _PAGE_CV	0x0000000000000010UL	/* Cacheable in Virtual Cache         */
-#define _PAGE_E		0x0000000000000008UL    /* side-Effect                        */
-#define _PAGE_P		0x0000000000000004UL	/* Privileged Page                    */
-#define _PAGE_W		0x0000000000000002UL	/* Writable                           */
-#define _PAGE_G		0x0000000000000001UL	/* Global                             */
+#define _PAGE_VALID	0x8000000000000000	/* Valid TTE                          */
+#define _PAGE_R		0x8000000000000000	/* Used to keep ref bit up to date    */
+#define _PAGE_SZ4MB	0x6000000000000000	/* 4MB Page                           */
+#define _PAGE_SZ512K	0x4000000000000000	/* 512K Page                          */
+#define _PAGE_SZ64K	0x2000000000000000	/* 64K Page                           */
+#define _PAGE_SZ8K	0x0000000000000000	/* 8K Page                            */
+#define _PAGE_NFO	0x1000000000000000	/* No Fault Only                      */
+#define _PAGE_IE	0x0800000000000000	/* Invert Endianness                  */
+#define _PAGE_SOFT2	0x07FC000000000000	/* Second set of software bits        */
+#define _PAGE_DIAG	0x0003FE0000000000	/* Diagnostic TTE bits                */
+#define _PAGE_PADDR	0x000001FFFFFFE000	/* Physical Address bits [40:13]      */
+#define _PAGE_SOFT	0x0000000000001F80	/* First set of software bits         */
+#define _PAGE_L		0x0000000000000040	/* Locked TTE                         */
+#define _PAGE_CP	0x0000000000000020	/* Cacheable in Physical Cache        */
+#define _PAGE_CV	0x0000000000000010	/* Cacheable in Virtual Cache         */
+#define _PAGE_E		0x0000000000000008	/* side-Effect                        */
+#define _PAGE_P		0x0000000000000004	/* Privileged Page                    */
+#define _PAGE_W		0x0000000000000002	/* Writable                           */
+#define _PAGE_G		0x0000000000000001	/* Global                             */
 
 /* Here are the SpitFire software bits we use in the TTE's. */
-#define _PAGE_PRESENT	0x0000000000001000UL	/* Present Page (ie. not swapped out) */
-#define _PAGE_MODIFIED	0x0000000000000800UL	/* Modified Page (ie. dirty)          */
-#define _PAGE_ACCESSED	0x0000000000000400UL	/* Accessed Page (ie. referenced)     */
-#define _PAGE_READ	0x0000000000000200UL	/* Readable SW Bit                    */
-#define _PAGE_WRITE	0x0000000000000100UL	/* Writable SW Bit                    */
+#define _PAGE_PRESENT	0x0000000000001000	/* Present Page (ie. not swapped out) */
+#define _PAGE_MODIFIED	0x0000000000000800	/* Modified Page (ie. dirty)          */
+#define _PAGE_ACCESSED	0x0000000000000400	/* Accessed Page (ie. referenced)     */
+#define _PAGE_READ	0x0000000000000200	/* Readable SW Bit                    */
+#define _PAGE_WRITE	0x0000000000000100	/* Writable SW Bit                    */
 
 #define _PAGE_CACHE	(_PAGE_CP | _PAGE_CV)
 
@@ -365,18 +368,17 @@
 
 extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir)
 {
-	register unsigned long paddr asm("%o5");
+	register unsigned long paddr asm("o5");
 
 	paddr = ((unsigned long) pgdir) - PAGE_OFFSET;
 
 	if(tsk->mm == current->mm) {
 		__asm__ __volatile__("
 			rdpr		%%pstate, %%g1
-			or		%%g1, %1, %%g2
-			wrpr		%%g2, %2, %%pstate
+			wrpr		%%g1, %2, %%pstate
 			mov		%0, %%g7
 			wrpr		%%g1, 0x0, %%pstate
-		" : : "r" (paddr), "i" (PSTATE_MG), "i" (PSTATE_IE)
+		" : : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE)
 		  : "g1", "g2");
 	}
 }
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/poll.h linux/include/asm-sparc64/poll.h
--- v2.1.28/linux/include/asm-sparc64/poll.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/poll.h	Wed Mar  5 17:04:33 1997
@@ -0,0 +1,21 @@
+#ifndef __SPARC64_POLL_H
+#define __SPARC64_POLL_H
+
+#define POLLIN		  1
+#define POLLPRI		  2
+#define POLLOUT		  4
+#define POLLERR		  8
+#define POLLHUP		 16
+#define POLLNVAL	 32
+#define POLLRDNORM	 64
+#define POLLWRNORM	POLLOUT
+#define POLLRDBAND	128
+#define POLLWRBAND	256
+
+struct pollfd {
+	int fd;
+	short events;
+	short revents;
+};
+
+#endif
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- v2.1.28/linux/include/asm-sparc64/processor.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/processor.h	Wed Mar  5 17:04:34 1997
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.6 1996/12/28 20:05:14 davem Exp $
+/* $Id: processor.h,v 1.8 1997/03/04 16:27:33 jj Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -32,30 +32,28 @@
 
 #ifndef __ASSEMBLY__
 
-/* Ok this is hot.  Sparc exception save area. */
-struct exception_struct {
-	unsigned long count;   /* Exception count */
-	unsigned long pc;      /* Callers PC for copy/clear user */
-	unsigned long expc;    /* Where to jump when exception signaled */
-	unsigned long address; /* Saved user base address for transfer */
+struct fpq {
+	unsigned long *insn_addr;
+	unsigned long insn;
 };
 
 #define NSWINS		8
 
 /* The Sparc processor specific thread struct. */
 struct thread_struct {
-	/* Context switch saved kernel state. */
-	unsigned long user_globals[8];		/* performance hack */
-	unsigned long ksp, kpc;
-
 	/* Floating point regs */
-	unsigned long   float_regs[64] __attribute__ ((aligned (64)));
+	/* Please check asm_offsets, so that not to much precious space
+	   is wasted by this alignment and move the float_regs wherever
+	   is better in this structure. Remember every byte of alignment
+	   is multiplied by 512 to get the amount of wasted kernel memory. */
+	unsigned int    float_regs[64] __attribute__ ((aligned (64)));
 	unsigned long   fsr;
 	unsigned long   fpqdepth;
-	struct fpq {
-		unsigned long *insn_addr;
-		unsigned long insn;
-	} fpqueue[16];
+	struct fpq	fpqueue[16];
+	
+	/* Context switch saved kernel state. */
+	unsigned long user_globals[8];		/* performance hack */
+	unsigned long ksp, kpc;
 
 	/* Storage for windows when user stack is bogus. */
 	struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16)));
@@ -69,7 +67,6 @@
 	unsigned long sig_address __attribute__ ((aligned (8)));
 	unsigned long sig_desc;
 
-	struct exception_struct;
 	struct sigstack sstk_info;
 	int current_ds, new_signal;
 	struct exec core_exec;     /* just what it says. */
@@ -86,16 +83,16 @@
 		    PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC }
 
 #define INIT_TSS  {							\
-/* user_globals */ 							\
-   { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 					\
-/* ksp, kpc */ 								\
-   0,   0, 								\
 /* FPU regs */   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	\
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	\
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	\
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },	\
 /* FPU status, FPU qdepth, FPU queue */ 				\
    0,          0,          { { 0, 0, }, }, 				\
+/* user_globals */ 							\
+   { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 					\
+/* ksp, kpc */ 								\
+   0,   0, 								\
 /* reg_window */							\
 { { { 0, }, { 0, } }, }, 						\
 /* rwbuf_stkptrs */							\
@@ -107,7 +104,7 @@
 /* sig_address, sig_desc */						\
    0,           0,							\
 /* ex,     sstk_info, current_ds, */					\
-   { 0, }, { 0, 0, }, USER_DS,						\
+   { 0, 0, }, USER_DS,							\
 /* new_signal */							\
   0,									\
 /* core_exec */								\
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/ptrace.h linux/include/asm-sparc64/ptrace.h
--- v2.1.28/linux/include/asm-sparc64/ptrace.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/ptrace.h	Wed Mar  5 17:04:34 1997
@@ -1,4 +1,4 @@
-/* $Id: ptrace.h,v 1.4 1996/12/28 18:39:54 davem Exp $ */
+/* $Id: ptrace.h,v 1.7 1997/03/04 16:27:32 jj Exp $ */
 #ifndef _SPARC64_PTRACE_H
 #define _SPARC64_PTRACE_H
 
@@ -102,13 +102,153 @@
 
 #else /* __ASSEMBLY__ */
 /* For assembly code. */
-#define TRACEREG_SZ		XXX
-#define STACKFRAME_SZ		YYY
-#define REGWIN_SZ		ZZZ
+#define TRACEREG_SZ		0xa0
+#define STACKFRAME_SZ		0xc0
+#define REGWIN_SZ		0x80
 
 #define TRACEREG32_SZ		0x50
 #define STACKFRAME32_SZ		0x60
 #define REGWIN32_SZ		0x40
+
+#include <asm/asm_offsets.h>
 #endif
+
+#define STACK_BIAS		2047
+
+/* These are for pt_regs. */
+#define PT_V9_G0     0x00
+#define PT_V9_G1     0x08
+#define PT_V9_G2     0x10
+#define PT_V9_G3     0x18
+#define PT_V9_G4     0x20
+#define PT_V9_G5     0x28
+#define PT_V9_G6     0x30
+#define PT_V9_G7     0x38
+#define PT_V9_I0     0x40
+#define PT_V9_I1     0x48
+#define PT_V9_I2     0x50
+#define PT_V9_I3     0x58
+#define PT_V9_I4     0x60
+#define PT_V9_I5     0x68
+#define PT_V9_I6     0x70
+#define PT_V9_FP     PT_V9_I6
+#define PT_V9_I7     0x78
+#define PT_V9_TSTATE 0x80
+#define PT_V9_TPC    0x88
+#define PT_V9_TNPC   0x90
+#define PT_V9_Y      0x98
+#define PT_TSTATE	PT_V9_TSTATE
+#define PT_TPC		PT_V9_TPC
+#define PT_TNPC		PT_V9_TNPC
+
+/* These for pt_regs32. */
+#define PT_PSR    0x0
+#define PT_PC     0x4
+#define PT_NPC    0x8
+#define PT_Y      0xc
+#define PT_G0     0x10
+#define PT_WIM    PT_G0
+#define PT_G1     0x14
+#define PT_G2     0x18
+#define PT_G3     0x1c
+#define PT_G4     0x20
+#define PT_G5     0x24
+#define PT_G6     0x28
+#define PT_G7     0x2c
+#define PT_I0     0x30
+#define PT_I1     0x34
+#define PT_I2     0x38
+#define PT_I3     0x3c
+#define PT_I4     0x40
+#define PT_I5     0x44
+#define PT_I6     0x48
+#define PT_FP     PT_I6
+#define PT_I7     0x4c
+
+/* Reg_window offsets */
+#define RW_V9_L0     0x00
+#define RW_V9_L1     0x08
+#define RW_V9_L2     0x10
+#define RW_V9_L3     0x18
+#define RW_V9_L4     0x20
+#define RW_V9_L5     0x28
+#define RW_V9_L6     0x30
+#define RW_V9_L7     0x38
+#define RW_V9_I0     0x40
+#define RW_V9_I1     0x48
+#define RW_V9_I2     0x50
+#define RW_V9_I3     0x58
+#define RW_V9_I4     0x60
+#define RW_V9_I5     0x68
+#define RW_V9_I6     0x70
+#define RW_V9_I7     0x78
+
+#define RW_L0     0x00
+#define RW_L1     0x04
+#define RW_L2     0x08
+#define RW_L3     0x0c
+#define RW_L4     0x10
+#define RW_L5     0x14
+#define RW_L6     0x18
+#define RW_L7     0x1c
+#define RW_I0     0x20
+#define RW_I1     0x24
+#define RW_I2     0x28
+#define RW_I3     0x2c
+#define RW_I4     0x30
+#define RW_I5     0x34
+#define RW_I6     0x38
+#define RW_I7     0x3c
+
+/* Stack_frame offsets */
+#define SF_V9_L0     0x00
+#define SF_V9_L1     0x08
+#define SF_V9_L2     0x10
+#define SF_V9_L3     0x18
+#define SF_V9_L4     0x20
+#define SF_V9_L5     0x28
+#define SF_V9_L6     0x30
+#define SF_V9_L7     0x38
+#define SF_V9_I0     0x40
+#define SF_V9_I1     0x48
+#define SF_V9_I2     0x50
+#define SF_V9_I3     0x58
+#define SF_V9_I4     0x60
+#define SF_V9_I5     0x68
+#define SF_V9_FP     0x70
+#define SF_V9_PC     0x78
+#define SF_V9_RETP   0x80
+#define SF_V9_XARG0  0x88
+#define SF_V9_XARG1  0x90
+#define SF_V9_XARG2  0x98
+#define SF_V9_XARG3  0xa0
+#define SF_V9_XARG4  0xa8
+#define SF_V9_XARG5  0xb0
+#define SF_V9_XXARG  0xb8
+
+#define SF_L0     0x00
+#define SF_L1     0x04
+#define SF_L2     0x08
+#define SF_L3     0x0c
+#define SF_L4     0x10
+#define SF_L5     0x14
+#define SF_L6     0x18
+#define SF_L7     0x1c
+#define SF_I0     0x20
+#define SF_I1     0x24
+#define SF_I2     0x28
+#define SF_I3     0x2c
+#define SF_I4     0x30
+#define SF_I5     0x34
+#define SF_FP     0x38
+#define SF_PC     0x3c
+#define SF_RETP   0x40
+#define SF_XARG0  0x44
+#define SF_XARG1  0x48
+#define SF_XARG2  0x4c
+#define SF_XARG3  0x50
+#define SF_XARG4  0x54
+#define SF_XARG5  0x58
+#define SF_XXARG  0x5c
 
 #endif /* !(_SPARC64_PTRACE_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/sbus.h linux/include/asm-sparc64/sbus.h
--- v2.1.28/linux/include/asm-sparc64/sbus.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/sbus.h	Wed Mar  5 17:04:34 1997
@@ -1,4 +1,4 @@
-/* $Id: sbus.h,v 1.1 1996/12/26 14:22:36 davem Exp $
+/* $Id: sbus.h,v 1.2 1997/02/25 20:00:35 jj Exp $
  * sbus.h:  Defines for the Sun SBus.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -53,7 +53,7 @@
   char linux_name[64];                 /* Name used internally by Linux */
 
   struct linux_prom_registers reg_addrs[PROMREG_MAX];
-  int num_registers;
+  int num_registers, ranges_applied;
 
   struct linux_prom_irqs irqs[PROMINTR_MAX];
   int num_irqs;
@@ -93,6 +93,7 @@
 
 /* Apply promlib probed SBUS ranges to registers. */
 extern void prom_apply_sbus_ranges(struct linux_sbus *sbus, 
-				   struct linux_prom_registers *sbusregs, int nregs);
+				   struct linux_prom_registers *sbusregs, int nregs, 
+				   struct linux_sbus_device *sdev);
 
 #endif /* !(_SPARC64_SBUS_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/semaphore.h linux/include/asm-sparc64/semaphore.h
--- v2.1.28/linux/include/asm-sparc64/semaphore.h	Fri Dec 13 01:37:47 1996
+++ linux/include/asm-sparc64/semaphore.h	Wed Mar  5 17:04:34 1997
@@ -7,7 +7,7 @@
 
 struct semaphore {
 	atomic_t count;
-	atomic_t waiting;
+	atomic_t waking;
 	struct wait_queue * wait;
 };
 
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/sigcontext.h linux/include/asm-sparc64/sigcontext.h
--- v2.1.28/linux/include/asm-sparc64/sigcontext.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc64/sigcontext.h	Wed Mar  5 17:04:34 1997
@@ -1,6 +1,6 @@
-/* $Id: sigcontext.h,v 1.2 1997/01/19 22:32:15 ecd Exp $ */
-#ifndef _ASMsparc64_SIGCONTEXT_H
-#define _ASMsparc64_SIGCONTEXT_H
+/* $Id: sigcontext.h,v 1.3 1997/03/03 16:51:55 jj Exp $ */
+#ifndef __SPARC64_SIGCONTEXT_H
+#define __SPARC64_SIGCONTEXT_H
 
 #include <asm/ptrace.h>
 
@@ -15,7 +15,7 @@
 };
 
 /* This is what SunOS does, so shall I. */
-struct sigcontext {
+struct sigcontext32 {
 	int sigc_onstack;      /* state to restore */
 	int sigc_mask;         /* sigmask to restore */
 	int sigc_sp;           /* stack pointer */
@@ -32,19 +32,42 @@
 
 	/* stack ptrs for each regwin buf */
 	/* XXX 32-bit ptrs pinhead... */
-	char *sigc_spbuf[SUNOS_MAXWIN];
+	unsigned sigc_spbuf[SUNOS_MAXWIN];
 
 	/* Windows to restore after signal */
 	struct reg_window32 sigc_wbuf[SUNOS_MAXWIN];
 };
 
+/* This is what SunOS doesn't, so we have to write this alone. */
+struct sigcontext {
+	int sigc_onstack;      /* state to restore */
+	int sigc_mask;         /* sigmask to restore */
+	int sigc_sp;           /* stack pointer */
+	int sigc_pc;           /* program counter */
+	int sigc_npc;          /* next program counter */
+	int sigc_psr;          /* for condition codes etc */
+	int sigc_g1;           /* User uses these two registers */
+	int sigc_o0;           /* within the trampoline code. */
+
+	/* Now comes information regarding the users window set
+	 * at the time of the signal.
+	 */
+	int sigc_oswins;       /* outstanding windows */
+
+	/* stack ptrs for each regwin buf */
+	char *sigc_spbuf[SUNOS_MAXWIN];
+
+	/* Windows to restore after signal */
+	struct reg_window sigc_wbuf[SUNOS_MAXWIN];
+};
+
 typedef struct {
 	struct pt_regs32	si_regs;
 	int			si_mask;
 } __siginfo32_t;
 
 typedef struct {
-	unsigned int si_float_regs [64];
+	unsigned int si_float_regs [32];
 	unsigned int si_fsr;
 	unsigned int si_fpqdepth;
 	struct {
@@ -60,7 +83,7 @@
 } __siginfo_t;
 
 typedef struct {
-	unsigned   long si_float_regs [64];
+	unsigned   int si_float_regs [64];
 	unsigned   long si_fsr;
 	unsigned   int si_fpqdepth;
 	struct {
@@ -71,4 +94,4 @@
 
 #endif /* !(__ASSEMBLY__) */
 
-#endif /* !(_ASMsparc64_SIGCONTEXT_H) */
+#endif /* !(__SPARC64_SIGCONTEXT_H) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/signal.h linux/include/asm-sparc64/signal.h
--- v2.1.28/linux/include/asm-sparc64/signal.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/signal.h	Wed Mar  5 17:04:34 1997
@@ -1,4 +1,4 @@
-/* $Id: signal.h,v 1.1 1996/12/26 14:22:37 davem Exp $ */
+/* $Id: signal.h,v 1.2 1997/03/03 16:51:57 jj Exp $ */
 #ifndef _ASMSPARC64_SIGNAL_H
 #define _ASMSPARC64_SIGNAL_H
 
@@ -164,13 +164,12 @@
 };
 
 struct sigaction32 {
-	/* XXX 32-bit func ptr... */
-	__sighandler_t  sa_handler;
+	unsigned	sa_handler;
 	sigset32_t      sa_mask;
 	unsigned int    sa_flags;
 
 	/* XXX 32-bit func ptr... */
-	void (*sa_restorer) (void);     /* not used by Linux/SPARC yet */
+	unsigned	sa_restorer;     /* not used by Linux/SPARC yet */
 };
 
 #endif /* !(__ASSEMBLY__) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/smp.h linux/include/asm-sparc64/smp.h
--- v2.1.28/linux/include/asm-sparc64/smp.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/smp.h	Wed Mar  5 17:04:34 1997
@@ -17,6 +17,9 @@
 	int prom_node;
 	int mid;
 };
+
+extern int linux_num_cpus;	/* number of CPUs probed  */
+
 #endif /* !(__ASSEMBLY__) */
 
 #ifdef __SMP__
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/string.h linux/include/asm-sparc64/string.h
--- v2.1.28/linux/include/asm-sparc64/string.h	Mon Dec 30 02:00:03 1996
+++ linux/include/asm-sparc64/string.h	Wed Mar  5 17:04:34 1997
@@ -1,8 +1,9 @@
-/* $Id: string.h,v 1.1 1996/12/26 14:22:40 davem Exp $
+/* $Id: string.h,v 1.3 1997/03/03 17:11:15 jj Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
 #ifndef __SPARC64_STRING_H__
@@ -10,13 +11,27 @@
 
 /* Really, userland/ksyms should not see any of this stuff. */
 
-#if defined(__KERNEL__) && !defined(EXPORT_SYMTAB)
+#ifdef __KERNEL__
+
+extern void __memmove(void *,const void *,__kernel_size_t);
+extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t);
+extern __kernel_size_t __memset(void *,int,__kernel_size_t);
+
+#ifndef EXPORT_SYMTAB
 
 /* First the mem*() things. */
 #define __HAVE_ARCH_BCOPY
 #define __HAVE_ARCH_MEMMOVE
+
+#undef memmove
+#define memmove(_to, _from, _n) \
+({ \
+	void *_t = (_to); \
+	__memmove(_t, (_from), (_n)); \
+	_t; \
+})
+
 #define __HAVE_ARCH_MEMCPY
-extern void *__memcpy(void *,const void *,__kernel_size_t);
 
 extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n)
 {
@@ -50,12 +65,11 @@
  __nonconstant_memcpy((t),(f),(n)))
 
 #define __HAVE_ARCH_MEMSET
-extern void *__memset(void *,int,__kernel_size_t);
 
-extern inline void *__constant_c_and_count_memset(void *s, char c, size_t count)
+extern inline void *__constant_c_and_count_memset(void *s, char c, __kernel_size_t count)
 {
 	extern void *bzero_1page(void *);
-	extern void *__bzero(void *, size_t);
+	extern __kernel_size_t __bzero(void *, __kernel_size_t);
 
 	if(!c) {
 		if(count == 8192)
@@ -68,9 +82,9 @@
 	return s;
 }
 
-extern inline void *__constant_c_memset(void *s, char c, size_t count)
+extern inline void *__constant_c_memset(void *s, char c, __kernel_size_t count)
 {
-	extern void *__bzero(void *, size_t);
+	extern __kernel_size_t __bzero(void *, __kernel_size_t);
 
 	if(!c)
 		__bzero(s, count);
@@ -79,12 +93,18 @@
 	return s;
 }
 
+extern inline void *__nonconstant_memset(void *s, char c, __kernel_size_t count)
+{
+	__memset(s, c, count);
+	return s;
+}
+
 #undef memset
 #define memset(s, c, count) \
 (__builtin_constant_p(c) ? (__builtin_constant_p(count) ? \
                             __constant_c_and_count_memset((s), (c), (count)) : \
                             __constant_c_memset((s), (c), (count))) \
-                         : __memset((s), (c), (count)))
+                          : __nonconstant_memset((s), (c), (count)))
 
 #define __HAVE_ARCH_MEMSCAN
 
@@ -173,6 +193,8 @@
  __constant_strncmp(__arg0, __arg1, __arg2) : \
  __strncmp(__arg0, __arg1, __arg2))
 
-#endif /* (__KERNEL__) && !(EXPORT_SYMTAB) */
+#endif /* !EXPORT_SYMTAB */
+
+#endif /* __KERNEL__ */
 
 #endif /* !(__SPARC64_STRING_H__) */
diff -u --recursive --new-file v2.1.28/linux/include/asm-sparc64/uaccess.h linux/include/asm-sparc64/uaccess.h
--- v2.1.28/linux/include/asm-sparc64/uaccess.h	Sun Jan 26 02:07:47 1997
+++ linux/include/asm-sparc64/uaccess.h	Wed Mar  5 17:04:34 1997
@@ -1,4 +1,4 @@
-/* $Id: uaccess.h,v 1.3 1997/01/16 14:19:08 davem Exp $ */
+/* $Id: uaccess.h,v 1.6 1997/03/03 16:51:54 jj Exp $ */
 #ifndef _ASM_UACCESS_H
 #define _ASM_UACCESS_H
 
@@ -114,45 +114,45 @@
 struct __large_struct { unsigned long buf[100]; };
 #define __m(x) ((struct __large_struct *)(x))
 
-#define __put_user_check(x,addr,size) ({ \
+#define __put_user_check(data,addr,size) ({ \
 register int __pu_ret; \
 if (__access_ok(addr,size)) { \
 switch (size) { \
-case 1: __put_user_asm(x,b,addr,__pu_ret); break; \
-case 2: __put_user_asm(x,h,addr,__pu_ret); break; \
-case 4: __put_user_asm(x,w,addr,__pu_ret); break; \
-case 8: __put_user_asm(x,x,addr,__pu_ret); break; \
+case 1: __put_user_asm(data,b,addr,__pu_ret); break; \
+case 2: __put_user_asm(data,h,addr,__pu_ret); break; \
+case 4: __put_user_asm(data,w,addr,__pu_ret); break; \
+case 8: __put_user_asm(data,x,addr,__pu_ret); break; \
 default: __pu_ret = __put_user_bad(); break; \
 } } else { __pu_ret = -EFAULT; } __pu_ret; })
 
-#define __put_user_check_ret(x,addr,size,retval) ({ \
+#define __put_user_check_ret(data,addr,size,retval) ({ \
 register int __foo __asm__ ("l1"); \
 if (__access_ok(addr,size)) { \
 switch (size) { \
-case 1: __put_user_asm_ret(x,b,addr,retval,__foo); break; \
-case 2: __put_user_asm_ret(x,h,addr,retval,__foo); break; \
-case 4: __put_user_asm_ret(x,w,addr,retval,__foo); break; \
-case 8: __put_user_asm_ret(x,x,addr,retval,__foo); break; \
+case 1: __put_user_asm_ret(data,b,addr,retval,__foo); break; \
+case 2: __put_user_asm_ret(data,h,addr,retval,__foo); break; \
+case 4: __put_user_asm_ret(data,w,addr,retval,__foo); break; \
+case 8: __put_user_asm_ret(data,x,addr,retval,__foo); break; \
 default: if (__put_user_bad()) return retval; break; \
 } } else return retval; })
 
-#define __put_user_nocheck(x,addr,size) ({ \
+#define __put_user_nocheck(data,addr,size) ({ \
 register int __pu_ret; \
 switch (size) { \
-case 1: __put_user_asm(x,b,addr,__pu_ret); break; \
-case 2: __put_user_asm(x,h,addr,__pu_ret); break; \
-case 4: __put_user_asm(x,w,addr,__pu_ret); break; \
-case 8: __put_user_asm(x,x,addr,__pu_ret); break; \
+case 1: __put_user_asm(data,b,addr,__pu_ret); break; \
+case 2: __put_user_asm(data,h,addr,__pu_ret); break; \
+case 4: __put_user_asm(data,w,addr,__pu_ret); break; \
+case 8: __put_user_asm(data,x,addr,__pu_ret); break; \
 default: __pu_ret = __put_user_bad(); break; \
 } __pu_ret; })
 
-#define __put_user_nocheck_ret(x,addr,size,retval) ({ \
+#define __put_user_nocheck_ret(data,addr,size,retval) ({ \
 register int __foo __asm__ ("l1"); \
 switch (size) { \
-case 1: __put_user_asm_ret(x,b,addr,retval,__foo); break; \
-case 2: __put_user_asm_ret(x,h,addr,retval,__foo); break; \
-case 4: __put_user_asm_ret(x,w,addr,retval,__foo); break; \
-case 8: __put_user_asm_ret(x,x,addr,retval,__foo); break; \
+case 1: __put_user_asm_ret(data,b,addr,retval,__foo); break; \
+case 2: __put_user_asm_ret(data,h,addr,retval,__foo); break; \
+case 4: __put_user_asm_ret(data,w,addr,retval,__foo); break; \
+case 8: __put_user_asm_ret(data,x,addr,retval,__foo); break; \
 default: if (__put_user_bad()) return retval; break; \
 } })
 
@@ -203,7 +203,7 @@
 
 extern int __put_user_bad(void);
 
-#define __get_user_check(x,addr,size,type) ({ \
+#define __get_user_check(data,addr,size,type) ({ \
 register int __gu_ret; \
 register unsigned long __gu_val; \
 if (__access_ok(addr,size)) { \
@@ -213,9 +213,9 @@
 case 4: __get_user_asm(__gu_val,uw,addr,__gu_ret); break; \
 case 8: __get_user_asm(__gu_val,x,addr,__gu_ret); break; \
 default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \
-} } else { __gu_val = 0; __gu_ret = -EFAULT; } x = (type) __gu_val; __gu_ret; })
+} } else { __gu_val = 0; __gu_ret = -EFAULT; } data = (type) __gu_val; __gu_ret; })
 
-#define __get_user_check_ret(x,addr,size,type,retval) ({ \
+#define __get_user_check_ret(data,addr,size,type,retval) ({ \
 register unsigned long __gu_val __asm__ ("l1"); \
 if (__access_ok(addr,size)) { \
 switch (size) { \
@@ -224,9 +224,9 @@
 case 4: __get_user_asm_ret(__gu_val,uw,addr,retval); break; \
 case 8: __get_user_asm_ret(__gu_val,x,addr,retval); break; \
 default: if (__get_user_bad()) return retval; \
-} x = (type) __gu_val; } else return retval; })
+} data = (type) __gu_val; } else return retval; })
 
-#define __get_user_nocheck(x,addr,size,type) ({ \
+#define __get_user_nocheck(data,addr,size,type) ({ \
 register int __gu_ret; \
 register unsigned long __gu_val; \
 switch (size) { \
@@ -235,9 +235,9 @@
 case 4: __get_user_asm(__gu_val,uw,addr,__gu_ret); break; \
 case 8: __get_user_asm(__gu_val,x,addr,__gu_ret); break; \
 default: __gu_val = 0; __gu_ret = __get_user_bad(); break; \
-} x = (type) __gu_val; __gu_ret; })
+} data = (type) __gu_val; __gu_ret; })
 
-#define __get_user_nocheck_ret(x,addr,size,type,retval) ({ \
+#define __get_user_nocheck_ret(data,addr,size,type,retval) ({ \
 register unsigned long __gu_val __asm__ ("l1"); \
 switch (size) { \
 case 1: __get_user_asm_ret(__gu_val,ub,addr,retval); break; \
@@ -245,7 +245,7 @@
 case 4: __get_user_asm_ret(__gu_val,uw,addr,retval); break; \
 case 8: __get_user_asm_ret(__gu_val,x,addr,retval); break; \
 default: if (__get_user_bad()) return retval; \
-} x = (type) __gu_val; })
+} data = (type) __gu_val; })
 
 #define __get_user_asm(x,size,addr,ret)					\
 __asm__ __volatile__(							\
@@ -295,14 +295,14 @@
 
 extern int __get_user_bad(void);
 
-extern int __copy_user(unsigned long to, unsigned long from, int size);
+extern __kernel_size_t __copy_user(void *to, void *from, __kernel_size_t size);
 
 #define copy_to_user(to,from,n) ({ \
-unsigned long __copy_to = (unsigned long) (to); \
-unsigned long __copy_size = (unsigned long) (n); \
-unsigned long __copy_res; \
+void *__copy_to = (void *) (to); \
+__kernel_size_t __copy_size = (__kernel_size_t) (n); \
+__kernel_size_t __copy_res; \
 if(__copy_size && __access_ok(__copy_to, __copy_size)) { \
-__copy_res = __copy_user(__copy_to, (unsigned long) (from), __copy_size); \
+__copy_res = __copy_user(__copy_to, (void *) (from), __copy_size); \
 } else __copy_res = __copy_size; \
 __copy_res; })
 
@@ -321,11 +321,11 @@
 })
 
 #define copy_from_user(to,from,n) ({ \
-unsigned long __copy_from = (unsigned long) (from); \
-unsigned long __copy_size = (unsigned long) (n); \
-unsigned long __copy_res; \
+void *__copy_from = (void *) (from); \
+__kernel_size_t __copy_size = (__kernel_size_t) (n); \
+__kernel_size_t __copy_res; \
 if(__copy_size && __access_ok(__copy_from, __copy_size)) { \
-__copy_res = __copy_user((unsigned long) (to), __copy_from, __copy_size); \
+__copy_res = __copy_user((void *) (to), __copy_from, __copy_size); \
 } else __copy_res = __copy_size; \
 __copy_res; })
 
diff -u --recursive --new-file v2.1.28/linux/include/linux/cd1400.h linux/include/linux/cd1400.h
--- v2.1.28/linux/include/linux/cd1400.h	Mon Mar 11 01:20:33 1996
+++ linux/include/linux/cd1400.h	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	cd1400.h  -- cd1400 UART hardware info.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.1.28/linux/include/linux/cdk.h linux/include/linux/cdk.h
--- v2.1.28/linux/include/linux/cdk.h	Thu Apr 11 23:49:46 1996
+++ linux/include/linux/cdk.h	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	cdk.h  -- CDK interface definitions.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.1.28/linux/include/linux/comstats.h linux/include/linux/comstats.h
--- v2.1.28/linux/include/linux/comstats.h	Sat Apr 20 01:25:31 1996
+++ linux/include/linux/comstats.h	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	comstats.h  -- Serial Port Stats.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.1.28/linux/include/linux/istallion.h linux/include/linux/istallion.h
--- v2.1.28/linux/include/linux/istallion.h	Wed Apr 24 02:48:04 1996
+++ linux/include/linux/istallion.h	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	istallion.h  -- stallion intelligent multiport serial driver.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.1.28/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.28/linux/include/linux/netdevice.h	Fri Feb  7 05:54:55 1997
+++ linux/include/linux/netdevice.h	Wed Mar  5 17:04:34 1997
@@ -69,10 +69,6 @@
 #define IFF_IP_MASK_OK	2
 #define IFF_IP_BRD_OK	4
 
-#ifdef __KERNEL__
-
-#include <linux/skbuff.h>
-
 /*
  *	We tag multicasts with these structures.
  */
@@ -130,6 +126,9 @@
 	
 };
 
+#ifdef __KERNEL__
+
+#include <linux/skbuff.h>
 
 /*
  *	The DEVICE structure.
diff -u --recursive --new-file v2.1.28/linux/include/linux/openpromio.h linux/include/linux/openpromio.h
--- v2.1.28/linux/include/linux/openpromio.h	Fri Dec 13 01:37:47 1996
+++ linux/include/linux/openpromio.h	Wed Mar  5 17:04:34 1997
@@ -1,6 +0,0 @@
-#ifndef _LINUX_OPENPROMIO_H
-#define _LINUX_OPENPROMIO_H 1
-
-#include <asm/openpromio.h>
-
-#endif
diff -u --recursive --new-file v2.1.28/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.28/linux/include/linux/pci.h	Thu Feb 27 10:57:31 1997
+++ linux/include/linux/pci.h	Tue Mar  4 13:24:05 1997
@@ -228,6 +228,11 @@
 #define PCI_DEVICE_ID_NCR_53C820	0x0002
 #define PCI_DEVICE_ID_NCR_53C825	0x0003
 #define PCI_DEVICE_ID_NCR_53C815	0x0004
+#define PCI_DEVICE_ID_NCR_53C860	0x0006
+#define PCI_DEVICE_ID_NCR_53C896	0x000b
+#define PCI_DEVICE_ID_NCR_53C895	0x000c
+#define PCI_DEVICE_ID_NCR_53C885	0x000d
+#define PCI_DEVICE_ID_NCR_53C875	0x000f
 
 #define PCI_VENDOR_ID_ATI		0x1002
 #define PCI_DEVICE_ID_ATI_68800		0x4158
diff -u --recursive --new-file v2.1.28/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.28/linux/include/linux/proc_fs.h	Tue Mar  4 10:25:26 1997
+++ linux/include/linux/proc_fs.h	Fri Mar  7 12:51:45 1997
@@ -224,7 +224,7 @@
 	struct proc_dir_entry *next, *parent, *subdir;
 	void *data;
 	int (*read_proc)(char *page, char **start, off_t off,
-			 int count, void *data);
+			 int count, int *eof, void *data);
 	int (*write_proc)(struct file *file, const char *buffer,
 			  unsigned long count, void *data);
 };
@@ -233,8 +233,8 @@
 				off_t offset, int length, int inout);
 
 extern struct proc_dir_entry proc_root;
-extern struct proc_dir_entry proc_net;
-extern struct proc_dir_entry proc_scsi;
+extern struct proc_dir_entry *proc_net;
+extern struct proc_dir_entry *proc_scsi;
 extern struct proc_dir_entry proc_sys;
 extern struct proc_dir_entry proc_openprom;
 extern struct proc_dir_entry proc_pid;
@@ -248,18 +248,16 @@
 extern void proc_net_init(void);
 
 extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *);
-extern int proc_register_dynamic(struct proc_dir_entry *, 
-				 struct proc_dir_entry *);
 extern int proc_unregister(struct proc_dir_entry *, int);
 
 static inline int proc_net_register(struct proc_dir_entry * x)
 {
-	return proc_register(&proc_net, x);
+	return proc_register(proc_net, x);
 }
 
 static inline int proc_net_unregister(int x)
 {
-	return proc_unregister(&proc_net, x);
+	return proc_unregister(proc_net, x);
 }
 
 static inline int proc_scsi_register(struct proc_dir_entry *driver, 
@@ -267,7 +265,7 @@
 {
     x->ops = &proc_scsi_inode_operations;
     if(x->low_ino < PROC_SCSI_FILE){
-	return(proc_register(&proc_scsi, x));
+	return(proc_register(proc_scsi, x));
     }else{
 	return(proc_register(driver, x));
     }
@@ -278,7 +276,7 @@
     extern void scsi_init_free(char *ptr, unsigned int size);
 
     if(x <= PROC_SCSI_FILE)
-	return(proc_unregister(&proc_scsi, x));
+	return(proc_unregister(proc_scsi, x));
     else {
 	struct proc_dir_entry **p = &driver->subdir, *dp;
 	int ret;
@@ -355,6 +353,7 @@
  */
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
 					 struct proc_dir_entry *parent);
+void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
 
 /*
  * proc_tty.c
diff -u --recursive --new-file v2.1.28/linux/include/linux/sc26198.h linux/include/linux/sc26198.h
--- v2.1.28/linux/include/linux/sc26198.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sc26198.h	Tue Mar  4 13:33:21 1997
@@ -0,0 +1,533 @@
+/*****************************************************************************/
+
+/*
+ *	sc26198.h  -- SC26198 UART hardware info.
+ *
+ *	Copyright (C) 1995-1997  Stallion Technologies (support@stallion.oz.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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef	_SC26198_H
+#define	_SC26198_H
+/*****************************************************************************/
+
+/*
+ *	Define the number of async ports per sc26198 uart device.
+ */
+#define	SC26198_PORTS	8
+
+/*
+ *	Baud rate timing clocks. All derived from a master 14.7456 MHz clock.
+ */
+#define	SC26198_MASTERCLOCK		14745600L
+#define	SC26198_DCLK			(SC26198_MASTERCLOCK)
+#define	SC26198_CCLK			(SC26198_MASTERCLOCK / 2)
+#define	SC26198_BCLK			(SC26198_MASTERCLOCK / 4)
+
+/*
+ *	Define internal FIFO sizes for the 26198 ports.
+ */
+#define	SC26198_TXFIFOSIZE	16
+#define	SC26198_RXFIFOSIZE	16
+
+/*****************************************************************************/
+
+/*
+ *	Global register definitions. These registers are global to each 26198
+ *	device, not specific ports on it.
+ */
+#define	TSTR		0x0d
+#define	GCCR		0x0f
+#define	ICR		0x1b
+#define	WDTRCR		0x1d
+#define	IVR		0x1f
+#define	BRGTRUA		0x84
+#define	GPOSR		0x87
+#define	GPOC		0x8b
+#define	UCIR		0x8c
+#define	CIR		0x8c
+#define	BRGTRUB		0x8d
+#define	GRXFIFO		0x8e
+#define	GTXFIFO		0x8e
+#define	GCCR2		0x8f
+#define	BRGTRLA		0x94
+#define	GPOR		0x97
+#define	GPOD		0x9b
+#define	BRGTCR		0x9c
+#define	GICR		0x9c
+#define	BRGTRLB		0x9d
+#define	GIBCR		0x9d
+#define	GITR		0x9f
+
+/*
+ *	Per port channel registers. These are the register offsets within
+ *	the port address space, so need to have the port address (0 to 7)
+ *	inserted in bit positions 4:6.
+ */
+#define	MR0		0x00
+#define	MR1		0x01
+#define	IOPCR		0x02
+#define	BCRBRK		0x03
+#define	BCRCOS		0x04
+#define	BCRX		0x06
+#define	BCRA		0x07
+#define	XONCR		0x08
+#define	XOFFCR		0x09
+#define	ARCR		0x0a
+#define	RXCSR		0x0c
+#define	TXCSR		0x0e
+#define	MR2		0x80
+#define	SR		0x81
+#define SCCR		0x81
+#define	ISR		0x82
+#define	IMR		0x82
+#define	TXFIFO		0x83
+#define	RXFIFO		0x83
+#define	IPR		0x84
+#define	IOPIOR		0x85
+#define	XISR		0x86
+
+/*
+ *	For any given port calculate the address to use to access a specified
+ *	register. This is only used for unusual access, mostly this is done
+ *	through the assembler access routines.
+ */
+#define	SC26198_PORTREG(port,reg)	((((port) & 0x07) << 4) | (reg))
+
+/*****************************************************************************/
+
+/*
+ *	Global configuration control register bit definitions.
+ */
+#define	GCCR_NOACK		0x00
+#define	GCCR_IVRACK		0x02
+#define	GCCR_IVRCHANACK		0x04
+#define	GCCR_IVRTYPCHANACK	0x06
+#define	GCCR_ASYNCCYCLE		0x00
+#define	GCCR_SYNCCYCLE		0x40
+
+/*****************************************************************************/
+
+/*
+ *	Mode register 0 bit definitions.
+ */
+#define	MR0_ADDRNONE		0x00
+#define	MR0_AUTOWAKE		0x01
+#define	MR0_AUTODOZE		0x02
+#define	MR0_AUTOWAKEDOZE	0x03
+#define	MR0_SWFNONE		0x00
+#define	MR0_SWFTX		0x04
+#define	MR0_SWFRX		0x08
+#define	MR0_SWFRXTX		0x0c
+#define	MR0_TXMASK		0x30
+#define	MR0_TXEMPTY		0x00
+#define	MR0_TXHIGH		0x10
+#define	MR0_TXHALF		0x20
+#define	MR0_TXRDY		0x00
+#define	MR0_ADDRNT		0x00
+#define	MR0_ADDRT		0x40
+#define	MR0_SWFNT		0x00
+#define	MR0_SWFT		0x80
+
+/*
+ *	Mode register 1 bit definitions.
+ */
+#define	MR1_CS5			0x00
+#define	MR1_CS6			0x01
+#define	MR1_CS7			0x02
+#define	MR1_CS8			0x03
+#define	MR1_PAREVEN		0x00
+#define	MR1_PARODD		0x04
+#define	MR1_PARENB		0x00
+#define	MR1_PARFORCE		0x08
+#define	MR1_PARNONE		0x10
+#define	MR1_PARSPECIAL		0x18
+#define	MR1_ERRCHAR		0x00
+#define	MR1_ERRBLOCK		0x20
+#define	MR1_ISRUNMASKED		0x00
+#define	MR1_ISRMASKED		0x40
+#define	MR1_AUTORTS		0x80
+
+/*
+ *	Mode register 2 bit definitions.
+ */
+#define	MR2_STOP1		0x00
+#define	MR2_STOP15		0x01
+#define	MR2_STOP2		0x02
+#define	MR2_STOP916		0x03
+#define	MR2_RXFIFORDY		0x00
+#define	MR2_RXFIFOHALF		0x04
+#define	MR2_RXFIFOHIGH		0x08
+#define	MR2_RXFIFOFULL		0x0c
+#define	MR2_AUTOCTS		0x10
+#define	MR2_TXRTS		0x20
+#define	MR2_MODENORM		0x00
+#define	MR2_MODEAUTOECHO	0x40
+#define	MR2_MODELOOP		0x80
+#define	MR2_MODEREMECHO		0xc0
+
+/*****************************************************************************/
+
+/*
+ *	Baud Rate Generator (BRG) selector values.
+ */
+#define	BRG_50			0x00
+#define	BRG_75			0x01
+#define	BRG_150			0x02
+#define	BRG_200			0x03
+#define	BRG_300			0x04
+#define	BRG_450			0x05
+#define	BRG_600			0x06
+#define	BRG_900			0x07
+#define	BRG_1200		0x08
+#define	BRG_1800		0x09
+#define	BRG_2400		0x0a
+#define	BRG_3600		0x0b
+#define	BRG_4800		0x0c
+#define	BRG_7200		0x0d
+#define	BRG_9600		0x0e
+#define	BRG_14400		0x0f
+#define	BRG_19200		0x10
+#define	BRG_28200		0x11
+#define	BRG_38400		0x12
+#define	BRG_57600		0x13
+#define	BRG_115200		0x14
+#define	BRG_230400		0x15
+#define	BRG_GIN0		0x16
+#define	BRG_GIN1		0x17
+#define	BRG_CT0			0x18
+#define	BRG_CT1			0x19
+#define	BRG_RX2TX316		0x1b
+#define	BRG_RX2TX31		0x1c
+
+#define	SC26198_MAXBAUD		921600
+
+/*****************************************************************************/
+
+/*
+ *	Command register command definitions.
+ */
+#define	CR_NULL			0x04
+#define	CR_ADDRNORMAL		0x0c
+#define	CR_RXRESET		0x14
+#define	CR_TXRESET		0x1c
+#define	CR_CLEARRXERR		0x24
+#define	CR_BREAKRESET		0x2c
+#define	CR_TXSTARTBREAK		0x34
+#define	CR_TXSTOPBREAK		0x3c
+#define	CR_RTSON		0x44
+#define	CR_RTSOFF		0x4c
+#define	CR_ADDRINIT		0x5c
+#define	CR_RXERRBLOCK		0x6c
+#define	CR_TXSENDXON		0x84
+#define	CR_TXSENDXOFF		0x8c
+#define	CR_GANGXONSET		0x94
+#define	CR_GANGXOFFSET		0x9c
+#define	CR_GANGXONINIT		0xa4
+#define	CR_GANGXOFFINIT		0xac
+#define	CR_HOSTXON		0xb4
+#define	CR_HOSTXOFF		0xbc
+#define	CR_CANCELXOFF		0xc4
+#define	CR_ADDRRESET		0xdc
+#define	CR_RESETALLPORTS	0xf4
+#define	CR_RESETALL		0xfc
+
+#define	CR_RXENABLE		0x01
+#define	CR_TXENABLE		0x02
+
+/*****************************************************************************/
+
+/*
+ *	Channel status register.
+ */
+#define	SR_RXRDY		0x01
+#define	SR_RXFULL		0x02
+#define	SR_TXRDY		0x04
+#define	SR_TXEMPTY		0x08
+#define	SR_RXOVERRUN		0x10
+#define	SR_RXPARITY		0x20
+#define	SR_RXFRAMING		0x40
+#define	SR_RXBREAK		0x80
+
+#define	SR_RXERRS		(SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN)
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt status register and interrupt mask register bit definitions.
+ */
+#define	IR_TXRDY		0x01
+#define	IR_RXRDY		0x02
+#define	IR_RXBREAK		0x04
+#define	IR_XONXOFF		0x10
+#define	IR_ADDRRECOG		0x20
+#define	IR_RXWATCHDOG		0x40
+#define	IR_IOPORT		0x80
+
+/*****************************************************************************/
+
+/*
+ *	Interrupt vector register field definitions.
+ */
+#define	IVR_CHANMASK		0x07
+#define	IVR_TYPEMASK		0x18
+#define	IVR_CONSTMASK		0xc0
+
+#define	IVR_RXDATA		0x10
+#define	IVR_RXBADDATA		0x18
+#define	IVR_TXDATA		0x08
+#define	IVR_OTHER		0x00
+
+/*****************************************************************************/
+
+/*
+ *	BRG timer control register bit definitions.
+ */
+#define	BRGCTCR_DISABCLK0	0x00
+#define	BRGCTCR_ENABCLK0	0x08
+#define	BRGCTCR_DISABCLK1	0x00
+#define	BRGCTCR_ENABCLK1	0x80
+
+#define	BRGCTCR_0SCLK16		0x00
+#define	BRGCTCR_0SCLK32		0x01
+#define	BRGCTCR_0SCLK64		0x02
+#define	BRGCTCR_0SCLK128	0x03
+#define	BRGCTCR_0X1		0x04
+#define	BRGCTCR_0X12		0x05
+#define	BRGCTCR_0IO1A		0x06
+#define	BRGCTCR_0GIN0		0x07
+
+#define	BRGCTCR_1SCLK16		0x00
+#define	BRGCTCR_1SCLK32		0x10
+#define	BRGCTCR_1SCLK64		0x20
+#define	BRGCTCR_1SCLK128	0x30
+#define	BRGCTCR_1X1		0x40
+#define	BRGCTCR_1X12		0x50
+#define	BRGCTCR_1IO1B		0x60
+#define	BRGCTCR_1GIN1		0x70
+
+/*****************************************************************************/
+
+/*
+ *	Watch dog timer enable register.
+ */
+#define	WDTRCR_ENABALL		0xff
+
+/*****************************************************************************/
+
+/*
+ *	XON/XOFF interrupt status register.
+ */
+#define	XISR_TXCHARMASK		0x03
+#define	XISR_TXCHARNORMAL	0x00
+#define	XISR_TXWAIT		0x01
+#define	XISR_TXXOFFPEND		0x02
+#define	XISR_TXXONPEND		0x03
+
+#define	XISR_TXFLOWMASK		0x0c
+#define	XISR_TXNORMAL		0x00
+#define	XISR_TXSTOPPEND		0x04
+#define	XISR_TXSTARTED		0x08
+#define	XISR_TXSTOPPED		0x0c
+
+#define	XISR_RXFLOWMASK		0x30
+#define	XISR_RXFLOWNONE		0x00
+#define	XISR_RXXONSENT		0x10
+#define	XISR_RXXOFFSENT		0x20
+
+#define	XISR_RXXONGOT		0x40
+#define	XISR_RXXOFFGOT		0x80
+
+/*****************************************************************************/
+
+/*
+ *	Current interrupt register.
+ */
+#define	CIR_TYPEMASK		0xc0
+#define	CIR_TYPEOTHER		0x00
+#define	CIR_TYPETX		0x40
+#define	CIR_TYPERXGOOD		0x80
+#define	CIR_TYPERXBAD		0xc0
+
+#define	CIR_RXDATA		0x80
+#define	CIR_RXBADDATA		0x40
+#define	CIR_TXDATA		0x40
+
+#define	CIR_CHANMASK		0x07
+#define	CIR_CNTMASK		0x38
+
+#define	CIR_SUBTYPEMASK		0x38
+#define	CIR_SUBNONE		0x00
+#define	CIR_SUBCOS		0x08
+#define	CIR_SUBADDR		0x10
+#define	CIR_SUBXONXOFF		0x18
+#define	CIR_SUBBREAK		0x28
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting channel register.
+ */
+#define	GICR_CHANMASK		0x07
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting byte count register.
+ */
+#define	GICR_COUNTMASK		0x0f
+
+/*****************************************************************************/
+
+/*
+ *	Global interrupting type register.
+ */
+#define	GITR_RXMASK		0xc0
+#define	GITR_RXNONE		0x00
+#define	GITR_RXBADDATA		0x80
+#define	GITR_RXGOODDATA		0xc0
+#define	GITR_TXDATA		0x20
+
+#define	GITR_SUBTYPEMASK	0x07
+#define	GITR_SUBNONE		0x00
+#define	GITR_SUBCOS		0x01
+#define	GITR_SUBADDR		0x02
+#define	GITR_SUBXONXOFF		0x03
+#define	GITR_SUBBREAK		0x05
+
+/*****************************************************************************/
+
+/*
+ *	Input port change register.
+ */
+#define	IPR_CTS			0x01
+#define	IPR_DTR			0x02
+#define	IPR_RTS			0x04
+#define	IPR_DCD			0x08
+#define	IPR_CTSCHANGE		0x10
+#define	IPR_DTRCHANGE		0x20
+#define	IPR_RTSCHANGE		0x40
+#define	IPR_DCDCHANGE		0x80
+
+#define	IPR_CHANGEMASK		0xf0
+
+/*****************************************************************************/
+
+/*
+ *	IO port interrupt and output register.
+ */
+#define	IOPR_CTS		0x01
+#define	IOPR_DTR		0x02
+#define	IOPR_RTS		0x04
+#define	IOPR_DCD		0x08
+#define	IOPR_CTSCOS		0x10
+#define	IOPR_DTRCOS		0x20
+#define	IOPR_RTSCOS		0x40
+#define	IOPR_DCDCOS		0x80
+
+/*****************************************************************************/
+
+/*
+ *	IO port configuration register.
+ */
+#define	IOPCR_SETCTS		0x00
+#define	IOPCR_SETDTR		0x04
+#define	IOPCR_SETRTS		0x10
+#define	IOPCR_SETDCD		0x00
+
+#define	IOPCR_SETSIGS		(IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD)
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output select register.
+ */
+#define	GPORS_TXC1XA		0x08
+#define	GPORS_TXC16XA		0x09
+#define	GPORS_RXC16XA		0x0a
+#define	GPORS_TXC16XB		0x0b
+#define	GPORS_GPOR3		0x0c
+#define	GPORS_GPOR2		0x0d
+#define	GPORS_GPOR1		0x0e
+#define	GPORS_GPOR0		0x0f
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output register.
+ */
+#define	GPOR_0			0x01
+#define	GPOR_1			0x02
+#define	GPOR_2			0x04
+#define	GPOR_3			0x08
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output clock register.
+ */
+#define	GPORC_0NONE		0x00
+#define	GPORC_0GIN0		0x01
+#define	GPORC_0GIN1		0x02
+#define	GPORC_0IO3A		0x02
+
+#define	GPORC_1NONE		0x00
+#define	GPORC_1GIN0		0x04
+#define	GPORC_1GIN1		0x08
+#define	GPORC_1IO3C		0x0c
+
+#define	GPORC_2NONE		0x00
+#define	GPORC_2GIN0		0x10
+#define	GPORC_2GIN1		0x20
+#define	GPORC_2IO3E		0x20
+
+#define	GPORC_3NONE		0x00
+#define	GPORC_3GIN0		0x40
+#define	GPORC_3GIN1		0x80
+#define	GPORC_3IO3G		0xc0
+
+/*****************************************************************************/
+
+/*
+ *	General purpose output data register.
+ */
+#define	GPOD_0MASK		0x03
+#define	GPOD_0SET1		0x00
+#define	GPOD_0SET0		0x01
+#define	GPOD_0SETR0		0x02
+#define	GPOD_0SETIO3B		0x03
+
+#define	GPOD_1MASK		0x0c
+#define	GPOD_1SET1		0x00
+#define	GPOD_1SET0		0x04
+#define	GPOD_1SETR0		0x08
+#define	GPOD_1SETIO3D		0x0c
+
+#define	GPOD_2MASK		0x30
+#define	GPOD_2SET1		0x00
+#define	GPOD_2SET0		0x10
+#define	GPOD_2SETR0		0x20
+#define	GPOD_2SETIO3F		0x30
+
+#define	GPOD_3MASK		0xc0
+#define	GPOD_3SET1		0x00
+#define	GPOD_3SET0		0x40
+#define	GPOD_3SETR0		0x80
+#define	GPOD_3SETIO3H		0xc0
+
+/*****************************************************************************/
+#endif
diff -u --recursive --new-file v2.1.28/linux/include/linux/stallion.h linux/include/linux/stallion.h
--- v2.1.28/linux/include/linux/stallion.h	Sat Apr 20 01:25:31 1996
+++ linux/include/linux/stallion.h	Tue Mar  4 13:33:21 1997
@@ -3,6 +3,7 @@
 /*
  *	stallion.h  -- stallion multiport serial driver.
  *
+ *	Copyright (C) 1996-1997  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -30,6 +31,7 @@
  */
 #define	STL_MAXBRDS		4
 #define	STL_MAXPANELS		4
+#define	STL_MAXBANKS		8
 #define	STL_PORTSPERPANEL	16
 #define	STL_MAXPORTS		64
 #define	STL_MAXDEVS		(STL_MAXBRDS * STL_MAXPORTS)
@@ -65,7 +67,7 @@
  *	is associated with, this makes it (fairly) easy to get back to the
  *	board/panel info for a port.
  */
-typedef struct {
+typedef struct stlport {
 	unsigned long		magic;
 	int			portnr;
 	int			panelnr;
@@ -87,8 +89,11 @@
 	unsigned int		sigs;
 	unsigned int		rxignoremsk;
 	unsigned int		rxmarkmsk;
+	unsigned int		imr;
+	unsigned int		crenable;
 	unsigned long		clk;
 	unsigned long		hwid;
+	void			*uartp;
 	struct tty_struct	*tty;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
@@ -99,27 +104,31 @@
 	stlrq_t			tx;
 } stlport_t;
 
-typedef struct {
+typedef struct stlpanel {
 	unsigned long	magic;
 	int		panelnr;
 	int		brdnr;
 	int		pagenr;
 	int		nrports;
 	int		iobase;
+	void		*uartp;
+	void		(*isr)(struct stlpanel *panelp, unsigned int iobase);
 	unsigned int	hwid;
 	unsigned int	ackmask;
 	stlport_t	*ports[STL_PORTSPERPANEL];
 } stlpanel_t;
 
-typedef struct {
+typedef struct stlbrd {
 	unsigned long	magic;
 	int		brdnr;
 	int		brdtype;
 	int		state;
 	int		nrpanels;
 	int		nrports;
+	int		nrbnks;
 	int		irq;
 	int		irqtype;
+	void		(*isr)(struct stlbrd *brdp);
 	unsigned int	ioaddr1;
 	unsigned int	ioaddr2;
 	unsigned int	iostatus;
@@ -127,6 +136,9 @@
 	unsigned int	ioctrlval;
 	unsigned int	hwid;
 	unsigned long	clk;
+	unsigned int	bnkpageaddr[STL_MAXBANKS];
+	unsigned int	bnkstataddr[STL_MAXBANKS];
+	stlpanel_t	*bnk2panel[STL_MAXBANKS];
 	stlpanel_t	*panels[STL_MAXPANELS];
 } stlbrd_t;
 
diff -u --recursive --new-file v2.1.28/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- v2.1.28/linux/include/linux/tty_driver.h	Tue Mar  4 10:25:26 1997
+++ linux/include/linux/tty_driver.h	Fri Mar  7 12:51:45 1997
@@ -153,7 +153,7 @@
 	void (*wait_until_sent)(struct tty_struct *tty, int timeout);
 	void (*send_xchar)(struct tty_struct *tty, char ch);
 	int (*read_proc)(char *page, char **start, off_t off,
-			  int count, void *data);
+			  int count, int *eof, void *data);
 	int (*write_proc)(struct file *file, const char *buffer,
 			  unsigned long count, void *data);
 
diff -u --recursive --new-file v2.1.28/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.1.28/linux/include/linux/wireless.h	Thu Feb  6 02:59:18 1997
+++ linux/include/linux/wireless.h	Tue Mar  4 13:19:06 1997
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :	3	18.12.96
+ * Version :	4	12.2.97
  *
  * Authors :	Jean Tourrilhes - HPLB - <jt@hplb.hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	3
+#define WIRELESS_EXT	4
 
 /*
  * Changes :
@@ -73,6 +73,10 @@
  *	Alan Cox start some imcompatibles changes. I've integrated a bit more.
  *	- Encryption renamed to Encode to avoid US regulation problems
  *	- Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ *	- Add sensitivity
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -86,6 +90,8 @@
 #define SIOCGIWFREQ	0x8B05		/* get channel/frequency */
 #define SIOCSIWENCODE	0x8B06		/* set encoding info */
 #define SIOCGIWENCODE	0x8B07		/* get encoding info */
+#define SIOCSIWSENS	0x8B08		/* set sensitivity */
+#define SIOCGIWSENS	0x8B09		/* get sensitivity */
 
 /* Informative stuff */
 #define SIOCSIWRANGE	0x8B0A		/* Unused ??? */
@@ -234,6 +240,8 @@
 			__u64	code;		/* Data used for algorithm */
 		}	encoding;
 
+		__u32	sensitivity;	/* signal level threshold */
+
 		struct		/* For all data bigger than 16 octets */
 		{
 			caddr_t	pointer;	/* Pointer to the data
@@ -271,6 +279,9 @@
 	/* Note : this frequency list doesn't need to fit channel numbers */
 
 	/* Encoder stuff */
+
+	/* signal level threshold range */
+	__u32	sensitivity;
 
 	/* Quality of link & SNR stuff */
 	struct iw_quality	max_qual;	/* Quality of the link */
diff -u --recursive --new-file v2.1.28/linux/init/main.c linux/init/main.c
--- v2.1.28/linux/init/main.c	Thu Feb 27 10:57:31 1997
+++ linux/init/main.c	Fri Mar  7 12:51:45 1997
@@ -887,6 +887,7 @@
 	}
 #endif
 	mem_init(memory_start,memory_end);
+	proc_root_init();
 	kmem_cache_sizes_init();
 	vma_init();
 	buffer_init();
diff -u --recursive --new-file v2.1.28/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.28/linux/kernel/sysctl.c	Thu Feb 27 10:57:31 1997
+++ linux/kernel/sysctl.c	Fri Mar  7 12:51:45 1997
@@ -424,11 +424,12 @@
 /* Scan the sysctl entries in table and add them all into /proc */
 static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
 {
-	struct proc_dir_entry *de, *tmp;
-	int exists;
+	struct proc_dir_entry *de;
+	int len;
+	mode_t mode;
 	
 	for (; table->ctl_name; table++) {
-		exists = 0;
+		de = 0;
 		/* Can't do anything without a proc name. */
 		if (!table->procname)
 			continue;
@@ -436,46 +437,32 @@
 		if (!table->proc_handler &&
 		    !table->child)
 			continue;
-		
-		de = kmalloc(sizeof(*de), GFP_KERNEL);
-		if (!de) continue;
-		de->namelen = strlen(table->procname);
-		de->name = table->procname;
-		de->mode = table->mode;
-		de->nlink = 1;
-		de->uid = 0;
-		de->gid = 0;
-		de->size = 0;
-		de->get_info = 0;	/* For internal use if we want it */
-		de->fill_inode = 0;	/* To override struct inode fields */
-		de->next = de->subdir = 0;
-		de->data = (void *) table;
-		/* Is it a file? */
-		if (table->proc_handler) {
-			de->ops = &proc_sys_inode_operations;
-			de->mode |= S_IFREG;
-		}
-		/* Otherwise it's a subdir */
-		else  {
-			/* First check to see if it already exists */
-			for (tmp = root->subdir; tmp; tmp = tmp->next) {
-				if (tmp->namelen == de->namelen &&
-				    !memcmp(tmp->name,de->name,de->namelen)) {
-					exists = 1;
-					kfree (de);
-					de = tmp;
-				}
-			}
-			if (!exists) {
-				de->ops = &proc_dir_inode_operations;
-				de->nlink++;
-				de->mode |= S_IFDIR;
+
+		len = strlen(table->procname);
+		mode = table->mode;
+
+		if (table->proc_handler)
+			mode |= S_IFREG;
+		else {
+			mode |= S_IFDIR;
+			for (de = root->subdir; de; de = de->next) {
+				if (proc_match(len, table->procname, de))
+					break;
 			}
+			/* If the subdir exists already, de is non-NULL */
+		}
+
+		if (!de) {
+			de = create_proc_entry(table->procname, mode, root);
+			if (!de)
+				continue;
+			de->data = (void *) table;
+			if (table->proc_handler)
+				de->ops = &proc_sys_inode_operations;
+
 		}
 		table->de = de;
-		if (!exists)
-			proc_register_dynamic(root, de);
-		if (de->mode & S_IFDIR )
+		if (de->mode & S_IFDIR)
 			register_proc_table(table->child, de);
 	}
 }
diff -u --recursive --new-file v2.1.28/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.28/linux/net/ipv4/tcp_ipv4.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv4/tcp_ipv4.c	Wed Mar  5 17:04:34 1997
@@ -230,8 +230,7 @@
 		}
 		sk->hashtable = NULL;
 	}
-	if(sk->state == TCP_CLOSE && sk->dead)
-		tcp_sk_unbindify(sk);
+	tcp_sk_unbindify(sk);
 	SOCKHASH_UNLOCK();
 }
 
@@ -251,8 +250,8 @@
 			}
 			htable = &((*htable)->next);
 		}
+		tcp_sk_unbindify(sk);
 	}
-	tcp_sk_unbindify(sk);
 	htable = NULL;
 	if(state != TCP_CLOSE || !sk->dead) {
 		if(state == TCP_LISTEN) {
diff -u --recursive --new-file v2.1.28/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.28/linux/net/ipv6/addrconf.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/addrconf.c	Fri Mar  7 12:51:45 1997
@@ -1350,7 +1350,7 @@
 	if (dev && (dev->flags & IFF_UP))
 		addrconf_eth_config(dev);
 	
-	proc_register_dynamic(&proc_net, &iface_proc_entry);
+	proc_register(&proc_net, &iface_proc_entry);
 	
 	addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
 	add_timer(&addr_chk_timer);
diff -u --recursive --new-file v2.1.28/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.28/linux/net/ipv6/ndisc.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/ndisc.c	Fri Mar  7 12:51:45 1997
@@ -1843,11 +1843,7 @@
 	add_timer(&ndisc_gc_timer);
 
 #ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IPV6_MODULE
-        proc_register_dynamic(&proc_net, &ndisc_proc_entry);
-#else
 	proc_net_register(&ndisc_proc_entry);
-#endif
 #endif
 #ifdef CONFIG_IPV6_MODULE
 	ndisc_eth_hook = ndisc_eth_resolv;
diff -u --recursive --new-file v2.1.28/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.28/linux/net/ipv6/udp.c	Tue Mar  4 10:25:27 1997
+++ linux/net/ipv6/udp.c	Wed Mar  5 17:04:34 1997
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.8 1997/02/28 09:56:35 davem Exp $
+ *	$Id: udp.c,v 1.9 1997/03/04 10:41:59 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -324,9 +324,10 @@
 	if(skb==NULL)
   		return err;
   
-  	truesize = skb->tail - skb->h.raw - sizeof(struct udphdr);
+ 	truesize=ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
   	
   	copied=truesize;
+
   	if(copied>len)
   	{
   		copied=len;
@@ -357,7 +358,7 @@
 		if (skb->protocol == __constant_htons(ETH_P_IP))
 		{
 			ipv6_addr_set(&sin6->sin6_addr, 0, 0,
-				      __constant_htonl(0xffff), skb->nh.iph->daddr);
+				      __constant_htonl(0xffff), skb->nh.iph->saddr);
 		}
 		else
 		{
diff -u --recursive --new-file v2.1.28/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.28/linux/net/unix/af_unix.c	Thu Feb 27 10:57:32 1997
+++ linux/net/unix/af_unix.c	Fri Mar  7 12:51:44 1997
@@ -1366,7 +1366,8 @@
 }
 
 #ifdef CONFIG_PROC_FS
-static int unix_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+static int unix_read_proc(char *buffer, char **start, off_t offset,
+			  int length, int *eof, void *data)
 {
 	off_t pos=0;
 	off_t begin=0;
@@ -1410,6 +1411,7 @@
 		if(pos>offset+length)
 			goto done;
 	}
+	*eof = 1;
 done:
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
@@ -1466,19 +1468,11 @@
 	unix_create
 };
 
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry proc_net_unix = {
-	PROC_NET_UNIX,  4, "unix",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	unix_get_info
-};
-#endif
-
-
 void unix_proto_init(struct net_proto *pro)
 {
 	struct sk_buff *dummy_skb;
+	struct proc_dir_entry *ent;
+	
 	printk(KERN_INFO "NET3: Unix domain sockets 0.15 for Linux NET3.038.\n");
 	if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
 	{
@@ -1487,7 +1481,8 @@
 	}
 	sock_register(&unix_family_ops);
 #ifdef CONFIG_PROC_FS
-	proc_net_register(&proc_net_unix);
+	ent = create_proc_entry("net/unix", 0, 0);
+	ent->read_proc = unix_read_proc;
 #endif
 }
 /*
diff -u --recursive --new-file v2.1.28/linux/net/wanrouter/wanproc.c linux/net/wanrouter/wanproc.c
--- v2.1.28/linux/net/wanrouter/wanproc.c	Sun Feb  2 06:44:21 1997
+++ linux/net/wanrouter/wanproc.c	Fri Mar  7 12:51:45 1997
@@ -273,13 +273,13 @@
 
 int wanrouter_proc_init (void)
 {
-	int err = proc_register_dynamic(&proc_net, &proc_router);
+	int err = proc_register(&proc_net, &proc_router);
 
 	if (!err)
 	{
-		proc_register_dynamic(&proc_router, &proc_router_info);
-		proc_register_dynamic(&proc_router, &proc_router_conf);
-		proc_register_dynamic(&proc_router, &proc_router_stat);
+		proc_register(&proc_router, &proc_router_info);
+		proc_register(&proc_router, &proc_router_conf);
+		proc_register(&proc_router, &proc_router_stat);
 	}
 	return err;
 }
@@ -313,7 +313,7 @@
 	wandev->dent.ops	= &wandev_inode;
 	wandev->dent.get_info	= &wandev_get_info;
 	wandev->dent.data	= wandev;
-	return proc_register_dynamic(&proc_router, &wandev->dent);
+	return proc_register(&proc_router, &wandev->dent);
 }
 
 /*
diff -u --recursive --new-file v2.1.28/linux/scripts/Configure linux/scripts/Configure
--- v2.1.28/linux/scripts/Configure	Thu Feb 27 10:57:32 1997
+++ linux/scripts/Configure	Mon Mar 10 12:38:56 1997
@@ -43,6 +43,9 @@
 #
 # 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is
 # chosen for an item, define the macro <option_name>_MODULE
+#
+# 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
+# expressions for GNU expr since version 1.15 and up use \? and \+.
 
 #
 # Make sure we're really running bash.
@@ -57,16 +60,6 @@
 # Enable function cacheing.
 set -f -h
 
-# Newer shellutils have a more POSIX compliant behavior which is
-# sadly not backward compatible.
-if expr "$(expr --version)" : '.*1\.1[56]' > /dev/null; then
-   INT_FILTER='0$\|-\?[1-9][0-9]*$'
-   HEX_FILTER='[0-9a-fA-F]\+$'
-else
-   INT_FILTER='0$\|-?[1-9][0-9]*$'
-   HEX_FILTER='[0-9a-fA-F]+$'
-fi
-
 #
 # Dummy functions for use with a config.in modified for menuconf
 #
@@ -299,7 +292,7 @@
 	def=${old:-$3}
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : $INT_FILTER > /dev/null; then
+	  if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
 	    define_int "$2" "$ans"
 	    break
 	  else
@@ -330,12 +323,12 @@
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
 	  ans=${ans#*[x,X]}
-	 if expr "$ans" : $HEX_FILTER > /dev/null; then
-	   define_hex "$2" "$ans"
-	   break
-	 else
+	  if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
+	    define_hex "$2" "$ans"
+	    break
+	  else
 	    help "$2"
-	 fi
+	  fi
 	done
 }
 
diff -u --recursive --new-file v2.1.28/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.1.28/linux/scripts/Menuconfig	Fri Feb  7 05:54:55 1997
+++ linux/scripts/Menuconfig	Mon Mar 10 12:38:56 1997
@@ -400,7 +400,9 @@
 			answer="`cat MCdialog.out`"
 			answer="${answer:-$3}"
 
-			if expr $answer : '0$\|-\?[1-9][0-9]*$' >/dev/null
+			# Semantics of + and ? in GNU expr changed, so
+			# we avoid them:
+			if expr "$answer" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -433,7 +435,7 @@
 			answer="${answer:-$3}"
 			answer="${answer##*[x,X]}"
 
-			if expr $answer : '[0-9a-fA-F]\+$' >/dev/null
+			if expr "$answer" : '[0-9a-fA-F][0-9a-fA-F]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -452,7 +454,7 @@
 }
 
 #
-# Handle a on-of-many choice list.
+# Handle a one-of-many choice list.
 #
 function l_choice () {
 	#
@@ -493,7 +495,7 @@
 	done
 
 	#
-	# Now set the boolean value of each option base on
+	# Now set the boolean value of each option based on
 	# the selection made from the radiolist.
 	#
 	set -- $choices
