diff -u --recursive --new-file v2.1.32/linux/CREDITS linux/CREDITS
--- v2.1.32/linux/CREDITS	Mon Apr  7 11:35:28 1997
+++ linux/CREDITS	Wed Apr  9 21:30:30 1997
@@ -133,13 +133,12 @@
 D: Original author of the Linux networking code
 
 N: Philip Blundell
-E: pjb27@cam.ac.uk 
-E: pb@nexus.co.uk
-E: phil@tazenda.demon.co.uk
+E: Philip.Blundell@pobox.com
 D: Device driver hacking (especially EtherExpress16/3C505 net cards)
 D: Some Linux/ARM stuff
-S: Trinity College
-S: Cambridge, UK. CB2 1TQ
+D: Co-architect of the parallel port sharing system
+S: 201 Gilbert Road
+S: Cambridge, UK. CB4 3PA
 
 N: Thomas Bogendoerfer
 E: tsbogend@alpha.franken.de
@@ -510,6 +509,7 @@
 N: Grant Guenther
 E: grant@torque.net
 D: drivers for parallel port devices: ppa, ez, bpcd
+D: original architect of the parallel-port sharing scheme. 
 S: 906-1001 Bay St.
 S: Toronto, Ontario, M5S 3A6 
 S: Canada
@@ -696,9 +696,11 @@
 S: USA
 
 N: Bernhard Kaindl
+E: bkaindl@netway.at
 E: edv@bartelt.via.at
 D: Author of a menu based configuration tool, kmenu, which 
 D: is the predecessor of 'make menuconfig' and 'make xconfig'.
+D: digiboard driver update(modularisation work and 2.1.x upd)
 S: Tallak 95
 S: 8103 Rein
 S: Austria
@@ -826,7 +828,8 @@
 
 N: Hans Lermen
 E: lermen@elserv.ffm.fgan.de
-D: Author of the LOADLIN Linux loader
+D: Author of the LOADLIN Linux loader, hacking on boot stuff
+D: Co-ordinator of DOSEMU releases
 S: Am Muehlenweg 38
 S: D53424 Remagen
 S: Germany
@@ -888,7 +891,7 @@
 N: James B. MacLean
 E: macleajb@ednet.ns.ca
 W: http://www.ednet.ns.ca/~macleajb/dosemu.html
-D: Coordinator of DOSEMU releases
+D: Former Co-ordinator of DOSEMU releases
 D: Program in DOSEMU
 S: PO BOX 220, HFX. CENTRAL
 S: Halifax, Nova Scotia
@@ -1520,6 +1523,13 @@
 S: Kruislaan 419
 S: 1098 VA Amsterdam 
 S: The Netherlands
+
+N: Tim Waugh
+E: tmw20@cyberelk.demon.co.uk
+D: Co-architect of the parallel-port sharing system
+S: 51 Frensham Road
+S: Southsea
+S: Hampshire, UK. PO4 8AE
 
 N: Juergen Weigert
 E: jnweiger@immd4.informatik.uni-erlangen.de
diff -u --recursive --new-file v2.1.32/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.32/linux/Documentation/Configure.help	Mon Apr  7 11:35:28 1997
+++ linux/Documentation/Configure.help	Wed Apr  9 21:30:30 1997
@@ -835,6 +835,23 @@
   be necessary to run older Mips systems, such as the Sony News and
   MIPS RC3xxx, in big endian mode.
 
+Plug and Play support
+CONFIG_PNP
+  Plug and Play support allows the kernel to automatically configure some
+  peripheral devices.  Say Y to enable PnP.
+
+Parallel-port support
+CONFIG_PNP_PARPORT
+  If you want to use devices connected to your parallel port (printer,
+  Zip driver, PLIP link, ...) then you should enable this option and
+  read Documentation/parport.txt.
+
+Autoprobe for parallel device IDs
+CONFIG_PNP_PARPORT_AUTOPROBE
+  Some IEEE-1284 conformant parallel-port devices can identify themselves
+  when requested.  If this option is enabled the kernel will probe to see
+  what devices are connected at boot time.
+
 Enable loadable module support
 CONFIG_MODULES
   Kernel modules are small pieces of compiled code which can be 
@@ -2076,6 +2093,19 @@
   drive: it will be supported automatically if you said Y to the
   generic "SCSI disk support", above.
 
+EPP FIFO Checking
+CONFIG_SCSI_PPA_HAVE_PEDANTIC
+  Some chipsets are slower then their motherboard. We have to control
+  the state of the FIFO now and then.  The values are 0 (don't check
+  FIFO), 1 (check FIFO every 4 bytes), 2 (check FIFO every other byte)
+  and 3 (check FIFO every time). If your EPP chipset is from the SMC
+  series, you are likely to have to set this value greater than 0.
+
+EPP Timing
+CONFIG_SCSI_PPA_EPP_TIME
+  This is the "reset time period", a delay time.  Too low a value may
+  cause all sorts of mid-level SCSI problems.
+
 Network device support?
 CONFIG_NETDEVICES
   You can say N here in case you don't intend to connect to any other
@@ -4091,24 +4121,12 @@
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
-  Documentation/modules.txt. The module will be called plip.o. If you
-  want to use both a parallel printer and PLIP, there are two cases:
-  1) If the printer and the PLIP cable are to use the same parallel
-  port (presumably because you have just one), it is best to compile
-  both drivers as modules and load and unload them as needed. 2) To
-  use different parallel ports for the printer and the PLIP cable, you
-  can say Y to this printer driver, specify the base address of the
-  parallel port(s) to use for the printer(s) with the "lp" kernel
-  command line option. (Try "man bootparam" or see the documentation
-  of your boot loader (lilo or loadlin) about how to pass options to
-  the kernel at boot time. The lilo procedure is also explained in the
-  SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses
-  as well as the syntax of the "lp" command line option can be found
-  in drivers/char/lp.c. You can then say Y to the PLIP driver or,
-  preferably, M in which case Documentation/networking/net-modules.txt
-  tells you how to specify the port and IRQ to be used by PLIP at
-  module load time.
+  Documentation/modules.txt.
+
+CONFIG_PRINTER_READBACK
+  If your printer conforms to IEEE 1284, it may be able to provide a status
+  indication when you read from it (for example, with `cat /dev/lp1').  To
+  use this feature, say Y here.
 
 Mouse Support (not serial mice)
 CONFIG_MOUSE
diff -u --recursive --new-file v2.1.32/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.1.32/linux/Documentation/devices.tex	Mon Mar 17 14:54:20 1997
+++ linux/Documentation/devices.tex	Tue Apr  8 12:17:22 1997
@@ -46,7 +46,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: March 3, 1997}
+\date{Last revised: April 7, 1997}
 \maketitle
 %
 \noindent
@@ -171,6 +171,7 @@
 \major{  }{}{block}{MicroSolutions BackPack parallel port CD-ROM}
 \major{42}{}{}{Demo/sample use}
 \major{43}{}{char }{isdn4linux virtual modem}
+\major{  }{}{block}{Network block devices}
 \major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
 \major{45}{}{char }{isdn4linux ISDN BRI driver}
 \major{46}{}{char }{Comtrol Rocketport serial card}
@@ -199,7 +200,14 @@
 \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{75}{}{char }{Specialix IO8+ serial card}
+\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
+\major{77}{}{char }{ComScire Quantum Noise Generator}
+\major{78}{}{char }{PAM Software's multimodem boards}
+\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
+\major{80}{}{char }{Photometrics AT200 CCD camera}
+\major{81}{}{char }{Brooktree Bt848 frame grabbers}
+\major{82}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -494,6 +502,8 @@
 	\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}
+	\minor{143}{/dev/pciconf}{PCI configuration space}
+	\minor{144}{/dev/nvram}{Non-volatile configuration RAM}
 \end{devicelist}
 
 \begin{devicelist}
@@ -659,6 +669,11 @@
 	\minordots
 \end{devicelist}
 
+\noindent
+Most distributions name these {\file /dev/sga}, {\file /dev/sgb}...
+This sets an unneccesary limit of 26 SCSI devices in the system, and
+is counter to standard Linux device-naming practice.
+
 \begin{devicelist}
 \major{22}{}{char }{Digiboard serial card}
 	\minor{0}{/dev/ttyD0}{First Digiboard port}
@@ -1033,8 +1048,20 @@
 	\minor{0}{/dev/ttyI0}{First virtual modem}
 	\minordots
 	\minor{63}{/dev/ttyI63}{64th virtual modem}
+\\
+\major{  }{}{block}{Network block devices}
+	\minor{0}{/dev/nd0}{First network block device}
+	\minor{1}{/dev/nd1}{Second network block device}
+	\minordots
 \end{devicelist}
 
+\noindent
+Network Block Device is somehow similar to loopback devices: If you
+read from it, it sends packet accross network asking server for
+data. If you write to it, it sends packet telling server to write. It
+could be used to mounting filesystems over the net, swapping over the
+net, implementing block device in userland etc.
+
 \begin{devicelist}
 \major{44}{}{char }{isdn4linux virtual modem -- alternate devices}
 	\minor{0}{/dev/cui0}{Callout device corresponding to {\file ttyI0}}
@@ -1310,7 +1337,62 @@
 Currently for Dolphin Interconnect Solutions' PCI-SCI bridge.
 
 \begin{devicelist}
-\major{75}{--119}{}{Unallocated}
+\major{75}{}{char }{Specialix IO8+ serial card}
+	\minor{0}{/dev/ttyW0}{First IO8+ port, first card}
+	\minor{1}{/dev/ttyW1}{Second IO8+ port, first card}
+	\minordots
+	\minor{8}{/dev/ttyW8}{First IO8+ port, second card}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{76}{}{char }{Specialix IO8+ serial card -- alternate devices}
+	\minor{0}{/dev/cuw0}{Callout device corresponding to {\file ttyW0}}
+	\minor{1}{/dev/cuw1}{Callout device corresponding to {\file ttyW1}}
+	\minordots
+	\minor{8}{/dev/cuw8}{Callout device corresponding to {\file ttyW8}}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{77}{}{char }{ComScire Quantum Noise Generator}
+	\minor{0}{/dev/qng}{ComScire Quantum Noise Generator}
+\end{devicelist}
+
+\begin{devicelist}
+\major{78}{}{char }{PAM Software's multimodem boards}
+	\minor{0}{/dev/ttyM0}{First PAM modem}
+	\minor{1}{/dev/ttyM1}{Second PAM modem}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{79}{}{char }{PAM Software's multimodem boards -- alternate devices}
+	\minor{0}{/dev/cum0}{Callout device corresponding to {\file ttyM0}}
+	\minor{1}{/dev/cum1}{Callout device corresponding to {\file ttyM1}}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{80}{}{char }{Photometrics AT200 CCD camera}
+	\minor{0}{/dev/at200}{Photometrics AT200 CCD camera}
+\end{devicelist}
+
+\begin{devicelist}
+\major{81}{}{char }{Brooktree Bt848 frame grabbers}
+        \minor{0}{/dev/bttv0}{First Bt848 card} 
+        \minor{0}{/dev/bttv1}{Second Bt848 card} 
+        \minordots
+        \minor{16}{/dev/bttvc0}{Control for first Bt848 card} 
+        \minor{17}{/dev/bttvc1}{Control for second Bt848 card} 
+        \minordots
+        \minor{32}{/dev/bttv-vbi0}{VBI data of first Bt848 card}
+        \minor{33}{/dev/bttv-vbi1}{VBI data of second Bt848 card}
+        \minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{82}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1466,6 +1548,9 @@
 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 names {\file /dev/ttyQ$\#$} and {\file /dev/cuq$\#$} are reserved
+for local use.
 
 The alternate devices provide for kernel-based exclusion and somewhat
 different defaults than the primary devices.  Their main purpose is to
diff -u --recursive --new-file v2.1.32/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.32/linux/Documentation/devices.txt	Mon Mar 17 14:54:20 1997
+++ linux/Documentation/devices.txt	Tue Apr  8 12:17:22 1997
@@ -1,8 +1,7 @@
 		       LINUX ALLOCATED DEVICES
-
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		     Last revised: March 3, 1997
+		     Last revised: April 7, 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
@@ -285,6 +284,9 @@
 		139 = /dev/openprom	SPARC OpenBoot PROM
 		140 = /dev/relay8	Berkshire Products Octal relay card
 		141 = /dev/relay16	Berkshire Products ISO-16 relay card
+		142 = /dev/msr		x86 model-specific registers
+		143 = /dev/pciconf	PCI configuration space
+		144 = /dev/nvram	Non-volatile configuration RAM
 
  11 char	Raw keyboard device
 		  0 = /dev/kbd		Raw keyboard device
@@ -410,6 +412,11 @@
 		  1 = /dev/sg1		Second generic SCSI device
 		      ...
 
+		Most distributions name these /dev/sga, /dev/sgb...;
+		this sets an unnecessary limit of 26 SCSI devices in
+		the system and is counter to standard Linux
+		device-naming practice.
+
  22 char	Digiboard serial card
 		  0 = /dev/ttyD0	First Digiboard port
 		  1 = /dev/ttyD1	Second Digiboard port
@@ -707,6 +714,17 @@
 		  0 = /dev/ttyI0	First virtual modem
 		      ...
 		 63 = /dev/ttyI63	64th virtual modem
+    block	Network block devices
+		  0 = /dev/nb0		First network block device
+		  1 = /dev/nb1		Second network block device
+		      ...
+
+		Network Block Device is somehow similar to loopback
+		devices: If you read from it, it sends packet accross
+		network asking server for data. If you write to it, it
+		sends packet telling server to write. It could be used
+		to mounting filesystems over the net, swapping over
+		the net, implementing block device in userland etc.
 
  44 char	isdn4linux virtual modem - alternate devices
 		  0 = /dev/cui0		Callout device corresponding to ttyI0
@@ -920,7 +938,48 @@
 		Currently for Dolphin Interconnect Solutions' PCI-SCI
 		bridge.
 
- 75-119		UNALLOCATED
+ 75 char	Specialix IO8+ serial card
+		  0 = /dev/ttyW0	First IO8+ port, first card
+		  1 = /dev/ttyW1	Second IO8+ port, first card
+		      ...
+		  8 = /dev/ttyW8	First IO8+ port, second card
+		      ...
+
+ 76 char	Specialix IO8+ serial card - alternate devices
+		  0 = /dev/cuw0		Callout device corresponding to ttyW0
+		  1 = /dev/cuw1		Callout device corresponding to ttyW1
+		      ...
+		  8 = /dev/cuw8		Callout device corresponding to ttyW8
+		      ...
+
+ 77 char	ComScire Quantum Noise Generator
+		  0 = /dev/qng		ComScire Quantum Noise Generator
+
+ 78 char	PAM Software's multimodem boards
+		  0 = /dev/ttyM0	First PAM modem
+		  1 = /dev/ttyM1	Second PAM modem
+		      ...
+
+ 79 char	PAM Software's multimodem boards - alternate devices
+		  0 = /dev/cum0		Callout device corresponding to ttyM0
+		  1 = /dev/cum1		Callout device corresponding to ttyM1
+		      ...
+
+ 80 char	Photometrics AT200 CCD camera
+		  0 = /dev/at200	Photometrics AT200 CCD camera
+
+ 81 char	Brooktree Bt848 frame grabbers
+		  0 = /dev/bttv0	First Bt848 card
+		  1 = /dev/bttv1	Second Bt848 card
+		      ...
+		 16 = /dev/bttvc0	Control for first Bt848 card
+		 17 = /dev/bttvc1	Control for second Bt848 card
+		      ...
+		 32 = /dev/bttv-vbi0	VBI data of first Bt848 card
+		 33 = /dev/bttv-vbi1	VBI data of second Bt848 card
+		      ...
+
+ 82-119		UNALLOCATED
 
 120-127		LOCAL/EXPERIMENTAL USE
 
@@ -931,9 +990,6 @@
 255		RESERVED
 
 
-
-
-
  ****	ADDITIONAL /dev DIRECTORY ENTRIES
 
 This section details additional entries that should or may exist in
@@ -1053,6 +1109,8 @@
 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 names /dev/ttyQ# and /dev/cuq# are reserved for local use.
 
 The alternate devices provide for kernel-based exclusion and somewhat
 different defaults than the primary devices.  Their main purpose is to
diff -u --recursive --new-file v2.1.32/linux/Documentation/digiboard.txt linux/Documentation/digiboard.txt
--- v2.1.32/linux/Documentation/digiboard.txt	Mon Sep 16 03:05:28 1996
+++ linux/Documentation/digiboard.txt	Wed Apr  9 21:27:16 1997
@@ -3,37 +3,184 @@
 
 The Digiboard Driver for Linux supports the following boards:
 
- DigiBoard PC/Xe, PC/Xi, PC/Xeve
+ DigiBoard PC/Xi, PC/Xe, PC/Xeve(which is the newer, smaller Xe with
+ a 8K window which is also known as PC/Xe(8K) and has no memory/irq
+ switches) You can use up to 4 cards with this driver and should work
+ on other architectures than intel also.
+
+In case you have problems with this version(1.6.1) of this driver, please
+email directly to me as I made the last update. It you have a report about
+runnning it on other architectures than intel, email me, so I can document
+it here.
+
+An version of this driver has been taken by Digiboard to make a driver
+software package which supports also PC/Xem cards and newer PCI cards
+but it don't support the old PC/Xi cards and it isn't yet ported to
+linux-2.1.x and may not be useable on other architectures than intel now.
+It is available from ftp.digi.com/ftp.digiboard.com. You can write me if
+you need an patch for this driver.
 
-Limitations:
-------------
-Currently the Driver does not do autoprobing!
+  Bernhard Kaindl (bkaindl@netway.at)  6. April 1997.
 
-The preconfigured I/O address is 0200h and the default memory address
-0D0000h, ALT-PIN feature on and 16 ports available.
+Configuring the Driver
+----------------------
+
+The driver can be build direct into the kernel or as module.
+The pcxx driver can be configured using the command line feature while
+loading the kernel with LILO or LOADLIN or, if built as a module,
+with arguments to insmod and modprobe or with parameters in
+/etc/conf.modules for modprobe and kerneld.
+
+After configuring the driver you need to create the device special files
+as described in "Device file creation:" below and set the appropriate
+permissions for your application.
+
+As Module
+---------
+
+modprobe pcxx io=<io> \
+  membase=<membase> \
+  memsize=<memsize> \
+  numports=<numports>  \
+  altpin=<altpin> \
+  verbose=<verbose>
+
+or, if several cards are installed
+
+modprobe pcxx io=<io-1>,<io-2>,... \
+  membase=<membase-1>,<membase-2>,... \
+  memsize=<memsize-1>,<memsize-2>,... \
+  numports=<numports-1>,<numports-2>,... \
+  altpin=<altpin-1>,<altpin-2>,... \
+  verbose=<verbose>
+
+where <io-N> is the io address of the Nth card and <membase-N> is the
+memory base address of the Nth card, etc.
+
+The parameters can be specified in any order. For example, the numports
+parameter can precede the membase parameter, or vice versa. If several
+cards are installed the ordering within the comma separated parameter
+lists must be consistent, of course.
+
+io       - I/O port address of that card.
+membase  - Memory start address of that card.
+memsize  - Memory size of that card, in kilobytes. If given, this value
+           is compared against the card to verify configuration and
+           hinder the driver to use a misconfigured card. If the parameter
+           does not match the board it is disabled with a memory size error.
+numports - Number of ports on this card. This is the number of devices to
+           assign to this card or reserve if disabled.
+altpin   - 1: swap DCD and DSR for 8-pin RJ-45 with modems.
+	   0: don't swap DCD and DSR.
+           other values count as 1.
+verbose  - 1: give nice verbose output during initialisation of the driver.
+              possibly helpful during board configuration.
+           0: normal terse output.
+
+Only the parameters which differ from the defaults need to be specified.
+If the io= parameter is not given, the default config is used. This is
+
+  io=0x200 membase=0xD0000 numports=16 altpin=0
+
+Only parameters applicable need be specified. For example to configure
+2 boards, first one at 0x200 with 8 ports, rest defaults, second one at
+0x120, memory at 0xD80000, altpin enabled, rest defaults, you can do this
+by using these parameters:
+
+  modprobe pcxx io=0x200,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+To disable a temporary unuseable board without changing the mapping of the
+devices following that board, you can empty the io-value for that board:
+
+  modprobe pcxx io=,0x120 numports=8,8 membase=,0xD80000 altpin=,1
+
+The remainig board still uses ttyD8-ttyD15 and cud8-cud15.
+
+Example line for /etc/conf.modules for use with kerneld and as default
+parameters for modprobe:
+
+options pcxx           io=0x200 numports=8
+
+For kerneld to work you will likely need to add these two lines to your
+/etc/conf.modules:
+
+alias char-major-22    pcxx
+alias char-major-23    pcxx
+
+
+Boot-time configuration when linked into the kernel
+---------------------------------------------------
+
+Per Board to be configured, pass a digi= commandline parameter to the
+kernel using lilo or loadlin. It consists of a string of comma separated
+identifiers or integers.  The 6 values in order are:
+
+Card status:      Enable      - use that board
+		  Disable     - don't actually use that board.
+
+Card type:        PC/Xi       - the old ones with 64/128/256/512K RAM.
+		  PC/Xe       - PC/Xe(old ones with 64k mem range).
+		  PC/Xeve     - PC/Xe(newers with 8k mem range).
+
+Note: This is for documentation only, the type is detected from the board.
+
+Altpin setting:   Enable      - swap DCD and DSR for 8-pin RJ-45 with modems.
+		  Disable     - don't swap DCD and DSR.
+
+Number of ports:  1 ... 16    - Number of ports on this card. This is the
+				number of devices to assign to this card.
+
+I/O port address: eg. 200     - I/O Port address where the card is configured.
+
+Memory base addr: eg. 80000   - Memory address where the board's memory starts.
+
+This is an example for a line which you can insert into you lilo.conf:
+
+   append="digi=Enable,PC/Xi,Disable,4,120,D0000"
+
+there is an alternate form, in which you must use decimal values only:
+
+   append="digi=1,0,0,16,512,851968"
+
+If you don't give a digi= commandline, the compiled-in defaults of
+board 1: io=0x200, membase=0xd0000, altpin=off and numports=16 are used.
+
+If you have the resources (io&mem) free for use, configure your board to
+these settings and you should be set up fine even if yours has not 16 ports.
 
-Use them and you will not have to worry about configuring anything.
 
-You can configure the driver via lilo. Have a look at the end of this
-message. The default settings vanish as soon as you give a digi= commandline.
-You can give multiple digi= commandline parameters to define multiple
-boards.
+Sources of Information
+----------------------
+
+Webpage: http://private.fuller.edu/clameter/digi.html
+
+Mailing List: digiboard@list.fuller.edu
+
+(Write e-mail to that address to subscribe. Common ListServ commands work.
+Archive of messages available)
+
+Christoph Lameter (clameter@fuller.edu) 16. April 1996.
+
+Supporting Tools
+----------------
 
-Supporting Tools:
------------------
 Some tools and more detailed information can be found at
 ftp://ftp.fuller.edu/Linux/digi
 
-WARNING: Most of the stuff available right now uses the WRONG Major Device
-numbers and the wrong call out devices. Be careful and check them first.
-Better not use any of the software in that directory if you run a recent
-1.3.X Kernel or later!
-
 The "ditty" tool described in the Digiboard Manuals for other Unixes
 is also available.
 
+
+Device file creation
+--------------------
+
 Currently the Linux MAKEDEV command does not support generating the Digiboard
-Devices. Use the following script to generate the devices:
+Devices. 
+
+The /dev/cud devices behave like the /dev/cua devices
+and the ttyD devices are like the /dev/ttyS devices.
+
+Use the following script to generate the devices:
 
 ------------------ mkdigidev begin
 #!/bin/sh
@@ -66,8 +213,10 @@
   boardnum=`expr $boardnum + 1`
 done
 ------------------ mkdigidev end
+
 or apply the following patch to /dev/MAKEDEV and do a 
-make digi
+sh /dev/MAKEDEV digi
+
 ----- MAKEDEV Patch
 --- /dev/MAKEDEV	Sun Aug 13 15:48:23 1995
 +++ MAKEDEV	Tue Apr 16 17:53:27 1996
@@ -105,51 +254,3 @@
  		;;
  	par[0-2])
 ----- End Makedev patch
-
-The /dev/cud?? devices behave like the /dev/cua?? devices
-and the ttyD devices are like the /dev/ttyS?? devices.
-
-Sources of Information
-----------------------
-
-Webpage: http://private.fuller.edu/clameter/digi.html
-
-Mailing List: digiboard@list.fuller.edu
-
-(Write e-mail to that address to subscribe. Common ListServ commands work.
-Archive of messages available)
-
-Christoph Lameter (clameter@fuller.edu) 16. April 1996.
-
------------------------------------------------------------------------------
-
-Changes v1.5.5:
-
-The ability to use the kernel's command line to pass in the configuration for 
-boards.  Using LILO's APPEND command, a string of comma separated identifiers 
-or integers can be used.  The 6 values in order are:
-
-   Enable/Disable this card,
-   Type of card: PC/Xi(0), PC/Xe(1), PC/Xeve(2), PC/Xem(3)
-   Enable/Disable alternate pin arrangement,
-   Number of ports on this card,
-   I/O Port where card is configured (in HEX if using string identifiers),
-   Base of memory window (in HEX if using string identifiers), 
-
-Samples:
-   append="digi=E,PC/Xi,D,16,200,D0000"
-   append="digi=1,0,0,16,512,(whatever D0000 is in base 10 :)
-
-Driver's minor device numbers are conserved. This means that instead of
-each board getting a block of 16 minors pre-assigned, it gets however
-many it should, with the next card following directly behind it.  A
-system with 4 2-port PC/Xi boards will use minor numbers 0-7.
-This conserves some memory, and removes a few hard coded constants.
-
-NOTE!! NOTE!! NOTE!!
-The definition of PC/Xem as a valid board type is the BEGINNING of support
-for this device.  The driver does not currently recognise the board, nor
-does it want to initialize it.  At least not the EISA version.
-
-Mike McLagan <mike.mclagan@linux.org> 5, April 1996.
-
diff -u --recursive --new-file v2.1.32/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.32/linux/Documentation/ioctl-number.txt	Mon Apr  7 11:35:28 1997
+++ linux/Documentation/ioctl-number.txt	Mon Apr  7 14:17:09 1997
@@ -1,5 +1,5 @@
 Ioctl Numbers
-12 Feb 1997
+5 Apr 1997
 Michael Chastain
 <mec@shout.net>
 
@@ -89,6 +89,8 @@
 'a'	all	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
 'c'	all	linux/comstats.h
 'f'	all	linux/ext2_fs.h
+'l'	00-3F	linux/tcfs_fs.h		in development:
+					<http://mikonos.dia.unisa.it/tcfs>
 'm'	all	linux/mtio.h		conflict!
 'm'	all	linux/soundcard.h	conflict!
 'n'	all	linux/ncp_fs.h
@@ -105,8 +107,10 @@
 0x89	E0-EF	linux/sockios.h		SIOCPROTOPRIVATE range
 0x89	F0-FF	linux/sockios.h		SIOCDEVPRIVATE range
 0x8B	all	linux/wireless.h
+0x8C	00-3F	WiNRADiO driver		in development:
+					<mailto:brian@proximity.com.au>
 0x90	00	linux/sbpcd.h
-0x99	00-0F	537-Addinboard driver	in development, e-mail contact:
-					b.kohl@ipn-b.comlink.apc.org
-0xA0	all	Small Device Project	in development, e-mail contact:
-					khollis@northwest.com
+0x99	00-0F	537-Addinboard driver	in development:
+					<mailto:b.kohl@ipn-b.comlink.apc.org>
+0xA0	all	Small Device Project	in development:
+					<mailto:khollis@northwest.com>
diff -u --recursive --new-file v2.1.32/linux/Documentation/parport.txt linux/Documentation/parport.txt
--- v2.1.32/linux/Documentation/parport.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/parport.txt	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,55 @@
+The `parport' code provides parallel-port support under Linux.  This
+includes the ability to share one port between multiple device
+drivers.
+
+You can pass parameters to the parport code to override its automatic
+detection of your hardware.  This is particularly useful if you want
+to use IRQs, since in general these can't be autoprobed successfully.
+
+If you load the parport code as a module, say
+
+	# insmod parport.o io=0x378,0x278 irq=7,5
+
+to tell the parport code that you want two ports, one at 0x378 using
+IRQ 7, and one at 0x278 using IRQ 5. 
+
+If you compile the parport code into the kernel, then you can use
+kernel boot parameters to get the same effect.  Add something like the
+following to your LILO command line:
+
+	parport=0x378,7 parport=0x278,5
+
+You can have many `parport=...' statements, one for each port you want
+to add.  Adding `parport=0' or just `parport=' to the command-line
+will disable parport support entirely.
+
+Once the parport code is initialised, you can attach device drivers to
+ports.  Normally this happens automatically; if the lp driver is
+loaded it will create one lp device for each port found.  You can
+override this, though, by using parameters either when you load the lp
+driver:
+
+	# insmod lp.o parport=0,2
+
+or on the LILO command line:
+
+	lp=parport0 lp=parport2
+
+Both the above examples would inform lp that you want /dev/lp0 to be
+the first parallel port, and /dev/lp1 to be the _third_ parallel port,
+with no lp device associated with the second port (parport1).  Note
+that this is different to the way older kernels worked; there used to
+be a static association between the I/O port address and the device
+name, so /dev/lp0 was always the port at 0x3bc.  This is no longer the
+case - if you only have one port, it will always be /dev/lp0,
+regardless of base address.
+
+Also:
+
+ * If you selected the device autoprobe at compile time, you can say
+`lp=auto' on the kernel command line, and lp will create devices only
+for those ports that seem to have printers attached.
+
+ * If you give PLIP the `timid' parameter, either with `plip=timid' on
+the command line, or with `insmod plip timid=1' when using modules, it
+will avoid any ports that seem to be in use by other devices.
diff -u --recursive --new-file v2.1.32/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.32/linux/MAINTAINERS	Mon Apr  7 11:35:28 1997
+++ linux/MAINTAINERS	Wed Apr  9 21:30:30 1997
@@ -407,6 +407,19 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+PARALLEL PORT SHARING SUPPORT
+P:	Phil Blundell
+M:	Philip.Blundell@pobox.com
+P:	Tim Waugh
+M:	tmw20@cyberelk.demon.co.uk
+P:	David Campbell
+M:	campbell@tirian.che.curtin.edu.au
+L:	linux-parport@torque.net
+L:	pnp-list@redhat.com
+W:	http://www.cyberelk.demon.co.uk/parport.html
+W:	http://www.cage.curtin.edu.au/~campbell/parbus/
+S:	Maintained
+
 FPU EMULATOR
 P:	Bill Metzenthen
 M:	billm@suburbia.net
@@ -421,7 +434,7 @@
 KERNEL AUTOMOUNTER (AUTOFS)
 P:	H. Peter Anvin
 M:	hpa@zytor.com
-L:	linux-kernel@vger.rutgers.edu
+L:	autofs@linux.kernel.org
 S:	Maintained
 
 DEVICE NUMBER REGISTRY
diff -u --recursive --new-file v2.1.32/linux/Makefile linux/Makefile
--- v2.1.32/linux/Makefile	Mon Apr  7 11:35:28 1997
+++ linux/Makefile	Wed Apr  9 23:28:33 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 32
+SUBLEVEL = 33
 
 ARCH = i386
 
@@ -14,7 +14,7 @@
 SMP = 1
 #
 # SMP profiling options
-# SMP_PROF = 1
+SMP_PROF = 1
 
 .EXPORT_ALL_VARIABLES:
 
@@ -142,6 +142,10 @@
 
 ifdef CONFIG_PCI
 DRIVERS := $(DRIVERS) drivers/pci/pci.a
+endif
+
+ifdef CONFIG_PNP
+DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
 endif
 
 ifdef CONFIG_SBUS
diff -u --recursive --new-file v2.1.32/linux/Rules.make linux/Rules.make
--- v2.1.32/linux/Rules.make	Fri Apr  4 08:52:16 1997
+++ linux/Rules.make	Wed Apr  9 21:30:30 1997
@@ -22,11 +22,14 @@
 unexport O_OBJS
 unexport L_OBJS
 unexport M_OBJS
+# intermediate objects that form part of a module
+unexport MI_OBJS
 unexport ALL_MOBJS
 # objects that export symbol tables
 unexport OX_OBJS
 unexport LX_OBJS
 unexport MX_OBJS
+unexport MIX_OBJS
 unexport SYMTAB_OBJS
 
 unexport MOD_LIST_NAME
@@ -105,7 +108,7 @@
 ifneq "$(strip $(ALL_MOBJS))" ""
 PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
 endif
-modules: $(ALL_MOBJS) dummy
+modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy
 ifdef MOD_SUB_DIRS
 	set -e; for i in $(MOD_SUB_DIRS); do $(MAKE) -C $$i modules; done
 endif
@@ -150,7 +153,7 @@
 #
 ifdef CONFIG_MODULES
 
-SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS)
+SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS)
 
 ifdef CONFIG_MODVERSIONS
 ifneq "$(strip $(SYMTAB_OBJS))" ""
diff -u --recursive --new-file v2.1.32/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.32/linux/arch/alpha/config.in	Mon Apr  7 11:35:28 1997
+++ linux/arch/alpha/config.in	Wed Apr  9 21:30:30 1997
@@ -95,7 +95,13 @@
 tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
+fi
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
 endmenu
+
+source drivers/pnp/Config.in
 
 source drivers/block/Config.in
 
diff -u --recursive --new-file v2.1.32/linux/arch/i386/boot/compressed/Makefile.debug linux/arch/i386/boot/compressed/Makefile.debug
--- v2.1.32/linux/arch/i386/boot/compressed/Makefile.debug	Wed Dec 13 22:16:52 1995
+++ linux/arch/i386/boot/compressed/Makefile.debug	Wed Dec 31 16:00:00 1969
@@ -1,21 +0,0 @@
-#
-# linux/arch/i386/boot/compressed/Makefile
-#
-# create a compressed vmlinux image from the original vmlinux
-#
-
-OBJECTS = misc.o 
-
-CFLAGS = -g -O2 -DSTDC_HEADERS -DSTANDALONE_DEBUG -Wall
-
-test-gzip: piggy.o $(OBJECTS)
-	$(CC) -g -o test-gzip $(OBJECTS) piggy.o
-
-clean: 
-	$(RM) inflate.o misc.o test-gzip
-
-inflate.o: inflate.c gzip.h
-
-misc.o: misc.c gzip.h
-
-
diff -u --recursive --new-file v2.1.32/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c
--- v2.1.32/linux/arch/i386/boot/compressed/misc.c	Fri Apr  4 08:52:16 1997
+++ linux/arch/i386/boot/compressed/misc.c	Wed Apr  9 21:33:22 1997
@@ -9,8 +9,6 @@
  * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
  */
 
-#include <string.h>
-
 #include <asm/segment.h>
 #include <asm/io.h>
 
@@ -97,7 +95,6 @@
 static void gzip_mark(void **);
 static void gzip_release(void **);
  
-#ifndef STANDALONE_DEBUG
 static void puts(const char *);
   
 extern int end;
@@ -196,7 +193,7 @@
 	outb_p(0xff & (pos >> 1), vidport+1);
 }
 
-__ptr_t memset(__ptr_t s, int c, size_t n)
+void* memset(void* s, int c, size_t n)
 {
 	int i;
 	char *ss = (char*)s;
@@ -204,7 +201,7 @@
 	for (i=0;i<n;i++) ss[i] = c;
 }
 
-__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
+void* memcpy(void* __dest, __const void* __src,
 			    size_t __n)
 {
 	int i;
@@ -212,7 +209,6 @@
 
 	for (i=0;i<__n;i++) d[i] = s[i];
 }
-#endif
 
 /* ===========================================================================
  * Fill the input buffer. This is called only when the buffer is empty
@@ -292,34 +288,6 @@
 	short b;
 	} stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS };
 
-#ifdef STANDALONE_DEBUG
-
-static void gzip_mark(void **ptr)
-{
-}
-
-static void gzip_release(void **ptr)
-{
-}
-
-char output_buffer[1024 * 800];
-
-int
-main(argc, argv)
-	int	argc;
-	char	**argv;
-{
-	output_data = output_buffer;
-
-	makecrc();
-	puts("Uncompressing Linux...");
-	gunzip();
-	puts("done.\n");
-	return 0;
-}
-
-#else
-
 void setup_normal_output_buffer()
 {
 #ifdef STANDARD_MEMORY_BIOS_CALL
@@ -388,8 +356,4 @@
 	if (high_loaded) close_output_buffer_if_we_run_high(mv);
 	return high_loaded;
 }
-#endif
-
-
-
 
diff -u --recursive --new-file v2.1.32/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.32/linux/arch/i386/config.in	Mon Apr  7 11:35:29 1997
+++ linux/arch/i386/config.in	Wed Apr  9 21:30:30 1997
@@ -44,7 +44,12 @@
 	 Pentium	CONFIG_M586	\
 	 PPro		CONFIG_M686" Pentium
 bool 'Video mode selection support' CONFIG_VIDEO_SELECT
+
+tristate 'Parallel port support' CONFIG_PNP_PARPORT
+
 endmenu
+
+source drivers/pnp/Config.in
 
 source drivers/block/Config.in
 
diff -u --recursive --new-file v2.1.32/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.32/linux/arch/i386/defconfig	Mon Apr  7 11:35:29 1997
+++ linux/arch/i386/defconfig	Tue Apr  8 11:15:38 1997
@@ -156,9 +156,10 @@
 # CONFIG_PCNET32 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
-CONFIG_DE4X5=y
+# CONFIG_DE4X5 is not set
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
+CONFIG_EEXPRESS_PRO100=y
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
@@ -192,9 +193,10 @@
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_ROOT_NFS is not set
 # CONFIG_SMB_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.32/linux/arch/i386/kernel/i386_ksyms.c	Fri Apr  4 08:52:17 1997
+++ linux/arch/i386/kernel/i386_ksyms.c	Tue Apr  8 10:19:42 1997
@@ -12,6 +12,7 @@
 #include <asm/uaccess.h>
 #include <asm/checksum.h>
 #include <asm/io.h>
+#include <asm/hardirq.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
@@ -26,6 +27,7 @@
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL_NOVERS(__down_failed);
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.32/linux/arch/i386/kernel/irq.c	Mon Apr  7 11:35:29 1997
+++ linux/arch/i386/kernel/irq.c	Mon Apr  7 14:02:43 1997
@@ -35,6 +35,10 @@
 #include <asm/smp.h>
 #include <asm/pgtable.h>
 
+#ifdef __SMP_PROF__
+extern volatile unsigned long smp_apic_timer_ticks[1+NR_CPUS];
+#endif
+
 #define CR0_NE 32
 
 static unsigned char cache_21 = 0xff;
@@ -137,6 +141,9 @@
 BUILD_SMP_INTERRUPT(reschedule_interrupt)
 BUILD_SMP_INTERRUPT(invalidate_interrupt)
 BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
+#ifdef __SMP_PROF__
+BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt)
+#endif
 #endif
 
 /*
@@ -252,6 +259,7 @@
 	unsigned long sum_spins_syscall = 0;
 	unsigned long sum_spins_sys_idle = 0;
 	unsigned long sum_smp_idle_count = 0;
+	unsigned long sum_apic_timer_ticks = 0;
 
 	for (i=0;i<smp_num_cpus;i++) {
 		int cpunum = cpu_logical_map[i];
@@ -259,6 +267,7 @@
 		sum_spins_syscall+=smp_spins_syscall[cpunum];
 		sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
 		sum_smp_idle_count+=smp_idle_count[cpunum];
+		sum_apic_timer_ticks+=smp_apic_timer_ticks[cpunum];
 	}
 
 	len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
@@ -315,6 +324,12 @@
 
 	len +=sprintf(buf+len,"   idle ticks\n");
 
+	len+=sprintf(buf+len,"TICK %10lu",sum_apic_timer_ticks);
+	for (i=0;i<smp_num_cpus;i++)
+		len+=sprintf(buf+len," %10lu",smp_apic_timer_ticks[cpu_logical_map[i]]);
+
+	len +=sprintf(buf+len,"   local APIC timer ticks\n");
+
 	len+=sprintf(buf+len, "IPI: %10lu   received\n",
 		ipi_count);
 
@@ -734,14 +749,30 @@
 	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
 	outb_p(LATCH & 0xff , 0x40);	/* LSB */
 	outb(LATCH >> 8 , 0x40);	/* MSB */
+
 	for (i = 0; i < 16 ; i++)
 		set_intr_gate(0x20+i,bad_interrupt[i]);
-	/* This bit is a hack because we don't send timer messages to all processors yet */
-	/* It has to be here .. it doesn't work if you put it down the bottom - assembler explodes 8) */
+
+	/*
+	 * This bit is a hack because we don't send timer messages to all
+	 * processors yet. It has to be here .. it doesn't work if you put
+	 * it down the bottom - assembler explodes 8)
+	 */
+
 #ifdef __SMP__	
-	set_intr_gate(0x20+i, reschedule_interrupt);	/* IRQ '16' - IPI for rescheduling */
-	set_intr_gate(0x21+i, invalidate_interrupt);	/* IRQ '17' - IPI for invalidation */
-	set_intr_gate(0x22+i, stop_cpu_interrupt);	/* IRQ '18' - IPI for CPU halt */
+	/* IRQ '16' - IPI for rescheduling */
+	set_intr_gate(0x20+i, reschedule_interrupt);
+
+	/* IRQ '17' - IPI for invalidation */
+	set_intr_gate(0x21+i, invalidate_interrupt);
+
+	/* IRQ '18' - IPI for CPU halt */
+	set_intr_gate(0x22+i, stop_cpu_interrupt);
+
+#ifdef __SMP_PROF__
+	/* IRQ '19' - self generated IPI for local APIC timer */
+	set_intr_gate(0x23+i, apic_timer_interrupt);
+#endif
 #endif	
 	request_region(0x20,0x20,"pic1");
 	request_region(0xa0,0x20,"pic2");
diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.32/linux/arch/i386/kernel/process.c	Fri Apr  4 08:52:17 1997
+++ linux/arch/i386/kernel/process.c	Mon Apr  7 14:00:35 1997
@@ -107,6 +107,7 @@
 	if (current->pid != 0)
 		goto out;
 	/* endless idle loop with no priority at all */
+	current->priority = -100;
 	current->counter = -100;
 	for (;;) 
 	{
@@ -145,6 +146,7 @@
 
 int cpu_idle(void *unused)
 {
+	current->priority = -100;
 	while(1)
 	{
 		if(cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
@@ -158,6 +160,8 @@
 			run_task_queue(&tq_scheduler);
 			unlock_kernel();
 		}
+		/* endless idle loop with no priority at all */
+		current->counter = -100;
 		schedule();
 	}
 }
@@ -474,7 +478,7 @@
 	p->tss.ss = KERNEL_DS;
 	p->tss.ds = KERNEL_DS;
 	p->tss.fs = USER_DS;
-	p->tss.gs = KERNEL_DS;
+	p->tss.gs = USER_DS;
 	p->tss.ss0 = KERNEL_DS;
 	p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
 	p->tss.tr = _TSS(nr);
diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.32/linux/arch/i386/kernel/smp.c	Mon Apr  7 11:35:29 1997
+++ linux/arch/i386/kernel/smp.c	Mon Apr  7 14:02:43 1997
@@ -23,7 +23,8 @@
  *	Michel Lespinasse	:	Changes for 2.1 kernel map.
  *	Michael Chastain	:	Change trampoline.S to gnu as.
  *		Alan Cox	:	Dumb bug: 'B' step PPro's are fine
- *
+ *		Ingo Molnar	:	Added APIC timers, based on code
+ *					from Jose Renau
  */
 
 #include <linux/kernel.h>
@@ -45,6 +46,7 @@
 #include <asm/io.h>
 
 extern unsigned long start_kernel, _etext;
+void setup_APIC_clock (void);
 
 /*
  *	Some notes on processor bugs:
@@ -137,6 +139,10 @@
 volatile unsigned long smp_spins_syscall_cur[NR_CPUS]={0};/* Count spins for the actual syscall                 */
 volatile unsigned long smp_spins_sys_idle[NR_CPUS]={0}; /* Count spins for sys_idle 				*/
 volatile unsigned long smp_idle_count[1+NR_CPUS]={0,};	/* Count idle ticks					*/
+
+/* Count local APIC timer ticks */
+volatile unsigned long smp_apic_timer_ticks[1+NR_CPUS]={0,};
+
 #endif
 #if defined (__SMP_PROF__)
 volatile unsigned long smp_idle_map=0;			/* Map for idle processors 				*/
@@ -621,6 +627,14 @@
  	l=apic_read(APIC_SPIV);
  	l|=(1<<8);		/* Enable */
  	apic_write(APIC_SPIV,l);
+
+#ifdef __SMP_PROF__
+	/*
+	 * Set up our APIC timer. 
+	 */
+	setup_APIC_clock ();
+#endif
+
  	sti();
 	/*
 	 *	Get our bogomips.
@@ -767,7 +781,14 @@
 	apic_write(APIC_SPIV,cfg);
 
 	udelay(10);
-			
+
+#ifdef __SMP_PROF__
+	/*
+	 * Set up our local APIC timer:
+	 */			
+	setup_APIC_clock ();
+#endif
+
 	/*
 	 *	Now scan the cpu present map and fire up the other CPUs.
 	 */
@@ -1305,3 +1326,274 @@
 		for(;;) __asm__("hlt");
 	for  (;;) ;
 }
+
+#ifdef __SMP_PROF__
+
+extern void (*do_profile)(struct pt_regs *);
+static void (*default_do_profile)(struct pt_regs *) = NULL;
+
+/*
+ *	APIC timer interrupt
+ */
+void smp_apic_timer_interrupt(struct pt_regs * regs)
+{
+	int cpu = smp_processor_id();
+
+	if (!user_mode(regs)) {
+		unsigned long flags;
+
+		/*
+		 * local timer interrupt is not NMI yet, so
+		 * it's simple, we just aquire the global cli
+		 * lock to mess around with profiling info.
+		 *
+		 * later, in the NMI version, we have to build
+		 * our own 'current' pointer (as we could have
+		 * interrupted code that just changes "current")
+		 * and have to lock profiling info between NMI
+		 * interrupts properly.
+		 */
+		save_flags(flags);
+		cli();
+		default_do_profile(regs);
+		restore_flags(flags);
+	}
+
+	/*
+	 * this is safe outside the lock.
+	 */ 
+	smp_apic_timer_ticks[cpu]++;
+
+	apic_read (APIC_SPIV);
+	apic_write (APIC_EOI, 0);
+}
+
+/*
+ * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
+ * per second. We assume that the caller has already set up the local
+ * APIC at apic_addr.
+ *
+ * Later we might want to split HZ into two parts: introduce
+ * PROFILING_HZ and scheduling HZ. The APIC timer is not exactly
+ * sync with the external timer chip, it closely follows bus clocks.
+ */
+
+#define RTDSC(x)	__asm__ __volatile__ (  ".byte 0x0f,0x31" \
+				:"=a" (((unsigned long*)&x)[0]),  \
+				 "=d" (((unsigned long*)&x)[1]))
+
+/*
+ * The timer chip is already set up at HZ interrupts per second here,
+ * but we do not accept timer interrupts yet. We only allow the BP
+ * to calibrate.
+ */
+unsigned int get_8254_timer_count (void)
+{
+	unsigned int count;
+
+        outb_p(0x00, 0x43);
+	count = inb_p(0x40);
+	count |= inb_p(0x40) << 8;
+
+	return count;
+}
+
+/*
+ * This function sets up the local APIC timer, with a timeout of
+ * 'clocks' APIC bus clock. During calibration we actually call
+ * this function twice, once with a bogus timeout value, second
+ * time for real. The other (noncalibrating) CPUs call this
+ * function only once, with the real value.
+ *
+ * We are strictly in irqs off mode here, as we do not want to
+ * get an APIC interrupt go off accidentally.
+ */
+
+#define APIC_DIVISOR 16
+
+/*
+ * We do reads before writes even if unnecessary, to get around the
+ * APIC double write 'bug'.
+ */
+
+void setup_APIC_timer (unsigned int clocks)
+{
+	unsigned long lvtt1_value; 
+	unsigned int tmp_value;
+
+	/*
+	 * Unfortunately the local APIC timer cannot be set up into NMI
+	 * mode. With the IO APIC we can re-route the external timer
+	 * interrupt and broadcast it as an NMI to all CPUs, so no pain.
+	 *
+	 * NOTE: this irq vector 19 and the gate in BUILD_SMP_TIMER_INTERRUPT
+	 * should be the same ;)
+	 */
+	tmp_value = apic_read(APIC_LVTT);
+	lvtt1_value = APIC_LVT_TIMER_PERIODIC | (0x20+19);
+	apic_write(APIC_LVTT , lvtt1_value);
+
+	/*
+	 * Divide PICLK by 16
+	 */
+	tmp_value = apic_read(APIC_TDCR);
+	apic_write(APIC_TDCR , (tmp_value & ~APIC_TDR_DIV_1 )
+				 | APIC_TDR_DIV_16);
+
+	tmp_value = apic_read(APIC_TMICT);
+	apic_write(APIC_TMICT, clocks/APIC_DIVISOR);
+}
+
+void wait_8254_wraparound (void)
+{
+	unsigned int curr_count, prev_count=~0;
+	int delta;
+
+	curr_count = get_8254_timer_count();
+
+	do {
+		prev_count = curr_count;
+		curr_count = get_8254_timer_count();
+		delta = curr_count-prev_count;
+
+	/*
+	 * This limit for delta seems arbitrary, but it isnt, it's
+	 * slightly above the level of error a buggy Mercury/Neptune
+	 * chipset timer can cause.
+	 */
+
+	} while (delta<1000);
+}
+
+/*
+ * In this function we calibrate APIC bus clocks to the external
+ * timer here. Unfortunately we cannot use jiffies and
+ * the timer irq to calibrate, since some later bootup
+ * code depends on getting the first irq? Ugh.
+ *
+ * We want to do the calibration only once since we
+ * want to have local timer irqs syncron. CPUs connected
+ * by the same APIC bus have the very same bus frequency.
+ * And we want to have irqs off anyways, no accidental
+ * APIC irq that way.
+ */
+
+int calibrate_APIC_clock (void)
+{
+	unsigned long long t1,t2;
+	unsigned long tt1,tt2;
+	unsigned int prev_count, curr_count;
+	long calibration_result;
+
+	printk("calibrating APIC timer ... ");
+
+	/*
+	 * Put whatever arbitrary (but long enough) timeout
+	 * value into the APIC clock, we just want to get the
+	 * counter running for calibration.
+	 */
+	setup_APIC_timer(1000000000);
+
+	/*
+	 * The timer chip counts down to zero. Lets wait
+	 * for a wraparound to start exact measurement:
+	 * (the current tick might have been already half done)
+	 */
+
+	wait_8254_wraparound ();
+
+	/*
+	 * We wrapped around just now, lets start:
+	 */
+	RTDSC(t1);
+	tt1=apic_read(APIC_TMCCT);
+
+	/*
+	 * lets wait until we get to the next wrapround:
+	 */
+	wait_8254_wraparound ();
+
+	tt2=apic_read(APIC_TMCCT);
+	RTDSC(t2);
+
+	/*
+	 * The APIC bus clock counter is 32 bits only, it
+	 * might have overflown:
+	 */
+	if (tt2<tt1) {
+		unsigned long tmp = tt2;
+		tt2=tt1;
+		tt1=tmp;
+	}
+
+	calibration_result = (tt2-tt1)*APIC_DIVISOR;
+
+	printk("\n..... %ld CPU clocks in 1 timer chip tick.\n",
+			 (unsigned long)(t2-t1));
+
+	printk("..... %ld APIC bus clocks in 1 timer chip tick.\n",
+			 calibration_result);
+
+
+	printk("..... CPU clock speed is %ld.%ld MHz.\n", 
+		((long)(t2-t1))/(1000000/HZ),
+		((long)(t2-t1))%(1000000/HZ)  );
+
+	printk("..... APIC bus clock speed is %ld.%ld MHz.\n", 
+		calibration_result/(1000000/HZ),
+		calibration_result%(1000000/HZ)  );
+
+	return calibration_result;
+}
+
+void setup_APIC_clock (void)
+{
+	unsigned long flags; 
+
+	static volatile int calibration_lock;
+	static unsigned int calibration_result;
+
+	return;
+	save_flags(flags);
+	cli();
+
+	printk("setup_APIC_clock() called.\n");
+
+	/*
+	 * We use a private profiling function. (This is preparation
+	 * for NMI local timer interrupts.)
+	 *
+	 * [ setup_APIC_clock() is called from all CPUs, but we want
+	 *   to do this part of the setup only once ... and it fits
+	 *   here best ]
+	 */
+	if (!set_bit(0,&calibration_lock)) {
+
+		default_do_profile = do_profile;
+		do_profile = NULL;
+
+		calibration_result=calibrate_APIC_clock();
+		/*
+	 	 * Signal completion to the other CPU[s]:
+	 	 */
+		calibration_lock = 3;
+
+	} else {
+		/*
+		 * Other CPU is calibrating, wait for finish:
+		 */
+		printk("waiting for other CPU calibrating APIC timer ... ");
+		while (calibration_lock == 1);
+		printk("done, continuing.\n");
+	}
+
+	setup_APIC_timer (calibration_result);
+
+	restore_flags(flags);
+}
+
+#undef APIC_DIVISOR
+#undef RTDSC
+
+#endif
+
diff -u --recursive --new-file v2.1.32/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.32/linux/arch/i386/kernel/traps.c	Mon Apr  7 11:35:29 1997
+++ linux/arch/i386/kernel/traps.c	Mon Apr  7 18:52:09 1997
@@ -114,7 +114,7 @@
 #define VMALLOC_OFFSET (8*1024*1024)
 #define MODULE_RANGE (8*1024*1024)
 
-static void show_regs(struct pt_regs *regs)
+static void show_registers(struct pt_regs *regs)
 {
 	int i;
 	unsigned long esp;
@@ -124,8 +124,6 @@
 
 	esp = (unsigned long) &regs->esp;
 	ss = KERNEL_DS;
-	if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
-		return;
 	if (regs->xcs & 3) {
 		esp = regs->esp;
 		ss = regs->xss & 0xffff;
@@ -184,9 +182,11 @@
 
 /*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
+	if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
+		return;
 	console_verbose();
 	printk("%s: %04lx\n", str, err & 0xffff);
-	show_regs(regs);
+	show_registers(regs);
 	do_exit(SIGSEGV);
 }
 
@@ -236,7 +236,7 @@
 
 asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
 {
-	printk("NMI\n"); show_regs(regs);
+	printk("NMI\n"); show_registers(regs);
 #ifdef CONFIG_SMP_NMI_INVAL
 	smp_flush_tlb_rcv();
 #else
diff -u --recursive --new-file v2.1.32/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.32/linux/arch/i386/mm/fault.c	Sun Jan 26 02:07:05 1997
+++ linux/arch/i386/mm/fault.c	Wed Apr  9 21:41:52 1997
@@ -187,11 +187,10 @@
  *
  * First we check if it was the bootup rw-test, though..
  */
-	if (wp_works_ok < 0 && !address && (error_code & 1)) {
+	if (wp_works_ok < 0 && address == 0xc0000000 && (error_code & 1)) {
 		wp_works_ok = 1;
 		pg0[0] = pte_val(mk_pte(0, PAGE_SHARED));
 		flush_tlb();
-		printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
 		goto out;
 	}
 	if (address < PAGE_SIZE) {
diff -u --recursive --new-file v2.1.32/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.32/linux/arch/i386/mm/init.c	Sun Jan 26 02:07:05 1997
+++ linux/arch/i386/mm/init.c	Wed Apr  9 21:42:43 1997
@@ -146,19 +146,19 @@
 	pg_dir = swapper_pg_dir;
 	/* unmap the original low memory mappings */
 	pgd_val(pg_dir[0]) = 0;
+
+	/* Map whole memory from 0xC0000000 */
+
 	while (address < end_mem) {
-		/*
-		 * The following code enabled 4MB page tables for the
-		 * Intel Pentium cpu, unfortunately the SMP kernel can't
-		 * handle the 4MB page table optimizations yet
-		 */
-		/*
-		 * This will create page tables that
-		 * span up to the next 4MB virtual
-		 * memory boundary, but that's ok,
-		 * we won't use that memory anyway.
-		 */
 		if (x86_capability & 8) {
+			/*
+			 * If we're running on a Pentium CPU, we can use the 4MB
+			 * page tables. 
+			 *
+			 * The page tables we create span up to the next 4MB
+			 * virtual memory boundary, but that's OK as we won't
+			 * use that memory anyway.
+			 */
 #ifdef GAS_KNOWS_CR4
 			__asm__("movl %%cr4,%%eax\n\t"
 				"orl $16,%%eax\n\t"
@@ -176,8 +176,10 @@
 			address += 4*1024*1024;
 			continue;
 		}
-		/* map the memory at virtual addr 0xC0000000 */
-		/* pg_table is physical at this point */
+		/*
+		 * We're on a [34]86, use normal page tables.
+		 * pg_table is physical at this point
+		 */
 		pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
 		if (!pg_table) {
 			pg_table = (pte_t *) __pa(start_mem);
@@ -273,18 +275,25 @@
 /* test if the WP bit is honoured in supervisor mode */
 	if (wp_works_ok < 0) {
 		unsigned char tmp_reg;
+		unsigned long old = pg0[0];
+		printk("Checking if this processor honours the WP bit even in supervisor mode... ");
 		pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_READONLY));
 		local_flush_tlb();
+		current->mm->mmap->vm_start += PAGE_SIZE;
 		__asm__ __volatile__(
 			"movb %0,%1 ; movb %1,%0"
 			:"=m" (*(char *) __va(0)),
 			 "=q" (tmp_reg)
 			:/* no inputs */
 			:"memory");
-		pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL));
+		pg0[0] = old;
 		local_flush_tlb();
-		if (wp_works_ok < 0)
+		current->mm->mmap->vm_start -= PAGE_SIZE;
+		if (wp_works_ok < 0) {
 			wp_works_ok = 0;
+			printk("No.\n");
+		} else
+			printk("Ok.\n");
 	}
 	return;
 }
diff -u --recursive --new-file v2.1.32/linux/drivers/Makefile linux/drivers/Makefile
--- v2.1.32/linux/drivers/Makefile	Thu Dec 19 01:03:33 1996
+++ linux/drivers/Makefile	Wed Apr  9 21:30:30 1997
@@ -9,10 +9,15 @@
 
 SUB_DIRS     := block char net #streams
 MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
+endif
+
+ifdef CONFIG_PNP
+SUB_DIRS += pnp
+MOD_SUB_DIRS += pnp
 endif
 
 ifdef CONFIG_SBUS
diff -u --recursive --new-file v2.1.32/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.32/linux/drivers/block/genhd.c	Mon Apr  7 11:35:29 1997
+++ linux/drivers/block/genhd.c	Wed Apr  9 21:30:30 1997
@@ -765,9 +765,15 @@
 void device_setup(void)
 {
 	extern void console_map_init(void);
+#ifdef CONFIG_PNP_PARPORT
+	extern int pnp_parport_init(void);
+#endif
 	struct gendisk *p;
 	int nr=0;
 
+#ifdef CONFIG_PNP_PARPORT
+	pnp_parport_init();
+#endif
 	chr_dev_init();
 	blk_dev_init();
 	sti();
diff -u --recursive --new-file v2.1.32/linux/drivers/block/xd.h linux/drivers/block/xd.h
--- v2.1.32/linux/drivers/block/xd.h	Mon Sep 30 06:52:47 1996
+++ linux/drivers/block/xd.h	Mon Apr  7 13:51:23 1997
@@ -112,7 +112,7 @@
 static int xd_open (struct inode *inode,struct file *file);
 static void do_xd_request (void);
 static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
-static void xd_release (struct inode *inode,struct file *file);
+static int xd_release (struct inode *inode,struct file *file);
 static int xd_reread_partitions (kdev_t dev);
 static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count);
 static void xd_recalibrate (u_char drive);
diff -u --recursive --new-file v2.1.32/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.32/linux/drivers/char/Config.in	Fri Apr  4 08:52:18 1997
+++ linux/drivers/char/Config.in	Wed Apr  9 21:30:30 1997
@@ -19,7 +19,7 @@
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
-   bool 'Digiboard PC/Xx Support' CONFIG_DIGI
+   tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
    tristate 'Cyclades async mux support' CONFIG_CYCLADES
    bool 'Stallion multiport serial support' CONFIG_STALDRV
    if [ "$CONFIG_STALDRV" = "y" ]; then
@@ -33,8 +33,12 @@
      int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
    fi
 fi
-tristate 'Parallel printer support' CONFIG_PRINTER
-
+if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PNP_PARPORT
+  if [ "$CONFIG_PRINTER" != "n" ]; then
+    bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
+  fi
+fi
 
 bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
 if [ "$CONFIG_MOUSE" = "y" ]; then
diff -u --recursive --new-file v2.1.32/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.32/linux/drivers/char/console.c	Fri Apr  4 08:52:19 1997
+++ linux/drivers/char/console.c	Mon Apr  7 14:17:10 1997
@@ -884,10 +884,10 @@
 	 * related to the kernel should not use this.
 	 */
 	 		data = shift_state;
-			return put_user(data, (char *) arg);
+			return __put_user(data, (char *) arg);
 		case 7:
 			data = mouse_reporting();
-			return put_user(data, (char *) arg);
+			return __put_user(data, (char *) arg);
 		case 10:
 			set_vesa_blanking(arg);
 			return 0;
@@ -1337,11 +1337,17 @@
 	if (currcons == sel_cons)
 		clear_selection();
 
+	if (from_user) {
+		/* just to make sure that noone lurks at places he shouldn't see. */
+		if (verify_area(VERIFY_READ, buf, count))
+			return 0; /* ?? are error codes legal here ?? */
+	}
+
 	disable_bh(CONSOLE_BH);
 	while (!tty->stopped &&	count) {
 		enable_bh(CONSOLE_BH);
 		if (from_user)
-			get_user(c, buf);
+			__get_user(c, buf);
 		else
 			c = *buf;
 		buf++; n++; count--;
diff -u --recursive --new-file v2.1.32/linux/drivers/char/consolemap.c linux/drivers/char/consolemap.c
--- v2.1.32/linux/drivers/char/consolemap.c	Mon Oct 28 04:29:20 1996
+++ linux/drivers/char/consolemap.c	Mon Apr  7 14:17:10 1997
@@ -235,7 +235,7 @@
 
 	for (i=0; i<E_TABSZ ; i++) {
 		unsigned char uc;
-		get_user(uc, arg+i);
+		__get_user(uc, arg+i);
 		p[i] = UNI_DIRECT_BASE | uc;
 	}
 
@@ -255,7 +255,7 @@
 	for (i=0; i<E_TABSZ ; i++)
 	  {
 	    ch = conv_uni_to_pc(p[i]);
-	    put_user((ch & ~0xff) ? 0 : ch, arg+i);
+	    __put_user((ch & ~0xff) ? 0 : ch, arg+i);
 	  }
 	return 0;
 }
@@ -272,7 +272,7 @@
 
 	for (i=0; i<E_TABSZ ; i++) {
 		unsigned short us;
-		get_user(us, arg+i);
+		__get_user(us, arg+i);
 		p[i] = us;
 	}
 
@@ -291,7 +291,7 @@
 		return i;
 
 	for (i=0; i<E_TABSZ ; i++)
-	  put_user(p[i], arg+i);
+	  __put_user(p[i], arg+i);
 	
 	return 0;
 }
@@ -381,8 +381,8 @@
   while( ct-- )
     {
       unsigned short unicode, fontpos;
-      get_user(unicode, &list->unicode);
-      get_user(fontpos, &list->fontpos);
+      __get_user(unicode, &list->unicode);
+      __get_user(fontpos, &list->fontpos);
       if ( (err1 = con_insert_unipair(unicode,fontpos)) != 0 )
 	err = err1;
       list++;
@@ -434,15 +434,15 @@
 		      {
 			if ( *p2 < MAX_GLYPH && ect++ < ct )
 			  {
-			    put_user((u_short)((i<<11)+(j<<6)+k),
+			    __put_user((u_short)((i<<11)+(j<<6)+k),
 				     &list->unicode);
-			    put_user((u_short) *p2, &list->fontpos);
+			    __put_user((u_short) *p2, &list->fontpos);
 			    list++;
 			  }
 			p2++;
 		      }
 	  }
-	put_user(ect, uct);
+	__put_user(ect, uct);
 	return ((ect <= ct) ? 0 : -ENOMEM);
 }
 
diff -u --recursive --new-file v2.1.32/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.32/linux/drivers/char/lp.c	Mon Apr  7 11:35:29 1997
+++ linux/drivers/char/lp.c	Wed Apr  9 21:30:30 1997
@@ -8,7 +8,8 @@
  * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
  * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
- * lp_read (Status readback) support added by Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ * lp_read (Status readback) support added by Carsten Gross,
+ *                                             carsten@sol.wohnheim.uni-ulm.de
  */
 
 #include <linux/module.h>
@@ -17,7 +18,6 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
-#include <linux/lp.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
@@ -26,6 +26,8 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <linux/parport.h>
+#include <linux/lp.h>
 
 /* the BIOS manuals say there can be up to 4 lpt devices
  * but I have not seen a board where the 4th address is listed
@@ -33,13 +35,20 @@
  * please let me know if you have different equipment
  * if you have more than 3 printers, remember to increase LP_NO
  */
-struct lp_struct lp_table[] = {
-	{ 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
-	{ 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
-	{ 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
+struct lp_struct lp_table[] =
+{
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}},
+ {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
+  {0}}
 };
 #define LP_NO 3
 
+/* Device name */
+static char *dev_name = "lp";
+
 /* Test if printer is ready (and optionally has no error conditions) */
 #define LP_READY(minor, status) \
   ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
@@ -49,61 +58,58 @@
    (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
       (LP_PBUSY|LP_PSELECD|LP_PERRORP)
 
-/*
- * All my debugging code assumes that you debug with only one printer at
- * a time. RWWH
- * Debug info moved into stats area, so this is no longer true (Rob Janssen)
- */
-
 #undef LP_DEBUG
+#undef LP_READ_DEBUG
 
 static int lp_reset(int minor)
 {
-	outb_p(LP_PSELECP, LP_C(minor));
+	w_ctr(minor, LP_PSELECP);
 	udelay(LP_DELAY);
-	outb_p(LP_PSELECP | LP_PINITP, LP_C(minor));
-	return LP_S(minor);
+	w_ctr(minor, LP_PSELECP | LP_PINITP);
+	return r_str(minor);
 }
 
 static inline int lp_char_polled(char lpchar, int minor)
 {
 	int status, wait = 0;
-	unsigned long count  = 0;
+	unsigned long count = 0;
 	struct lp_stats *stats;
 
 	do {
-		status = LP_S(minor);
-		count ++;
-		if(need_resched)
+		status = r_str(minor);
+		count++;
+		if (need_resched)
 			schedule();
-	} while(!LP_READY(minor,status) && count < LP_CHAR(minor));
+	} while (!LP_READY(minor, status) && count < LP_CHAR(minor));
 
 	if (count == LP_CHAR(minor)) {
 		return 0;
 		/* we timed out, and the character was /not/ printed */
 	}
-	outb_p(lpchar, LP_B(minor));
+	w_dtr(minor, lpchar);
 	stats = &LP_STAT(minor);
 	stats->chars++;
 	/* must wait before taking strobe high, and after taking strobe
 	   low, according spec.  Some printers need it, others don't. */
-	while(wait != LP_WAIT(minor)) wait++;
+	while (wait != LP_WAIT(minor))
+		wait++;
 	/* control port takes strobe high */
-	outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-	while(wait) wait--;
+	w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+	while (wait)
+		wait--;
 	/* take strobe low */
-	outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+	w_ctr(minor, LP_PSELECP | LP_PINITP);
 	/* update waittime statistics */
 	if (count > stats->maxwait) {
 #ifdef LP_DEBUG
-	    printk(KERN_DEBUG "lp%d success after %d counts.\n",minor,count);
+		printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count);
 #endif
-	    stats->maxwait = count;
+		stats->maxwait = count;
 	}
 	count *= 256;
-	wait = (count > stats->meanwait)? count - stats->meanwait :
-					  stats->meanwait - count;
-	stats->meanwait = (255*stats->meanwait + count + 128) / 256;
+	wait = (count > stats->meanwait) ? count - stats->meanwait :
+	    stats->meanwait - count;
+	stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
 	stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
 
 	return 1;
@@ -117,35 +123,37 @@
 	struct lp_stats *stats;
 
 	do {
-	    if(need_resched)
-		schedule();
-	    if ((status = LP_S(minor)) & LP_PBUSY) {
-		if (!LP_CAREFUL_READY(minor, status))
-			return 0;
-		outb_p(lpchar, LP_B(minor));
-		stats = &LP_STAT(minor);
-		stats->chars++;
-		/* must wait before taking strobe high, and after taking strobe
-		   low, according spec.  Some printers need it, others don't. */
-		wait = 0;
-		while(wait != LP_WAIT(minor)) wait++;
-		/* control port takes strobe high */
-		outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-		while(wait) wait--;
-		/* take strobe low */
-		outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
-		/* update waittime statistics */
-		if (count) {
-		    if (count > stats->maxwait)
-			stats->maxwait = count;
-		    count *= 256;
-		    wait = (count > stats->meanwait)? count - stats->meanwait :
-						      stats->meanwait - count;
-		    stats->meanwait = (255*stats->meanwait + count + 128) / 256;
-		    stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
+		if(need_resched)
+			schedule();
+		if ((status = r_str(minor)) & LP_PBUSY) {
+			if (!LP_CAREFUL_READY(minor, status))
+				return 0;
+			w_dtr(minor, lpchar);
+			stats = &LP_STAT(minor);
+			stats->chars++;
+			/* must wait before taking strobe high, and after taking strobe
+			   low, according spec.  Some printers need it, others don't. */
+			wait = 0;
+			while (wait != LP_WAIT(minor))
+				wait++;
+			/* control port takes strobe high */
+			w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);
+			while (wait)
+				wait--;
+			/* take strobe low */
+			w_ctr(minor, LP_PSELECP | LP_PINITP);
+			/* update waittime statistics */
+			if (count) {
+				if (count > stats->maxwait)
+					stats->maxwait = count;
+				count *= 256;
+				wait = (count > stats->meanwait) ? count - stats->meanwait :
+				    stats->meanwait - count;
+				stats->meanwait = (255 * stats->meanwait + count + 128) / 256;
+				stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
+			}
+			return 1;
 		}
-		return 1;
-	    }
 	} while (count++ < LP_CHAR(minor));
 
 	return 0;
@@ -153,17 +161,15 @@
 
 static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct lp_struct *lp = &lp_table[0];
+	struct parport *pb = (struct parport *) dev_id;
+	struct ppd *pd = pb->cad;
+	struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
 
-	while (irq != lp->irq) {
-		if (++lp >= &lp_table[LP_NO])
-			return;
-	}
-
-	wake_up(&lp->lp_wait_q);
+	if (lp_dev->lp_wait_q)
+		wake_up(&lp_dev->lp_wait_q);
 }
 
-static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count)
+static inline int lp_write_interrupt(unsigned int minor, const char *buf, int count)
 {
 	unsigned long copy_size;
 	unsigned long total_bytes_written = 0;
@@ -171,6 +177,11 @@
 	struct lp_struct *lp = &lp_table[minor];
 	unsigned char status;
 
+	if (minor >= LP_NO)
+		return -ENXIO;
+	if (lp_table[minor].dev == NULL)
+		return -ENXIO;
+
 	do {
 		bytes_written = 0;
 		copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
@@ -184,35 +195,35 @@
 			} else {
 				int rc = total_bytes_written + bytes_written;
 				if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
-					 LP_STAT(minor).maxrun = lp_table[minor].runchars;
-				status = LP_S(minor);
+					LP_STAT(minor).maxrun = lp_table[minor].runchars;
+				status = r_str(minor);
 				if ((status & LP_POUTPA)) {
 					printk(KERN_INFO "lp%d out of paper\n", minor);
 					if (LP_F(minor) & LP_ABORT)
-						return rc?rc:-ENOSPC;
+						return rc ? rc : -ENOSPC;
 				} else if (!(status & LP_PSELECD)) {
 					printk(KERN_INFO "lp%d off-line\n", minor);
 					if (LP_F(minor) & LP_ABORT)
-						return rc?rc:-EIO;
+						return rc ? rc : -EIO;
 				} else if (!(status & LP_PERRORP)) {
 					printk(KERN_ERR "lp%d printer error\n", minor);
 					if (LP_F(minor) & LP_ABORT)
-						return rc?rc:-EIO;
+						return rc ? rc : -EIO;
 				}
 				LP_STAT(minor).sleeps++;
 				cli();
-				outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
-				status = LP_S(minor);
+				w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);
+				status = r_str(minor);
 				if ((!(status & LP_PACK) || (status & LP_PBUSY))
-				  && LP_CAREFUL_READY(minor, status)) {
-					outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+				    && LP_CAREFUL_READY(minor, status)) {
+					w_ctr(minor, LP_PSELECP | LP_PINITP);
 					sti();
 					continue;
 				}
-				lp_table[minor].runchars=0;
+				lp_table[minor].runchars = 0;
 				current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
 				interruptible_sleep_on(&lp->lp_wait_q);
-				outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+				w_ctr(minor, LP_PSELECP | LP_PINITP);
 				sti();
 				if (current->signal & ~current->blocked) {
 					if (total_bytes_written + bytes_written)
@@ -232,9 +243,9 @@
 	return total_bytes_written;
 }
 
-static inline int lp_write_polled(unsigned int minor, const char * buf, int count)
+static inline int lp_write_polled(unsigned int minor, const char *buf, int count)
 {
-	int  retval,status;
+	int  retval, status;
 	char c;
 	const char *temp;
 
@@ -249,7 +260,7 @@
 		} else { /* if printer timed out */
 			if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
 				 LP_STAT(minor).maxrun = lp_table[minor].runchars;
-			status = LP_S(minor);
+			status = r_str(minor);
 
 			if (status & LP_POUTPA) {
 				printk(KERN_INFO "lp%d out of paper\n", minor);
@@ -269,7 +280,7 @@
 			} else
 			/* not offline or out of paper. on fire? */
 			if (!(status & LP_PERRORP)) {
-				printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
+				printk(KERN_ERR "lp%d on fire\n", minor);
 				if(LP_F(minor) & LP_ABORT)
 					return temp-buf?temp-buf:-EIO;
 				current->state = TASK_INTERRUPTIBLE;
@@ -302,15 +313,27 @@
 	const char * buf, unsigned long count)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
+	int retv;
 
 	if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
 		lp_table[minor].runchars = 0;
+
 	lp_table[minor].lastcall = jiffies;
 
-	if (LP_IRQ(minor))
-		return lp_write_interrupt(minor, buf, count);
-	else
-		return lp_write_polled(minor, buf, count);
+ 	/* Claim Parport or sleep until it becomes available
+ 	 * (see lp_wakeup() for details)
+ 	 */
+ 	if (parport_claim(lp_table[minor].dev)) {
+ 		sleep_on(&lp_table[minor].lp_wait_q);
+ 		lp_table[minor].lp_wait_q = NULL;
+ 	}
+ 	if (LP_IRQ(minor) > 0)
+ 		retv = lp_write_interrupt(minor, buf, count);
+  	else
+ 		retv = lp_write_polled(minor, buf, count);
+ 
+ 	parport_release(lp_table[minor].dev);
+ 	return retv;
 }
 
 static long long lp_lseek(struct inode * inode, struct file * file,
@@ -319,38 +342,19 @@
 	return -ESPIPE;
 }
 
-/* Test if nibble mode for status readback is okay. 
- * returns false if not, otherwise true
- */
-static int lp_nibble_mode_ok(int minor) 
-{
-	int reply=1;
-
-	outb_p(0, LP_B(minor)); 	/* Request "nibble mode" */
-	udelay(LP_DELAY);		
-	outb((inb(LP_C(minor)) & ~8), LP_C(minor)); /* SelectIN low */
-	outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
-	udelay(LP_DELAY);
-	outb((inb(LP_C(minor)) | 1), LP_C(minor)); /* Strobe high */
-	if (( LP_S(minor) & ~0x80) != 0x38) reply=0; /* expected answer? */
-	outb((inb(LP_C(minor)) & ~1 ), LP_C(minor)); /* Strobe low */
-	outb((inb(LP_C(minor)) & ~2 ), LP_C(minor)); /* Autofeed low */
-	udelay(LP_DELAY);
-	return(reply);
-}
-
+#ifdef CONFIG_PRINTER_READBACK
 
 static int lp_read_nibble(int minor) 
 {
 	unsigned char i;
-	i=LP_S(minor)>>3;
+	i=r_str(minor)>>3;
 	i&=~8;
 	if ( ( i & 0x10) == 0) i|=8;
 	return(i & 0x0f);
 }
 
 static void lp_select_in_high(int minor) {
-	outb((inb(LP_C(minor)) | 8), LP_C(minor));
+	w_ctr(minor, (r_ctr(minor) | 8));
 }
 
 /* Status readback confirming to ieee1284 */
@@ -364,6 +368,14 @@
 	unsigned int i;
 	unsigned int minor=MINOR(inode->i_rdev);
 	
+ 	/* Claim Parport or sleep until it becomes available
+ 	 * (see lp_wakeup() for details)
+ 	 */
+ 	if (parport_claim(lp_table[minor].dev)) {
+ 		sleep_on(&lp_table[minor].lp_wait_q);
+ 		lp_table[minor].lp_wait_q = NULL;
+ 	}
+
 	temp=buf;	
 #ifdef LP_READ_DEBUG 
 	printk(KERN_INFO "lp%d: read mode\n", minor);
@@ -372,14 +384,19 @@
 	retval = verify_area(VERIFY_WRITE, buf, count);
 	if (retval)
 		return retval;
-	if (lp_nibble_mode_ok(minor)==0) {
+	if (parport_ieee1284_nibble_mode_ok(lp_table[minor].dev->port, 0)==0) {
+#ifdef LP_READ_DEBUG
+		printk(KERN_INFO "lp%d: rejected IEEE1284 negotiation.\n",
+		       minor);
+#endif
 		lp_select_in_high(minor);
+		parport_release(lp_table[minor].dev);
 		return temp-buf;          /*  End of file */
 	}
 	for (i=0; i<=(count*2); i++) {
-		outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+		w_ctr(minor, r_ctr(minor) | 2); /* AutoFeed high */
 		do {
-			status=(LP_S(minor) & 0x40);
+			status=(r_str(minor) & 0x40);
 			udelay(50);
 			counter++;
 			if (need_resched)
@@ -389,15 +406,16 @@
 #ifdef LP_READ_DEBUG
 			printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
 #endif		
-			outb((inb(LP_C(minor)) & ~2), LP_C(minor));
+			w_ctr(minor, r_ctr(minor) & ~2);
 			lp_select_in_high(minor);
+			parport_release(lp_table[minor].dev);
 			return temp-buf; /* end the read at timeout */
 		}
 		counter=0;
 		z=lp_read_nibble(minor);
-		outb((inb(LP_C(minor)) & ~2), LP_C(minor)); /* AutoFeed low */
+		w_ctr(minor, r_ctr(minor) & ~2); /* AutoFeed low */
 		do {
-			status=(LP_S(minor) & 0x40);
+			status=(r_str(minor) & 0x40);
 			udelay(20);
 			counter++;
 			if (need_resched)
@@ -409,6 +427,7 @@
 #endif
 			if (current->signal & ~current->blocked) {
 				lp_select_in_high(minor);
+				parport_release(lp_table[minor].dev);
 				if (temp !=buf)
 					return temp-buf;
 				else 
@@ -426,14 +445,15 @@
 		} else Byte=z;
 	}
 	lp_select_in_high(minor);
+	parport_release(lp_table[minor].dev);
 	return temp-buf;	
 }
 
+#endif
+
 static int lp_open(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
-	int ret;
-	unsigned int irq;
 
 	if (minor >= LP_NO)
 		return -ENXIO;
@@ -450,7 +470,7 @@
 	   a non-standard manner.  This is strictly a Linux hack, and
 	   should most likely only ever be used by the tunelp application. */
 	if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
-		int status = LP_S(minor);
+		int status = r_str(minor);
 		if (status & LP_POUTPA) {
 			printk(KERN_INFO "lp%d out of paper\n", minor);
 			MOD_DEC_USE_COUNT;
@@ -465,24 +485,13 @@
 			return -EIO;
 		}
 	}
-
-	if ((irq = LP_IRQ(minor))) {
+	if (LP_IRQ(minor) > 0) {
 		lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 		if (!lp_table[minor].lp_buffer) {
 			MOD_DEC_USE_COUNT;
 			return -ENOMEM;
 		}
-
-		ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
-		if (ret) {
-			kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
-			lp_table[minor].lp_buffer = NULL;
-			printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
-			MOD_DEC_USE_COUNT;
-			return ret;
-		}
 	}
-
 	LP_F(minor) |= LP_BUSY;
 	return 0;
 }
@@ -493,11 +502,9 @@
 	unsigned int irq;
 
 	if ((irq = LP_IRQ(minor))) {
-		free_irq(irq, NULL);
 		kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 		lp_table[minor].lp_buffer = NULL;
 	}
-
 	LP_F(minor) &= ~LP_BUSY;
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -545,49 +552,9 @@
 		case LPWAIT:
 			LP_WAIT(minor) = arg;
 			break;
-		case LPSETIRQ: {
-			int oldirq;
-			int newirq = arg;
-			struct lp_struct *lp = &lp_table[minor];
-
-			if (!suser())
-				return -EPERM;
-
-			oldirq = LP_IRQ(minor);
-
-			/* Allocate buffer now if we are going to need it */
-			if (!oldirq && newirq) {
-				lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
-				if (!lp->lp_buffer)
-					return -ENOMEM;
-			}
-
-			if (oldirq) {
-				free_irq(oldirq, NULL);
-			}
-			if (newirq) {
-				/* Install new irq */
-				if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer", NULL))) {
-					if (oldirq) {
-						/* restore old irq */
-						request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
-					} else {
-						/* We don't need the buffer */
-						kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
-						lp->lp_buffer = NULL;
-					}
-					return retval;
-				}
-			}
-			if (oldirq && !newirq) {
-				/* We don't need the buffer */
-				kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
-				lp->lp_buffer = NULL;
-			}
-			LP_IRQ(minor) = newirq;
-			lp_reset(minor);
+		case LPSETIRQ: 
+			return -EINVAL;
 			break;
-		}
 		case LPGETIRQ:
 			retval = verify_area(VERIFY_WRITE, (void *) arg,
 			    sizeof(int));
@@ -601,7 +568,7 @@
 		    	if (retval)
 		    		return retval;
 			else {
-				int status = LP_S(minor);
+				int status = r_str(minor);
 				copy_to_user((int *) arg, &status, sizeof(int));
 			}
 			break;
@@ -638,7 +605,11 @@
 
 static struct file_operations lp_fops = {
 	lp_lseek,
+#ifdef CONFIG_PRINTER_READBACK
 	lp_read,
+#else
+	NULL,
+#endif
 	lp_write,
 	NULL,		/* lp_readdir */
 	NULL,		/* lp_poll */
@@ -648,130 +619,108 @@
 	lp_release
 };
 
-static int lp_probe(int offset)
-{
-	int base, size;
-	unsigned int testvalue;
+static int parport[LP_NO] = { -1, };
 
-	base = LP_B(offset);
-	if (base == 0) 
-		return -1;		/* de-configured by command line */
-	if (LP_IRQ(offset) > 15) 
-		return -1;		/* bogus interrupt value */
-	size = (base == 0x3bc)? 3 : 8;
-	if (check_region(base, size) < 0)
-		return -1;
-	/* write to port & read back to check */
-	outb_p(LP_DUMMY, base);
-	udelay(LP_DELAY);
-	testvalue = inb_p(base);
-	if (testvalue == LP_DUMMY) {
-		LP_F(offset) |= LP_EXIST;
-		lp_reset(offset);
-		printk(KERN_INFO "lp%d at 0x%04x, ", offset, base);
-		request_region(base, size, "lp");
-		if (LP_IRQ(offset))
-			printk("(irq = %d)\n", LP_IRQ(offset));
-		else
-			printk("(polling)\n");
-		return 1;
-	} else
-		return 0;
-}
+#ifdef MODULE
+#define lp_init init_module
+MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
 
-/* Command line parameters:
+#else
 
-   When the lp driver is built in to the kernel, you may use the
-   LILO/LOADLIN command line to set the port addresses and interrupts
-   that the driver will use.
+static int parport_ptr = 0;
 
-   Syntax:	lp=port0[,irq0[,port1[,irq1[,port2[,irq2]]]]]
+void lp_setup(char *str, int *ints)
+{
+	/* Ugh. */
+	if (!strncmp(str, "parport", 7)) {
+		int n = simple_strtoul(str+7, NULL, 10);
+		if (parport_ptr < LP_NO)
+			parport[parport_ptr++] = n;
+		else
+			printk(KERN_INFO "lp: too many ports, %s ignored.\n",
+			       str);
+	} else if (!strcmp(str, "auto")) {
+		parport[0] = -3;
+	} else {
+		if (ints[0] == 0 || ints[1] == 0) {
+			/* disable driver on "parport=" or "parport=0" */
+			parport[0] = -2;
+		} else {
+			printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]);
+		}
+	}
+}
 
-   For example:   lp=0x378,0   or   lp=0x278,5,0x378,7
+#endif
 
-   Note that if this feature is used, you must specify *all* the ports
-   you want considered, there are no defaults.  You can disable a
-   built-in driver with lp=0 .
+int lp_wakeup(void *ref)
+{
+	struct lp_struct *lp_dev = (struct lp_struct *) ref;
 
-*/
+	if (!lp_dev->lp_wait_q)
+		return 1;	/* Wake up whom? */
 
-void	lp_setup(char *str, int *ints)
+	/* Claim the Parport */
+	if (parport_claim(lp_dev->dev))
+		return 1;	/* Shouldn't happen */
 
-{	
-        LP_B(0)   = ((ints[0] > 0) ? ints[1] : 0 );
-        LP_IRQ(0) = ((ints[0] > 1) ? ints[2] : 0 );
-        LP_B(1)   = ((ints[0] > 2) ? ints[3] : 0 );
-        LP_IRQ(1) = ((ints[0] > 3) ? ints[4] : 0 );
-        LP_B(2)   = ((ints[0] > 4) ? ints[5] : 0 );
-        LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : 0 );
+	wake_up(&lp_dev->lp_wait_q);
+	return 0;
 }
 
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-#define lp_init init_module
-#endif
+static int inline lp_searchfor(int list[], int a)
+{
+	int i;
+	for (i = 0; i < LP_NO && list[i] != -1; i++) {
+		if (list[i] == a) return 1;
+	}
+	return 0;
+}
 
 int lp_init(void)
 {
-	int offset = 0;
 	int count = 0;
-#ifdef MODULE
-	int failed = 0;
-#endif
-
-	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
-		printk("lp: unable to get major %d\n", LP_MAJOR);
-		return -EIO;
-	}
-#ifdef MODULE
-	/* When user feeds parameters, use them */
-	for (offset=0; offset < LP_NO; offset++) {
-		int specified=0;
-
-		if (io[offset] != 0) {
-			LP_B(offset) = io[offset];
-			specified++;
-		}
-		if (irq[offset] != 0) {
-			LP_IRQ(offset) = irq[offset];
-			specified++;
-		}
-		if (specified) {
-			if (lp_probe(offset) <= 0) {
-				printk(KERN_INFO "lp%d: Not found\n", offset);
-				failed++;
-			} else
-				count++;
+	struct parport *pb;
+  
+	if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
+  		printk("lp: unable to get major %d\n", LP_MAJOR);
+  		return -EIO;
+	}
+
+	if (parport[0] == -2) return 0;
+
+	pb = parport_enumerate();
+
+	while (pb) {
+		if (parport[0] == -1 || lp_searchfor(parport, count) ||
+		    (parport[0] == -3 &&
+		     pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
+			lp_table[count].dev =
+			    parport_register_device(pb, dev_name, NULL, 
+					    lp_wakeup,
+					    lp_interrupt, PARPORT_DEV_TRAN,
+					    (void *) &lp_table[count]);
+			lp_table[count].flags |= LP_EXIST;
+			printk(KERN_INFO "lp%d: using %s at 0x%x, ", count,
+			       pb->name, pb->base);
+			if (pb->irq == -1)
+				printk("polling.\n");
+			else
+				printk("irq %d.\n", pb->irq);
 		}
-	}
-	/* Successful specified devices increase count
-	 * Unsuccessful specified devices increase failed
-	 */
-	if (count)
-		return 0;
-	if (failed) {
-		printk(KERN_INFO "lp: No override devices found.\n");
-		unregister_chrdev(LP_MAJOR,"lp");
-		return -EIO;
-	}
-	/* Only get here if there were no specified devices. To continue 
-	 * would be silly since the above code has scribbled all over the
-	 * probe list.
-	 */
-#endif
-	/* take on all known port values */
-	for (offset = 0; offset < LP_NO; offset++) {
-		int ret = lp_probe(offset);
-		if (ret < 0)
-			continue;
-		count += ret;
-	}
-	if (count == 0)
-		printk("lp: Driver configured but no interfaces found.\n");
+		if (++count == LP_NO)
+			break;
+		pb = pb->next;
+  	}
 
-	return 0;
+  	/* Successful specified devices increase count
+  	 * Unsuccessful specified devices increase failed
+  	 */
+  	if (count)
+  		return 0;
+  
+	printk(KERN_INFO "lp: driver loaded but no devices found\n");
+	return 1;
 }
 
 #ifdef MODULE
@@ -779,13 +728,11 @@
 {
 	int offset;
 
-	unregister_chrdev(LP_MAJOR,"lp");
+	unregister_chrdev(LP_MAJOR, "lp");
 	for (offset = 0; offset < LP_NO; offset++) {
-		int base, size;
-		base = LP_B(offset);
-		size = (base == 0x3bc)? 3 : 8;
-		if (LP_F(offset) & LP_EXIST)
-			release_region(LP_B(offset),size);
+		if (lp_table[offset].dev == NULL)
+			continue;
+		parport_unregister_device(lp_table[offset].dev);
 	}
 }
 #endif
diff -u --recursive --new-file v2.1.32/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v2.1.32/linux/drivers/char/pcxx.c	Sun Dec 22 05:07:11 1996
+++ linux/drivers/char/pcxx.c	Wed Apr  9 21:27:16 1997
@@ -33,10 +33,14 @@
  *  1.5.7 July 22, 1996 Martin Mares: CLOCAL fix, pcxe_table clearing.
  *		David Nugent: Bug in pcxe_open.
  *		Brian J. Murrell: Modem Control fixes, Majors correctly assigned
+ *  1.6.1 April 6, 1997 Bernhard Kaindl: fixed virtual memory access for 2.1
+ *              i386-kernels and use on other archtitectures, Allowing use
+ *              as module, added module parameters, added switch to enable
+ *              verbose messages to assist user during card configuration.
+ *              Currently only tested on a PC/Xi card, but should work on Xe
+ *              and Xeve also.
  *
  */
-#undef MODULE
-/* Module code is broken right now. Don't enable this unless you want to fix it */
 
 #undef SPEED_HACK
 /* If you define SPEED_HACK then you get the following Baudrate translation
@@ -67,13 +71,7 @@
 #include <linux/string.h>
 
 #ifndef MODULE
-
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
-
-#include <linux/ctype.h>
+#include <linux/ctype.h> /* We only need it for parsing the "digi="-line */
 #endif
 
 #include <asm/system.h>
@@ -81,8 +79,7 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 
-#define VERSION 	"1.5.7"
-static char *banner = "Digiboard PC/X{i,e,eve} driver v1.5.7";
+#define VERSION 	"1.6.1"
 
 #include "digi.h"
 #include "fep.h"
@@ -90,11 +87,45 @@
 #include "digi_fep.h"
 #include "digi_bios.h"
 
-/* Define one default setting if no digi= config line is used.
- * Default is ALTPIN = ON, PC/16E, 16 ports, I/O 200h Memory 0D0000h
+/*
+ * Define one default setting if no digi= config line is used.
+ * Default is altpin = disabled, 16 ports, I/O 200h, Memory 0D0000h
  */
-static struct board_info boards[MAX_DIGI_BOARDS] = { {	ENABLED, 0, ON, 16, 0x200, 0xd0000,0 } };
+static struct board_info boards[MAX_DIGI_BOARDS] = { {
+/* Board is enabled       */	ENABLED,
+/* Type is auto-detected  */	0,
+/* altping is disabled    */    DISABLED,
+/* number of ports = 16   */	16,
+/* io address is 0x200    */	0x200,
+/* card memory at 0xd0000 */	0xd0000,
+/* first minor device no. */	0
+} };
  
+static int verbose = 0;
+static int debug   = 0;
+
+#ifdef MODULE
+/* Variables for insmod */
+static int io[]           = {0, 0, 0, 0};
+static int membase[]      = {0, 0, 0, 0};
+static int memsize[]      = {0, 0, 0, 0};
+static int altpin[]       = {0, 0, 0, 0};
+static int numports[]     = {0, 0, 0, 0};
+
+# if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Bernhard Kaindl");
+MODULE_DESCRIPTION("Digiboard PC/X{i,e,eve} driver");
+MODULE_PARM(verbose,     "i");
+MODULE_PARM(debug,       "i");
+MODULE_PARM(io,          "1-4i");
+MODULE_PARM(membase,     "1-4i");
+MODULE_PARM(memsize,     "1-4i");
+MODULE_PARM(altpin,      "1-4i");
+MODULE_PARM(numports,    "1-4i");
+# endif
+
+#endif MODULE
+
 static int numcards = 1;
 static int nbdevs = 0;
  
@@ -159,19 +190,14 @@
 
 /* function definitions */
 #ifdef MODULE
-int		init_module(void);
-void		cleanup_module(void);
 
 /*
- *	Loadable module initialization stuff.
+ * pcxe_init() is our init_module():
  */
+#define pcxe_init init_module
 
-int init_module()
-{
-
-	return pcxe_init();
+void	cleanup_module(void);
 
-}
 
 /*****************************************************************************/
 
@@ -184,7 +210,7 @@
 	struct board_info *bd;
 	struct channel *ch;
 
-	printk(KERN_INFO "Unloading PC/Xx: version %s\n", VERSION);
+	printk(KERN_NOTICE "Unloading PC/Xx version %s\n", VERSION);
 
 	save_flags(flags);
 	cli();
@@ -287,7 +313,7 @@
 static inline void pcxe_sched_event(struct channel *info, int event)
 {
 	info->event |= 1 << event;
-	queue_task_irq_off(&info->tqueue, &tq_pcxx);
+	queue_task(&info->tqueue, &tq_pcxx);
 	mark_bh(DIGI_BH);
 }
 
@@ -861,12 +887,12 @@
 	}
 }
 
-/* Flag if lilo configuration option is used. If so the
- * default settings are removed
- */
-
-static int liloconfig=0;
+#ifndef MODULE
 
+/*
+ * Driver setup function when linked into the kernel to optionally parse multible
+ * "digi="-lines and initialize the driver at boot time. No probing.
+ */
 void pcxx_setup(char *str, int *ints)
 {
 
@@ -875,11 +901,7 @@
 	char              *temp, *t2;
 	unsigned          len;
 
-#if 0
-	if (!numcards)
-		memset(&boards, 0, sizeof(boards));
-#endif
-	if (liloconfig==0) { liloconfig=1;numcards=0; }
+	numcards=0;
 
 	memset(&board, 0, sizeof(board));
 
@@ -921,7 +943,6 @@
 				return;
 		}
 
-	
 	while (str && *str) 
 	{
 		/* find the next comma or terminator */
@@ -985,11 +1006,6 @@
 
 			case 4:
 				t2 = str;
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
 				while (isdigit(*t2))
 					t2++;
 
@@ -998,39 +1014,27 @@
 					printk("PC/Xx: Invalid port count %s\n", str);
 					return;
 				}
-#endif
 
 				board.numports = simple_strtoul(str, NULL, 0);
 				last = i;
 				break;
 
 			case 5:
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
 				t2 = str;
 				while (isxdigit(*t2))
 					t2++;
 
 				if (*t2)
 				{
-					printk("PC/Xx: Invalid port count %s\n", str);
+					printk("PC/Xx: Invalid io port address %s\n", str);
 					return;
 				}
-#endif
 
 				board.port = simple_strtoul(str, NULL, 16);
 				last = i;
 				break;
 
 			case 6:
-#ifndef MODULE
-/* is* routines not available in modules
-** the need for this should go away when probing is done.  :-)
-** brian@ilinx.com
-*/
 				t2 = str;
 				while (isxdigit(*t2))
 					t2++;
@@ -1040,7 +1044,6 @@
 					printk("PC/Xx: Invalid memory base %s\n", str);
 					return;
 				}
-#endif
 
 				board.membase = simple_strtoul(str, NULL, 16);
 				last = i;
@@ -1075,33 +1078,97 @@
 	/* yeha!  string parameter was successful! */
 	numcards++;
 }
+#endif
 
-
+/*
+ * function to initialize the driver with the given parameters, which are either
+ * the default values from this file or the parameters given at boot.
+ */
 int pcxe_init(void)
 {
-	ulong flags, memory_seg=0, memory_size;
-	int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
+	ulong memory_seg=0, memory_size=0;
+	int lowwater, enabled_cards=0, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
 	unchar *fepos, *memaddr, *bios, v;
 	volatile struct global_data *gd;
-	struct board_info *bd;
 	volatile struct board_chan *bc;
+	struct board_info *bd;
 	struct channel *ch;
 
-	printk("%s\n", banner);
+	printk(KERN_NOTICE "Digiboard PC/X{i,e,eve} driver v%s\n", VERSION);
+
+#ifdef MODULE
+	for (i = 0; i < 4; i++) {
+		if (io[i]) {
+			numcards = 0;
+			break;
+		}
+	}
+	if (numcards == 0) {
+		int first_minor = 0;
+
+		for (i = 0; i < 4; i++) {
+			if (io[i] == 0) {
+				boards[i].port    = 0;
+				boards[i].status  = DISABLED;
+			}
+			else {
+				boards[i].port         = (ushort)io[i];
+				boards[i].status       = ENABLED;
+				boards[i].first_minor  = first_minor;
+				numcards=i+1;
+			}
+			if (membase[i])
+				boards[i].membase = (ulong)membase[i];
+			else
+				boards[i].membase = 0xD0000;
+
+			if (memsize[i])
+				boards[i].memsize = (ulong)(memsize[i] * 1024);
+			else
+				boards[i].memsize = 0;
+
+			if (altpin[i])
+				boards[i].altpin  = ON;
+			else
+				boards[i].altpin  = OFF;
+
+			if (numports[i])
+				boards[i].numports  = (ushort)numports[i];
+			else
+				boards[i].numports  = 16;
+
+			first_minor += boards[i].numports;
+		}
+	}
+#endif
 
 	if (numcards <= 0)
 	{
-		printk("PC/Xx: No cards configured, exiting.\n");
-		return(0);
+		printk("PC/Xx: No cards configured, driver not active.\n");
+		return -EIO;
 	}
+#if 1
+	if (debug)
+	    for (i = 0; i < numcards; i++) {
+		    printk("Card %d:status=%d, port=0x%x, membase=0x%lx, memsize=0x%lx, altpin=%d, numports=%d, first_minor=%d\n",
+			    i+1,
+			    boards[i].status,
+			    boards[i].port,
+			    boards[i].membase,
+			    boards[i].memsize,
+			    boards[i].altpin,
+			    boards[i].numports,
+			    boards[i].first_minor);
+	    }
+#endif
 
 	for (i=0;i<numcards;i++)
 		nbdevs += boards[i].numports;
 
 	if (nbdevs <= 0)
 	{
-		printk("PC/Xx: No devices activated, exiting.\n");
-		return(0);
+		printk("PC/Xx: No devices activated, driver not active.\n");
+		return -EIO;
 	}
 
 	/*
@@ -1175,22 +1242,22 @@
 	pcxe_callout.subtype = SERIAL_TYPE_CALLOUT;
 	pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 
-	save_flags(flags);
-	cli();
-
 	for(crd=0; crd < numcards; crd++) {
 		bd = &boards[crd];
 		outb(FEPRST, bd->port);
 		pcxxdelay(1);
 
 		for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) {
-			if(i > 1000) {
+			if(i > 100) {
 				printk("PC/Xx: Board not found at port 0x%x! Check switch settings.\n",
 					bd->port);
 				bd->status = DISABLED;
 				break;
 			}
-			pcxxdelay(1);
+#ifdef MODULE
+			schedule();
+#endif
+			pcxxdelay(10);
 		}
 		if(bd->status == DISABLED)
 			continue;
@@ -1237,18 +1304,38 @@
 				memory_size = 0x10000;
 			}
 		}
+		if (verbose)
+			printk("Configuring card %d as a %s %ldK card. io=0x%x, mem=%lx-%lx\n",
+				crd+1, board_desc[bd->type], memory_size/1024,
+				bd->port,bd->membase,bd->membase+memory_size-1);
+
+		if (boards[crd].memsize == 0)
+			boards[crd].memsize = memory_size;
+		else
+			if (boards[crd].memsize != memory_size) {
+			    printk("PC/Xx: memory size mismatch:supplied=%lx(%ldK) probed=%ld(%ldK)\n",
+				    boards[crd].memsize, boards[crd].memsize / 1024,
+				    memory_size, memory_size / 1024);
+			    continue;
+			}
 
-		memaddr = (unchar *) bd->membase;
+		memaddr = (unchar *)phys_to_virt(bd->membase);
+
+		if (verbose)
+			printk("Resetting board and testing memory access:");
 
 		outb(FEPRST|FEPMEM, bd->port);
 
 		for(i=0; (inb(bd->port) & FEPMASK) != (FEPRST|FEPMEM); i++) {
-			if(i > 10000) {
-				printk("PC/Xx: %s not resetting at port 0x%x! Check switch settings.\n",
+			if(i > 1000) {
+				printk("\nPC/Xx: %s not resetting at port 0x%x! Check switch settings.\n",
 					board_desc[bd->type], bd->port);
 				bd->status = DISABLED;
 				break;
 			}
+#ifdef MODULE
+			schedule();
+#endif
 			pcxxdelay(1);
 		}
 		if(bd->status == DISABLED)
@@ -1265,6 +1352,8 @@
 			bd->status = DISABLED;
 			continue;
 		}
+		if (verbose)
+			printk(" done.\n");
 
 		for(i=0; i < 16; i++) {
 			memaddr[MISCGLOBAL+i] = 0;
@@ -1273,19 +1362,36 @@
 		if(bd->type == PCXI || bd->type == PCXE) {
 			bios = memaddr + BIOSCODE + ((0xf000 - memory_seg) << 4);
 
+			if (verbose)
+				printk("Downloading BIOS to 0x%lx:", virt_to_phys(bios));
+
 			memcpy(bios, pcxx_bios, pcxx_nbios);
 
+			if (verbose)
+				printk(" done.\n");
+
 			outb(FEPMEM, bd->port);
 
-			for(i=0; i <= 10000; i++) {
+			if (verbose)
+				printk("Waiting for BIOS to become ready");
+
+			for(i=1; i <= 30; i++) {
 				if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
 					goto load_fep;
 				}
-				pcxxdelay(1);
+				if (verbose) {
+					printk(".");
+					if (i % 50 == 0)
+						printk("\n");
+				}
+#ifdef MODULE
+				schedule();
+#endif
+				pcxxdelay(50);
 			}
 
-			printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
-							board_desc[bd->type], bd->port);
+			printk("\nPC/Xx: BIOS download failed for board at 0x%x(addr=%lx-%lx)!\n",
+							bd->port, bd->membase, bd->membase+bd->memsize);
 			bd->status = DISABLED;
 			continue;
 		}
@@ -1299,14 +1405,22 @@
 			outb(FEPCLR, bd->port);
 			memwinon(bd,0);
 
-			for(i=0; i <= 10000; i++) {
+			for(i=0; i <= 1000; i++) {
 				if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
 					goto load_fep;
 				}
-				pcxxdelay(1);
+				if (verbose) {
+					printk(".");
+					if (i % 50 == 0)
+						printk("\n");
+				}
+#ifdef MODULE
+				schedule();
+#endif
+				pcxxdelay(10);
 			}
 
-			printk("PC/Xx: BIOS download failed on the %s at 0x%x!\n",
+			printk("\nPC/Xx: BIOS download failed on the %s at 0x%x!\n",
 				board_desc[bd->type], bd->port);
 			bd->status = DISABLED;
 			continue;
@@ -1317,10 +1431,16 @@
 		if(bd->type == PCXEVE)
 			fepos = memaddr + (FEPCODE & 0x1fff);
 
+		if (verbose)
+			printk(" ok.\nDownloading FEP/OS to 0x%lx:", virt_to_phys(fepos));
+
 		memwinon(bd, (FEPCODE >> 13));
 		memcpy(fepos, pcxx_cook, pcxx_ncook);
 		memwinon(bd, 0);
 
+		if (verbose)
+			printk(" done.\n");
+
 		*(ushort *)((ulong)memaddr + MBOX +  0) = 2;
 		*(ushort *)((ulong)memaddr + MBOX +  2) = memory_seg + FEPCODESEG;
 		*(ushort *)((ulong)memaddr + MBOX +  4) = 0;
@@ -1338,12 +1458,18 @@
 				bd->status = DISABLED;
 				break;
 			}
+#ifdef MODULE
+			schedule();
+#endif
 			pcxxdelay(1);
 		}
 
 		if(bd->status == DISABLED)
 			continue;
 
+		if (verbose)
+			printk("Waiting for FEP/OS to become ready");
+
 		*(ushort *)(memaddr + FEPSTAT) = 0;
 		*(ushort *)(memaddr + MBOX + 0) = 1;
 		*(ushort *)(memaddr + MBOX + 2) = FEPCODESEG;
@@ -1353,18 +1479,29 @@
 		outb(FEPCLR, bd->port);
 		memwinon(bd, 0);
 
-		for(i=0; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
-			if(i > 10000) {
-				printk("PC/Xx: FEP/OS download failed on the %s at 0x%x!\n",
+		for(i=1; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
+			if(i > 1000) {
+				printk("\nPC/Xx: FEP/OS download failed on the %s at 0x%x!\n",
 					board_desc[bd->type], bd->port);
 				bd->status = DISABLED;
 				break;
 			}
+			if (verbose) {
+				printk(".");
+				if (i % 50 == 0)
+					printk("\n%5d",i/50);
+			}
+#ifdef MODULE
+			schedule();
+#endif
 			pcxxdelay(1);
 		}
 		if(bd->status == DISABLED)
 			continue;
 
+		if (verbose)
+			printk(" ok.\n");
+
 		ch = digi_channels+bd->first_minor;
 		pcxxassert(ch < digi_channels+nbdevs, "ch out of range");
 
@@ -1423,9 +1560,7 @@
 
 			ch->txbufsize = bc->tmax + 1;
 			ch->rxbufsize = bc->rmax + 1;
-
 			ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL);
-
 			lowwater = ch->txbufsize >= 2000 ? 1024 : ch->txbufsize/2;
 			fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
 			fepcmd(ch, SRXLWATER, ch->rxbufsize/4, 0, 10, 0);
@@ -1454,17 +1589,25 @@
 			ch->normal_termios = pcxe_driver.init_termios;
 			ch->open_wait = 0;
 			ch->close_wait = 0;
-			/* zero out flags as it is unassigned at this point
-			 * brian@ilinx.com
-			 */
 			ch->asyncflags = 0;
 		}
 
-		printk("PC/Xx: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
-			board_desc[bd->type], board_mem[bd->type], bd->port, 
-			bd->membase, bd->numports);
+		if (verbose)
+		    printk("Card No. %d ready: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
+			    crd+1, board_desc[bd->type], board_mem[bd->type], bd->port, 
+			    bd->membase, bd->numports);
+		else
+		    printk("PC/Xx: %s (%s) I/O=0x%x Mem=0x%lx Ports=%d\n", 
+			    board_desc[bd->type], board_mem[bd->type], bd->port, 
+			    bd->membase, bd->numports);
 
 		memwinoff(bd, 0);
+		enabled_cards++;
+	}
+
+	if (enabled_cards <= 0) {
+		printk(KERN_NOTICE "PC/Xx: No cards enabled, no driver.\n");
+		return -EIO;
 	}
 
 	if(tty_register_driver(&pcxe_driver))
@@ -1473,15 +1616,13 @@
 	if(tty_register_driver(&pcxe_callout))
 		panic("Couldn't register PC/Xe callout");
 
-#if 0
-	loops_per_sec = save_loops_per_sec;  /* reset it to what it should be */
-#endif
-
 	/*
 	 * Start up the poller to check for events on all enabled boards
 	 */
 	timer_active |= 1 << DIGI_TIMER;
-	restore_flags(flags);
+
+	if (verbose)
+		printk(KERN_NOTICE "PC/Xx: Driver with %d card(s) ready.\n", enabled_cards);
 
 	return 0;
 }
@@ -1546,7 +1687,7 @@
 
 	while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) {
 		assertgwinon(chan0);
-		eventbuf = (volatile unchar *)bd->membase + tail + ISTART;
+		eventbuf = (volatile unchar *)phys_to_virt(bd->membase + tail + ISTART);
 		channel = eventbuf[0];
 		event = eventbuf[1];
 		mstat = eventbuf[2];
@@ -1657,7 +1798,7 @@
 
 	assertgwinon(ch);
 
-	memaddr = (unchar *)ch->board->membase;
+	memaddr = (unchar *)phys_to_virt(ch->board->membase);
 	head = ch->mailbox->cin;
 
 	if(head >= (CMAX-CSTART) || (head & 03)) {
@@ -1698,6 +1839,7 @@
 
 		if(n <= ncmds * (sizeof(short)*4))
 			break;
+		/* Seems not to be good here: schedule(); */
 	}
 }
 
diff -u --recursive --new-file v2.1.32/linux/drivers/char/pcxx.h linux/drivers/char/pcxx.h
--- v2.1.32/linux/drivers/char/pcxx.h	Sat Apr  6 22:42:34 1996
+++ linux/drivers/char/pcxx.h	Wed Apr  9 21:27:16 1997
@@ -49,6 +49,7 @@
 	ushort numports;
 	ushort port;
 	ulong  membase;
+	ulong  memsize;
 	ushort first_minor;
 };
 
diff -u --recursive --new-file v2.1.32/linux/drivers/char/selection.c linux/drivers/char/selection.c
--- v2.1.32/linux/drivers/char/selection.c	Fri Apr  4 08:52:19 1997
+++ linux/drivers/char/selection.c	Wed Apr  9 22:01:41 1997
@@ -118,8 +118,9 @@
 	int sel_mode, new_sel_start, new_sel_end, spc;
 	char *bp, *obp;
 	int i, ps, pe;
-
+	
 	do_unblank_screen();
+	poke_blanked_console();
 
 	{ unsigned short *args, xs, ys, xe, ye;
 
@@ -291,7 +292,7 @@
 	
 	if (!bp || !c)
 		return 0;
-	do_unblank_screen();
+	poke_blanked_console();
 	add_wait_queue(&vt->paste_wait, &wait);
 	do {
 		current->state = TASK_INTERRUPTIBLE;
diff -u --recursive --new-file v2.1.32/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.32/linux/drivers/char/tty_io.c	Fri Apr  4 08:52:19 1997
+++ linux/drivers/char/tty_io.c	Mon Apr  7 14:02:43 1997
@@ -44,6 +44,10 @@
  * 
  * Restrict vt switching via ioctl()
  *      -- grif@cs.ucr.edu, 5-Dec-95
+ *
+ * Move console and virtual terminal code to more apropriate files,
+ * implement CONFIG_VT and generalize console device interface.
+ *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
  */
 
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.32/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.32/linux/drivers/char/vt.c	Fri Apr  4 08:52:19 1997
+++ linux/drivers/char/vt.c	Mon Apr  7 14:29:26 1997
@@ -6,6 +6,7 @@
  *  Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
  *  Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
  *  Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
+ *  Some code moved for less code duplication - Andi Kleen - Mar 1997
  */
 
 #include <linux/types.h>
@@ -189,7 +190,271 @@
 }
 
 void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_mksound;
-	
+
+
+#define i (tmp.kb_index)
+#define s (tmp.kb_table)
+#define v (tmp.kb_value)
+static inline int
+do_kdsk_ioctl(int cmd, struct kbentry *user_kbe, int perm, struct kbd_struct *kbd)
+{
+	struct kbentry tmp;
+	ushort *key_map, val, ov;
+
+	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+		return -EFAULT;
+	if (i >= NR_KEYS || s >= MAX_NR_KEYMAPS)
+		return -EINVAL;	
+
+	switch (cmd) {
+	case KDGKBENT:
+		key_map = key_maps[s];
+		if (key_map) {
+		    val = U(key_map[i]);
+		    if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+			val = K_HOLE;
+		} else
+		    val = (i ? K_HOLE : K_NOSUCHMAP);
+		return __put_user(val, &user_kbe->kb_value);
+	case KDSKBENT:
+		if (!perm)
+			return -EPERM;
+		if (!i && v == K_NOSUCHMAP) {
+			/* disallocate map */
+			key_map = key_maps[s];
+			if (s && key_map) {
+			    key_maps[s] = 0;
+			    if (key_map[0] == U(K_ALLOCATED)) {
+					kfree_s(key_map, sizeof(plain_map));
+					keymap_count--;
+			    }
+			}
+			break;
+		}
+
+		if (KTYP(v) < NR_TYPES) {
+		    if (KVAL(v) > max_vals[KTYP(v)])
+				return -EINVAL;
+		} else
+		    if (kbd->kbdmode != VC_UNICODE)
+				return -EINVAL;
+
+		/* assignment to entry 0 only tests validity of args */
+		if (!i)
+			break;
+
+		if (!(key_map = key_maps[s])) {
+			int j;
+
+			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
+				return -EPERM;
+
+			key_map = (ushort *) kmalloc(sizeof(plain_map),
+						     GFP_KERNEL);
+			if (!key_map)
+				return -ENOMEM;
+			key_maps[s] = key_map;
+			key_map[0] = U(K_ALLOCATED);
+			for (j = 1; j < NR_KEYS; j++)
+				key_map[j] = U(K_HOLE);
+			keymap_count++;
+		}
+		ov = U(key_map[i]);
+		if (v == ov)
+			break;	/* nothing to do */
+		/*
+		 * Attention Key.
+		 */
+		if (((ov == K_SAK) || (v == K_SAK)) && !suser())
+			return -EPERM;
+		key_map[i] = U(v);
+		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+			compute_shiftstate();
+		break;
+	}
+	return 0;
+}
+#undef i
+#undef s
+#undef v
+
+static inline int 
+do_kbkeycode_ioctl(int cmd, struct kbkeycode *user_kbkc, int perm)
+{
+	struct kbkeycode tmp;
+	int kc = 0;
+
+	if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode)))
+		return -EFAULT;
+	switch (cmd) {
+	case KDGETKEYCODE:
+		kc = getkeycode(tmp.scancode);
+		if (kc >= 0)
+			kc = __put_user(kc, &user_kbkc->keycode);
+		break;
+	case KDSETKEYCODE:
+		if (!perm)
+			return -EPERM;
+		kc = setkeycode(tmp.scancode, tmp.keycode);
+		break;
+	}
+	return kc;
+}
+
+static inline int
+do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm)
+{
+	struct kbsentry tmp;
+	char *p;
+	u_char *q;
+	int sz;
+	int delta;
+	char *first_free, *fj, *fnw;
+	int j, k, i = 0;
+
+	/* we mostly copy too much here (512bytes), but who cares ;) */
+	if (copy_from_user(&tmp, user_kdgkb, sizeof(struct kbsentry)))
+		return -EFAULT;
+	tmp.kb_string[sizeof(tmp.kb_string)-1] = '\0';
+	if (tmp.kb_func >= MAX_NR_FUNC)
+		return -EINVAL;
+
+	switch (cmd) {
+	case KDGKBSENT:
+		sz = sizeof(tmp.kb_string) - 1; /* sz should have been
+						  a struct member */
+		q = user_kdgkb->kb_string;
+		p = func_table[i];
+		if(p)
+			for ( ; *p && sz; p++, sz--)
+				__put_user(*p, q++);
+		__put_user('\0', q);
+		return ((p && *p) ? -EOVERFLOW : 0);
+	case KDSKBSENT:
+		if (!perm)
+			return -EPERM;
+
+		q = func_table[i];
+		first_free = funcbufptr + (funcbufsize - funcbufleft);
+		for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) 
+			;
+		if (j < MAX_NR_FUNC)
+			fj = func_table[j];
+		else
+			fj = first_free;
+
+		delta = (q ? -strlen(q) : 1) + strlen(tmp.kb_string);
+		if (delta <= funcbufleft) { 	/* it fits in current buf */
+		    if (j < MAX_NR_FUNC) {
+			memmove(fj + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			    if (func_table[k])
+				func_table[k] += delta;
+		    }
+		    if (!q)
+		      func_table[i] = fj;
+		    funcbufleft -= delta;
+		} else {			/* allocate a larger buffer */
+		    sz = 256;
+		    while (sz < funcbufsize - funcbufleft + delta)
+		      sz <<= 1;
+		    fnw = (char *) kmalloc(sz, GFP_KERNEL);
+		    if(!fnw)
+		      return -ENOMEM;
+
+		    if (!q)
+		      func_table[i] = fj;
+		    if (fj > funcbufptr)
+			memmove(fnw, funcbufptr, fj - funcbufptr);
+		    for (k = 0; k < j; k++)
+		      if (func_table[k])
+			func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+		    if (first_free > fj) {
+			memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			  if (func_table[k])
+			    func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+		    }
+		    if (funcbufptr != func_buf)
+		      kfree_s(funcbufptr, funcbufsize);
+		    funcbufptr = fnw;
+		    funcbufleft = funcbufleft - delta + sz - funcbufsize;
+		    funcbufsize = sz;
+		}
+		strcpy(func_table[i], tmp.kb_string);
+		break;
+	}
+	return 0;
+}
+
+static inline int 
+do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm)
+{
+	int nchar;
+	struct consolefontdesc cfdarg;
+	int i = 0;
+
+	if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) 
+		return -EFAULT;
+	if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+		return -EINVAL;
+ 	
+	switch (cmd) {
+	case PIO_FONTX:
+		if (!perm)
+			return -EPERM;
+		if ( cfdarg.charcount == 256 ||
+		     cfdarg.charcount == 512 ) {
+			i = con_set_font(cfdarg.chardata,
+				cfdarg.charcount == 512);
+			if (i)
+				return i;
+			i = con_adjust_height(cfdarg.charheight);
+			return (i <= 0) ? i : kd_size_changed(i, 0);
+		} else
+			return -EINVAL;
+	case GIO_FONTX:
+		i = cfdarg.charcount;
+		cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256;
+		cfdarg.charheight = video_font_height;
+		__copy_to_user(user_cfd, &cfdarg,
+			    sizeof(struct consolefontdesc)); 
+		if ( cfdarg.chardata )
+		{
+			if ( i < nchar )
+				return -ENOMEM;
+			return con_get_font(cfdarg.chardata);
+		} else
+			return 0;
+	}
+	return 0;
+}
+
+static inline int 
+do_unimap_ioctl(int cmd, struct unimapdesc *user_ud,int perm)
+{
+	struct unimapdesc tmp;
+	int i = 0; 
+
+	if (copy_from_user(&tmp, user_ud, sizeof tmp))
+		return -EFAULT;
+	if (tmp.entries) {
+		i = verify_area(VERIFY_WRITE, tmp.entries, 
+						tmp.entry_ct*sizeof(struct unipair));
+		if (i) return i;
+	}
+	switch (cmd) {
+	case PIO_UNIMAP:
+		if (!perm)
+			return -EPERM;
+		return con_set_unimap(tmp.entry_ct, tmp.entries);
+	case GIO_UNIMAP:
+		return con_get_unimap(tmp.entry_ct, &(user_ud->entry_ct), tmp.entries);
+	}
+	return 0;
+}
+
 /*
  * We handle the console-specific ioctl's here.  We allow the
  * capability to modify any console, not just the fg_console. 
@@ -215,7 +480,7 @@
 	perm = 0;
 	if (current->tty == tty || suser())
 		perm = 1;
-
+ 
 	kbd = kbd_table + console;
 	switch (cmd) {
 	case KIOCSOUND:
@@ -247,10 +512,8 @@
 		/*
 		 * this is naive.
 		 */
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-		if (!i)
-			put_user(KB_101, (char *) arg);
-		return i;
+		ucval = KB_101;
+		goto setchar;
 
 #ifndef __alpha__
 		/*
@@ -307,10 +570,8 @@
 		return 0;
 
 	case KDGETMODE:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (!i)
-			put_user(vt_cons[console]->vc_mode, (int *) arg);
-		return i;
+		ucval = vt_cons[console]->vc_mode;
+		goto setint;
 
 	case KDMAPDISP:
 	case KDUNMAPDISP:
@@ -346,15 +607,11 @@
 		return 0;
 
 	case KDGKBMODE:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (!i) {
-			ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
+		ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
 				 (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
 				 (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
 				 K_XLATE);
-			put_user(ucval, (int *) arg);
-		}
-		return i;
+		goto setint;
 
 	/* this could be folded into KDSKBMODE, but for compatibility
 	   reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
@@ -372,253 +629,21 @@
 		return 0;
 
 	case KDGKBMETA:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (!i) {
-			ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
-				 K_METABIT);
-			put_user(ucval, (int *) arg);
-		}
-		return i;
+		ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
+	setint:
+		return put_user(ucval, (int *)arg); 
 
 	case KDGETKEYCODE:
-	{
-		struct kbkeycode * const a = (struct kbkeycode *)arg;
-		unsigned int sc;
-		int kc;
-
-		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbkeycode));
-		if (i)
-			return i;
-		get_user(sc, &a->scancode);
-		kc = getkeycode(sc);
-		if (kc < 0)
-			return kc;
-		put_user(kc, &a->keycode);
-		return 0;
-	}
-
 	case KDSETKEYCODE:
-	{
-		struct kbkeycode * const a = (struct kbkeycode *)arg;
-		unsigned int sc, kc;
-
-		if (!perm)
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
-		if (i)
-			return i;
-		get_user(sc, &a->scancode);
-		get_user(kc, &a->keycode);
-		return setkeycode(sc, kc);
-	}
+		return do_kbkeycode_ioctl(cmd, (struct kbkeycode *)arg, perm);
 
 	case KDGKBENT:
-	{
-		struct kbentry * const a = (struct kbentry *)arg;
-		ushort *key_map, val;
-		u_char s;
-
-		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
-		if (i)
-			return i;
-		get_user(i, &a->kb_index);
-		if (i >= NR_KEYS)
-			return -EINVAL;
-		get_user(s, &a->kb_table);
-		if (s >= MAX_NR_KEYMAPS)
-			return -EINVAL;
-		key_map = key_maps[s];
-		if (key_map) {
-		    val = U(key_map[i]);
-		    if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
-			val = K_HOLE;
-		} else
-		    val = (i ? K_HOLE : K_NOSUCHMAP);
-		put_user(val, &a->kb_value);
-		return 0;
-	}
-
 	case KDSKBENT:
-	{
-		const struct kbentry * a = (struct kbentry *)arg;
-		ushort *key_map;
-		u_char s;
-		u_short v, ov;
-
-		if (!perm)
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (const void *)a, sizeof(struct kbentry));
-		if (i)
-			return i;
-		get_user(i, &a->kb_index);
-		if (i >= NR_KEYS)
-			return -EINVAL;
-		get_user(s, &a->kb_table);
-		if (s >= MAX_NR_KEYMAPS)
-			return -EINVAL;
-		get_user(v, &a->kb_value);
-		if (!i && v == K_NOSUCHMAP) {
-			/* disallocate map */
-			key_map = key_maps[s];
-			if (s && key_map) {
-			    key_maps[s] = 0;
-			    if (key_map[0] == U(K_ALLOCATED)) {
-				kfree_s(key_map, sizeof(plain_map));
-				keymap_count--;
-			    }
-			}
-			return 0;
-		}
-
-		if (KTYP(v) < NR_TYPES) {
-		    if (KVAL(v) > max_vals[KTYP(v)])
-			return -EINVAL;
-		} else
-		    if (kbd->kbdmode != VC_UNICODE)
-			return -EINVAL;
-
-		/* assignment to entry 0 only tests validity of args */
-		if (!i)
-			return 0;
-
-		if (!(key_map = key_maps[s])) {
-			int j;
-
-			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
-				return -EPERM;
-
-			key_map = (ushort *) kmalloc(sizeof(plain_map),
-						     GFP_KERNEL);
-			if (!key_map)
-				return -ENOMEM;
-			key_maps[s] = key_map;
-			key_map[0] = U(K_ALLOCATED);
-			for (j = 1; j < NR_KEYS; j++)
-				key_map[j] = U(K_HOLE);
-			keymap_count++;
-		}
-		ov = U(key_map[i]);
-		if (v == ov)
-			return 0;	/* nothing to do */
-		/*
-		 * Only the Superuser can set or unset the Secure
-		 * Attention Key.
-		 */
-		if (((ov == K_SAK) || (v == K_SAK)) && !suser())
-			return -EPERM;
-		key_map[i] = U(v);
-		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
-			compute_shiftstate();
-		return 0;
-	}
+		return do_kdsk_ioctl(cmd, (struct kbentry *)arg, perm, kbd);
 
 	case KDGKBSENT:
-	{
-		struct kbsentry *a = (struct kbsentry *)arg;
-		char *p;
-		u_char *q;
-		int sz;
-
-		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
-		if (i)
-			return i;
-		get_user(i, &a->kb_func);
-		if (i >= MAX_NR_FUNC || i < 0)
-			return -EINVAL;
-		sz = sizeof(a->kb_string) - 1; /* sz should have been
-						  a struct member */
-		q = a->kb_string;
-		p = func_table[i];
-		if(p)
-			for ( ; *p && sz; p++, sz--)
-				put_user(*p, q++);
-		put_user('\0', q);
-		return ((p && *p) ? -EOVERFLOW : 0);
-	}
-
 	case KDSKBSENT:
-	{
-		struct kbsentry * const a = (struct kbsentry *)arg;
-		int delta;
-		char *first_free, *fj, *fnw;
-		int j, k, sz;
-		u_char *p;
-		char *q;
-
-		if (!perm)
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
-		if (i)
-			return i;
-		get_user(i, &a->kb_func);
-		if (i >= MAX_NR_FUNC)
-			return -EINVAL;
-		q = func_table[i];
-
-		first_free = funcbufptr + (funcbufsize - funcbufleft);
-		for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
-		if (j < MAX_NR_FUNC)
-			fj = func_table[j];
-		else
-			fj = first_free;
-
-		delta = (q ? -strlen(q) : 1);
-		sz = sizeof(a->kb_string); 	/* sz should have been
-						   a struct member */
-		for (p = a->kb_string; sz; p++,sz--) {
-			unsigned char uc;
-			get_user(uc, p);
-			if (!uc)
-				break;
-			delta++;
-		}
-		if (!sz)
-			return -EOVERFLOW;
-		if (delta <= funcbufleft) { 	/* it fits in current buf */
-		    if (j < MAX_NR_FUNC) {
-			memmove(fj + delta, fj, first_free - fj);
-			for (k = j; k < MAX_NR_FUNC; k++)
-			    if (func_table[k])
-				func_table[k] += delta;
-		    }
-		    if (!q)
-		      func_table[i] = fj;
-		    funcbufleft -= delta;
-		} else {			/* allocate a larger buffer */
-		    sz = 256;
-		    while (sz < funcbufsize - funcbufleft + delta)
-		      sz <<= 1;
-		    fnw = (char *) kmalloc(sz, GFP_KERNEL);
-		    if(!fnw)
-		      return -ENOMEM;
-
-		    if (!q)
-		      func_table[i] = fj;
-		    if (fj > funcbufptr)
-			memmove(fnw, funcbufptr, fj - funcbufptr);
-		    for (k = 0; k < j; k++)
-		      if (func_table[k])
-			func_table[k] = fnw + (func_table[k] - funcbufptr);
-
-		    if (first_free > fj) {
-			memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
-			for (k = j; k < MAX_NR_FUNC; k++)
-			  if (func_table[k])
-			    func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
-		    }
-		    if (funcbufptr != func_buf)
-		      kfree_s(funcbufptr, funcbufsize);
-		    funcbufptr = fnw;
-		    funcbufleft = funcbufleft - delta + sz - funcbufsize;
-		    funcbufsize = sz;
-		}
-		for (p = a->kb_string, q = func_table[i]; ; p++, q++) {
-			get_user(*q, p);
-			if (!*q)
-				break;
-		}
-		return 0;
-	}
+		return do_kdgkb_ioctl(cmd, (struct kbsentry *)arg, perm);
 
 	case KDGKBDIACR:
 	{
@@ -627,8 +652,8 @@
 		i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
 		if (i)
 			return i;
-		put_user(accent_table_size, &a->kb_cnt);
-		copy_to_user(a->kbdiacr, accent_table,
+		__put_user(accent_table_size, &a->kb_cnt);
+		__copy_to_user(a->kbdiacr, accent_table,
 			    accent_table_size*sizeof(struct kbdiacr));
 		return 0;
 	}
@@ -643,23 +668,19 @@
 		i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
 		if (i)
 			return i;
-		get_user(ct,&a->kb_cnt);
+		__get_user(ct,&a->kb_cnt);
 		if (ct >= MAX_DIACR)
 			return -EINVAL;
 		accent_table_size = ct;
-		copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
+		__copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
 		return 0;
 	}
 
 	/* the ioctls below read/set the flags usually shown in the leds */
 	/* don't use them - they will go away without warning */
 	case KDGKBLED:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-		if (i)
-			return i;
-		put_user(kbd->ledflagstate |
-			 (kbd->default_ledflagstate << 4), (char *) arg);
-		return 0;
+		ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4);
+		goto setchar;
 
 	case KDSKBLED:
 		if (!perm)
@@ -674,11 +695,9 @@
 	/* the ioctls below only set the lights, not the functions */
 	/* for those, see KDGKBLED and KDSKBLED above */
 	case KDGETLED:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
-		if (i)
-			return i;
-		put_user(getledstate(), (char *) arg);
-		return 0;
+		ucval = getledstate();
+	setchar:
+		return put_user(ucval, (char*)arg);
 
 	case KDSETLED:
 		if (!perm)
@@ -710,21 +729,15 @@
 
 	case VT_SETMODE:
 	{
-		struct vt_mode *vtmode = (struct vt_mode *)arg;
-		char mode;
+		struct vt_mode tmp;
 
 		if (!perm)
 			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *)vtmode, sizeof(struct vt_mode));
-		if (i)
-			return i;
-		get_user(mode, &vtmode->mode);
-		if (mode != VT_AUTO && mode != VT_PROCESS)
+		if (copy_from_user(&tmp, (void*)arg, sizeof(struct vt_mode)))
+			return -EFAULT;
+		if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS)
 			return -EINVAL;
-		vt_cons[console]->vt_mode.mode = mode;
-		get_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
-		get_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
-		get_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
+		vt_cons[console]->vt_mode = tmp;
 		/* the frsig is ignored, so we set it to 0 */
 		vt_cons[console]->vt_mode.frsig = 0;
 		vt_cons[console]->vt_pid = current->pid;
@@ -734,19 +747,8 @@
 	}
 
 	case VT_GETMODE:
-	{
-		struct vt_mode *vtmode = (struct vt_mode *)arg;
-
-		i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
-		if (i)
-			return i;
-		put_user(vt_cons[console]->vt_mode.mode, &vtmode->mode);
-		put_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
-		put_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
-		put_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
-		put_user(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
-		return 0;
-	}
+		return copy_to_user((void*)arg, &(vt_cons[console]->vt_mode), 
+							sizeof(struct vt_mode)) ? -EFAULT : 0; 
 
 	/*
 	 * Returns global vt state. Note that VT 0 is always open, since
@@ -761,27 +763,23 @@
 		i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
 		if (i)
 			return i;
-		put_user(fg_console + 1, &vtstat->v_active);
+		__put_user(fg_console + 1, &vtstat->v_active);
 		state = 1;	/* /dev/tty0 is always open */
 		for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
 			if (VT_IS_IN_USE(i))
 				state |= mask;
-		put_user(state, &vtstat->v_state);
-		return 0;
+		return __put_user(state, &vtstat->v_state);
 	}
 
 	/*
 	 * Returns the first available (non-opened) console.
 	 */
 	case VT_OPENQRY:
-		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-		if (i)
-			return i;
 		for (i = 0; i < MAX_NR_CONSOLES; ++i)
 			if (! VT_IS_IN_USE(i))
 				break;
-		put_user(i < MAX_NR_CONSOLES ? (i+1) : -1, (int *) arg);
-		return 0;
+		ucval = i < MAX_NR_CONSOLES ? (i+1) : -1;
+		goto setint;		 
 
 	/*
 	 * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
@@ -904,8 +902,8 @@
 		i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
 		if (i)
 			return i;
-		get_user(ll, &vtsizes->v_rows);
-		get_user(cc, &vtsizes->v_cols);
+		__get_user(ll, &vtsizes->v_rows);
+		__get_user(cc, &vtsizes->v_cols);
 		i = vc_resize(ll, cc);
 		return i ? i : 	kd_size_changed(ll, cc);
 	}
@@ -919,19 +917,19 @@
 		i = verify_area(VERIFY_READ, (void *)vtconsize, sizeof(struct vt_consize));
 		if (i)
 			return i;
-		get_user(ll, &vtconsize->v_rows);
-		get_user(cc, &vtconsize->v_cols);
-		get_user(vlin, &vtconsize->v_vlin);
-		get_user(clin, &vtconsize->v_clin);
-		get_user(vcol, &vtconsize->v_vcol);
-		get_user(ccol, &vtconsize->v_ccol);
+		__get_user(ll, &vtconsize->v_rows);
+		__get_user(cc, &vtconsize->v_cols);
+		__get_user(vlin, &vtconsize->v_vlin);
+		__get_user(clin, &vtconsize->v_clin);
+		__get_user(vcol, &vtconsize->v_vcol);
+		__get_user(ccol, &vtconsize->v_ccol);
 		vlin = vlin ? vlin : video_scan_lines;
 		if ( clin )
 		  {
 		    if ( ll )
 		      {
 			if ( ll != vlin/clin )
-			  return EINVAL; /* Parameters don't add up */
+			  return -EINVAL; /* Parameters don't add up */
 		      }
 		    else 
 		      ll = vlin/clin;
@@ -941,14 +939,14 @@
 		    if ( cc )
 		      {
 			if ( cc != vcol/ccol )
-			  return EINVAL;
+			  return -EINVAL;
 		      }
 		    else
 		      cc = vcol/ccol;
 		  }
 
 		if ( clin > 32 )
-		  return EINVAL;
+		  return -EINVAL;
 		    
 		if ( vlin )
 		  video_scan_lines = vlin;
@@ -989,30 +987,8 @@
                 /* con_get_cmap() defined in console.c */
 
 	case PIO_FONTX:
-	{
-	        struct consolefontdesc cfdarg;
-
-		if (!perm)
-			return -EPERM;
-		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-			return -EINVAL;
-		i = verify_area(VERIFY_READ, (void *)arg,
-				sizeof(struct consolefontdesc));
-		if (i) return i;
-		copy_from_user(&cfdarg, (void *)arg,
-			      sizeof(struct consolefontdesc)); 
-		
-		if ( cfdarg.charcount == 256 ||
-		     cfdarg.charcount == 512 ) {
-			i = con_set_font(cfdarg.chardata,
-				cfdarg.charcount == 512);
-			if (i)
-				return i;
-			i = con_adjust_height(cfdarg.charheight);
-			return (i <= 0) ? i : kd_size_changed(i, 0);
-		} else
-			return -EINVAL;
-	}
+	case GIO_FONTX:
+		return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm);
 
 	case PIO_FONTRESET:
 	{
@@ -1038,32 +1014,6 @@
 #endif
 	}
 
-	case GIO_FONTX:
-	{
-	        struct consolefontdesc cfdarg;
-		int nchar;
-
-		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-			return -EINVAL;
-		i = verify_area(VERIFY_WRITE, (void *)arg,
-			sizeof(struct consolefontdesc));
-		if (i) return i;	
-		copy_from_user(&cfdarg, (void *) arg,
-			      sizeof(struct consolefontdesc)); 
-		i = cfdarg.charcount;
-		cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256;
-		cfdarg.charheight = video_font_height;
-		copy_to_user((void *) arg, &cfdarg,
-			    sizeof(struct consolefontdesc)); 
-		if ( cfdarg.chardata )
-		{
-			if ( i < nchar )
-				return -ENOMEM;
-			return con_get_font(cfdarg.chardata);
-		} else
-			return 0;
-	}
-
 	case PIO_SCRNMAP:
 		if (!perm)
 			return -EPERM;
@@ -1084,52 +1034,16 @@
 	      { struct unimapinit ui;
 		if (!perm)
 			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
-		if (i)
-		  return i;
-		copy_from_user(&ui, (void *)arg, sizeof(struct unimapinit));
+		i = copy_from_user(&ui, (void *)arg, sizeof(struct unimapinit));
+		if (i) return -EFAULT;
 		con_clear_unimap(&ui);
 		return 0;
 	      }
 
 	case PIO_UNIMAP:
-	      { struct unimapdesc *ud;
-		u_short ct;
-		struct unipair *list;
-
-		if (!perm)
-			return -EPERM;
-		i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
-		if (i == 0) {
-		    ud = (struct unimapdesc *) arg;
-		    get_user(ct, &ud->entry_ct);
-		    get_user(list, &ud->entries);
-		    i = verify_area(VERIFY_READ, (void *) list,
-				    ct*sizeof(struct unipair));
-		    if(!i)
-			    return con_set_unimap(ct, list);
-		}
-		return i;
-	      }
-
 	case GIO_UNIMAP:
-	      { struct unimapdesc *ud;
-		u_short ct;
-		struct unipair *list;
-
-		i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
-		if (i == 0) {
-		    ud = (struct unimapdesc *) arg;
-		    get_user(ct, &ud->entry_ct);
-		    get_user(list, &ud->entries);
-		    if (ct)
-		      i = verify_area(VERIFY_WRITE, (void *) list,
-				      ct*sizeof(struct unipair));
-		    if(!i)
-			    return con_get_unimap(ct, &(ud->entry_ct), list);
-		}
-		return i;
-	      }
+		return do_unimap_ioctl(cmd, (struct unimapdesc *)arg, perm);
+
 	case VT_LOCKSWITCH:
 		if (!suser())
 		   return -EPERM;
diff -u --recursive --new-file v2.1.32/linux/drivers/net/3c505.c linux/drivers/net/3c505.c
--- v2.1.32/linux/drivers/net/3c505.c	Sun Feb  2 05:18:35 1997
+++ linux/drivers/net/3c505.c	Mon Apr  7 14:17:09 1997
@@ -621,6 +621,7 @@
 	if (set_bit(0, (void *) &adapter->dmaing))
 		printk("%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction);
 
+	skb->dev = dev;
 	adapter->current_dma.direction = 0;
 	adapter->current_dma.length = rlen;
 	adapter->current_dma.skb = skb;
@@ -698,12 +699,12 @@
 			} else {
 				struct sk_buff *skb = adapter->current_dma.skb;
 				if (skb) {
-				  skb->dev = dev;
 				  if (adapter->current_dma.target) {
 				    /* have already done the skb_put() */
 				    memcpy(adapter->current_dma.target, adapter->dma_buffer, adapter->current_dma.length);
 				  }
 				  skb->protocol = eth_type_trans(skb,dev);
+				  adapter->stats.rx_bytes += skb->len;
 				  netif_rx(skb);
 				}
 			}
@@ -1032,10 +1033,8 @@
 			printk("%s: transmit blocked\n", dev->name);
 		return FALSE;
 	}
-	adapter = dev->priv;
-
 
-	adapter->stats.tx_bytes+=nlen;
+	adapter->stats.tx_bytes += nlen;
 	
 	/*
 	 * send the adapter a transmit packet command. Ignore segment and offset
diff -u --recursive --new-file v2.1.32/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.32/linux/drivers/net/Config.in	Fri Apr  4 08:52:20 1997
+++ linux/drivers/net/Config.in	Wed Apr  9 21:30:30 1997
@@ -79,6 +79,7 @@
     tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
     tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
     tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
+    tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
@@ -113,7 +114,9 @@
   fi
 fi
 
-tristate 'PLIP (parallel port) support' CONFIG_PLIP
+if [ ! "$CONFIG_PNP_PARPORT" = "n" ]; then
+  dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PNP_PARPORT
+fi
 
 tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
diff -u --recursive --new-file v2.1.32/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.32/linux/drivers/net/Makefile	Fri Apr  4 08:52:20 1997
+++ linux/drivers/net/Makefile	Mon Apr  7 15:20:13 1997
@@ -371,6 +371,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_EEXPRESS_PRO100),y)
+L_OBJS += eepro100.o
+else
+  ifeq ($(CONFIG_EEXPRESS_PRO100),m)
+  M_OBJS += eepro100.o
+  endif
+endif
+
 ifeq ($(CONFIG_WAVELAN),y)
 L_OBJS += wavelan.o
 else
diff -u --recursive --new-file v2.1.32/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.32/linux/drivers/net/Space.c	Fri Apr  4 08:52:20 1997
+++ linux/drivers/net/Space.c	Wed Apr  9 21:30:30 1997
@@ -50,6 +50,7 @@
 extern int znet_probe(struct device *);
 extern int express_probe(struct device *);
 extern int eepro_probe(struct device *);
+extern int eepro100_probe(struct device *);
 extern int el3_probe(struct device *);
 extern int at1500_probe(struct device *);
 extern int pcnet32_probe(struct device *);
@@ -178,6 +179,9 @@
 #ifdef CONFIG_EEXPRESS_PRO	/* Intel EtherExpress Pro/10 */
 	&& eepro_probe(dev)
 #endif
+#ifdef CONFIG_EEXPRESS_PRO100	/* Intel EtherExpress Pro/100 */
+	&& eepro100_probe(dev)
+#endif
 #ifdef CONFIG_DEPCA		/* DEC DEPCA */
 	&& depca_probe(dev)
 #endif
@@ -326,18 +330,6 @@
 
 #   undef NEXT_DEV
 #   define NEXT_DEV	(&eth0_dev)
-
-#if defined(PLIP) || defined(CONFIG_PLIP)
-    extern int plip_init(struct device *);
-    static struct device plip2_dev = {
-	"plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
-    static struct device plip1_dev = {
-	"plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
-    static struct device plip0_dev = {
-	"plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, };
-#   undef NEXT_DEV
-#   define NEXT_DEV	(&plip0_dev)
-#endif  /* PLIP */
 
 #if defined(SLIP) || defined(CONFIG_SLIP)
 	/* To be exact, this node just hooks the initialization
diff -u --recursive --new-file v2.1.32/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.1.32/linux/drivers/net/eepro100.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/eepro100.c	Mon Apr  7 16:43:59 1997
@@ -0,0 +1,1732 @@
+/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
+/*
+   NOTICE: this version tested with kernels 1.3.72 and later only!
+	Written 1996 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	This driver is for the Intel EtherExpress Pro 100B boards.
+	It should work with other i82557 boards (if any others exist).
+	To use a built-in driver, install as drivers/net/eepro100.c.
+	To use as a module, use the compile-command at the end of the file.
+
+	The author may be reached as becker@CESDIS.usra.edu, or C/O
+	Center of Excellence in Space Data and Information Sciences
+	   Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
+	For updates see
+	<base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
+*/
+
+static const char *version =
+"eepro100.c:v0.31 3/29/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+
+/* A few user-configurable values that apply to all boards.
+   First set are undocumented and spelled per Intel recommendations. */
+
+static int congenb = 0;		/* Enable congestion control in the DP83840. */
+static int txfifo = 8;		/* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8;		/* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0;	/* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0;	/* Rx DMA length, 0 means no preemption. */
+
+/* If defined use the copy-only-tiny-buffer scheme for higher performance.
+   The value sets the copy breakpoint.  Lower uses more memory, but is
+   faster. */
+#define SKBUFF_RX_COPYBREAK 256
+
+#include <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* A nominally proper method to handle version dependencies is to use
+   LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */
+#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s)
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,0))
+#define KERNEL_1_2
+#else /* 1.3.0 */
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+#define NEW_MULTICAST
+#define LINUX_1_4
+#else
+#warning "This driver is tested for 1.3.44 and later development kernels only."
+#endif /* 1.3.44 */
+#endif
+#else
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef HAVE_HEADER_CACHE
+#define LINUX_1_4
+#define NEW_MULTICAST
+#else
+#ifdef ETH_P_DDCMP				/* Warning: Bogus!  This means IS_LINUX_1_3. */
+#define KERNEL_1_3
+#else
+#define KERNEL_1_2
+#endif
+#endif
+
+#endif
+/* This should be in a header file. */
+#if (LINUX_VERSION_CODE < VERSION(1,3,44))
+struct device *init_etherdev(struct device *dev, int sizeof_priv,
+							 unsigned long *mem_startp);
+#endif
+#if LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+/* The total I/O port extent of the board.  Nominally 0x18, but rounded up
+   for PCI allocation. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry eepro100_drv =
+{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef SPEEDO3_DEBUG
+int speedo_debug = SPEEDO3_DEBUG;
+#else
+int speedo_debug = 3;
+#endif
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
+single-chip fast ethernet controller for PCI, as used on the Intel
+EtherExpress Pro 100 adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line.  While it's
+possible to share PCI interrupt lines, it negatively impacts performance and
+only recent kernels support it. 
+
+III. Driver operation
+
+IIIA. General
+The Speedo3 is very similar to other Intel network chips, that is to say
+"apparently designed on a different planet".  This chips retains the complex
+Rx and Tx descriptors and multiple buffers pointers as previous chips, but
+also has simplified Tx and Rx buffer modes.  This driver uses the "flexible"
+Tx mode, but in a simplified lower-overhead manner: it associates only a
+single buffer descriptor with each frame descriptor.
+
+Despite the extra space overhead in each recieve skbuff, the driver must use
+the simplified Rx buffer mode to assure that only a single data buffer is
+associated with each RxFD. The driver implements this by reserving space
+for the Rx descriptor at the head of each Rx skbuff
+
+The Speedo-3 has receive and command unit base addresses that are added to
+almost all descriptor pointers.  The driver sets these to zero, so that all
+pointer fields are absolute addresses.
+
+The System Control Block (SCB) of some previous Intel chips exists on the
+chip in both PCI I/O and memory space.  This driver uses the I/O space
+registers, but might switch to memory mapped mode to better support non-x86
+processors.
+
+IIIB. Transmit structure
+
+The driver must use the complex Tx command+descriptor mode in order to
+have a indirect pointer to the skbuff data section.  Each Tx command block
+(TxCB) is associated with a single, immediately appended Tx buffer descriptor
+(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
+speedo_private data structure for each adapter instance.
+
+This ring structure is used for all normal transmit packets, but the
+transmit packet descriptors aren't long enough for most non-Tx commands such
+as CmdConfigure.  This is complicated by the possibility that the chip has
+already loaded the link address in the previous descriptor.  So for these
+commands we convert the next free descriptor on the ring to a NoOp, and point
+that descriptor's link to the complex command.
+
+An additional complexity of these non-transmit commands are that they may be
+added asynchronous to the normal transmit queue, so we disable interrupts
+whenever the Tx descriptor ring is manipulated.
+
+A notable aspect of the these special configure commands is that they do
+work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
+is done at interrupt time using the 'dirty_tx' index, and checking for the
+command-complete bit.  While the setup frames may have the NoOp command on the
+Tx ring marked as complete, but not have completed the setup command, this
+is not a problem.  The tx_ring entry can be still safely reused, as the
+tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
+
+Commands may have bits set e.g. CmdSuspend in the command word to either
+suspend or stop the transmit/command unit.  This driver always flags the last
+command with CmdSuspend, erases the CmdSuspend in the previous command, and
+then issues a CU_RESUME.
+Note: Watch out for the potential race condition here: imagine
+	erasing the previous suspend
+		the chip processes the previous command
+		the chip processes the final command, and suspends
+	doing the CU_RESUME
+		the chip processes the next-yet-valid post-final-command.
+So blindly sending a CU_RESUME is only safe if we do it immediately after
+after erasing the previous CmdSuspend, without the possibility of an
+intervening delay.  Thus the resume command is always within the
+interrupts-disabled region.  This is a timing dependence, but handling this
+condition in a timing-independent way would considerably complicate the code.
+
+Note: In previous generation Intel chips, restarting the command unit was a
+notoriously slow process.  This is presumably no longer true.
+
+IIIC. Receive structure
+
+Because of the bus-master support on the Speedo3 this driver uses the new
+SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
+This scheme allocates full-sized skbuffs as receive buffers.  The value
+SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
+trade-off the memory wasted by passing the full-sized skbuff to the queue
+layer for all frames vs. the copying cost of copying a frame to a
+correctly-sized skbuff.
+
+For small frames the copying cost is negligible (esp. considering that we
+are pre-loading the cache with immediately useful header information), so we
+allocate a new, minimally-sized skbuff.  For large frames the copying cost
+is non-trivial, and the larger copy might flush the cache of useful data, so
+we pass up the skbuff the packet was received into.
+
+IIID. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'sp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.)	 After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero.	 Iff the 'sp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
+that stated that I could disclose the information.  But I still resent
+having to sign an Intel NDA when I'm helping Intel sell their own product!
+
+*/
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE	16		/* Effectively 2 entries fewer. */
+#define RX_RING_SIZE	16
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ		1536
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK	16
+
+/* How to wait for the command unit to accept a command.
+   Typically this takes 0 ticks. */
+static inline void wait_for_cmd_done(int cmd_ioaddr)
+{
+  short wait = 100;
+  do   ;
+  while(inb(cmd_ioaddr) && --wait >= 0);
+}
+
+/* Operational parameter that usually are not changed. */
+
+#ifndef PCI_VENDOR_ID_INTEL		/* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_INTEL		0x8086 /* Hmmmm, how did they pick that? */
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82557
+#define PCI_DEVICE_ID_INTEL_82557	0x1229
+#endif
+
+/* The rest of these values should never change. */
+
+/* Offsets to the various registers.
+   All accesses need not be longword aligned. */
+enum speedo_offsets {
+	SCBStatus = 0, SCBCmd = 2,	/* Rx/Command Unit command and status. */
+	SCBPointer = 4,				/* General purpose pointer. */
+	SCBPort = 8,				/* Misc. commands and operands.  */
+	SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+	SCBCtrlMDI = 16,			/* MDI interface control. */
+	SCBEarlyRx = 20,			/* Early receive byte count. */
+};
+/* Commands that can be put in a command list entry. */
+enum commands {
+	CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
+	CmdSuspend = 0x4000,		/* Suspend after completion. */
+	CmdIntr = 0x2000,			/* Interrupt after completion. */
+	CmdTxFlex = 0x0008,			/* Use "Flexible mode" for CmdTx command. */
+};
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define	 CU_START		0x0010
+#define	 CU_RESUME		0x0020
+#define	 CU_STATSADDR	0x0040
+#define	 CU_SHOWSTATS	0x0050	/* Dump statistics counters. */
+#define	 CU_CMD_BASE	0x0060	/* Base address to add to add CU commands. */
+#define	 CU_DUMPSTATS	0x0070	/* Dump then reset stats counters. */
+
+#define	 RX_START	0x0001
+#define	 RX_RESUME	0x0002
+#define	 RX_ABORT	0x0004
+#define	 RX_ADDR_LOAD	0x0006
+#define	 RX_RESUMENR	0x0007
+#define INT_MASK	0x0100
+#define DRVR_INT	0x0200		/* Driver generated interrupt. */
+
+/* The Speedo3 Rx and Tx frame/buffer descriptors. */
+struct descriptor {			/* A generic descriptor. */
+	s16 status;		/* Offset 0. */
+	s16 command;		/* Offset 2. */
+	u32 link;					/* struct descriptor *  */
+	unsigned char params[0];
+};
+
+/* The Speedo3 Rx and Tx buffer descriptors. */
+struct RxFD {					/* Receive frame descriptor. */
+	s32 status;
+	u32 link;					/* struct RxFD * */
+	u32 rx_buf_addr;			/* void * */
+	u16 count;
+	u16 size;
+};
+
+/* Elements of the RxFD.status word. */
+#define RX_COMPLETE 0x8000
+
+struct TxFD {					/* Transmit frame descriptor set. */
+	s32 status;
+	u32 link;					/* void * */
+	u32 tx_desc_addr;			/* Always points to the tx_buf_addr element. */
+	s32 count;					/* # of TBD (=1), Tx start thresh., etc. */
+	/* This constitutes a single "TBD" entry -- we only use one. */
+	u32 tx_buf_addr;			/* void *, frame to be transmitted.  */
+	s32 tx_buf_size;			/* Length of Tx frame. */
+};
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+struct speedo_stats {
+	u32 tx_good_frames;
+	u32 tx_coll16_errs;
+	u32 tx_late_colls;
+	u32 tx_underruns;
+	u32 tx_lost_carrier;
+	u32 tx_deferred;
+	u32 tx_one_colls;
+	u32 tx_multi_colls;
+	u32 tx_total_colls;
+	u32 rx_good_frames;
+	u32 rx_crc_errs;
+	u32 rx_align_errs;
+	u32 rx_resource_errs;
+	u32 rx_overrun_errs;
+	u32 rx_colls_errs;
+	u32 rx_runt_errs;
+	u32 done_marker;
+};
+
+struct speedo_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct device *next_module;
+	struct TxFD	tx_ring[TX_RING_SIZE];	/* Commands (usually CmdTxPacket). */
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	struct descriptor  *last_cmd;	/* Last command sent. */
+	/* Rx descriptor ring & addresses of receive-in-place skbuffs. */
+	struct RxFD *rx_ringp[RX_RING_SIZE];
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+#if (LINUX_VERSION_CODE < 0x10300)	/* Kernel v1.2.*. */
+	struct RxFD saved_skhead[RX_RING_SIZE];	/* Saved skbuff header chunk. */
+#endif
+	struct RxFD *last_rxf;	/* Last command sent. */
+	struct enet_statistics stats;
+	struct speedo_stats lstats;
+	struct timer_list timer;	/* Media selection timer. */
+	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */
+	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
+	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
+	struct descriptor config_cmd;	/* A configure command, with header... */
+	u8 config_cmd_data[22];			/* .. and setup parameters. */
+	int mc_setup_frm_len;			 	/* The length of an allocated.. */
+	struct descriptor *mc_setup_frm; 	/* ..multicast setup frame. */
+	char rx_mode;						/* Current PROMISC/ALLMULTI setting. */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int default_port:1;		/* Last dev->if_port value. */
+	unsigned int rx_bug:1;				/* Work around receiver hang errata. */
+	unsigned int rx_bug10:1;			/* Receiver might hang at 10mbps. */
+	unsigned int rx_bug100:1;			/* Receiver might hang at 100mbps. */
+	unsigned short phy[2];				/* PHY media interfaces available. */
+};
+
+/* The parameters for a CmdConfigure operation.
+   There are so many options that it would be difficult to document each bit.
+   We mostly use the default or recommended settings. */
+const char basic_config_cmd[22] = {
+	22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
+	0, 0x2E, 0,  0x60, 0,
+	0xf2, 0x48,   0, 0x40, 0xf2, 0x80, 		/* 0x40=Force full-duplex */
+	0x3f, 0x05, };
+
+/* PHY media interface chips. */
+static const char *phys[] = { "None", "i82553-A/B", "i82553-C", "i82503",
+								  "DP83840", "80c240", "80c24", "unknown" };
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+					 S80C24, PhyUndefined, };
+static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options);
+
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static int mdio_write(int ioaddr, int phy_id, int location, int value);
+static int speedo_open(struct device *dev);
+static void speedo_timer(unsigned long data);
+static void speedo_init_rx_ring(struct device *dev);
+static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
+static int speedo_rx(struct device *dev);
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs);
+#endif
+static int speedo_close(struct device *dev);
+static struct enet_statistics *speedo_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+
+
+
+#ifdef MODULE
+/* The parameters that may be passed in... */
+/* 'options' is used to pass a transceiver override or full-duplex flag
+   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1;			/* The debug level */
+
+/* A list of all installed Speedo devices, for removing the driver module. */
+static struct device *root_speedo_dev = NULL;
+#endif
+
+int eepro100_init(struct device *dev)
+{
+	int cards_found = 0;
+
+	if (pcibios_present()) {
+		int pci_index;
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+			int pci_ioaddr;
+#else
+			long pci_ioaddr;
+#endif
+			unsigned short pci_command;
+
+			if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
+									PCI_DEVICE_ID_INTEL_82557,
+									pci_index, &pci_bus,
+									&pci_device_fn))
+			  break;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			/* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_1, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+			if (speedo_debug > 2)
+				printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
+					   (int)pci_ioaddr, pci_irq_line);
+
+			/* Get and check the bus-master and latency values. */
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_COMMAND, &pci_command);
+			if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+				printk("  PCI Master Bit has not been set! Setting...\n");
+				pci_command |= PCI_COMMAND_MASTER;
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, pci_command);
+			}
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+										 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < 10) {
+				printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+					   "  Setting to 255 clocks.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 255);
+			} else if (speedo_debug > 1)
+				printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+
+#ifdef MODULE
+			speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+#else
+			speedo_found1(dev, pci_ioaddr, pci_irq_line,
+						  dev ? dev->mem_start : 0);
+#endif
+			cards_found++;
+		}
+	}
+
+	return cards_found;
+}
+
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
+{
+	static int did_version = 0;			/* Already printed version info. */
+	struct speedo_private *sp;
+	int i;
+	u16 eeprom[0x40];
+
+	if (speedo_debug > 0  &&  did_version++ == 0)
+		printk(version);
+
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+	dev = init_etherdev(dev, sizeof(struct speedo_private));
+#else
+	dev = init_etherdev(dev, sizeof(struct speedo_private), 0);
+#endif
+
+	/* Read the station address EEPROM before doing the reset.
+	   Perhaps this should even be done before accepting the device,
+	   then we wouldn't have a device name with which to report the error. */
+	{
+		u16 sum = 0;
+		int j;
+		for (j = 0, i = 0; i < 0x40; i++) {
+			unsigned short value = read_eeprom(ioaddr, i);
+			eeprom[i] = value;
+			sum += value;
+			if (i < 3) {
+				dev->dev_addr[j++] = value;
+				dev->dev_addr[j++] = value >> 8;
+			}
+		}
+		if (sum != 0xBABA)
+			printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
+				   "check settings before activating this device!\n",
+				   dev->name, sum);
+		/* Don't  unregister_netdev(dev);  as the EEPro may actually be
+		   usable, especially if the MAC address is set later. */
+	}
+
+	/* Reset the chip: stop Tx and Rx processes and clear counters.
+	   This takes less than 10usec and will easily finish before the next
+	   action. */
+	outl(0, ioaddr + SCBPort);
+
+	printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
+		   dev->name, ioaddr);
+	for (i = 0; i < 5; i++)
+		printk("%2.2X:", dev->dev_addr[i]);
+	printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifndef kernel_bloat
+	/* OK, this is pure kernel bloat.  I don't like it when other drivers
+	   waste non-pageable kernel space to emit similar messages, but I need
+	   them for bug reports. */
+	{
+		const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
+		/* The self-test results must be paragraph aligned. */
+		int str[6], *volatile self_test_results;
+		int boguscnt = 16000;	/* Timeout for set-test. */
+		if (eeprom[3] & 0x03)
+			printk(KERN_INFO "  Receiver lock-up bug exists -- enabling"
+				   " work-around.\n");
+		printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical"
+			   " connectors present:",
+			   eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
+		for (i = 0; i < 4; i++)
+			if (eeprom[5] & (1<<i))
+				printk(connectors[i]);
+		printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n",
+			   phys[(eeprom[6]>>8)&7], eeprom[6] & 0x1f);
+		if (eeprom[7] & 0x0700)
+			printk(KERN_INFO "    Secondary interface chip %s.\n",
+				   phys[(eeprom[7]>>8)&7]);
+#if defined(notdef)
+		/* ToDo: Read and set PHY registers through MDIO port. */
+		for (i = 0; i < 2; i++)
+			printk("  MDIO register %d is %4.4x.\n",
+				   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+		for (i = 5; i < 7; i++)
+			printk("  MDIO register %d is %4.4x.\n",
+				   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
+		printk("  MDIO register %d is %4.4x.\n",
+			   25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
+#endif
+		if (((eeprom[6]>>8) & 0x3f) == DP83840) {
+			int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
+			if (congenb)
+			  mdi_reg23 |= 0x0100;
+			printk("  DP83840 specific setup, setting register 23 to %4.4x.\n",
+				   mdi_reg23);
+			mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
+		}
+		if ((options >= 0) && (options & 0x60)) {
+			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
+				   (options & 0x20 ? 100 : 10),
+				   (options & 0x10 ? "full" : "half"));
+			mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
+					   ((options & 0x20) ? 0x2000 : 0) | 	/* 100mbps? */
+					   ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+		}
+
+		/* Perform a system self-test. */
+		self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+		self_test_results[0] = 0;
+		self_test_results[1] = -1;
+		outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
+		do {
+#ifdef _LINUX_DELAY_H
+			udelay(10);
+#else
+			SLOW_DOWN_IO;
+#endif
+		} while (self_test_results[1] == -1  &&  --boguscnt >= 0);
+
+		if (boguscnt < 0) {		/* Test optimized out. */
+			printk(KERN_ERR "Self test failed, status %8.8x:\n"
+				   KERN_ERR " Failure to initialize the i82557.\n"
+				   KERN_ERR " Verify that the card is a bus-master"
+				   " capable slot.\n",
+				   self_test_results[1]);
+		} else 
+			printk(KERN_INFO "  General self-test: %s.\n"
+				   KERN_INFO "  Serial sub-system self-test: %s.\n"
+				   KERN_INFO "  Internal registers self-test: %s.\n"
+				   KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n",
+				   self_test_results[1] & 0x1000 ? "failed" : "passed",
+				   self_test_results[1] & 0x0020 ? "failed" : "passed",
+				   self_test_results[1] & 0x0008 ? "failed" : "passed",
+				   self_test_results[1] & 0x0004 ? "failed" : "passed",
+				   self_test_results[0]);
+	}
+#endif  /* kernel_bloat */
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
+	sp = dev->priv;
+	memset(sp, 0, sizeof(*sp));
+#ifdef MODULE
+	sp->next_module = root_speedo_dev;
+	root_speedo_dev = dev;
+#endif
+
+	sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+	sp->default_port = options >= 0 ? (options & 0x0f) : 0;
+
+	sp->phy[0] = eeprom[6];
+	sp->phy[1] = eeprom[7];
+	sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+
+	printk(KERN_INFO "  Operating in %s duplex mode.\n",
+		   sp->full_duplex ? "full" : "half");
+	if (sp->rx_bug)
+	  printk(KERN_INFO "  Reciever lock-up workaround activated.\n");
+
+	/* The Speedo-specific entries in the device structure. */
+	dev->open = &speedo_open;
+	dev->hard_start_xmit = &speedo_start_xmit;
+	dev->stop = &speedo_close;
+	dev->get_stats = &speedo_get_stats;
+#ifdef NEW_MULTICAST
+	dev->set_multicast_list = &set_rx_mode;
+#endif
+
+	return;
+}
+
+/* Serial EEPROM section.
+   A "bit" grungy, but we work our way through bit-by-bit :->. */
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */
+#define EE_CS			0x02	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   This is a "nasty" timing loop, but PC compatible machines are defined
+   to delay an ISA compatible period for the SLOW_DOWN_IO macro.  */
+#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ_CMD		(6 << 6)
+#define EE_ERASE_CMD	(7 << 6)
+
+static int read_eeprom(int ioaddr, int location)
+{
+	int i;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + SCBeeprom;
+	int read_cmd = location | EE_READ_CMD;
+	
+	outw(EE_ENB & ~EE_CS, ee_addr);
+	outw(EE_ENB, ee_addr);
+	
+	/* Shift the read command bits out. */
+	for (i = 10; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outw(EE_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(150);
+		outw(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay(250);
+	}
+	outw(EE_ENB, ee_addr);
+	
+	for (i = 15; i >= 0; i--) {
+		outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(100);
+		retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outw(EE_ENB, ee_addr);
+		eeprom_delay(100);
+	}
+
+	/* Terminate the EEPROM access. */
+	outw(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+	int val, boguscnt = 64*4;		/* <64 usec. to complete, typ 27 ticks */
+	outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+	do {
+#ifdef _LINUX_DELAY_H
+		udelay(16);
+#else
+		SLOW_DOWN_IO;
+#endif
+		val = inl(ioaddr + SCBCtrlMDI);
+		if (--boguscnt < 0) {
+			printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
+		}
+	} while (! (val & 0x10000000));
+	return val & 0xffff;
+}
+
+static int mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+	int val, boguscnt = 64*4;		/* <64 usec. to complete, typ 27 ticks */
+	outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+		 ioaddr + SCBCtrlMDI);
+	do {
+#ifdef _LINUX_DELAY_H
+		udelay(16);
+#else
+		SLOW_DOWN_IO;
+#endif
+		val = inl(ioaddr + SCBCtrlMDI);
+		if (--boguscnt < 0) {
+			printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
+		}
+	} while (! (val & 0x10000000));
+	return val & 0xffff;
+}
+
+
+static int
+speedo_open(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+#ifdef notdef
+	/* We could reset the chip, but should not need to. */
+	outl(0, ioaddr + SCBPort);
+	for (i = 40; i >= 0; i--)
+		SLOW_DOWN_IO;			/* At least 250ns */
+#endif
+
+#ifdef SA_SHIRQ
+	if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
+					"Intel EtherExpress Pro 10/100 Ethernet", dev)) {
+		return -EAGAIN;
+	}
+#else
+#ifdef USE_SHARED_IRQ
+	if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
+						   "Intel EtherExpress Pro 10/100 Ethernet"))
+		return -EAGAIN;
+#else
+	if (dev->irq < 2  ||  dev->irq > 15  ||  irq2dev_map[dev->irq] != NULL)
+		return -EAGAIN;
+	irq2dev_map[dev->irq] = dev;
+	if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
+		irq2dev_map[dev->irq] = NULL;
+		return -EAGAIN;
+	}
+#endif
+#endif
+
+	if (speedo_debug > 1)
+		printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
+
+	/* Load the statistics block address. */
+	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
+	outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
+	synchronize_irq();
+	sp->lstats.done_marker = 0;
+
+	speedo_init_rx_ring(dev);
+	outl(0, ioaddr + SCBPointer);
+	outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+	synchronize_irq();	
+
+	/* Todo: verify that we must wait for previous command completion. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
+	outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+	synchronize_irq();
+
+	/* Fill the first command with our physical address. */
+	{ 
+		unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+		unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
+
+		/* Avoid a bug(?!) here by marking the command already completed. */
+		sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
+		sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
+		*setup_frm++ = eaddrs[0];
+		*setup_frm++ = eaddrs[1];
+		*setup_frm++ = eaddrs[2];
+	}
+	sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
+	sp->cur_tx = 1;
+	sp->dirty_tx = 0;
+	sp->tx_full = 0;
+
+	outl(0, ioaddr + SCBPointer);
+	outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+	synchronize_irq();
+
+	dev->if_port = sp->default_port;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	/* Start the chip's Tx process and unmask interrupts. */
+	/* Todo: verify that we must wait for previous command completion. */
+	wait_for_cmd_done(ioaddr + SCBCmd);
+	outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
+	outw(CU_START, ioaddr + SCBCmd);
+
+	/* Setup the chip and configure the multicast list. */
+	sp->mc_setup_frm = NULL;
+	sp->mc_setup_frm_len = 0;
+	sp->rx_mode = -1;			/* Invalid -> always reset the mode. */
+	set_rx_mode(dev);
+
+	if (speedo_debug > 2) {
+		printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+	}
+	/* Set the timer.  The timer serves a dual purpose:
+	   1) to monitor the media interface (e.g. link beat) and perhaps switch
+	   to an alternate media type
+	   2) to monitor Rx activity, and restart the Rx process if the receiver
+	   hangs. */
+	init_timer(&sp->timer);
+	sp->timer.expires = RUN_AT((24*HZ)/10); 			/* 2.4 sec. */
+	sp->timer.data = (unsigned long)dev;
+	sp->timer.function = &speedo_timer;					/* timer handler */
+	add_timer(&sp->timer);
+
+	outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+	return 0;
+}
+
+/* Media monitoring and control. */
+static void speedo_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int tickssofar = jiffies - sp->last_rx_time;
+
+	if (speedo_debug > 3) {
+		int ioaddr = dev->base_addr;
+		printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+	}
+	if (sp->rx_bug) {
+		if (tickssofar > 2*HZ  || sp->rx_mode < 0) {
+			/* We haven't received a packet in a Long Time.  We might have been
+			   bitten by the receiver hang bug.  This can be cleared by sending
+			   a set multicast list command. */
+			set_rx_mode(dev);
+		}
+		/* We must continue to monitor the media. */
+		sp->timer.expires = RUN_AT(2*HZ); 			/* 2.0 sec. */
+		add_timer(&sp->timer);
+	}
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+speedo_init_rx_ring(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	struct RxFD *rxf, *last_rxf = NULL;
+	int i;
+
+	sp->cur_rx = 0;
+	sp->dirty_rx = RX_RING_SIZE - 1;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb;
+#ifndef KERNEL_1_2
+		skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#else
+		skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#endif
+		sp->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;			/* Bad news!  */
+		skb->dev = dev;			/* Mark as being used by this device. */
+
+#if LINUX_VERSION_CODE >= 0x10300
+		rxf = (struct RxFD *)skb->tail;
+		skb_reserve(skb, sizeof(struct RxFD));
+#else
+		/* Save the data in the header region -- it's restored later. */
+		rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD));
+		memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD));
+#endif
+		sp->rx_ringp[i] = rxf;
+		if (last_rxf)
+			last_rxf->link = virt_to_bus(rxf);
+		last_rxf = rxf;
+		rxf->status = 0x00000001; 			/* '1' is flag value only. */
+		rxf->link = 0;						/* None yet. */
+#if LINUX_VERSION_CODE < 0x10300
+		/* This field unused by i82557, we use it as a consistency check. */
+		rxf->rx_buf_addr = virt_to_bus(skb->data);
+#else
+		rxf->rx_buf_addr = virt_to_bus(skb->tail);
+#endif
+		rxf->count = 0;
+		rxf->size = PKT_BUF_SZ;
+	}
+	/* Mark the last entry as end-of-list. */
+	last_rxf->status = 0xC0000002; 			/* '2' is flag value only. */
+	sp->last_rxf = last_rxf;
+}
+
+static void speedo_tx_timeout(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int i;
+
+	printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
+		   "command %4.4x.\n",
+		   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+#ifndef final_version
+	printk("%s:  Tx timeout  fill index %d  scavenge index %d.\n",
+		   dev->name, sp->cur_tx, sp->dirty_tx);
+	printk("    Tx queue ");
+	for (i = 0; i < TX_RING_SIZE; i++)
+	  printk(" %8.8x", (int)sp->tx_ring[i].status);
+	printk(".\n    Rx ring ");
+	for (i = 0; i < RX_RING_SIZE; i++)
+	  printk(" %8.8x", (int)sp->rx_ringp[i]->status);
+	printk(".\n");
+		
+#else
+	dev->if_port ^= 1;
+	printk("  (Media type switching not yet implemented.)\n");
+	/* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
+#endif
+	if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
+	  printk("%s: Trying to restart the transmitter...\n", dev->name);
+	  outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
+		   ioaddr + SCBPointer);
+	  outw(CU_START, ioaddr + SCBCmd);
+	} else {
+	  outw(DRVR_INT, ioaddr + SCBCmd);
+	}
+	sp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+}
+
+static int
+speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int entry;
+
+	if (skb == NULL || skb->len <= 0) {
+		printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+	   If this ever occurs the queue layer is doing something evil! */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < TX_TIMEOUT - 2)
+			return 1;
+		if (tickssofar < TX_TIMEOUT) {
+			/* Reap sent packets from the full Tx queue. */
+			outw(DRVR_INT, ioaddr + SCBCmd);
+			return 1;
+		}
+		speedo_tx_timeout(dev);
+		return 0;
+	}
+
+	/* Caution: the write order is important here, set the base address
+	   with the "ownership" bits last. */
+
+	{	/* Prevent interrupts from changing the Tx ring from underneath us. */
+		unsigned long flags;
+
+		save_flags(flags);
+		cli();
+		/* Calculate the Tx descriptor entry. */
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+
+		sp->tx_skbuff[entry] = skb;
+		/* Todo: be a little more clever about setting the interrupt bit. */
+		sp->tx_ring[entry].status =
+			(CmdSuspend | CmdTx | CmdTxFlex) << 16;
+		sp->tx_ring[entry].link =
+		  virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+		sp->tx_ring[entry].tx_desc_addr =
+		  virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
+		/* The data region is always in one buffer descriptor, Tx FIFO
+		   threshold of 256. */
+		sp->tx_ring[entry].count = 0x01208000;
+		sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
+		sp->tx_ring[entry].tx_buf_size = skb->len;
+		/* Todo: perhaps leave the interrupt bit set if the Tx queue is more
+		   than half full.  Argument against: we should be receiving packets
+		   and scavenging the queue.  Argument for: if so, it shouldn't
+		   matter. */
+		sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
+		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+		/* Trigger the command unit resume. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		restore_flags(flags);
+	}
+
+	/* Leave room for set_rx_mode() to fill two entries. */
+	if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
+		sp->tx_full = 1;
+	else
+		dev->tbusy = 0;
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+#ifdef SA_SHIRQ
+static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+#else
+static void speedo_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+#ifdef SA_SHIRQ
+	struct device *dev = (struct device *)dev_instance;
+#else
+#ifdef USE_SHARED_IRQ
+	struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+#endif
+	struct speedo_private *sp;
+	int ioaddr, boguscnt = INTR_WORK;
+	unsigned short status;
+
+#ifndef final_version
+	if (dev == NULL) {
+		printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+#endif
+
+	ioaddr = dev->base_addr;
+	sp = (struct speedo_private *)dev->priv;
+#ifndef final_version
+	if (dev->interrupt) {
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
+	dev->interrupt = 1;
+#endif
+
+	do {
+		status = inw(ioaddr + SCBStatus);
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		outw(status & 0xfc00, ioaddr + SCBStatus);
+
+		if (speedo_debug > 4)
+			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
+				   dev->name, status);
+
+		if ((status & 0xfc00) == 0)
+			break;
+
+		if (status & 0x4000)	 /* Packet received. */
+			speedo_rx(dev);
+
+		if (status & 0x1000) {
+#ifdef notdef
+		  int i;
+		  printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
+				 " state -- %4.4x!  Index %d (%d).\n", dev->name, status,
+				 sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
+		  printk("   Rx ring:\n ");
+		  for (i = 0; i < RX_RING_SIZE; i++)
+			printk("   %d %8.8x %8.8x %8.8x %d %d.\n",
+				   i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
+				   sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count,
+				   sp->rx_ringp[i]->size);
+#endif
+
+		  if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
+			outw(RX_RESUMENR, ioaddr + SCBCmd);
+		  else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
+			/* No idea of what went wrong.  Restart the receiver. */
+			outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
+				 ioaddr + SCBPointer);
+			outw(RX_START, ioaddr + SCBCmd);
+		  }
+		  sp->stats.rx_errors++;
+		}
+
+		/* User interrupt, Command/Tx unit interrupt or CU not active. */
+		if (status & 0xA400) {
+			unsigned int dirty_tx = sp->dirty_tx;
+
+			while (sp->cur_tx - dirty_tx > 0) {
+				int entry = dirty_tx % TX_RING_SIZE;
+				int status = sp->tx_ring[entry].status;
+
+				if (speedo_debug > 5)
+					printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n",
+						   entry, status);
+				if ((status & 0x8000) == 0)
+					break;			/* It still hasn't been processed. */
+				/* Free the original skb. */
+				if (sp->tx_skbuff[entry]) {
+					sp->stats.tx_packets++;	/* Count only user packets. */
+					dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
+					sp->tx_skbuff[entry] = 0;
+				}
+				dirty_tx++;
+			}
+
+#ifndef final_version
+			if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dirty_tx, sp->cur_tx, sp->tx_full);
+				dirty_tx += TX_RING_SIZE;
+			}
+#endif
+
+			if (sp->tx_full && dev->tbusy
+				&& dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
+				/* The ring is no longer full, clear tbusy. */
+				sp->tx_full = 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+
+			sp->dirty_tx = dirty_tx;
+		}
+
+		if (--boguscnt < 0) {
+			printk("%s: Too much work at interrupt, status=0x%4.4x.\n",
+				   dev->name, status);
+			/* Clear all interrupt sources. */
+			outl(0xfc00, ioaddr + SCBStatus);
+			break;
+		}
+	} while (1);
+
+	if (speedo_debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+
+#ifndef final_version
+	/* Special code for testing *only*. */
+	{
+		static int stopit = 100;
+		if (dev->start == 0  &&  --stopit < 0) {
+			printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
+				   dev->name);
+#ifdef SA_SHIRQ
+			free_irq(irq, dev);
+#else
+			free_irq(irq);
+#endif
+		}
+	}
+#endif
+
+	dev->interrupt = 0;
+	return;
+}
+
+static int
+speedo_rx(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int entry = sp->cur_rx % RX_RING_SIZE;
+	int status;
+		
+	if (speedo_debug > 4)
+		printk(KERN_DEBUG " In speedo_rx().\n");
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
+
+		if (speedo_debug > 4)
+			printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
+				   sp->rx_ringp[entry]->count & 0x3fff);
+		if (status & 0x0200) {
+			printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n",
+				   dev->name, status);
+		} else if ( ! (status & 0x2000)) {
+			/* There was a fatal error.  This *should* be impossible. */
+			sp->stats.rx_errors++;
+			printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+				   dev->name, status);
+		} else {
+			/* Malloc up new buffer, compatible with net-2e. */
+			short pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+			struct sk_buff *skb;
+			int rx_in_place = 0;
+
+			/* Check if the packet is long enough to just accept without
+			   copying to a properly sized skbuff. */
+			if (pkt_len > SKBUFF_RX_COPYBREAK) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Pass up the skb already on the Rx ring. */
+				skb = sp->rx_skbuff[entry];
+#ifdef KERNEL_1_2
+				temp = skb->data;
+				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+					printk("%s: Warning -- the skbuff addresses do not match"
+						   " in speedo_rx: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+						   temp, skb->data);
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
+#else
+				temp = skb_put(skb, pkt_len);
+				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
+					printk("%s: Warning -- the skbuff addresses do not match"
+						   " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
+						   sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+#endif
+				if (newskb) {
+					struct RxFD *rxf;
+					rx_in_place = 1;
+					sp->rx_skbuff[entry] = newskb;
+					newskb->dev = dev;
+#ifdef KERNEL_1_2
+					/* Restore the data in the old header region. */
+					memcpy(skb->data - sizeof(struct RxFD),
+						   &sp->saved_skhead[entry], sizeof(struct RxFD));
+					/* Save the data in this header region. */
+					rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD));
+					sp->rx_ringp[entry] = rxf;
+					memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD));
+					rxf->rx_buf_addr = virt_to_bus(newskb->data);
+#else
+					rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
+					skb_reserve(newskb, sizeof(struct RxFD));
+					/* Unused by i82557, consistency check only. */
+					rxf->rx_buf_addr = virt_to_bus(newskb->tail);
+#endif
+					rxf->status = 0x00000001;
+				} else			/* No memory, drop the packet. */
+				  skb = 0;
+			} else
+#ifdef KERNEL_1_2
+				skb = alloc_skb(pkt_len, GFP_ATOMIC);
+#else
+				skb = dev_alloc_skb(pkt_len + 2);
+#endif
+			if (skb == NULL) {
+				int i;
+				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+				/* Check that at least two ring entries are free.
+				   If not, free one and mark stats->rx_dropped++. */
+				/* ToDo: This is not correct!!!!  We should count the number
+				   of linked-in Rx buffer to very that we have at least two
+				   remaining. */
+				for (i = 0; i < RX_RING_SIZE; i++)
+					if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
+						   & RX_COMPLETE))
+						break;
+
+				if (i > RX_RING_SIZE -2) {
+					sp->stats.rx_dropped++;
+					sp->rx_ringp[entry]->status = 0;
+					sp->cur_rx++;
+				}
+				break;
+			}
+			skb->dev = dev;
+#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
+			if (! rx_in_place) {
+				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+#ifdef KERNEL_1_3
+#warning This code has only been tested with later 1.3.* kernels.
+			skb->len = pkt_len;
+			memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+				   pkt_len);
+			/* Needed for 1.3.*. */
+			skb->protocol = eth_type_trans(skb, dev);
+#else	/* KERNEL_1_2 */
+			skb->len = pkt_len;
+			if (! rx_in_place) {
+				memcpy(skb->data,
+					   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+			}
+#endif
+#endif
+			netif_rx(skb);
+			sp->stats.rx_packets++;
+		}
+
+		/*	ToDo: This is better than before, but should be checked. */
+		{ 
+			struct RxFD *rxf = sp->rx_ringp[entry];
+			rxf->status = 0xC0000003; 		/* '3' for verification only */
+			rxf->link = 0;			/* None yet. */
+			rxf->count = 0;
+			rxf->size = PKT_BUF_SZ;
+			sp->last_rxf->link = virt_to_bus(rxf);
+			sp->last_rxf->status &= ~0xC0000000;
+			sp->last_rxf = rxf;
+			entry = (++sp->cur_rx) % RX_RING_SIZE;
+		}
+	}
+
+	sp->last_rx_time = jiffies;
+	return 0;
+}
+
+static int
+speedo_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int i;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (speedo_debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+			   dev->name, inw(ioaddr + SCBStatus));
+
+	/* Shut off the media monitoring timer. */
+	del_timer(&sp->timer);
+
+	/* Disable interrupts, and stop the chip's Rx process. */
+	outw(INT_MASK, ioaddr + SCBCmd);
+	outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+	synchronize_irq();
+
+#ifdef SA_SHIRQ
+	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
+
+	/* Free all the skbuffs in the Rx and Tx queues. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = sp->rx_skbuff[i];
+		sp->rx_skbuff[i] = 0;
+		/* Clear the Rx descriptors. */
+		if (skb)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		struct sk_buff *skb = sp->tx_skbuff[i];
+		sp->tx_skbuff[i] = 0;
+		/* Clear the Tx descriptors. */
+		if (skb)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+	if (sp->mc_setup_frm) {
+		kfree(sp->mc_setup_frm);
+		sp->mc_setup_frm_len = 0;
+	}
+
+	/* Print a few items for debugging. */
+	if (speedo_debug > 3) {
+		printk("%s:Printing Rx ring (next to receive into %d).\n",
+			   dev->name, sp->cur_rx);
+
+		for (i = 0; i < RX_RING_SIZE; i++)
+			printk("  Rx ring entry %d  %8.8x.\n",
+				   i, (int)sp->rx_ringp[i]->status);
+
+		for (i = 0; i < 5; i++)
+			printk("  PHY index %d register %d is %4.4x.\n",
+				   1, i, mdio_read(ioaddr, 1, i));
+		for (i = 21; i < 26; i++)
+			printk("  PHY index %d register %d is %4.4x.\n",
+				   1, i, mdio_read(ioaddr, 1, i));
+	}
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/* The Speedo-3 has an especially awkward and unusable method of getting
+   statistics out of the chip.  It takes an unpredictable length of time
+   for the dump-stats command to complete.  To avoid a busy-wait loop we
+   update the stats with the previous dump results, and then trigger a
+   new dump.
+
+   These problems are mitigated by the current /proc implementation, which
+   calls this routine first to judge the output length, and then to emit the
+   output.
+
+   Oh, and incoming frames are dropped while executing dump-stats!
+   */
+static struct enet_statistics *
+speedo_get_stats(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	if (sp->lstats.done_marker == 0xA007) {	/* Previous dump finished */
+		sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
+		sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
+		sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
+		sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
+		/*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
+		sp->stats.collisions += sp->lstats.tx_total_colls;
+		sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
+		sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
+		sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
+		sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
+		sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
+		sp->lstats.done_marker = 0x0000;
+		if (dev->start)
+			outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+	}
+	return &sp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   This is very ugly with Intel chips -- we usually have to execute an
+   entire configuration command, plus process a multicast command.
+   This is complicated.  We must put a large configuration command and
+   an arbitrarily-sized multicast command in the transmit list.
+   To minimize the disruption -- the previous command might have already
+   loaded the link -- we convert the current command block, normally a Tx
+   command, into a no-op and link it to the new command.
+*/
+static void
+set_rx_mode(struct device *dev)
+{
+	struct speedo_private *sp = (struct speedo_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	char new_rx_mode;
+	unsigned long flags;
+	int entry, i;
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		new_rx_mode = 3;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		new_rx_mode = 1;
+	} else
+		new_rx_mode = 0;
+
+	if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
+	  /* The Tx ring is full -- don't add anything!  Presumably the new mode
+	     is in config_cmd_data and will be added anyway. */
+		sp->rx_mode = -1;
+		return;
+	}
+
+	if (new_rx_mode != sp->rx_mode) {
+		/* We must change the configuration. Construct a CmdConfig frame. */
+		memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
+		sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
+		sp->config_cmd_data[4] = rxdmacount;
+		sp->config_cmd_data[5] = txdmacount + 0x80;
+		sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
+		sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
+		sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
+		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */
+		  sp->config_cmd_data[15] |= 0x80;
+		  sp->config_cmd_data[8] = 0;
+		}
+		save_flags(flags);
+		cli();
+		/* Fill the "real" tx_ring frame with a no-op and point it to us. */
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+		sp->tx_skbuff[entry] = 0;	/* Nothing to free. */
+		sp->tx_ring[entry].status = CmdNOp << 16;
+		sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
+		sp->config_cmd.status = 0;
+		sp->config_cmd.command = CmdSuspend | CmdConfigure;
+		sp->config_cmd.link =
+		  virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+		sp->last_cmd->command &= ~CmdSuspend;
+		/* Immediately trigger the command unit resume. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		sp->last_cmd = &sp->config_cmd;
+		restore_flags(flags);
+		if (speedo_debug > 5) {
+			int i;
+			printk(" CmdConfig frame in entry %d.\n", entry);
+			for(i = 0; i < 32; i++)
+				printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
+			printk(".\n");
+		}
+	}
+
+	if (new_rx_mode == 0  &&  dev->mc_count < 3) {
+		/* The simple case of 0-2 multicast list entries occurs often, and
+		   fits within one tx_ring[] entry. */
+		u16 *setup_params;
+		unsigned short *eaddrs;
+		struct dev_mc_list *mclist;
+
+		save_flags(flags);
+		cli();
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+		sp->tx_skbuff[entry] = 0;
+		sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
+		sp->tx_ring[entry].link =
+		  virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
+		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
+		setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr;
+		*setup_params++ = dev->mc_count*6;
+		/* Fill in the multicast addresses. */
+		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			 i++, mclist = mclist->next) {
+			eaddrs = (unsigned short *)mclist->dmi_addr;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+		}
+
+		sp->last_cmd->command &= ~CmdSuspend;
+		/* Immediately trigger the command unit resume. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+		restore_flags(flags);
+	} else if (new_rx_mode == 0) {
+		/* This does not work correctly, but why not? */
+		struct dev_mc_list *mclist;
+		unsigned short *eaddrs;
+		struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+		u16 *setup_params = (short *)mc_setup_frm->params;
+		int i;
+
+		if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
+			|| sp->mc_setup_frm == NULL) {
+			/* Allocate a new frame, 10bytes + addrs, with a few
+			   extra entries for growth. */
+			if (sp->mc_setup_frm)
+				kfree(sp->mc_setup_frm);
+			sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
+			printk("%s: Allocating a setup frame of size %d.\n",
+				   dev->name, sp->mc_setup_frm_len);
+			sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
+									   intr_count ? GFP_ATOMIC : GFP_KERNEL);
+			if (sp->mc_setup_frm == NULL) {
+			  printk("%s: Failed to allocate a setup frame.\n", dev->name);
+				sp->rx_mode = -1; /* We failed, try again. */
+				return;
+			}
+		}
+		mc_setup_frm = sp->mc_setup_frm;
+		/* Construct the new setup frame. */
+		printk("%s: Constructing a setup frame at %p, %d bytes.\n",
+			   dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+		mc_setup_frm->status = 0;
+		mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
+		/* Link set below. */
+		setup_params = (short *)mc_setup_frm->params;
+		*setup_params++ = dev->mc_count*6;
+		/* Fill in the multicast addresses. */
+		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			 i++, mclist = mclist->next) {
+			eaddrs = (unsigned short *)mclist->dmi_addr;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+			*setup_params++ = *eaddrs++;
+		}
+		
+		/* Disable interrupts while playing with the Tx Cmd list. */
+		save_flags(flags);
+		cli();
+		entry = sp->cur_tx++ % TX_RING_SIZE;
+
+		if (speedo_debug > 5)
+			printk(" CmdMCSetup frame length %d in entry %d.\n",
+				   dev->mc_count, entry);
+
+		/* Change the command to a NoOp, pointing to the CmdMulti command. */
+		sp->tx_skbuff[entry] = 0;
+		sp->tx_ring[entry].status = CmdNOp << 16;
+		sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
+
+		/* Set the link in the setup frame. */
+		mc_setup_frm->link =
+		  virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
+
+		sp->last_cmd->command &= ~CmdSuspend;
+		/* Immediately trigger the command unit resume. */
+		outw(CU_RESUME, ioaddr + SCBCmd);
+		sp->last_cmd = mc_setup_frm;
+		restore_flags(flags);
+		printk("%s: Last command at %p is %4.4x.\n",
+			   dev->name, sp->last_cmd, sp->last_cmd->command);
+	}
+
+	sp->rx_mode = new_rx_mode;
+}
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < VERSION(1,3,38))	/* 1.3.38 and later */
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+int
+init_module(void)
+{
+	int cards_found;
+
+	if (debug >= 0)
+		speedo_debug = debug;
+	if (speedo_debug)
+		printk(KERN_INFO "%s", version);
+
+	root_speedo_dev = NULL;
+	cards_found = eepro100_init(NULL);
+	return cards_found < 0 ? cards_found : 0;
+}
+
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_speedo_dev) {
+		next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
+		unregister_netdev(root_speedo_dev);
+		release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
+		kfree(root_speedo_dev);
+		root_speedo_dev = next_dev;
+	}
+}
+#else   /* not MODULE */
+int eepro100_probe(struct device *dev)
+{
+	int cards_found = 0;
+
+	cards_found = eepro100_init(dev);
+
+	if (speedo_debug > 0  &&  cards_found)
+		printk(version);
+
+	return cards_found ? 0 : -ENODEV;
+}
+#endif  /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.1.32/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.1.32/linux/drivers/net/net_init.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/net_init.c	Wed Apr  9 21:30:30 1997
@@ -318,7 +318,7 @@
 	save_flags(flags);
 	cli();
 
-	if (dev && dev->init) {
+	if (dev) {
 		if (dev->name &&
 			((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
 			for (i = 0; i < MAX_ETH_CARDS; ++i)
@@ -330,13 +330,15 @@
 				}
 		}
 
-		sti();	/* device probes assume interrupts enabled */
-		if (dev->init(dev) != 0) {
+		if (dev->init) {
+		  sti();	/* device probes assume interrupts enabled */
+		  if (dev->init(dev) != 0) {
 		    if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
 			restore_flags(flags);
 			return -EIO;
+		  }
+		  cli();
 		}
-		cli();
 
 		/* Add device to end of chain */
 		if (dev_base) {
diff -u --recursive --new-file v2.1.32/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.32/linux/drivers/net/plip.c	Fri Apr  4 08:52:21 1997
+++ linux/drivers/net/plip.c	Wed Apr  9 21:30:30 1997
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
+/* $Id: plip.c,v 1.1.1.1.2.6 1997/03/26 17:52:20 phil Exp $ */
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -11,16 +11,9 @@
  *
  *		Modularization and ifreq/ifmap support by Alan Cox.
  *		Rewritten by Niibe Yutaka.
+ *              parport-sharing awareness code by Philip Blundell.
  *
  * Fixes:
- *		9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- *		  - only claim 3 bytes of I/O space for port at 0x3bc
- *		  - treat NULL return from register_netdev() as success in
- *		    init_module()
- *		  - added message if driver loaded as a module but no
- *		    interfaces present.
- *		  - release claimed I/O ports if malloc() fails during init.
- *
  *		Niibe Yutaka
  *		  - Module initialization.  You can specify I/O addr and IRQ:
  *			# insmod plip.o io=0x3bc irq=7
@@ -52,7 +45,7 @@
  *     To use with DOS box, please do (Turn on ARP switch):
  *	# ifconfig plip[0-2] arp
  */
-static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n";
 
 /*
   Sources:
@@ -114,6 +107,11 @@
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 
+#include <linux/parport.h>
+
+/* Maximum number of devices to support. */
+#define PLIP_MAX  8
+
 /* Use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
 #define NET_DEBUG 1
@@ -150,6 +148,8 @@
 static struct net_device_stats *plip_get_stats(struct device *dev);
 static int plip_config(struct device *dev, struct ifmap *map);
 static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int plip_preempt(void *handle);
+static int plip_wakeup(void *handle);
 
 enum plip_connection_state {
 	PLIP_CN_NONE=0,
@@ -203,61 +203,47 @@
 	struct tq_struct deferred;
 	struct plip_local snd_data;
 	struct plip_local rcv_data;
+	struct ppd *pardev;
 	unsigned long  trigger;
 	unsigned long  nibble;
 	enum plip_connection_state connection;
 	unsigned short timeout_count;
-	char is_deferred;
+	int is_deferred;
+	int port_owner;
+	int should_relinquish;
 	int (*orig_rebuild_header)(struct sk_buff *skb);
 };
 
 /* Entry point of PLIP driver.
-   Probe the hardware, and register/initialize the driver. */
+   Probe the hardware, and register/initialize the driver.
 
-int plip_init(struct device *dev)
+   PLIP is rather weird, because of the way it interacts with the parport
+   system.  It is _not_ initialised from Space.c.  Instead, plip_init()
+   is called, and that function makes up a "struct device" for each port, and
+   then calls us here.
+
+   */
+int
+plip_init_dev(struct device *dev, struct parport *pb)
 {
 	struct net_local *nl;
-	int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
+	struct ppd *pardev;
 
-	/* Check region before the probe */
-	if (check_region(PAR_DATA(dev), iosize) < 0)
-		return -ENODEV;
+	dev->irq = pb->irq;
+	dev->base_addr = pb->base;
 
-	/* Check that there is something at base_addr. */
-	outb(0, PAR_DATA(dev));
-	udelay(1000);
-	if (inb(PAR_DATA(dev)) != 0)
+	if (pb->irq == -1) {
+		printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
 		return -ENODEV;
-
-	printk(version);
-	printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
-	if (dev->irq) {
-		printk("using assigned IRQ %d.\n", dev->irq);
-	} else {
-		int irq = 0;
-#ifdef MODULE
-		/* dev->irq==0 means autoprobe, but we don't try to do so
-		   with module.  We can change it by ifconfig */
-#else
-		unsigned int irqs = probe_irq_on();
-
-		outb(0x00, PAR_CONTROL(dev));
-		udelay(1000);
-		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
-		outb(PAR_INTR_ON, PAR_CONTROL(dev));
-		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
-		udelay(1000);
-		irq = probe_irq_off(irqs);
-#endif
-		if (irq > 0) {
-			dev->irq = irq;
-			printk("using probed IRQ %d.\n", dev->irq);
-		} else
-			printk("failed to detect IRQ(%d) --"
-			       " Please set IRQ by ifconfig.\n", irq);
 	}
+	
+	pardev = parport_register_device(pb, dev->name, plip_preempt,
+					plip_wakeup,
+					plip_interrupt, PARPORT_DEV_LURK, dev);
 
-	request_region(PAR_DATA(dev), iosize, dev->name);
+	printk(version);
+	printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
+	       dev->base_addr, dev->irq);
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -276,7 +262,7 @@
 	dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
 	if (dev->priv == NULL) {
 		printk(KERN_ERR "%s: out of memory\n", dev->name);
-		release_region(PAR_DATA(dev), iosize);
+		parport_unregister_device(pardev);
 		return -ENOMEM;
 	}
 	memset(dev->priv, 0, sizeof(struct net_local));
@@ -284,6 +270,9 @@
 
 	nl->orig_rebuild_header = dev->rebuild_header;
 	dev->rebuild_header 	= plip_rebuild_header;
+	nl->pardev = pardev; 
+
+	nl->port_owner = 0;
 
 	/* Initialize constants */
 	nl->trigger	= PLIP_TRIGGER_WAIT;
@@ -306,8 +295,8 @@
 /* Bottom half handler for the delayed request.
    This routine is kicked by do_timer().
    Request `plip_bh' to be invoked. */
-   
-static void plip_kick_bh(struct device *dev)
+static void
+plip_kick_bh(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 
@@ -350,7 +339,8 @@
 };
 
 /* Bottom half handler of PLIP. */
-static void plip_bh(struct device *dev)
+static void
+plip_bh(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct plip_local *snd = &nl->snd_data;
@@ -367,7 +357,8 @@
 	}
 }
 
-static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+static int
+plip_bh_timeout_error(struct device *dev, struct net_local *nl,
 		      struct plip_local *snd, struct plip_local *rcv,
 		      int error)
 {
@@ -429,7 +420,8 @@
 	return TIMEOUT;
 }
 
-static int plip_none(struct device *dev, struct net_local *nl,
+static int
+plip_none(struct device *dev, struct net_local *nl,
 	  struct plip_local *snd, struct plip_local *rcv)
 {
 	return OK;
@@ -437,7 +429,8 @@
 
 /* PLIP_RECEIVE --- receive a byte(two nibbles)
    Returns OK on success, TIMEOUT on timeout */
-extern inline int plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
+inline static int
+plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
 	     enum plip_nibble_state *ns_p, unsigned char *data_p)
 {
 	unsigned char c0, c1;
@@ -486,7 +479,8 @@
 }
 
 /* PLIP_RECEIVE_PACKET --- receive a packet */
-static int plip_receive_packet(struct device *dev, struct net_local *nl,
+static int
+plip_receive_packet(struct device *dev, struct net_local *nl,
 		    struct plip_local *snd, struct plip_local *rcv)
 {
 	unsigned short status_addr = PAR_STATUS(dev);
@@ -602,7 +596,8 @@
 
 /* PLIP_SEND --- send a byte (two nibbles)
    Returns OK on success, TIMEOUT when timeout    */
-extern inline int plip_send(unsigned short nibble_timeout, unsigned short data_addr,
+inline static int
+plip_send(unsigned short nibble_timeout, unsigned short data_addr,
 	  enum plip_nibble_state *ns_p, unsigned char data)
 {
 	unsigned char c0;
@@ -648,7 +643,8 @@
 }
 
 /* PLIP_SEND_PACKET --- send a packet */
-static int plip_send_packet(struct device *dev, struct net_local *nl,
+static int
+plip_send_packet(struct device *dev, struct net_local *nl,
 		 struct plip_local *snd, struct plip_local *rcv)
 {
 	unsigned short data_addr = PAR_DATA(dev);
@@ -752,7 +748,8 @@
 	return OK;
 }
 
-static int plip_connection_close(struct device *dev, struct net_local *nl,
+static int
+plip_connection_close(struct device *dev, struct net_local *nl,
 		      struct plip_local *snd, struct plip_local *rcv)
 {
 	cli();
@@ -762,11 +759,16 @@
 		mark_bh(NET_BH);
 	}
 	sti();
+	if (nl->should_relinquish) {
+		nl->should_relinquish = nl->port_owner = 0;
+		parport_release(nl->pardev);
+	}
 	return OK;
 }
 
 /* PLIP_ERROR --- wait till other end settled */
-static int plip_error(struct device *dev, struct net_local *nl,
+static int
+plip_error(struct device *dev, struct net_local *nl,
 	   struct plip_local *snd, struct plip_local *rcv)
 {
 	unsigned char status;
@@ -776,6 +778,7 @@
 		if (net_debug > 2)
 			printk("%s: reset interface.\n", dev->name);
 		nl->connection = PLIP_CN_NONE;
+		nl->should_relinquish = 0;
 		dev->tbusy = 0;
 		dev->interrupt = 0;
 		outb(PAR_INTR_ON, PAR_CONTROL(dev));
@@ -790,11 +793,12 @@
 }
 
 /* Handle the parallel port interrupts. */
-static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static void
+plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
 	struct device *dev = (struct device *) irq2dev_map[irq];
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct plip_local *rcv = &nl->rcv_data;
+	struct net_local *nl;
+	struct plip_local *rcv;
 	unsigned char c0;
 
 	if (dev == NULL) {
@@ -802,6 +806,9 @@
 		return;
 	}
 
+	nl = (struct net_local *)dev->priv;
+	rcv = &nl->rcv_data;
+
 	if (dev->interrupt)
 		return;
 
@@ -843,7 +850,8 @@
 }
 
 /* We don't need to send arp, for plip is point-to-point. */
-static int plip_rebuild_header(struct sk_buff *skb)
+static int
+plip_rebuild_header(struct sk_buff *skb)
 {
 	struct device *dev = skb->dev;
 	struct net_local *nl = (struct net_local *)dev->priv;
@@ -877,7 +885,8 @@
 	return 0;
 }
 
-static int plip_tx_packet(struct sk_buff *skb, struct device *dev)
+static int
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct plip_local *snd = &nl->snd_data;
@@ -885,6 +894,21 @@
 	if (dev->tbusy)
 		return 1;
 
+	/* We may need to grab the bus */
+	if (!nl->port_owner) {
+		if (parport_claim(nl->pardev))
+			return 1;
+		nl->port_owner = 1;
+	}
+
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself. */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
 	if (set_bit(0, (void*)&dev->tbusy) != 0) {
 		printk("%s: Transmitter access conflict.\n", dev->name);
 		return 1;
@@ -921,23 +945,19 @@
    This routine gets exclusive access to the parallel port by allocating
    its IRQ line.
  */
-static int plip_open(struct device *dev)
+static int
+plip_open(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	int i;
 
-	if (dev->irq == 0) {
-		printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
-		return -EAGAIN;
+	/* Grab the port */
+	if (!nl->port_owner) {
+		if (parport_claim(nl->pardev)) return -EAGAIN;
+		nl->port_owner = 1;
 	}
-	cli();
-	if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) {
-		sti();
-		printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
-		return -EAGAIN;
-	}
-	irq2dev_map[dev->irq] = dev;
-	sti();
+
+	nl->should_relinquish = 0;
 
 	/* Clear the data port. */
 	outb (0x00, PAR_DATA(dev));
@@ -954,17 +974,21 @@
 	/* Fill in the MAC-level header. */
 	for (i=0; i < ETH_ALEN - sizeof(u32); i++)
 		dev->dev_addr[i] = 0xfc;
+	/* Ugh, this is like death. */
 	*(u32 *)(dev->dev_addr+i) = dev->pa_addr;
 
 	dev->interrupt = 0;
 	dev->start = 1;
 	dev->tbusy = 0;
+	irq2dev_map[dev->irq] = dev;
+
 	MOD_INC_USE_COUNT;
 	return 0;
 }
 
 /* The inverse routine to plip_open (). */
-static int plip_close(struct device *dev)
+static int
+plip_close(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct plip_local *snd = &nl->snd_data;
@@ -973,12 +997,17 @@
 	dev->tbusy = 1;
 	dev->start = 0;
 	cli();
-	free_irq(dev->irq, NULL);
 	irq2dev_map[dev->irq] = NULL;
-	nl->is_deferred = 0;
-	nl->connection = PLIP_CN_NONE;
 	sti();
+#ifdef NOTDEF
 	outb(0x00, PAR_DATA(dev));
+#endif
+	nl->is_deferred = 0;
+	nl->connection = PLIP_CN_NONE;
+	if (nl->port_owner) {
+		parport_release(nl->pardev);
+		nl->port_owner = 0;
+	}
 
 	snd->state = PLIP_PK_DONE;
 	if (snd->skb) {
@@ -991,13 +1020,62 @@
 		rcv->skb = NULL;
 	}
 
+#ifdef NOTDEF
 	/* Reset. */
 	outb(0x00, PAR_CONTROL(dev));
+#endif
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
-static struct net_device_stats *plip_get_stats(struct device *dev)
+static int
+plip_preempt(void *handle)
+{
+	struct device *dev = (struct device *)handle;
+	struct net_local *nl = (struct net_local *)dev->priv;
+
+	/* Stand our ground if a datagram is on the wire */
+	if (nl->connection != PLIP_CN_NONE) {
+		nl->should_relinquish = 1;
+		return 1;
+	}
+
+	nl->port_owner = 0;	/* Remember that we released the bus */
+	return 0;
+}
+
+static int
+plip_wakeup(void *handle)
+{
+	struct device *dev = (struct device *)handle;
+	struct net_local *nl = (struct net_local *)dev->priv;
+
+	if (nl->port_owner) {
+		/* Why are we being woken up? */
+		printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name);
+		if (!parport_claim(nl->pardev))
+			/* bus_owner is already set (but why?) */
+			printk(KERN_DEBUG "%s: I'm broken.\n", dev->name);
+		else
+			return 1;
+	}
+	
+	if (!(dev->flags & IFF_UP))
+		/* Don't need the port when the interface is down */
+		return 1;
+
+	if (!parport_claim(nl->pardev)) {
+		nl->port_owner = 1;
+		irq2dev_map[dev->irq] = dev;
+		/* Clear the data port. */
+		outb (0x00, PAR_DATA(dev));
+	}
+
+	return 0;
+}
+
+static struct net_device_stats *
+plip_get_stats(struct device *dev)
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 	struct net_device_stats *r = &nl->enet_stats;
@@ -1005,7 +1083,8 @@
 	return r;
 }
 
-static int plip_config(struct device *dev, struct ifmap *map)
+static int
+plip_config(struct device *dev, struct ifmap *map)
 {
 	if (dev->flags & IFF_UP)
 		return -EBUSY;
@@ -1019,7 +1098,8 @@
 	return 0;
 }
 
-static int plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int
+plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
 	struct net_local *nl = (struct net_local *) dev->priv;
 	struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
@@ -1039,89 +1119,133 @@
 	return 0;
 }
 
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
+static int parport[PLIP_MAX] = { -1, };
+static int timid = 0;
 
-MODULE_PARM(io, "1-3i");
-MODULE_PARM(irq, "1-3i");
+MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
+MODULE_PARM(timid, "1i");
 
-static struct device dev_plip[] = {
-	{
-		"plip0",
-		0, 0, 0, 0,		/* memory */
-		0x3BC, 5,		/* base, irq */
-		0, 0, 0, NULL, plip_init
-	},
-	{
-		"plip1",
-		0, 0, 0, 0,		/* memory */
-		0x378, 7,		/* base, irq */
-		0, 0, 0, NULL, plip_init
-	},
-	{
-		"plip2",
-		0, 0, 0, 0,		/* memory */
-		0x278, 2,		/* base, irq */
-		0, 0, 0, NULL, plip_init
-	}
-};
+static struct device *dev_plip[PLIP_MAX] = { NULL, };
 
-int init_module(void)
+#ifdef MODULE
+void
+cleanup_module(void)
 {
-	int no_parameters=1;
-	int devices=0;
 	int i;
 
-	/* When user feeds parameters, use them */
-	for (i=0; i < 3; i++) {
-		int specified=0;
-
-		if (io[i] != 0) {
-			dev_plip[i].base_addr = io[i];
-			specified++;
-		}
-		if (irq[i] != 0) {
-			dev_plip[i].irq = irq[i];
-			specified++;
-		}
-		if (specified) {
-			if (register_netdev(&dev_plip[i]) != 0) {
-				printk(KERN_INFO "plip%d: Not found\n", i);
-				return -EIO;
-			}
-			no_parameters = 0;
+	for (i=0; i < PLIP_MAX; i++) {
+		if (dev_plip[i]) {
+			struct net_local *nl =
+				(struct net_local *)dev_plip[i]->priv;
+			unregister_netdev(dev_plip[i]);
+			if (nl->port_owner)
+				parport_release(nl->pardev);
+			parport_unregister_device(nl->pardev);
+			kfree(dev_plip[i]->priv);
+			kfree(dev_plip[i]->name);
+			kfree(dev_plip[i]);
+			dev_plip[i] = NULL;
 		}
 	}
-	if (!no_parameters)
-		return 0;
+}
+
+#define plip_init  init_module
 
-	/* No parameters.  Default action is probing all interfaces. */
-	for (i=0; i < 3; i++) {
-		if (register_netdev(&dev_plip[i]) == 0)
-			devices++;
+#else /* !MODULE */
+
+static int parport_ptr = 0;
+
+void plip_setup(char *str, int *ints)
+{
+	/* Ugh. */
+	if (!strncmp(str, "parport", 7)) {
+		int n = simple_strtoul(str+7, NULL, 10);
+		if (parport_ptr < PLIP_MAX)
+			parport[parport_ptr++] = n;
+		else
+			printk(KERN_INFO "plip: too many ports, %s ignored.\n",
+			       str);
+	} else if (!strcmp(str, "timid")) {
+		timid = 1;
+	} else {
+		if (ints[0] == 0 || ints[1] == 0) {
+			/* disable driver on "parport=" or "parport=0" */
+			parport[0] = -2;
+		} else {
+			printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n", 
+			       ints[1]);
+		}
 	}
-	if (devices == 0) {
-		printk(KERN_INFO "plip: no interfaces found\n");
-		return -EIO;
+}
+
+#endif /* MODULE */
+
+static int inline 
+plip_searchfor(int list[], int a)
+{
+	int i;
+	for (i = 0; i < 3 && list[i] != -1; i++) {
+		if (list[i] == a) return 1;
 	}
 	return 0;
 }
 
-void cleanup_module(void)
+int
+plip_init(void)
 {
-	int i;
+	struct parport *pb = parport_enumerate();
+	int devices=0;
+	int i=0;
+
+	if (parport[0] == -2)
+		return 0;
+
+	if (parport[0] != -1 && timid) {
+		printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
+		timid = 0;
+	}
 
-	for (i=0; i < 3; i++) {
-		if (dev_plip[i].priv) {
-			unregister_netdev(&dev_plip[i]);
-			release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
-			kfree_s(dev_plip[i].priv, sizeof(struct net_local));
-			dev_plip[i].priv = NULL;
+	/* When user feeds parameters, use them */
+	while (pb) {
+		if ((parport[0] == -1 && (!timid || !pb->devices)) || 
+		    plip_searchfor(parport, i)) {
+			if (i == PLIP_MAX) {
+				printk(KERN_ERR "plip: too many devices\n");
+				break;
+			}
+			dev_plip[i] = kmalloc(sizeof(struct device),
+					      GFP_KERNEL);
+			if (!dev_plip[i]) {
+				printk(KERN_ERR "plip: memory squeeze\n");
+				break;
+			}
+			memset(dev_plip[i], 0, sizeof(struct device));
+			dev_plip[i]->name = 
+				kmalloc(strlen("plipXXX"), GFP_KERNEL);
+			if (!dev_plip[i]->name) {
+				printk(KERN_ERR "plip: memory squeeze.\n");
+				kfree(dev_plip[i]);
+				break;
+			}
+			sprintf(dev_plip[i]->name, "plip%d", i);
+			dev_plip[i]->priv = pb;
+			if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) {
+				kfree(dev_plip[i]->name);
+				kfree(dev_plip[i]);
+			} else {
+				devices++;
+			}
 		}
+		i++;
+		pb = pb->next;
+  	}
+
+	if (devices == 0) {
+		printk(KERN_INFO "plip: no devices registered\n");
+		return -EIO;
 	}
+	return 0;
 }
-#endif /* MODULE */
 
 /*
  * Local variables:
diff -u --recursive --new-file v2.1.32/linux/drivers/net/scc.c linux/drivers/net/scc.c
--- v2.1.32/linux/drivers/net/scc.c	Sun Feb  2 05:18:41 1997
+++ linux/drivers/net/scc.c	Tue Apr  8 11:37:47 1997
@@ -1,4 +1,4 @@
-#define RCS_ID "$Id: scc.c,v 1.66 1997/01/08 22:56:06 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.69 1997/04/06 19:22:45 jreuter Exp jreuter $"
 
 #define VERSION "3.0"
 #define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
@@ -16,7 +16,7 @@
 
    ********************************************************************
 
-	Copyright (c) 1993, 1996 Joerg Reuter DL1BKE
+	Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
 
 	portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -86,7 +86,9 @@
    		  You can use 'kissbridge' if you need a KISS TNC emulator.
 
    961213	- Fixed for Linux networking changes. (G4KLX)
-   960108	- Fixed the remaining problems.
+   970108	- Fixed the remaining problems.
+   970402	- Hopefully fixed the problems with the new *_timer()
+   		  routines, added calibration code.
 
    Thanks to all who contributed to this driver with ideas and bug
    reports!
@@ -151,8 +153,6 @@
 #include <linux/if_arp.h>
 #include <linux/socket.h>
 
-#include <linux/config.h> /* for CONFIG_PROC_FS */
-
 #include <linux/scc.h>
 #include "z8530.h"
 
@@ -222,6 +222,9 @@
 static int Nchips = 0;
 static io_port Vector_Latch = 0;
 
+MODULE_AUTHOR("Joerg Reuter <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("scc");
 
 /* ******************************************************************** */
 /* *			Port Access Functions			      * */
@@ -511,7 +514,7 @@
 		}
 		
 		or(scc,R10,ABUNDER);
-		scc_start_tx_timer(scc, t_txdelay, 1);	/* restart transmission */
+		scc_start_tx_timer(scc, t_txdelay, 0);	/* restart transmission */
 	}
 		
 	scc->status = status;
@@ -799,10 +802,10 @@
  
 static void init_channel(struct scc_channel *scc)
 {
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
 	disable_irq(scc->irq);
-	
-	if (scc->tx_t.next)    del_timer(&scc->tx_t);
-	if (scc->tx_wdog.next) del_timer(&scc->tx_wdog);
 
 	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
 	wr(scc,R1,0);			/* no W/REQ operation */
@@ -982,11 +985,11 @@
 {
 	unsigned long flags;
 	
+	
 	save_flags(flags);
 	cli();
 
-	if (scc->tx_t.next) 
-		del_timer(&scc->tx_t);
+	del_timer(&scc->tx_t);
 
 	if (when == 0)
 	{
@@ -1000,7 +1003,7 @@
 		add_timer(&scc->tx_t);
 	}
 	
-	restore_flags(flags);	
+	restore_flags(flags);
 }
 
 static void scc_start_defer(struct scc_channel *scc)
@@ -1010,8 +1013,7 @@
 	save_flags(flags);
 	cli();
 
-	if (scc->tx_wdog.next)
-		del_timer(&scc->tx_wdog);
+	del_timer(&scc->tx_wdog);
 	
 	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
 	{
@@ -1030,8 +1032,7 @@
 	save_flags(flags);
 	cli();
 
-	if (scc->tx_wdog.next)
-		del_timer(&scc->tx_wdog);
+	del_timer(&scc->tx_wdog);
 	
 	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
 	{
@@ -1184,9 +1185,7 @@
  	save_flags(flags);
  	cli();
  
- 	if (scc->tx_wdog.next)
- 		del_timer(&scc->tx_wdog);
- 		
+ 	del_timer(&scc->tx_wdog);	
  	scc_key_trx(scc, TX_OFF);
 
  	restore_flags(flags);
@@ -1215,16 +1214,8 @@
 static void t_busy(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
-	unsigned long flags;
 
-	save_flags(flags);
-	cli();
-	
-	if (scc->tx_t.next)
-		del_timer(&scc->tx_t);
-
-	restore_flags(flags);
-	
+	del_timer(&scc->tx_t);
 	scc_lock_dev(scc);
 
 	scc_discard_buffers(scc);
@@ -1256,8 +1247,7 @@
 	scc_lock_dev(scc);
 	scc_discard_buffers(scc);
 
-	if (scc->tx_t.next)
-		del_timer(&scc->tx_t);
+	del_timer(&scc->tx_t);
 
 	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
 	cl(scc, R15, TxUIE);		/* count it. */
@@ -1280,16 +1270,9 @@
 static void t_idle(unsigned long channel)
 {
 	struct scc_channel *scc = (struct scc_channel *) channel;
-	unsigned long flags;
 	
-	save_flags(flags);
-	cli();
-	
-	if (scc->tx_wdog.next)
-		del_timer(&scc->tx_wdog);
+	del_timer(&scc->tx_wdog);
 
-	restore_flags(flags);
-	
 	scc_key_trx(scc, TX_OFF);
 
 	if (scc->kiss.mintime != TIMER_OFF)
@@ -1416,6 +1399,65 @@
 #undef SVAL
 
 /* ******************************************************************* */
+/* *			Send calibration pattern		     * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	scc_key_trx(scc, TX_OFF);
+	wr(scc, R6, 0);
+	wr(scc, R7, FLAG);
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	scc_unlock_dev(scc);
+	
+	restore_flags(flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	scc_lock_dev(scc);
+	scc_discard_buffers(scc);
+
+	del_timer(&scc->tx_wdog);
+
+	scc->tx_wdog.data = (unsigned long) scc;
+	scc->tx_wdog.function = scc_stop_calibrate;
+	scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+	add_timer(&scc->tx_wdog);
+	
+	wr(scc, R6, 0);
+	wr(scc, R7, pattern);
+
+	/* 
+	 * Don't know if this works. 
+	 * Damn, where is my Z8530 programming manual...? 
+	 */
+
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	scc_key_trx(scc, TX_ON);
+	
+	restore_flags(flags);
+}
+
+/* ******************************************************************* */
 /* *		Init channel structures, special HW, etc...	     * */
 /* ******************************************************************* */
 
@@ -1644,26 +1686,8 @@
 	struct scc_channel *scc = (struct scc_channel *) dev->priv;
 	unsigned long flags;
 	char kisscmd;
-	long ticks;
 	
-	if (dev->tbusy)
-	{
-		ticks = (signed) (jiffies - dev->trans_start);
-
-		if (ticks < scc->kiss.maxdefer*HZ || scc == NULL)
-			return 1;
-
-		/* 
-		 * Throw away transmission queue. 
-		 */
-
-		if (scc->tx_wdog.next)
-			del_timer(&scc->tx_wdog);
-		t_busy((unsigned long) scc);
-                dev->trans_start = jiffies;
-        }
-        
-	if (scc == NULL || scc->magic != SCC_MAGIC)
+	if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy)
 	{
 		dev_kfree_skb(skb, FREE_WRITE);
 		return 0;
@@ -1692,10 +1716,13 @@
 	save_flags(flags);
 	cli();
 	
+	if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
+	{
+		struct sk_buff *skb_del;
+		skb_del = __skb_dequeue(&scc->tx_queue);
+		dev_kfree_skb(skb_del, FREE_WRITE);
+	}
 	__skb_queue_tail(&scc->tx_queue, skb);
-	
-	if (skb_queue_len(&scc->tx_queue) == MAXQUEUE)
-		scc_lock_dev(scc);
 
 	dev->trans_start = jiffies;
 
@@ -1729,6 +1756,7 @@
  * SIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
  * SIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
  * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
+ * SIOCSCCCAL		- send calib. pattern	arg: (struct scc_calibrate *) arg
  */
 
 static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
@@ -1736,6 +1764,7 @@
 	struct scc_kiss_cmd kiss_cmd;
 	struct scc_mem_config memcfg;
 	struct scc_hw_config hwcfg;
+	struct scc_calibrate cal;
 	int chan;
 	unsigned char device_name[10];
 	void *arg;
@@ -1942,6 +1971,14 @@
 			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
 				return -EINVAL;
 			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
+		
+		case SIOCSCCCAL:
+			if (!suser()) return -EPERM;
+			if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
+				return -EINVAL;
+
+			scc_start_calibrate(scc, cal.time, cal.pattern);
+			return 0;
 
 		default:
 			return -ENOIOCTLCMD;
@@ -2164,7 +2201,7 @@
 	result = scc_init();
 
 	if (result == 0)
-		printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
+		printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
 		
 	return result;
 }
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/BUGS-parport linux/drivers/pnp/BUGS-parport
--- v2.1.32/linux/drivers/pnp/BUGS-parport	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/BUGS-parport	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,15 @@
+Currently known (or at least suspected) bugs in parport:
+
+o /proc/parport is buggy under 2.0.29 (ls /proc/parport/0 gives no such
+  file or directory).  David has suggested a fix for this.  [/proc/parport
+  has been temporarily taken out]
+
+o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
+
+o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
+  the resulting kernel won't link.
+
+o IEEE1284 code does not do the terminating handshake after transfers, which
+  seems to upset some devices.
+
+o lp doesn't allow you to read status while printing is in progress.
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/Config.in linux/drivers/pnp/Config.in
--- v2.1.32/linux/drivers/pnp/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/Config.in	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,16 @@
+#
+# Plug and Play configuration
+#
+
+mainmenu_option next_comment
+comment 'Plug and Play support'
+ 
+bool 'Plug and Play support' CONFIG_PNP
+
+if [ "$CONFIG_PNP" = "y" ]; then
+  if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+    bool '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT_AUTOPROBE
+  fi
+fi
+
+endmenu
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/Makefile linux/drivers/pnp/Makefile
--- v2.1.32/linux/drivers/pnp/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/Makefile	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,45 @@
+#
+# Makefile for the kernel Plug-and-Play device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# Note 3! Plug and Play is the Borg.  We have assimilated some other
+# drivers in the `char', `net' and `scsi' directories, but left them
+# there to allay suspicion.
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := pnp.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PNP_PARPORT),y)
+  L_OBJS += parport_share.o parport_procfs.o
+  ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+    L_OBJS += parport_probe.o
+  endif
+  LX_OBJS += parport_init.o
+else
+  ifeq ($(CONFIG_PNP_PARPORT),m)
+    MI_OBJS += parport_share.o parport_procfs.o
+    ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
+      MI_OBJS += parport_probe.o
+    endif
+    MIX_OBJS += parport_init.o
+    M_OBJS += parport.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+parport.o: $(MI_OBJS) $(MIX_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/TODO-parport linux/drivers/pnp/TODO-parport
--- v2.1.32/linux/drivers/pnp/TODO-parport	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/TODO-parport	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,43 @@
+Things to be done.
+
+0. Fix the bugs (see BUGS-parport).
+
+1. Write a /proc interface for parport.  As a starting point, we could
+   probably have something like this:
+
+   /proc/parport/N/hardware
+
+        Details of the port hardware - chipset, capabilities and so on.
+        Read-only.
+
+   /proc/parport/N/irq
+
+        IRQ number of the port.  Read-write.
+
+   /proc/parport/N/devices
+
+        List of devices connected to this bus, with the currently active
+        one marked in some way.  Probably you'd have the device in the
+        first column, and '*' (for the current device) or '-' (for a
+        lurker) in the second column.
+
+   NOTE: The directory structure has been coded -- but the files are
+         missing ...
+
+2. Proper documentation.
+
+3. Overhaul lp.c:
+
+   a) It's a mess, and there is a lot of code duplication.
+
+   b) ECP support would be nice.  This can only work if both the port and
+      the printer support it.
+
+   c) Errors could do with being handled better.  There's no point logging a
+      message every 10 seconds when the printer is out of paper. 
+
+   d) Handle status readback automatically.  IEEE1284 printers can post status
+      bits when they have something to say.  We should read out and deal 
+      with (maybe just log) whatever the printer wants to tell the world.
+
+4. Assimilate more drivers.
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_init.c linux/drivers/pnp/parport_init.c
--- v2.1.32/linux/drivers/pnp/parport_init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/parport_init.c	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,815 @@
+/* $Id: parport_init.c,v 1.1.2.4 1997/04/01 18:19:10 phil Exp $
+ * Parallel-port initialisation code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *	    Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static int io[PARPORT_MAX] = { 0, };
+static int irq[PARPORT_MAX] = { -1, };
+static int dma[PARPORT_MAX] = { -1, };
+
+/******************************************************
+ *  DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+	int tmp = 0;
+	int i,retv;
+	
+	for (i = 0; i < 8; i++) {
+		retv = request_dma(i, "probe");
+		if (retv)
+			continue;
+		tmp |= 1 << i;
+
+		cli();
+		disable_dma(i);
+		clear_dma_ff(i);
+		set_dma_addr(i, virt_to_bus(buff));
+		set_dma_count(i, size);
+		set_dma_mode(i, DMA_MODE_READ);
+		sti();
+	}
+
+	return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+	int i;
+	
+	for (i = 0; i < 8; i++)
+		if (dma & (1 << i)) {
+		cli();
+		enable_dma(i);
+		sti();
+	    }
+
+	return dma;
+}
+
+static int parport_detect_dma_transfer(int dma,int size)
+{
+	int i,n,retv;
+	int count=0;
+
+	retv = -1;
+	for (i = 0; i < 8; i++)
+		if (dma & (1 << i)) {
+			disable_dma(i);
+			clear_dma_ff(i);
+			n = get_dma_residue(i);
+			if (n != size) {
+				retv = i;
+				if (count > 0) {
+					retv = -1;	/* Multiple DMA's */
+					printk("Multiple DMA detected.\n");
+				}
+				count++;
+			}
+			free_dma(i);
+		}
+
+	return retv;	
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+	int dma;
+
+	w_ecr(pb,0xE0); /* Configuration MODE */
+	
+	dma = r_cnfgB(pb) & 0x07;
+
+	w_ecr(pb,pb->ecr);
+	
+	if( dma == 0 || dma == 4 ) /* Jumper selection */
+		return -1;
+	else
+		return dma;
+}
+
+/* Only called if port support ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of -1 is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ *    1Byte DMA transfer
+ * else // 4 < DMA < 8
+ *    2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+	int dma,retv;
+	int dsr,dsr_read;
+	char *buff;
+
+	retv = programmable_dma_support(pb);
+	if( retv != -1 )
+		return retv;
+	
+	buff = kmalloc(16, GFP_KERNEL | GFP_DMA);
+	if( !buff ){
+	    printk("parport: memory squezze\n");
+	    return -1;
+	}
+	
+ 	dsr = r_ctr(pb);
+	dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
+
+	w_ecr(pb, 0xc0);	   /* ECP MODE */
+ 	w_ctr(pb, dsr_read );
+	dma=parport_prepare_dma(buff,8);
+	w_ecr(pb, 0xd8);	   /* ECP FIFO + enable DMA */
+	parport_enable_dma(dma);
+	udelay(30);           /* Give some for DMA tranfer */
+	retv = parport_detect_dma_transfer(dma,8);
+	
+	/*
+	 * National Semiconductors only supports DMA tranfers
+	 * in ECP MODE
+	 */
+	if( retv == -1 ){
+		w_ecr(pb, 0x60);	   /* ECP MODE */
+		w_ctr(pb, dsr_read );
+		dma=parport_prepare_dma(buff,8);
+		w_ecr(pb, 0x68);	   /* ECP FIFO + enable DMA */
+		parport_enable_dma(dma);
+		udelay(30);           /* Give some for DMA tranfer */
+		retv = parport_detect_dma_transfer(dma,8);
+	}
+	
+	kfree(buff);
+	
+	w_ctr(pb, pb->ctr);
+	w_ecr(pb, pb->ecr);
+
+	return retv;
+}
+/******************************************************
+ *  MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+	int r;
+
+    if( !(r_str(pb) & 0x01) )
+		return 1;
+
+	/* To clear timeout some chip requiere double read */
+	r_str(pb);
+	r = r_str(pb);
+	w_str(pb, r | 0x01); /* Some reset by writing 1 */
+	w_str(pb, r & 0xfe); /* Others by writing 0 */
+	r = r_str(pb);
+
+	return !(r & 0x01);
+}
+
+
+/*
+ * Checks for por existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+	int r,rr;
+	
+	/* Do a simple read-write test to make sure the port exists. */
+	w_dtr(pb, 0xaa);
+	r = r_dtr(pb);
+	
+	w_dtr(pb, 0x55);
+	rr = r_dtr(pb);
+	
+	if (r != 0xaa || rr != 0x55) {
+		return 0;
+	}
+	
+	return PARPORT_MODE_SPP;
+}
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+	int r;
+
+	if( pb->base == 0x3BC )
+		return 0;
+	
+	r= r_ctr(pb);	
+	if( (r_ecr(pb) & 0x03) == (r & 0x03) ){
+		w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+		r= r_ctr(pb);	
+		if( (r_ecr(pb) & 0x03) == (r & 0x03) )
+			return 0; /* Sure that no ECR register exists */
+	}
+	
+	if( (r_ecr(pb) & 0x03 ) != 0x01 )
+		return 0;
+
+	w_ecr(pb,0x34);
+	if( r_ecr(pb) != 0x35 )
+		return 0;
+
+	w_ecr(pb,pb->ecr);
+	w_ctr(pb,pb->ctr);
+	
+	return PARPORT_MODE_ECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+	int i;
+	
+	if( !(pb->modes & PARPORT_MODE_ECR) )
+		return 0;
+	/*
+	 * Usign LGS chipset it uses ECR register, but
+	 * it doesn't support ECP or FIFO MODE
+	 */
+	
+	w_ecr(pb,0xc0); /* TEST FIFO */
+	for( i=0 ; i < 1024 && (r_ecr(pb) & 0x01) ; i++ )
+		w_fifo(pb, 0xaa);
+
+	w_ecr(pb,pb->ecr);
+
+	if( i >= 1024 )
+		return 0;
+	
+	return PARPORT_MODE_ECP;
+}
+
+/* EPP mode detection
+ * Theory:
+ *	Bit 0 of STR is the EPP timeout bit, this bit is 0
+ *	when EPP is possible and is set high when an EPP timeout
+ *	occurs (EPP uses the HALT line to stop the CPU while it does
+ *	the byte transfer, an EPP timeout occurs if the attached
+ *	device fails to respond after 10 micro seconds).
+ *
+ *	This bit is cleared by either reading it (National Semi)
+ *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ *	This bit is always high in non EPP modes.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+	if( pb->base == 0x3BC )
+		return 0;
+
+	/* If EPP timeout bit clear then EPP available */
+	if( !epp_clear_timeout(pb) )
+		return 0;  /* No way to clear timeout */
+
+	w_ctr(pb, r_ctr(pb) | 0x20);
+	w_ctr(pb, r_ctr(pb) | 0x10);
+	epp_clear_timeout(pb);
+	
+	r_epp(pb);
+	udelay(30);  /* Wait for possible EPP timeout */
+	
+	if( r_str(pb) & 0x01 ){
+		epp_clear_timeout(pb);
+		return PARPORT_MODE_EPP;
+	}
+
+	return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+	int mode;
+
+	if( !(pb->modes & PARPORT_MODE_ECR) )
+		return 0;
+	
+	/* Search for SMC style EPP+ECP mode */
+	w_ecr(pb, 0x80);
+	
+	mode = parport_EPP_supported(pb);
+
+	w_ecr(pb,pb->ecr);
+	
+	if( mode )
+		return PARPORT_MODE_ECPEPP;
+	
+	return 0;
+}
+
+/* Detect LP_PS2 support
+ * Bit 5 (0x20) sets the PS/2 data direction, setting this high
+ * allows us to read data from the data lines, old style SPP ports
+ * will return 0xff.  This may not be reliable if there is a
+ * peripheral attached to the port. 
+ */
+static int parport_PS2_supported(struct parport *pb)
+{
+	int r,rr;
+
+	epp_clear_timeout(pb);
+
+	w_ctr(pb, pb->ctr | 0x20);	/* Tri-state the buffer */
+	
+	w_dtr(pb, 0xAA);
+	r = r_dtr(pb);
+
+	w_dtr(pb, 0x55);
+	rr = r_dtr(pb);
+	
+	w_ctr(pb, pb->ctr);	/* Reset CTR register */
+
+	if (r != 0xAA || rr != 0x55 )
+		return PARPORT_MODE_PS2;
+	
+	return 0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+	int mode;
+
+	if( !(pb->modes & PARPORT_MODE_ECR) )
+		return 0;
+	
+	w_ecr(pb, 0x20);
+	
+	mode = parport_PS2_supported(pb);
+
+	w_ecr(pb,pb->ecr);
+	
+	if (mode)
+		return PARPORT_MODE_ECPPS2;
+	
+	return 0;
+}
+
+/******************************************************
+ *  IRQ detection section:
+ */
+/*
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ */
+static int intr_vote[16];
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	intr_vote[irq]++;
+	return;
+}
+
+static long open_intr_election(void)
+{
+	long tmp = 0;
+	int i;
+
+	/* We ignore the timer - irq 0 */
+	for (i = 1; i < 16; i++) {
+		intr_vote[i] = 0;
+		if (request_irq(i, parport_vote_intr_func,
+		       SA_INTERRUPT, "probe", intr_vote) == 0)
+			tmp |= 1 << i;
+	}
+	return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+	long max_vote = 0;
+	int irq = -1;
+	int i;
+
+	/* We ignore the timer - irq 0 */
+	for (i = 1; i < 16; i++)
+		if (tmp & (1 << i)) {
+			if (intr_vote[i] > max_vote) {
+				if (max_vote)
+					return -1;
+				max_vote = intr_vote[i];
+				irq = i;
+			}
+			free_irq(i, intr_vote);
+		}
+	return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+	int irq;
+
+	w_ecr(pb,0xE0); /* Configuration MODE */
+	
+	irq = (r_cnfgB(pb) >> 3) & 0x07;
+
+	switch(irq){
+	  case 2:
+		  irq = 9;
+		  break;
+	  case 7:
+		  irq = 5;
+		  break;
+	  case 0:
+		  irq = -1;
+		  break;
+	  default:
+		  irq += 7;
+	}
+		  
+	w_ecr(pb,pb->ecr);
+	
+	return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+	int irqs,i;
+		
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = open_intr_election();
+		
+	w_ecr(pb, 0x00);	    /* Reset FIFO */
+	w_ctr(pb, pb->ctr );    /* Force direction = 0 */
+	w_ecr(pb, 0xd0);	    /* TEST FIFO + nErrIntrEn */
+
+	/* If Full FIFO sure that WriteIntrThresold is generated */
+	for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
+		w_fifo(pb, 0xaa);
+	}
+		
+	pb->irq = close_intr_election(irqs);
+	if (pb->irq == 0)
+		pb->irq = -1;	/* No interrupt detected */
+	
+	w_ecr(pb, pb->ecr);
+
+	return pb->irq;
+}
+
+/*
+ * It's called only if supports EPP on National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond 
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+	int irqs;
+
+#ifndef ADVANCED_DETECT
+	return -1;
+#endif
+	
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = open_intr_election();
+
+	if( pb->modes & PARPORT_MODE_ECR )
+		w_ecr(pb, r_ecr(pb) | 0x10 );
+	
+	epp_clear_timeout(pb);
+	w_ctr(pb, r_ctr(pb) | 0x20);
+	w_ctr(pb, r_ctr(pb) | 0x10);
+	epp_clear_timeout(pb);
+
+	/*  Device isn't expecting an EPP read
+	 * and generates an IRQ.
+	 */
+	r_epp(pb);
+	udelay(20);
+
+	pb->irq = close_intr_election(irqs);
+	if (pb->irq == 0)
+		pb->irq = -1;	/* No interrupt detected */
+	
+	w_ctr(pb,pb->ctr);
+	
+	return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+	int irqs;
+
+#ifndef ADVANCED_DETECT
+	return -1;
+#endif
+
+	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
+	irqs = probe_irq_on();
+
+	if( pb->modes & PARPORT_MODE_ECR )
+		w_ecr(pb, 0x10 );
+
+	w_dtr(pb,0x00);
+	w_ctr(pb,0x00);
+	w_ctr(pb,0x0c);
+	udelay(5);
+	w_ctr(pb,0x0d);
+	udelay(5);
+	w_ctr(pb,0x0c);
+	udelay(25);
+	w_ctr(pb,0x08);
+	udelay(25);
+	w_ctr(pb,0x0c);
+	udelay(50);
+
+	pb->irq = probe_irq_off(irqs);
+	if (pb->irq <= 0)
+		pb->irq = -1;	/* No interrupt detected */
+	
+	w_ctr(pb,pb->ctr);
+	
+	return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When LP_ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+	if( pb->modes & PARPORT_MODE_ECR )
+		pb->irq = programmable_irq_support(pb);
+
+	if( pb->modes & PARPORT_MODE_ECP )
+		pb->irq = irq_probe_ECP(pb);
+			
+	if( pb->irq == -1 && (pb->modes & PARPORT_MODE_ECPEPP)){
+		w_ecr(pb,0x80);
+		pb->irq = irq_probe_EPP(pb);
+		w_ecr(pb,pb->ecr);
+	}
+
+	epp_clear_timeout(pb);
+
+	if( pb->irq == -1 && (pb->modes & PARPORT_MODE_EPP))
+		pb->irq = irq_probe_EPP(pb);
+
+	epp_clear_timeout(pb);
+
+	if( pb->irq == -1 )
+		pb->irq = irq_probe_SPP(pb);
+
+	return pb->irq;
+}
+
+
+int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
+{
+	/* Check some parameters */
+	if (dma < -2) {
+		printk("parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
+		return 0;
+	}
+
+	if (irq < -2) {
+		printk("parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
+		return 0;
+	}
+	
+	/* Init our structure */
+ 	memset(pb, 0, sizeof(struct parport));
+	pb->base = base;
+	pb->irq = irq;
+	pb->dma = dma;
+	pb->modes = 0;
+ 	pb->next = NULL;
+	pb->devices = pb->cad = pb->lurker = NULL;
+	pb->flags = 0;
+
+	/* Before we start, set the control registers to something sensible. */
+	pb->ecr = 0xc;
+	pb->ctr = 0xc;
+
+	pb->name = kmalloc(15, GFP_KERNEL);
+	if (!pb->name) {
+		printk("parport: memory squeeze\n");
+		return 0;
+	}
+	sprintf(pb->name, "parport%d", count);
+
+	if (!parport_SPP_supported(pb)) {
+		epp_clear_timeout(pb);
+		if (!parport_SPP_supported(pb)) {
+			kfree(pb->name);
+			return 0;
+		}
+	}
+
+	pb->modes |= PARPORT_MODE_SPP; 	/* All ports support SPP mode. */
+	pb->modes |= parport_ECR_present(pb);	
+	pb->modes |= parport_ECP_supported(pb);
+	pb->modes |= parport_PS2_supported(pb);
+	pb->modes |= parport_ECPPS2_supported(pb);
+	pb->modes |= parport_EPP_supported(pb);
+	pb->modes |= parport_ECPEPP_supported(pb);
+
+	/* Now register regions */
+	if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) && 
+	    (check_region(pb->base, 8))) {
+		printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
+		pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
+	}
+	pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
+
+	request_region(pb->base, pb->size, pb->name);
+	if (pb->modes & PARPORT_MODE_ECR)
+		request_region(pb->base+0x400, 3, pb->name);
+
+	/* DMA check */
+	if (pb->modes & PARPORT_MODE_ECP) {
+		if (pb->dma == -1)
+			pb->dma = parport_dma_probe(pb);
+		else if (pb->dma == -2)
+			pb->dma = -1;
+	}
+
+	/* IRQ check */
+	if (pb->irq == -1)
+		pb->irq = parport_irq_probe(pb);
+	else if (pb->irq == -2)
+		pb->irq = -1;
+
+	return 1;
+}
+
+#ifndef MODULE
+static int parport_setup_ptr = 0;
+
+void parport_setup(char *str, int *ints)
+{
+	if (ints[0] == 0 || ints[1] == 0) {
+		/* Disable parport if "parport=" or "parport=0" in cmdline */
+		io[0] = -2; 
+		return;
+	}
+	if (parport_setup_ptr < PARPORT_MAX) {
+		io[parport_setup_ptr] = ints[1];
+		if (ints[0]>1) {
+			irq[parport_setup_ptr] = ints[2];
+			if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+		}
+		parport_setup_ptr++;
+	} else {
+		printk(KERN_ERR "parport=0x%x", ints[1]);
+		if (ints[0]>1) {
+			printk(",%d", ints[2]);
+			if (ints[0]>2) printk(",%d", ints[3]);
+		}
+		printk(" ignored, too many ports.\n");
+	}
+}
+#endif
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+extern void parport_probe_one(struct parport *port);
+#endif
+
+#ifdef MODULE
+MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
+
+int init_module(void)
+#else
+int pnp_parport_init(void)
+#endif				/* MODULE */
+{
+	struct parport *pb;
+
+	printk(KERN_INFO "Parallel port sharing: %s\n",
+	       "$Revision: 1.1.2.4 $");
+
+	if (io[0] == -2) return 1; 
+
+	/* Register /proc/parport */
+	parport_proc_register(NULL);
+
+	/* Run probes to ensure parport does exist */
+#define PORT(a,b,c) \
+		if ((pb = parport_register_port((a), (b), (c))))  \
+		        parport_destroy(pb); 
+	if (io[0]) {
+		/* If the user specified any ports, use them */
+		int i;
+		for (i = 0; io[i] && i < PARPORT_MAX; i++) {
+			PORT(io[i], irq[i], dma[i]);
+		}
+	} else {
+		/* Go for the standard ports. */
+		PORT(0x378, -1, -1);
+		PORT(0x278, -1, -1);
+		PORT(0x3bc, -1, -1);
+#undef PORT
+	}
+
+	for (pb = parport_enumerate(); pb; pb = pb->next)
+		parport_probe_one(pb);
+
+	return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	struct parport *port, *next;
+   
+	for (port = parport_enumerate(); port; port = next) {
+		next = port->next;
+		parport_destroy(port);
+		parport_proc_unregister(port);
+		kfree(port->name);
+		kfree(port);
+	}
+	
+	parport_proc_unregister(NULL);
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_destroy);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+
+#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
+EXPORT_SYMBOL(parport_probe);
+EXPORT_SYMBOL(parport_probe_one);
+#endif
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_ll_io.h linux/drivers/pnp/parport_ll_io.h
--- v2.1.32/linux/drivers/pnp/parport_ll_io.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/parport_ll_io.h	Wed Apr  9 21:30:30 1997
@@ -0,0 +1,21 @@
+/* $Id: parport_ll_io.h,v 1.1.2.1 1997/03/26 13:01:09 phil Exp $ 
+ * David Campbell's "favourite IO routines" for parallel ports
+ */
+
+#define r_dtr(x)	inb((x)->base)
+#define r_str(x)	inb((x)->base+1)
+#define r_ctr(x)	inb((x)->base+2)
+#define r_epp(x)	inb((x)->base+4)
+#define r_fifo(x)	inb((x)->base+0x400)
+#define r_ecr(x)	inb((x)->base+0x402)
+#define r_cnfgA(x)	inb((x)->base+0x400)
+#define r_cnfgB(x)	inb((x)->base+0x401)
+
+#define w_dtr(x,y)	outb((y), (x)->base)
+#define w_str(x,y)	outb((y), (x)->base+1)
+#define w_ctr(x,y)	outb((y), (x)->base+2)
+#define w_epp(x,y)	outb((y), (x)->base+4)
+#define w_fifo(x,y)	outb((y), (x)->base+0x400)
+#define w_ecr(x,y)	outb((y), (x)->base+0x402)
+#define w_cnfgA(x,y)	outb((y), (x)->base+0x400)
+#define w_cnfgB(x,y)	outb((y), (x)->base+0x401)
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c
--- v2.1.32/linux/drivers/pnp/parport_probe.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/parport_probe.c	Wed Apr  9 21:30:31 1997
@@ -0,0 +1,266 @@
+/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $ 
+ * Parallel port device probing code
+ * 
+ * Authors:    Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
+ *             Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+
+#include <linux/lp.h>
+
+#include <asm/uaccess.h>
+
+#undef DEBUG_PROBE
+
+static inline int read_nibble(struct parport *port) 
+{
+	unsigned char i;
+	i = parport_r_status(port)>>3;
+	i&=~8;
+	if ( ( i & 0x10) == 0) i|=8;
+	return(i & 0x0f);
+}
+
+static void read_terminate(struct parport *port) {
+	parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+	/* SelectIN high, AutoFeed low */
+	if (parport_wait_peripheral(port, 0x80, 0)) 
+		/* timeout, SelectIN high, Autofeed low */
+		return;
+	parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+	/* AutoFeed high */
+	parport_wait_peripheral(port, 0x80, 0x80);
+	/* no timeout possible, Autofeed low, SelectIN high */
+	parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+	return;
+}
+
+static long read_polled(struct parport *port, char *buf, 
+			   unsigned long length)
+{
+	int i;
+	char *temp=buf;
+	int count = 0;
+	unsigned char z=0;
+	unsigned char Byte=0;
+
+	for (i=0; ; i++) {
+		parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+		if (parport_wait_peripheral(port, 0x40, 0)) {
+			printk("%s: read1 timeout.\n", port->name);
+			parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+			break;
+		}
+		z = read_nibble(port);
+		parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+		if (parport_wait_peripheral(port, 0x40, 0x40)) {
+			printk("%s: read2 timeout.\n", port->name);
+			break;
+		}
+		if (( i & 1) != 0) {
+			Byte= (Byte | z<<4);
+			if (temp) 
+				*(temp++) = Byte; 
+			if (count++ == length)
+				temp = NULL;
+			/* Does the error line indicate end of data? */
+			if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP) 
+				break;
+		} else Byte=z;
+	}
+	read_terminate(port);
+	return count; 
+}
+
+static struct wait_queue *wait_q = NULL;
+
+static int wakeup(void *ref)
+{
+	struct ppd **dev = (struct ppd **)ref;
+	
+	if (!wait_q || parport_claim(*dev))
+		return 1;
+
+	wake_up(&wait_q);
+	return 0;
+}
+
+int parport_probe(struct parport *port, char *buffer, int len)
+{
+	struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
+						  NULL, wakeup, NULL,
+						  PARPORT_DEV_TRAN, &dev);
+
+	int result = 0;
+
+	if (!dev) {
+		printk("%s: unable to register for probe.\n", port->name);
+		return -EINVAL;
+	}
+
+	if (parport_claim(dev)) {
+		sleep_on(&wait_q);
+		wait_q = NULL;
+	}
+
+	switch (parport_ieee1284_nibble_mode_ok(port, 4)) {
+	case 1:
+		current->state=TASK_INTERRUPTIBLE;
+		current->timeout=jiffies+1;
+		schedule();	/* HACK: wait 10ms because printer seems to
+				 * ack wrong */
+		result = read_polled(port, buffer, len);
+		break;
+	case 0:
+		result = -EIO;
+		break;
+	}
+
+	parport_release(dev);
+	parport_unregister_device(dev);
+
+	return result;
+}
+
+static struct {
+	char *token;
+	char *descr;
+} classes[] = {
+	{ "",        "Legacy device" },
+	{ "PRINTER", "Printer" }, 
+	{ "MODEM",   "Modem" },
+	{ "NET",     "Network device" },
+	{ "HDC",     "Hard disk" },
+	{ "PCMCIA",  "PCMCIA" },
+	{ "MEDIA",   "Multimedia device" },
+	{ "FDC",     "Floppy disk" },
+	{ "PORTS",   "Ports" },
+	{ "SCANNER", "Scanner" },
+	{ "DIGICAM", "Digital camera" },
+	{ "",        "Unknown device" },
+	{ "",        "Unspecified" }, 
+	{ NULL,      NULL }
+};
+
+static char *strdup(char *str)
+{
+	int n = strlen(str)+1;
+	char *s = kmalloc(n, GFP_KERNEL);
+	if (!s) return NULL;
+	return strcpy(s, str);
+}
+
+static void parse_data(struct parport *port, char *str)
+{
+	char *txt = kmalloc(strlen(str)+1, GFP_KERNEL);
+	char *p = txt, *q; 
+	int guessed_class = PARPORT_CLASS_UNSPEC;
+
+	if (!txt) {
+		printk("%s probe: memory squeeze\n", port->name);
+		return;
+	}
+	strcpy(txt, str);
+	while (p) {
+		char *sep; 
+		q = strchr(p, ';');
+		if (q) *q = 0;
+		sep = strchr(p, ':');
+		if (sep) {
+			char *u = p;
+			*(sep++) = 0;
+			while (*u) {
+				*u = toupper(*u);
+				u++;
+			}
+			if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) {
+				port->probe_info.mfr = strdup(sep);
+			} else if (!strcmp(p, "MDL") || !strcmp(p, "MODEL")) {
+				port->probe_info.model = strdup(sep);
+			} else if (!strcmp(p, "CLS") || !strcmp(p, "CLASS")) {
+				int i;
+				for (u = sep; *u; u++)
+					*u = toupper(*u);
+				for (i = 0; classes[i].token; i++) {
+					if (!strcmp(classes[i].token, sep)) {
+						port->probe_info.class = i;
+						goto rock_on;
+					}
+				}
+				printk(KERN_WARNING "%s probe: warning, class '%s' not understood.\n", port->name, sep);
+				port->probe_info.class = PARPORT_CLASS_OTHER;
+			} else if (!strcmp(p, "CMD") || !strcmp(p, "COMMAND SET")) {
+				/* if it speaks printer language, it's
+				   probably a printer */
+				if (strstr(sep, "PJL") || strstr(sep, "PCL"))
+					guessed_class = PARPORT_CLASS_PRINTER;
+			} else if (!strcmp(p, "DES") || !strcmp(p, "DESCRIPTION")) {
+				port->probe_info.description = strdup(sep);
+			}
+		}
+	rock_on:
+		if (q) p = q+1; else p=NULL;
+	}
+
+	/* If the device didn't tell us its class, maybe we have managed to
+	   guess one from the things it did say. */
+	if (port->probe_info.class == PARPORT_CLASS_UNSPEC)
+		port->probe_info.class = guessed_class;
+
+	kfree(txt);
+}
+
+static void pretty_print(struct parport *port)
+{
+	printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr);
+	if (port->probe_info.class) {
+		printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr);
+	}
+	printk("\n");
+}
+
+void parport_probe_one(struct parport *port)
+{
+	char *buffer = kmalloc(2048, GFP_KERNEL);
+	int r;
+
+	port->probe_info.model = "Unknown device";
+	port->probe_info.mfr = "Unknown vendor";
+	port->probe_info.description = NULL;
+	port->probe_info.class = PARPORT_CLASS_UNSPEC;
+
+	if (!buffer) {
+		printk(KERN_ERR "%s probe: Memory squeeze.\n", port->name);
+		return;
+	}
+
+	r = parport_probe(port, buffer, 2047);
+
+	if (r < 0) {
+		printk(KERN_INFO "%s: no IEEE-1284 device present.\n",
+		       port->name);
+		port->probe_info.class = PARPORT_CLASS_LEGACY;
+	} else if (r == 0) {
+		printk(KERN_INFO "%s: no ID data returned by device.\n",
+		       port->name);
+	} else {
+		buffer[r] = 0; 
+#ifdef DEBUG_PROBE
+		printk("%s id: %s\n", port->name, buffer+2);
+#endif
+		parse_data(port, buffer+2); 
+		pretty_print(port);
+	}
+	kfree(buffer);
+}
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_procfs.c linux/drivers/pnp/parport_procfs.c
--- v2.1.32/linux/drivers/pnp/parport_procfs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/parport_procfs.c	Wed Apr  9 21:30:31 1997
@@ -0,0 +1,126 @@
+/* $Id: parport_procfs.c,v 1.1.2.2 1997/03/26 17:50:36 phil Exp $
+ * Parallel port /proc interface code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <linux/parport.h>
+
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+
+/************************************************/
+static long proc_readparport(struct inode * inode, struct file * file,
+							 char * buf, unsigned long count)
+{
+	printk("proc_readparport\n");
+	return 0;
+}
+
+static long proc_writeparport(struct inode * inode, struct file * file,
+							  const char * buf, unsigned long count)
+{
+	printk("proc_writeparport\n");
+	
+	return 0;
+}
+
+static long long proc_parportlseek(struct inode * inode, struct file * file, 
+				long long offset, int orig)
+{
+    switch (orig) {
+    case 0:
+	file->f_pos = offset;
+	return(file->f_pos);
+    case 1:
+	file->f_pos += offset;
+	return(file->f_pos);
+    case 2:
+	return(-EINVAL);
+    default:
+	return(-EINVAL);
+    }
+}
+
+static struct file_operations proc_dir_operations = {
+    proc_parportlseek,	/* lseek   */
+    proc_readparport,	/* read	   */
+    proc_writeparport,	/* write   */
+    proc_readdir,	    /* readdir */
+    NULL,		/* poll    */
+    NULL,		/* ioctl   */
+    NULL,		/* mmap	   */
+    NULL,		/* no special open code	   */
+    NULL,		/* no special release code */
+    NULL		/* can't fsync */
+};
+
+/************************************************/
+static struct inode_operations parport_proc_dir_inode_operations = {
+	&proc_dir_operations,	/* default net directory file-ops */
+	NULL,			/* create */
+	proc_lookup,	/* 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 */
+};
+
+static struct proc_dir_entry proc_root_parport = {
+	PROC_PARPORT, 7, "parport",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+	0, &parport_proc_dir_inode_operations,
+	NULL, NULL,
+	NULL, &proc_root, NULL,
+	NULL, NULL
+};
+#endif
+
+int parport_proc_register(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+	return proc_register(&proc_root, &proc_root_parport);
+#else
+	return 0;
+#endif
+}
+
+void parport_proc_unregister(struct parport *pp)
+{
+#if defined(CONFIG_PROC_FS) && defined(NOT_DEFINED)
+	if( pp ){
+		proc_unregister(&proc_root_parport, pp->proc_dir->low_ino);
+		kfree(pp->proc_dir);
+	}else{
+		proc_unregister(&proc_root, proc_root_parport.low_ino);
+	}
+#endif
+}
diff -u --recursive --new-file v2.1.32/linux/drivers/pnp/parport_share.c linux/drivers/pnp/parport_share.c
--- v2.1.32/linux/drivers/pnp/parport_share.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/pnp/parport_share.c	Wed Apr  9 21:30:31 1997
@@ -0,0 +1,461 @@
+/* $Id: parport_share.c,v 1.1.2.4 1997/04/01 18:19:11 phil Exp $
+ * Parallel-port resource manager code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *	    Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include "parport_ll_io.h"
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* from parport_init.c */
+extern int initialize_parport(struct parport *, unsigned long base, 
+			      int irq, int dma, int count);
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+	return portlist;
+}
+
+static void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* NULL function - Does nothing */
+	return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma)
+{
+	struct parport new, *tmp;
+
+	/* Check for a previously registered port.
+	 * NOTE: we will ignore irq and dma if we find a previously
+	 * registered device.
+	 */
+	for (tmp = portlist; tmp; tmp = tmp->next) {
+		if (tmp->base == base)
+			return tmp;
+	}
+
+	/* Has someone grabbed the address yet? */
+	if (check_region(base, 3))
+		return NULL;
+
+	if (!initialize_parport(&new,base,irq,dma,portcount))
+		return NULL;
+					   
+	if (new.dma >= 0) {
+		if (request_dma(new.dma, new.name)) {
+			printk(KERN_INFO "%s: unable to claim DMA %d\n", 
+			       new.name, new..dma);
+			release_region(new.base, new.size);
+			if( new.modes & PARPORT_MODE_ECR )
+				release_region(new.base+0x400, 3);
+			kfree(new.name);
+			return NULL;
+		}
+	}
+
+	tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+	if (!tmp) {
+		printk(KERN_WARNING "parport: memory squeeze\n");
+		release_region(new.base, new.size);
+		if( new.modes & PARPORT_MODE_ECR )
+			release_region(new.base+0x400, 3);
+		kfree(new.name);
+		return NULL;
+	}
+	memcpy(tmp, &new, sizeof(struct parport));
+
+	if (new.irq != -1) {
+		if (request_irq(new.irq, parport_null_intr_func,
+			  SA_INTERRUPT, new.name, tmp) != 0) {
+			printk(KERN_INFO "%s: unable to claim IRQ %d\n", 
+			       new.name, new.irq);
+			kfree(tmp);
+			release_region(new.base, new.size);
+			if( new.modes & PARPORT_MODE_ECR )
+				release_region(new.base+0x400, 3);
+			kfree(new.name);
+			return NULL;
+		}
+	}
+
+	/* Here we chain the entry to our list. */
+	if (portlist_tail)
+		portlist_tail->next = tmp;
+	portlist_tail = tmp;
+	if (!portlist)
+		portlist = tmp;
+
+	printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
+	if (tmp->irq >= 0)
+		printk(", irq %d", tmp->irq);
+	if (tmp->dma >= 0)
+		printk(", dma %d", tmp->dma);
+	printk(" [");
+	{
+		/* Ugh! */
+#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
+		int f = 0;
+		printmode(SPP);
+		printmode(PS2);
+		printmode(EPP);
+		printmode(ECP);
+		printmode(ECPEPP);
+		printmode(ECPPS2);
+#undef printmode
+	}
+	printk("]\n");
+	portcount++;
+
+	/* Restore device back to default conditions */
+	if (tmp->modes & PARPORT_MODE_ECR)
+		w_ecr(tmp, tmp->ecr);
+	w_ctr(tmp, tmp->ctr);
+
+	tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
+	return tmp;
+}
+
+void parport_destroy(struct parport *port)
+{
+	/* Dangerous to try destroying a port if its friends are nearby. */
+	if (port->devices) {
+		printk("%s: attempt to release active port\n", port->name);
+		return;		/* Devices still present */
+	}
+
+	/* No point in further destroying a port that already lies in ruins. */
+	if (port->flags & PARPORT_FLAG_COMA) 
+		return;
+
+	/* Now clean out the port entry */
+	if (port->irq >= 0)
+		free_irq(port->irq, port);
+	if (port->dma >= 0)
+		free_dma(port->dma);
+	release_region(port->base, port->size);
+	if( port->modes & PARPORT_MODE_ECR )
+		release_region(port->base+0x400, 3);
+	port->flags |= PARPORT_FLAG_COMA; 
+}
+
+struct ppd *parport_register_device(struct parport *port, const char *name,
+				   callback_func pf, callback_func kf,
+				   irq_handler_func irq_func, int flags,
+				   void *handle)
+{
+	struct ppd *tmp;
+
+	/* We only allow one lurker device (eg PLIP) */
+	if (flags & PARPORT_DEV_LURK) {
+		if (port->lurker) {
+			printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+				   port->name, name);
+			return NULL;
+		}
+		if (!pf || !kf) {
+			printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
+				   ,port->name, name);
+			return NULL;
+		}
+	}
+
+	/* We may need to claw back the port hardware. */
+	if (port->flags & PARPORT_FLAG_COMA) {
+		if (check_region(port->base, 3)) {
+			return NULL;
+		}
+		request_region(port->base, port->size, port->name);
+		if( port->modes & PARPORT_MODE_ECR )
+			request_region(port->base+0x400, 3,port->name);
+			
+		if (port->dma >= 0) {
+			if (request_dma(port->dma, port->name)) {
+				release_region(port->base, port->size);
+				if( port->modes & PARPORT_MODE_ECR )
+					release_region(port->base+0x400, 3);
+				return NULL;
+			}
+		}
+		if (port->irq != -1) {
+			if (request_irq(port->irq, 
+							parport_null_intr_func,
+							SA_INTERRUPT, port->name,
+							port) != 0) {
+				release_region(port->base, port->size);
+				if( port->modes & PARPORT_MODE_ECR )
+					release_region(port->base+0x400, 3);
+				if (port->dma >= 0)
+					free_dma(port->dma);
+				return NULL;
+			}
+		}
+		port->flags &= ~PARPORT_FLAG_COMA;
+	}
+
+
+	tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
+	tmp->name = (char *) name;
+	tmp->port = port;
+	tmp->preempt = pf;
+	tmp->wakeup = kf;
+	tmp->private = handle;
+	tmp->irq_func = irq_func;
+	tmp->ctr = port->ctr;
+	tmp->ecr = port->ecr;
+
+	/* Chain this onto the list */
+	tmp->prev = NULL;
+	tmp->next = port->devices;
+	if (port->devices)
+		port->devices->prev = tmp;
+	port->devices = tmp;
+
+	if (flags & PARPORT_DEV_LURK)
+		port->lurker = tmp;
+
+	inc_parport_count();
+
+	return tmp;
+}
+
+void parport_unregister_device(struct ppd *dev)
+{
+	struct parport *port;
+
+	if (!dev) {
+		printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+		return;
+	}
+
+	port = dev->port;
+
+	if (port->cad == dev) {
+		printk(KERN_INFO "%s: refused to unregister currently active device %s\n",
+			   port->name, dev->name);
+		return;
+	}
+
+	if (port->lurker == dev)
+		port->lurker = NULL;
+
+	if (dev->next)
+		dev->next->prev = dev->prev;
+	if (dev->prev)
+		dev->prev->next = dev->next;
+	else
+		port->devices = dev->next;
+
+	kfree(dev);
+
+	dec_parport_count();
+
+	/* If there are no more devices, put the port to sleep. */
+	if (!port->devices)
+		parport_destroy(port);
+
+	return;
+}
+
+int parport_claim(struct ppd *dev)
+{
+	struct ppd *pd1;
+
+	if (dev->port->cad == dev) {
+		printk(KERN_INFO "%s: %s already owner\n",
+			   dev->port->name,dev->name);
+		return 0;
+	}
+
+	/* Preempt any current device */
+	pd1 = dev->port->cad;
+	if (dev->port->cad) {
+		if (dev->port->cad->preempt) {
+			/* Now try to preempt */
+			if (dev->port->cad->preempt(dev->port->cad->private))
+				return -EAGAIN;
+
+			/* Save control registers */
+			if (dev->port->modes & PARPORT_MODE_ECR)
+				dev->port->cad->ecr = dev->port->ecr = 
+					r_ecr(dev->port);
+			dev->port->cad->ctr = dev->port->ctr =
+				r_ctr(dev->port);
+		} else
+			return -EAGAIN;
+	}
+
+	/* Watch out for bad things */
+	if (dev->port->cad != pd1) {
+		printk(KERN_WARNING "%s: death while preempting %s\n",
+		       dev->port->name, dev->name);
+		if (dev->port->cad)
+			return -EAGAIN;
+	}
+
+	/* Now we do the change of devices */
+	dev->port->cad = dev;
+
+	if (dev->port->irq >= 0) {
+		free_irq(dev->port->irq, dev->port);
+		request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+			    parport_null_intr_func, SA_INTERRUPT, dev->name,
+			    dev->port);
+	}
+
+	/* Restore control registers */
+	if (dev->port->modes & PARPORT_MODE_ECR)
+		if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
+	if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
+
+	return 0;
+}
+
+void parport_release(struct ppd *dev)
+{
+	struct ppd *pd1;
+
+	/* Make sure that dev is the current device */
+	if (dev->port->cad != dev) {
+		printk(KERN_WARNING "%s: %s tried to release parport when not owner\n",
+			   dev->port->name, dev->name);
+		return;
+	}
+	dev->port->cad = NULL;
+
+	/* Save control registers */
+	if (dev->port->modes & PARPORT_MODE_ECR)
+		dev->ecr = dev->port->ecr = r_ecr(dev->port);
+	dev->ctr = dev->port->ctr = r_ctr(dev->port);
+	
+	if (dev->port->irq >= 0) {
+		free_irq(dev->port->irq, dev->port);
+		request_irq(dev->port->irq, parport_null_intr_func,
+					SA_INTERRUPT, dev->port->name, dev->port);
+ 	}
+
+	/* Walk the list, offering a wakeup callback to everybody other
+	 * than the lurker and the device that called us.
+	 */
+	for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+		if (!(pd1->flags & PARPORT_DEV_LURK)) {
+			if (pd1->wakeup) {
+				pd1->wakeup(pd1->private);
+				if (dev->port->cad)
+					return;
+			}
+		}
+	}
+
+	for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+		if (!(pd1->flags & PARPORT_DEV_LURK)) {
+			if (pd1->wakeup) {
+				pd1->wakeup(pd1->private);
+				if (dev->port->cad)
+					return;
+			}
+		}
+	}
+
+	/* Now give the lurker a chance.
+	 * There should be a wakeup callback because we checked for it
+	 * at registration.
+	 */
+	if (dev->port->lurker && (dev->port->lurker != dev)) {
+		if (dev->port->lurker->wakeup) {
+			dev->port->lurker->wakeup(dev->port->lurker->private);
+			return;
+		}
+		printk(KERN_DEBUG
+		       "%s (%s): lurker's wakeup callback went away!\n",
+		       dev->port->name, dev->name);
+	}
+}
+
+/* The following read funktions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */ 
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask, 
+	unsigned char result)
+{
+	int counter=0;
+	unsigned char status; 
+	
+	do {
+		status = parport_r_status(port);
+		udelay(25);
+		counter++;
+		if (need_resched)
+			schedule();
+	} while ( ((status & mask) != result) && (counter < 20) );
+	if ( (counter == 20) && ((status & mask) != result) ) { 
+		current->state=TASK_INTERRUPTIBLE;
+		current->timeout=jiffies+4;
+		schedule(); /* wait for 4 scheduler runs (40ms) */
+		status = parport_r_status(port);
+		if ((status & mask) != result) return 1; /* timeout */
+	}
+	return 0; /* okay right response from device */
+}		
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time).  There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
+{
+	parport_w_data(port, mode);
+	udelay(5);		
+	parport_w_ctrl(port, parport_r_ctrl(port) & ~8);  /* SelectIN low */
+	parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+	if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+		parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+		return 0; /* first stage of negotiation failed, 
+                           * no IEEE1284 compliant device on this port 
+                           */ 
+	}
+	parport_w_ctrl(port, parport_r_ctrl(port) | 1);      /* Strobe high */
+	udelay(5);				     /* Strobe wait */
+	parport_w_ctrl(port, parport_r_ctrl(port) & ~1);     /* Strobe low */
+	udelay(5);
+	parport_w_ctrl(port, parport_r_ctrl(port) & ~2);     /* AutoFeed low */
+	return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.32/linux/drivers/scsi/ChangeLog.ncr53c8xx	Mon Mar 17 14:54:29 1997
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue Apr  8 13:12:55 1997
@@ -1,3 +1,37 @@
+Sat Apr 5  13:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 1.18d
+	- Probe NCR pci device ids in reverse order if asked by user from 
+	  the boot command line. Suggested by Richard Waltham.
+	- Make a separate function that prints out verbose information on 
+	  severe error (assumed from hardware).
+	- Add the transfer period factor and the max commands per lun value 
+	  to the proc info data. If debug flags are set or verbosity is 
+	  greater than 1, debug flags and verbosity are returned in proc  
+	  info data.
+	- Update the documentation.
+
+Thu Mar 20 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 1.18c
+	- Add special features support for NCR53C885 and NCR53C896 chip.
+	  Quite obvious, but untested, and based on the fact that:
+	  The 885 supports same features as the 875.
+	  The 896 is a 64 bits PCI version of the 895.
+	- Improve recovery from SCSI GROSS ERRORS.
+	  I can get such errors by making the driver negotiate offset 8 with 
+	  a disk and setting the ncr chip to a lower offset value.
+	  I got bunches of errors that have been gracefully recovered by 
+	  the driver.
+	  The driver now uses its timer handler in order to wait 2 sec. for 
+	  devices to settle after SCSI reset and so does not uselessly freeze 
+	  the system with interrupt masked for seconds.
+	- Enable 'burst op code fetch' and 'read line' for 815 chips.
+	- Use a 2 commands queue depth instead of 1 for devices that does 
+          not support tagged command queuing.
+	- The ULTRA timing flag setting was based on the output resulting 
+	  period factor of the ncr and not on the negotiated one.
+	  This flag setting was wrong only for 24 ns negotiated period factor.
+	- Some other minor changes and cleanups.
+
 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.
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.32/linux/drivers/scsi/Config.in	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/Config.in	Wed Apr  9 21:30:31 1997
@@ -71,9 +71,16 @@
   fi
 fi
 if [ "$CONFIG_MCA" = "y" ]; then
+  bool 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA
+fi
+if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
+  dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PNP_PARPORT
+  if [ "$CONFIG_SCSI_PPA" != "n" ]; then
+    int  '  Pedantic EPP-checking'   CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
+    int  '  EPP timeout'             CONFIG_SCSI_PPA_EPP_TIME 128
+  fi
   dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
 fi
-dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.32/linux/drivers/scsi/README.ncr53c8xx	Mon Mar 17 14:54:29 1997
+++ linux/drivers/scsi/README.ncr53c8xx	Tue Apr  8 13:12:55 1997
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-27 February 1997
+6 April 1997
 ===============================================================================
 
 1.  Introduction
@@ -34,7 +34,11 @@
 13. Control commands under linux-1.2.13
 14. Known problems
       14.1 Tagged commands with Iomega Jaz device
+      14.2 Device names change when another controller is added
 15. SCSI problem troubleshooting
+16. Synchonous transfer negotiation tables
+      16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+      16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
 
 ===============================================================================
 
@@ -56,6 +60,18 @@
 
           http://www.symbios.com/
 
+SCSI standard documentations are available at SYMBIOS ftp server:
+
+          ftp://ftp.symbios.com/
+
+Usefull SCSI tools written by Eric Youngdale are available at tsx-11:
+
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsiinfo-X.Y.tar.gz
+          ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/scsidev-X.Y.tar.gz
+
+These tools are not ALPHA but quite clean and work quite well.
+It is essential you have the 'scsiinfo' package.
+
 This short documentation only describes the features of the NCR53C8XX
 driver, configuration parameters and control commands available
 through the proc SCSI file system read / write operations.
@@ -186,6 +202,8 @@
   Chip NCR53C810, device id 0x1, revision id 0x2
   IO port address 0x6000, IRQ number 10
   Using memory mapped IO at virtual address 0x282c000
+  Synchronous transfer period 25, max commands per lun 4
+
 Profiling information:
   num_trans    = 18014
   num_kbytes   = 671314
@@ -554,6 +572,11 @@
     irqm:1	same as initial settings (assumed BIOS settings)
     irqm:2	always totem pole
 
+Reverse probe
+    revprob:n   probe chip ids from the PCI configuration in this order:
+                810, 815, 820, 860, 875, 885, 895, 896
+    revprob:y   probe chip ids in the reverse order.
+
 Boot fail safe
     safe:y	load the following assumed fail safe initial setup
 
@@ -563,6 +586,7 @@
   special features		disabled		specf:n
   ultra scsi			disabled		ultra:n
   force sync negotiation	disabled		fsn:n
+  reverse probe			disabled		revprob:n
   verbosity level		2			verb:2
   tagged command queuing	disabled		tags:0
   synchronous negotiation	disabled		sync:255
@@ -579,8 +603,8 @@
 If the driver has been configured with default options, the equivalent 
 boot setup is:
 
-    ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,verb:2,tags:0\
-              sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+             tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
 
 For an installation diskette or a safe but not fast system,
 boot setup can be:
@@ -592,8 +616,8 @@
 
 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
+   ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+             tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
 
 The driver prints its actual setup when verbosity level is 2. You can try 
 "ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" 
@@ -712,9 +736,9 @@
 
 You must untar the distribution with the following command:
 
-	tar zxvf ncrBsd2Linux-1.18b-src.tar.gz
+	tar zxvf ncrBsd2Linux-1.18d-src.tar.gz
 
-The sub-directory ncr53c8xx-1.18b will be created. Change to this directory.
+The sub-directory ncr53c8xx-1.18d will be created. Change to this directory.
 
 
 12.2 Installation procedure
@@ -770,6 +794,23 @@
 timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
 current timeout values.
 
+14.2 Device names change when another controller is added.
+
+When you add a new NCR53C8XX chip based controller to a system that already 
+has one or more controllers of this family, it may happen that the order 
+the driver registers them to the kernel causes problems due to device 
+name changes.
+SDMS BIOS version 4 allows you to define the order the BIOS will scan the 
+scsi boards and stores this information for next reboots. Unfortunately, the 
+driver is not currently able to read this information and so may register 
+controllers in a different order.
+
+If you have such a problem, you can:
+
+- Ask the driver to probe chip ids in reverse order from the boot command
+  line: ncr53c8xx=revprob:y
+- Make appropriate changes in the fstab.
+- Use the 'scsidev' tool from Eric Youngdale.
 
 15. SCSI problem troubleshooting
 
@@ -819,6 +860,98 @@
 Once you have found the device and the feature that cause problems, just 
 disable that feature for that device.
 
+
+16. Synchonous transfer negotiation tables
+
+Tables below have been created by calling the routine the driver uses
+for synchronisation negotiation timing calculation and chip setting.
+The first table corresponds to Ultra chips 53875 and 53C860 with 80 MHz 
+clock and 5 clock divisors.
+The second one has been calculated by setting the scsi clock to 40 Mhz 
+and using 4 clock divisors and so applies to all NCR53C8XX chips in fast 
+SCSI-2 mode.
+
+Periods are in nano-seconds and speeds are in Mega-transfers per second.
+1 Mega-transfers/second means 1 MB/s with 8 bits SCSI and 2 MB/s with 
+Wide16 SCSI.
+
+16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 12       50       20.000       50       20.000
+ 13       52       19.230       62       16.000
+ 14       56       17.857       62       16.000
+ 15       60       16.666       62       16.000
+ 16       64       15.625       75       13.333
+ 17       68       14.705       75       13.333
+ 18       72       13.888       75       13.333
+ 19       76       13.157       87       11.428
+ 20       80       12.500       87       11.428
+ 21       84       11.904       87       11.428
+ 22       88       11.363       93       10.666
+ 23       92       10.869       93       10.666
+ 24       96       10.416      100       10.000
+ 25      100       10.000      100       10.000
+ 26      104        9.615      112        8.888
+ 27      108        9.259      112        8.888
+ 28      112        8.928      112        8.888
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
+
+
+16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+
+ ----------------------------------------------
+ Negotiated                     NCR settings
+ Factor   Period   Speed        Period   Speed
+ ------   ------   ------       ------   ------
+ 25      100       10.000      100       10.000
+ 26      104        9.615      125        8.000
+ 27      108        9.259      125        8.000
+ 28      112        8.928      125        8.000
+ 29      116        8.620      125        8.000
+ 30      120        8.333      125        8.000
+ 31      124        8.064      125        8.000
+ 32      128        7.812      131        7.619
+ 33      132        7.575      150        6.666
+ 34      136        7.352      150        6.666
+ 35      140        7.142      150        6.666
+ 36      144        6.944      150        6.666
+ 37      148        6.756      150        6.666
+ 38      152        6.578      175        5.714
+ 39      156        6.410      175        5.714
+ 40      160        6.250      175        5.714
+ 41      164        6.097      175        5.714
+ 42      168        5.952      175        5.714
+ 43      172        5.813      175        5.714
+ 44      176        5.681      187        5.333
+ 45      180        5.555      187        5.333
+ 46      184        5.434      187        5.333
+ 47      188        5.319      200        5.000
+ 48      192        5.208      200        5.000
+ 49      196        5.102      200        5.000
 ===============================================================================
 End of NCR53C8XX driver README file
 
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.32/linux/drivers/scsi/ncr53c8xx.c	Mon Mar 17 14:54:30 1997
+++ linux/drivers/scsi/ncr53c8xx.c	Tue Apr  8 13:12:55 1997
@@ -40,7 +40,7 @@
 */
 
 /*
-**	27 February 1997, version 1.18b
+**	6 April 1997, version 1.18d
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -469,6 +469,7 @@
 	unsigned special_features : 1;
 	unsigned ultra_scsi	: 2;
 	unsigned force_sync_nego: 1;
+	unsigned reverse_probe: 1;
 	u_char	verbose;
 	u_char	default_tags;
 	u_short	default_sync;
@@ -476,7 +477,7 @@
 	u_char	burst_max;
 	u_char	led_pin;
 	u_char	max_wide;
-	u_char	settle_time;
+	u_char	settle_delay;
 	u_char	diff_support;
 	u_char	irqm;
 };
@@ -1344,8 +1345,8 @@
 	int	ncr_cache;		/* Cache test variable               */
 	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
 					/* that we can't put into the squeue */
+	u_long	settle_time;		/* Reset in progess		     */
 	u_char	release_stage;		/* Synchronisation stage on release  */
-	u_char	resetting;		/* Reset in progess		     */
 
 	/*-----------------------------------------------
 	**	Added field to support differences
@@ -1691,6 +1692,7 @@
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
+static	void	ncr_start_reset	(ncb_p np, int settle_delay);
 
 #ifdef SCSI_NCR_USER_COMMAND
 static	void	ncr_usercmd	(ncb_p np);
@@ -1718,16 +1720,6 @@
 **==========================================================
 */
 
-#if 0
-static char ident[] =
- 	"\n$Id: ncr.c,v 1.67 1996/03/11 19:36:07 se Exp $\n";
-static u_long	ncr_version = NCR_VERSION	* 11
-	+ (u_long) sizeof (struct ncb)	*  7
-	+ (u_long) sizeof (struct ccb)	*  5
-	+ (u_long) sizeof (struct lcb)	*  3
-	+ (u_long) sizeof (struct tcb)	*  2;
-#endif
-
 #ifdef SCSI_NCR_DEBUG
 static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
 #endif
@@ -3255,7 +3247,7 @@
 **	#define MAX_SCATTER parameter,
 **	it is filled in at runtime.
 **
-**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_IN)),
+**	SCR_JUMP ^ IFFALSE (WHEN (SCR_DATA_OUT)),
 **		PADDR (no_data),
 **	SCR_COPY (sizeof (u_long)),
 **		KVAR(SCRIPT_KVAR_JIFFIES),
@@ -3596,7 +3588,7 @@
 #define PRINT_LUN(np, target, lun) \
 printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
-static inline void PRINT_ADDR(Scsi_Cmnd *cmd)
+static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
 	struct host_data *host_data = (struct host_data *) cmd->host->hostdata;
 	ncb_p np                    = host_data->ncb;
@@ -3744,13 +3736,18 @@
 		np->clock_divn	= 5;
 		break;
 	case PCI_DEVICE_ID_NCR_53C875:
+	case PCI_DEVICE_ID_NCR_53C885:
 		np->maxwide = 1;
 		if (driver_setup.special_features)
 			np->maxoffs = 16;
 		np->clock_divn	= 5;
-		ncr_getclock(np, revision_id >= 2 ? 2 : 1);
+		if (device_id == PCI_DEVICE_ID_NCR_53C875)
+			ncr_getclock(np, revision_id >= 2 ? 2 : 1);
+		else
+			ncr_getclock(np, 2);
 		break;
 	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
 		np->maxwide = 1;
 		if (driver_setup.special_features)
 			np->maxoffs = 31;
@@ -3767,7 +3764,9 @@
 		if (revision_id < 0x10)
 			break;
 	case PCI_DEVICE_ID_NCR_53C875:
+	case PCI_DEVICE_ID_NCR_53C885:
 	case PCI_DEVICE_ID_NCR_53C895:
+	case PCI_DEVICE_ID_NCR_53C896:
 		if (driver_setup.special_features) {
 			OUTONB(nc_ctest2, 0x8);
 			np->paddr2 = INL(nc_scr0);
@@ -3794,7 +3793,7 @@
 		}
 		else
 			if (bootverbose > 1)
-				printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr);
+				printf("%s: on-board ram mapped at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr2);
 	}
 #endif /* !defined NCR_IOMAPPED */
 
@@ -3911,17 +3910,12 @@
 	**	After SCSI devices have been opened, we cannot
 	**	reset the bus safely, so we do it here.
 	**	Interrupt handler does the real work.
-	*/
-
-	OUTB (nc_scntl1, CRST);
-	DELAY (1000);
-
-	/*
 	**	Process the reset exception,
 	**	if interrupts are not enabled yet.
 	**	Then enable disconnects.
 	*/
 	save_flags(flags); cli();
+	ncr_start_reset(np, driver_setup.settle_delay);
 	ncr_exception (np);
 	restore_flags(flags);
 
@@ -3930,12 +3924,13 @@
 	/*
 	**	The middle-level SCSI driver does not
 	**	wait devices to settle.
+	**	Wait synchronously if more than 2 seconds.
 	*/
-	if (driver_setup.settle_time > 2)
+	if (driver_setup.settle_delay > 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);
+			ncr_name(np), driver_setup.settle_delay);
+		DELAY(1000000UL * driver_setup.settle_delay);
+	}
 
 	/*
 	**	Now let the generic SCSI driver
@@ -3945,8 +3940,8 @@
 	/*
 	**	start the timeout daemon
 	*/
-	ncr_timeout (np);
 	np->lasttime=0;
+	ncr_timeout (np);
 
 	/*
 	**  use SIMPLE TAG messages by default
@@ -4060,22 +4055,6 @@
 
 	/*---------------------------------------------
 	**
-	**   Reset SCSI bus
-	**
-	**	Interrupt handler does the real work.
-	**
-	**---------------------------------------------
-	*/
-#if 0
-	if (flags & SCSI_RESET) {
-		OUTB (nc_scntl1, CRST);
-		DELAY (1000);
-		return(COMPLETE);
-	}
-#endif
-
-	/*---------------------------------------------
-	**
 	**      Some shortcuts ...
 	**
 	**---------------------------------------------
@@ -4095,13 +4074,14 @@
 	/*---------------------------------------------------
 	**
 	**	Assign a ccb / bind cmd
-	**	If no free ccb, insert cmd into the waiting list.
+	**	If resetting or no free ccb,
+	**	insert cmd into the waiting list.
 	**
 	**----------------------------------------------------
 	*/
 	save_flags(flags); cli();
 
-        if (!(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
+        if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
 		insert_into_waiting_list(np, cmd);
 		restore_flags(flags);
 		return(DID_OK);
@@ -4500,6 +4480,40 @@
 /*==========================================================
 **
 **
+**	Start reset process.
+**	If reset in progress do nothing.
+**	The interrupt handler will reinitialize the chip.
+**	The timeout handler will wait for settle_time before 
+**	clearing it and so resuming command processing.
+**
+**
+**==========================================================
+*/
+static void ncr_start_reset(ncb_p np, int settle_delay)
+{
+	u_long flags;
+
+	save_flags(flags); cli();
+
+	if (!np->settle_time) {
+		if (bootverbose > 1)
+			printf("%s: resetting, command processing suspended for %d seconds\n",
+				ncr_name(np), settle_delay);
+		np->settle_time	= jiffies + settle_delay * HZ;
+		OUTB (nc_istat, SRST);
+		DELAY (1000);
+		OUTB (nc_istat, 0);
+		OUTW (nc_sien, RST);
+		OUTB (nc_scntl1, CRST);
+		DELAY (100);
+	}
+
+	restore_flags(flags);
+}
+
+/*==========================================================
+**
+**
 **	Reset the SCSI BUS.
 **	This is called from the generic SCSI driver.
 **
@@ -4518,23 +4532,19 @@
 
 	save_flags(flags); cli();
 /*
- * Return immediately from recursive call
+ * Return immediately if reset is in progress.
  */
-	if (np->resetting) {
+	if (np->settle_time) {
 		restore_flags(flags);
 		return SCSI_RESET_PUNT;
 	}
-	++np->resetting;
 /*
- * Perform chip reset, SCSI reset, wait 2 seconds
+ * Start the reset process.
+ * The script processor is then assumed to be stopped.
+ * Commands will now be queued in the waiting list until a settle 
+ * delay of 2 seconds will be completed.
  */
-	OUTB (nc_istat, SRST);
-	DELAY (1000);
-	OUTB (nc_istat, 0);
-	OUTB (nc_scntl1, CRST);
-	DELAY (100);
-	OUTB (nc_scntl1, 0);
-	DELAY (2000000);
+	ncr_start_reset(np, 2);
 /*
  * First, look in the wakeup list
  */
@@ -4554,25 +4564,23 @@
 	if (!found && retrieve_from_waiting_list(0, np, cmd))
 		found = 1;
 /*
- * Reinitialise the NCR and wake-up pending commands
+ * Wake-up all awaiting commands with DID_RESET.
  */
-	ncr_init(np, "scsi bus reset", HS_RESET);
-	np->disc = 1;
+	reset_waiting_list(np);
 /*
- * Complete awaiting commands with DID_RESET
+ * Wake-up all pending commands with HS_RESET -> DID_RESET.
  */
-	reset_waiting_list(np);
+	ncr_wakeup(np, HS_RESET);
 /*
- * If the involved command was not in a driver queue, complete it 
- * with DID_RESET, in order to keep it alive.
+ * If the involved command was not in a driver queue, and is 
+ * not in the waiting list, complete it with DID_RESET status,
+ * in order to keep it alive.
  */
-	if (!found && cmd && cmd->scsi_done) {
+	if (!found && cmd && !remove_from_waiting_list(np, cmd)) {
 		cmd->result = ScsiResult(DID_RESET, 0);
 		cmd->scsi_done(cmd);
 	}
 
-	--np->resetting;
-
 	restore_flags(flags);
 
 	return SCSI_RESET_SUCCESS;
@@ -4622,11 +4630,17 @@
 			break;
 		}
 	}
+
 	if (!found) {
 		restore_flags(flags);
 		return SCSI_ABORT_NOT_RUNNING;
 	}
 
+	if (np->settle_time) {
+		restore_flags(flags);
+		return SCSI_ABORT_SNOOZE;
+	}
+
 	/*
 	**	Disable reselect.
 	**      Remove it from startqueue.
@@ -5255,17 +5269,22 @@
 /**	NCR53C810			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) {
 		burst_max	= burst_max < 4 ? burst_max : 4;
+		if (driver_setup.special_features)
+			np->rv_dmode	=  ERL;		/* read line */
 	}
 	else
 /**	NCR53C815			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) {
 		burst_max	= burst_max < 4 ? burst_max : 4;
+		if (driver_setup.special_features)
+			np->rv_dmode	= BOF | ERL;	/* burst opcode fetch, read line */
 	}
 	else
 /**	NCR53C825			**/
 	if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) {
 		burst_max	= burst_max < 4 ? burst_max : 4;
-		np->rv_dmode	= 0x0a;	/* burst opcode fetch */
+		if (driver_setup.special_features)
+			np->rv_dmode	= BOF | ERL;	/* burst opcode fetch, read line */
 	}
 	else
 /**	NCR53C810A or NCR53C860		**/
@@ -5282,10 +5301,12 @@
 		}
 	}
 	else
-/**	NCR53C825A or NCR53C875 or NCR53C895		**/
+/**	NCR53C825A or NCR53C875 or NCR53C885 or NCR53C895 or NCR53C896	**/
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
 	    ChipDevice == PCI_DEVICE_ID_NCR_53C875 ||
-	    ChipDevice == PCI_DEVICE_ID_NCR_53C895) {
+	    ChipDevice == PCI_DEVICE_ID_NCR_53C885 ||
+	    ChipDevice == PCI_DEVICE_ID_NCR_53C895 ||
+	    ChipDevice == PCI_DEVICE_ID_NCR_53C896) {
 		if (!driver_setup.special_features)
 			burst_max	= burst_max < 4 ? burst_max : 4;
 		else {
@@ -5296,7 +5317,7 @@
 			np->rv_dcntl	= PFEN | CLSE;
 						/* prefetch, cache line size */
 			np->rv_ctest3	= WRIE;	/* write and invalidate */
-			np->rv_ctest5	= 0x20;	/* large dma fifo (0x20) */
+			np->rv_ctest5	= DFS;	/* large dma fifo (0x20) */
 		}
 	}
 /**	OTHERS				**/
@@ -5305,14 +5326,6 @@
 	}
 #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
 	 */
@@ -5612,7 +5625,7 @@
 	**	Compute and return sync parameters for the ncr
 	*/
 	*fakp		= fak - 4;
-	*scntl3p	= ((idiv+1) << 4) + (per < 100 ? 0x80 : 0);
+	*scntl3p	= ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
 
 #ifdef DEBUG
 printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
@@ -5964,6 +5977,23 @@
 	add_timer(&np->timer);
 
 	/*
+	**	If we are resetting the ncr, wait for settle_time before 
+	**	clearing it. Then command processing will be resumed.
+	*/
+	if (np->settle_time) {
+		if (np->settle_time <= thistime) {
+			if (bootverbose > 1)
+				printf("%s: command processing resumed\n", ncr_name(np));
+			save_flags(flags); cli();
+			np->settle_time	= 0;
+			np->disc	= 1;
+			requeue_waiting_list(np);
+			restore_flags(flags);
+		}
+		return;
+	}
+
+	/*
 	**	Since the generic scsi driver only allows us 0.5 second 
 	**	to perform abort of a command, we must look at ccbs about 
 	**	every 0.25 second.
@@ -5997,18 +6027,6 @@
 		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
-			**      processor will sleep on SEL_WAIT_RESEL.
-			**      But we have to check whether it died.
-			**      Let's wake it up.
-			*/
-
-			OUTB (nc_istat, SIGP);
-		}
-#endif
 
 		/*----------------------------------------------------
 		**
@@ -6087,6 +6105,75 @@
 
 /*==========================================================
 **
+**	log message for real hard errors
+**
+**	"ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ name (dsp:dbc)."
+**	"	      reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
+**
+**	exception register:
+**		ds:	dstat
+**		si:	sist
+**
+**	SCSI bus lines:
+**		so:	control lines as driver by NCR.
+**		si:	control lines as seen by NCR.
+**		sd:	scsi data lines as seen by NCR.
+**
+**	wide/fastmode:
+**		sxfer:	(see the manual)
+**		scntl3:	(see the manual)
+**
+**	current script command:
+**		dsp:	script adress (relative to start of script).
+**		dbc:	first word of script command.
+**
+**	First 16 register of the chip:
+**		r0..rf
+**
+**==========================================================
+*/
+
+static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
+{
+	u_int32	dsp;
+	int	script_ofs;
+	char	*script_name;
+	u_char	*script_base;
+	int	i;
+
+	dsp	= INL (nc_dsp);
+
+	if (dsp > np->p_script && dsp <= np->p_script + sizeof(struct script)) {
+		script_ofs	= dsp - np->p_script;
+		script_base	= (u_char *) np->script;
+		script_name	= "script";
+	}
+	else {
+		script_ofs	= dsp - np->p_scripth;
+		script_base	= (u_char *) np->scripth;
+		script_name	= "scripth";
+	}
+
+	printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+		ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
+		(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
+		(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
+		(unsigned)INL (nc_dbc));
+
+	if (((script_ofs & 3) == 0) &&
+	    (unsigned)script_ofs < sizeof(struct script)) {
+		printf ("%s: script cmd = %08x\n", ncr_name(np),
+			(int) *(ncrcmd *)(script_base + script_ofs));
+	}
+
+        printf ("%s: regdump:", ncr_name(np));
+        for (i=0; i<16;i++)
+            printf (" %02x", (unsigned)INB_OFF(i));
+        printf (".\n");
+}
+
+/*==========================================================
+**
 **
 **	ncr chip exception handler.
 **
@@ -6098,8 +6185,6 @@
 {
 	u_char	istat, dstat;
 	u_short	sist;
-	u_int32	dsp, dsa;
-	int	script_ofs;
 	int	i;
 
 	/*
@@ -6222,54 +6307,8 @@
 	/*=========================================
 	**	log message for real hard errors
 	**=========================================
-
-	"ncr0 targ 0?: ERROR (ds:si) (so-si-sd) (sxfer/scntl3) @ (dsp:dbc)."
-	"	      reg: r0 r1 r2 r3 r4 r5 r6 ..... rf."
-
-	exception register:
-		ds:	dstat
-		si:	sist
-
-	SCSI bus lines:
-		so:	control lines as driver by NCR.
-		si:	control lines as seen by NCR.
-		sd:	scsi data lines as seen by NCR.
-
-	wide/fastmode:
-		sxfer:	(see the manual)
-		scntl3:	(see the manual)
-
-	current script command:
-		dsp:	script adress (relative to start of script).
-		dbc:	first word of script command.
-
-	First 16 register of the chip:
-		r0..rf
-
-	=============================================
 	*/
-
-	dsp = (unsigned) INL (nc_dsp);
-	dsa = (unsigned) INL (nc_dsa);
-
-	script_ofs = dsp - np->p_script;
-
-	printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%x:%08x).\n",
-		ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
-		(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
-		(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_ofs,
-		(unsigned) INL (nc_dbc));
-
-	if (((script_ofs & 3) == 0) &&
-	    (unsigned)script_ofs < sizeof(struct script)) {
-		printf ("%s: script cmd = %08x\n", ncr_name(np),
-			(int) *(ncrcmd *)((char*)np->script +script_ofs));
-	}
-
-        printf ("%s: reg:", ncr_name(np));
-        for (i=0; i<16;i++)
-            printf (" %02x", (unsigned)INB_OFF(i));
-        printf (".\n");
+	ncr_log_hard_error(np, sist, dstat);
 
 	/*----------------------------------------
 	**	clean up the dma fifo
@@ -6418,8 +6457,7 @@
 	**	sorry, have to kill ALL jobs ...
 	*/
 
-	ncr_init (np, "fatal error", HS_FAIL);
-	np->disc = 1;
+	ncr_start_reset(np, 2);
 }
 
 /*==========================================================
@@ -6521,8 +6559,8 @@
 		u_short	delta;
 
 		if (!(INB(nc_dstat) & DFE)) {
-			ctest5 = (np->rv_ctest5 & 0x20) ? INB (nc_ctest5) : 0;
-			if (ctest5 & 0x20)
+			ctest5 = (np->rv_ctest5 & DFS) ? INB (nc_ctest5) : 0;
+			if (ctest5 & DFS)
 				delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff;
 			else
 				delta=(INB (nc_dfifo) - rest) & 0x7f;
@@ -7699,7 +7737,7 @@
 			segment = 1;
 		}
 	}
-	else if (use_sg < MAX_SCATTER) {
+	else if (use_sg <= MAX_SCATTER) {
 		struct scatterlist *scatter = (struct scatterlist *)cmd->buffer;
 
 		while (segment < use_sg) {
@@ -8245,6 +8283,8 @@
 			driver_setup.ultra_scsi	= val;
 		else if	(!strncmp(cur, "fsn:", 4))
 			driver_setup.force_sync_nego	= val;
+		else if	(!strncmp(cur, "revprob:", 8))
+			driver_setup.reverse_probe	= val;
 		else if	(!strncmp(cur, "tags:", 5)) {
 			if (val > SCSI_NCR_MAX_TAGS)
 				val = SCSI_NCR_MAX_TAGS;
@@ -8263,7 +8303,7 @@
 		else if	(!strncmp(cur, "wide:", 5))
 			driver_setup.max_wide	= val? 1:0;
 		else if	(!strncmp(cur, "settle:", 7))
-			driver_setup.settle_time= val;
+			driver_setup.settle_delay= val;
 		else if	(!strncmp(cur, "diff:", 5))
 			driver_setup.diff_support= val;
 		else if	(!strncmp(cur, "irqm:", 5))
@@ -8318,7 +8358,7 @@
 
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 {
-     int i;
+     int i, j;
      int count = 0;			/* Number of boards detected */
      uchar pci_bus, pci_device_fn;
      short pci_index;	/* Device index to PCI BIOS calls */
@@ -8341,7 +8381,7 @@
 			driver_setup.verbose,
 			driver_setup.debug,
 			YesNo(driver_setup.led_pin),
-			driver_setup.settle_time,
+			driver_setup.settle_delay,
 			driver_setup.irqm);
    }
 #undef YesNo
@@ -8358,7 +8398,8 @@
 #endif
 
      if (pcibios_present()) {
-	  for (i = 0; i < NPCI_CHIP_IDS; ++i) 
+	  for (j = 0; j < NPCI_CHIP_IDS; ++j) {
+	       i = driver_setup.reverse_probe ? NPCI_CHIP_IDS-1 - j : j;
 	       for (pci_index = 0;
 		    !pcibios_find_device(PCI_VENDOR_ID_NCR, 
 					 pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
@@ -8367,6 +8408,7 @@
 		    if (!ncr53c8xx_pci_init(tpnt, count, 0, pci_chip_ids[i].chip, 
 			      pci_bus, pci_device_fn, /* no options */ 0))
 		    ++count;
+	 }
      }
 
      return count;
@@ -8487,12 +8529,17 @@
 
 	for (device = devlist; device; device = device->next) {
 		if (device->host == host) {
+#if SCSI_NCR_MAX_TAGS > 1
 			if (device->tagged_supported) {
 				device->queue_depth = SCSI_NCR_MAX_TAGS;
 			}
 			else {
-				device->queue_depth = 1;
+				device->queue_depth = 2;
 			}
+#else
+			device->queue_depth = 1;
+#endif
+
 #ifdef DEBUG
 printk("ncr53c8xx_select_queue_depth: id=%d, lun=%d, queue_depth=%d\n",
 	device->id, device->lun, device->queue_depth);
@@ -9014,6 +9061,13 @@
 		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
 		                  (u_long) np->reg);
 #endif
+	copy_info(&info, "  Synchronous period factor %d, ", (int) np->ns_sync);
+	copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
+
+	if (driver_setup.debug || driver_setup.verbose > 1) {
+		copy_info(&info, "  Debug flags 0x%x, ", driver_setup.debug);
+		copy_info(&info, "verbosity level %d\n", driver_setup.verbose);
+	}
 
 #ifdef SCSI_NCR_PROFILE
 	copy_info(&info, "Profiling information:\n");
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.32/linux/drivers/scsi/ncr53c8xx.h	Mon Mar 17 14:54:30 1997
+++ linux/drivers/scsi/ncr53c8xx.h	Wed Apr  9 23:32:37 1997
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.18b"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 1.18d"
  
 /*
 **	If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined,
@@ -273,14 +273,14 @@
 */
 
 #define SCSI_NCR_ALWAYS_SIMPLE_TAG
-#define SCSI_NCR_MAX_SCATTER	(128)
+#define SCSI_NCR_MAX_SCATTER	(127)
 #define SCSI_NCR_MAX_TARGET	(16)
 #define SCSI_NCR_MAX_HOST	(2)
 #define SCSI_NCR_TIMEOUT_ALERT	(3*HZ)
 
 #define SCSI_NCR_CAN_QUEUE	(7*SCSI_NCR_MAX_TAGS)
 #define SCSI_NCR_CMD_PER_LUN	(SCSI_NCR_MAX_TAGS)
-#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER-1)
+#define SCSI_NCR_SG_TABLESIZE	(SCSI_NCR_MAX_SCATTER)
 
 #define SCSI_NCR_TIMER_INTERVAL	((HZ+5-1)/5)
 
@@ -302,6 +302,7 @@
 	SCSI_NCR_SETUP_SPECIAL_FEATURES,	\
 	SCSI_NCR_SETUP_ULTRA_SCSI,		\
 	SCSI_NCR_SETUP_FORCE_SYNC_NEGO,		\
+	0,					\
 	1,					\
 	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
 	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
@@ -327,6 +328,7 @@
 	0,					\
 	0,					\
 	0,					\
+	0,					\
 	2,					\
 	0,					\
 	255,					\
@@ -552,6 +554,7 @@
 
 /*03*/  u_char    nc_scntl3;    /* cnf system clock dependent       */
 	#define   EWS     0x08  /* cmd: enable wide scsi         [W]*/
+	#define   ULTRA   0x80  /* cmd: ULTRA enable                */
 
 /*04*/  u_char    nc_scid;	/* cnf host adapter scsi address    */
 	#define   RRE     0x40  /* r/w:e enable response to resel.  */
@@ -643,6 +646,7 @@
 	#define   MPEE    0x08  /* mod: master parity error enable  */
 
 /*22*/  u_char    nc_ctest5;
+	#define   DFS     0x20  /* mod: dma fifo size               */
 /*23*/  u_char    nc_ctest6;
 
 /*24*/  u_int32    nc_dbc;	/* ### Byte count and command       */
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.1.32/linux/drivers/scsi/ppa.c	Fri Dec 27 02:03:23 1996
+++ linux/drivers/scsi/ppa.c	Wed Apr  9 21:30:31 1997
@@ -1,504 +1,1326 @@
-/*      ppa.c   --  low level driver for the IOMEGA PPA3 
-                    parallel port SCSI host adapter.
-
-        (The PPA3 is the embedded controller in the ZIP drive.)
-
-        (c) 1995,1996 Grant R. Guenther, grant@torque.net,
-                      under the terms of the GNU Public License.
-
-*/
+/* ppa.c   --  low level driver for the IOMEGA PPA3 
+ * parallel port SCSI host adapter.
+ * 
+ * (The PPA3 is the embedded controller in the ZIP drive.)
+ * 
+ * (c) 1995,1996 Grant R. Guenther, grant@torque.net,
+ * under the terms of the GNU Public License.
+ * 
+ */
 
 /*      This driver was developed without the benefit of any technical
-        specifications for the interface.  Instead, a modified version of
-        DOSemu was used to monitor the protocol used by the DOS driver
-        for this adapter.  I have no idea how my programming model relates
-        to IOMEGA's design.
-
-        IOMEGA's driver does not generate linked commands.  I've never
-        observed a SCSI message byte in the protocol transactions, so
-        I am assuming that as long as linked commands are not used
-        we won't see any.  
-
-        So far, this driver has been tested with the embedded PPA3 in the
-        ZIP drive, only.   It can detect and adapt to 4- and 8-bit parallel
-        ports, but there is currently no support for EPP or ECP ports, as
-        I have been unable to make the DOS drivers work in these modes on
-        my test rig.
-
-        For more information, see the file drivers/scsi/README.ppa.
-
-*/
-
-#define   PPA_VERSION   "0.26"
-
-/* Change these variables here or with insmod or with a LILO or LOADLIN
-   command line argument
-*/
-
-static int      ppa_base       = 0x378;  /* parallel port address    */
-static int      ppa_speed_high = 1;      /* port delay in data phase */
-static int      ppa_speed_low  = 6;      /* port delay otherwise     */
-static int      ppa_nybble     = 0;      /* don't force nybble mode  */
-
-
-#define   PPA_CAN_QUEUE         1       /* use "queueing" interface */
-#define   PPA_SELECT_TMO        5000    /* how long to wait for target ? */
-#define   PPA_SPIN_TMO          5000000 /* ppa_wait loop limiter */
-#define   PPA_SECTOR_SIZE       512     /* for a performance hack only */
-
-#include  <linux/stddef.h>
-#include  <linux/module.h>
-#include  <linux/kernel.h>
-#include  <linux/tqueue.h>
-#include  <linux/ioport.h>
-#include  <linux/delay.h>
-#include  <linux/blk.h>
-#include  <linux/proc_fs.h>
-#include  <linux/stat.h>
-#include  <asm/io.h>
-#include  "sd.h"
-#include  "hosts.h"
-#include  "ppa.h"
-
-struct proc_dir_entry proc_scsi_ppa = 
-                { PROC_SCSI_PPA, 3, "ppa", S_IFDIR|S_IRUGO|S_IXUGO, 2 };
-
-static int              ppa_abort_flag = 0;
-static int              ppa_error_code = DID_OK;
-static char             ppa_info_string[132];
-static Scsi_Cmnd        *ppa_current = 0;
-static void             (*ppa_done) (Scsi_Cmnd *);
-static int              ppa_port_delay;
-
-void    out_p( short port, char byte)
-
-{       outb(byte,ppa_base+port);
-        udelay(ppa_port_delay);
-}
-
-char    in_p( short port)
-
-{       return inb(ppa_base+port);
-        udelay(ppa_port_delay);
-}
-
-void    ppa_d_pulse( char b )
-
-{       out_p(0,b);
-        out_p(2,0xc); out_p(2,0xe); out_p(2,0xc); out_p(2,0x4); out_p(2,0xc);
-}
-
-void    ppa_disconnect( void )
-
-{       ppa_d_pulse(0);
-        ppa_d_pulse(0x3c);
-        ppa_d_pulse(0x20);
-        ppa_d_pulse(0xf);
-}
-
-void    ppa_c_pulse( char b )
-
-{       out_p(0,b);
-        out_p(2,0x4); out_p(2,0x6); out_p(2,0x4); out_p(2,0xc);
-}
-
-void    ppa_connect( void )
-
-{       ppa_c_pulse(0);
-        ppa_c_pulse(0x3c);
-        ppa_c_pulse(0x20);
-        ppa_c_pulse(0x8f);
-}
-
-void    ppa_do_reset( void )
-
-{       out_p(2,0);             /* This is really just a guess */
-        udelay(100);
-}
-
-char    ppa_select( int  initiator, int target )
-
-{       char    r;
-        int     k;
-
-        r = in_p(1);
-        out_p(0,(1<<target)); out_p(2,0xe); out_p(2,0xc);
-        out_p(0,(1<<initiator)); out_p(2,0x8);
+ * specifications for the interface.  Instead, a modified version of
+ * DOSemu was used to monitor the protocol used by the DOS driver
+ * for this adapter.  I have no idea how my programming model relates
+ * to IOMEGA's design.
+ * 
+ * IOMEGA's driver does not generate linked commands.  I've never
+ * observed a SCSI message byte in the protocol transactions, so
+ * I am assuming that as long as linked commands are not used
+ * we won't see any.  
+ * 
+ * For more information, see the file drivers/scsi/README.ppa.
+ * 
+ */
+
+/* 
+ * this driver has been hacked by Matteo Frigo (athena@theory.lcs.mit.edu)
+ * to support EPP and scatter-gather.                        [0.26-athena]
+ *
+ * additional hacks by David Campbell (campbell@tirian.che.curtin.edu.au)
+ * in response to this driver "mis-behaving" on his machine.
+ *      Fixed EPP to handle "software" changing of EPP port data direction.
+ *      Chased down EPP timeouts
+ *      Made this driver "kernel version friendly"           [0.28-athena]
+ *
+ * Really hacked it out of existance (David Campbell)
+ *      EPP timeouts no longer occur (yet better handling)
+ *      Probes known parallel ports
+ *      Autodetects EPP / ECP / PS2 / NIBBLE
+ *      Support for multiple devices (does anyone need this??)
+ *                                                           [0.29-Curtin]
+ * [ Stuff removed ]
+ *
+ *      Modified PEDANTIC for less PEDANTIC drivers as people
+ *      were complaining about speed (I received a report indicating
+ *      that PEDANTIC is necessary for WinBond chipsets.
+ *      Updated config_ppa and Makefile
+ *                                                           [0.36b-Curtin]
+ *
+ * First round of cleanups
+ *      Remove prior 1.3.34 kernel support
+ *      SMC support changed
+ *              ECP+EPP detection always invoked.
+ *              If compat mode => PS/2
+ *              else ecp_sync() called (ECP+EPP uses FIFO).
+ *      Added routine to detect interrupt channel for ECP (not used)
+ *      Changed version numbering
+ *              1       Major number
+ *              00      Minor revision number
+ *              ALPHA   Expected stability (alpha, beta, stable)
+ *                                              [Curtin-1-00-ALPHA]
+ * Second round of cleanups
+ *      Clean up timer queues
+ *      Fixed problem with non-detection of PS/2 ports
+ *      SMC ECP+EPP confirmed to work, remove option from config_ppa
+ *                                              [Curtin-1-01-ALPHA]
+ *
+ * Parport hits with a vengance!!
+ *      Several internal revisions have been made with huge amounts of
+ *      fixes including:
+ *              ioport_2_hostno removed (unique_id is quicker)
+ *              SMC compat option is history
+ *              Driver name / info hardwired
+ *              Played with inlines and saved 4k on module
+ *      Parport support
+ *              Using PnP Parport allows use of printer attached to
+ *              ZIP drive.
+ *              Numerous fixups for device registration and to allow
+ *              proper aborts.
+ *      Version jumps a few numbers here - considered BETA
+ *      Shipping Parport with monolithic driver :)
+ *                                              [Curtin-1-05-BETA]
+ *
+ * Fixed code to ensure SCSI abort will release the SCSI command
+ *      if the driver is STILL trying to claim the parport (PNP ver)
+ *      Now I have to fix the lp driver then there will NEVER be a
+ *      problem.
+ *      Got around to doing the ppa_queuecommand() clean up
+ *      Fixed bug relating to SMC EPP+ECP and monolithic driver
+ *                                              [Curtin-1-06-BETA]
+ *
+ * Where did the ppa_setup() code disappear to ??
+ *      Back in now...
+ *      Distribution of ppa now independent of parport (less work for me).
+ *      Also cleaned up the port detection to allow for variations on
+ *      IO aliasing (in an attempt to fix a few problems with some
+ *      machines...)
+ *                                              [Curtin-1-07-BETA]
+ *
+ * Rewrote detection code for monolithic driver and ported changes to
+ *      parport driver. Result is more stable detection of hardware and
+ *      better immunity to port aliasing (old XT cards).
+ *      Parport 0.16 (or better) is required for parport operation and
+ *      ECP+EPP modes, otherwise the latest parport edition is recommended.
+ *
+ *      When using EPP and writing to disk CPU usage > 40%, while reading <10%.
+ *      This is due to ZIP drive IO scheduling, the drive does a verify after
+ *      write to ensure data integrity (removable media is ALWAYS questionable
+ *      since you never know where it has been).
+ *      Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
+ *      over 10 sectors per jiffy.
+ *
+ *      Hmm... I think I know a way but it will send the driver into
+ *      ALPHA state again.
+ *                                              [Curtin-1-08-STABLE]
+ */
 
-        k = 0;
-        while ( !(r = (in_p(1) & 0xf0)) && (k++ < PPA_SELECT_TMO)) barrier();
-        return r;
-}
-
-char    ppa_wait( void ) 
-
-/*      Wait for the high bit to be set.
-
-        In principle, this could be tied to an interrupt, but the adapter
-        doesn't appear to be designed to support interrupts.  We spin on
-        the 0x80 ready bit. 
-*/
-
-{       int     k;
-        char    r;
-
-        ppa_error_code = DID_OK;
-        k = 0;
-        while (!((r = in_p(1)) & 0x80) 
-                && (k++ < PPA_SPIN_TMO) && !ppa_abort_flag  ) barrier();
-                        
-        if (ppa_abort_flag) {
-                if (ppa_abort_flag == 1) ppa_error_code = DID_ABORT;
-                else {  ppa_do_reset();
-                        ppa_error_code = DID_RESET;
-                }
-                ppa_disconnect();
-                return 0;
-        }
-        if (k >= PPA_SPIN_TMO) { 
-                ppa_error_code = DID_TIME_OUT;
-                ppa_disconnect();
-                return 0;               /* command timed out */
-        }
-        return (r & 0xf0);
-}
-
-int     ppa_init( void )
-
-/* This is based on a trace of what the Iomega DOS 'guest' driver does.
-   I've tried several different kinds of parallel ports with guest and
-   coded this to react in the same ways that it does.
-
-   The return value from this function is just a hint about where the
-   handshaking failed.
-
-*/
-
-{       char    r, s;
-
-        out_p(0,0xaa); 
-        if (in_p(0) != (char) 0xaa) return 1; 
-        ppa_disconnect();
-        ppa_connect();
-        out_p(2,0x6); 
-        if ((in_p(1) & 0xf0) != 0xf0) return 2; 
-        out_p(2,0x4);
-        if ((in_p(1) & 0xf0) != 0x80) return 3; 
-        ppa_disconnect();
-        s = in_p(2);
-        out_p(2,0xec);
-        out_p(0,0x55); 
-        r = in_p(0);    
-        if (r != (char) 0xff) { 
-                ppa_nybble = 1;
-                if (r != (char) 0x55) return 4; 
-                out_p(0,0xaa); if (in_p(0) != (char) 0xaa) return 5; 
-        }
-        out_p(2,s);
-        ppa_connect();
-        out_p(0,0x40); out_p(2,0x8); out_p(2,0xc);
-        ppa_disconnect();
-
-        return 0;
-}
-
-int     ppa_start( Scsi_Cmnd * cmd )
-
-{       int     k;
-
-        ppa_error_code = DID_OK;
-        ppa_abort_flag = 0;
-
-        if (cmd->target == PPA_INITIATOR) {
-                ppa_error_code = DID_BAD_TARGET;
-                return 0;
-        }
-        ppa_connect();
-        if (!ppa_select(PPA_INITIATOR,cmd->target)) {
-                ppa_disconnect();
-                ppa_error_code = DID_NO_CONNECT;
-                return 0;
-        }
-        out_p(2,0xc);
-
-        for (k=0; k < cmd->cmd_len; k++) {        /* send the command */
-                if (!ppa_wait()) return 0;
-                out_p(0,cmd->cmnd[k]);
-                out_p(2,0xe);
-                out_p(2,0xc);
-        }
+/* The following #define is to avoid a clash with hosts.c */
+#define PPA_CODE 1
+#include  "ppa.h"
+/* batteries not included :-) */
 
-#ifdef PPA_DEBUG
-        printk("PPA: command out: ");
-        for (k=0; k < cmd->cmd_len; k++)
-                printk("%3x",(cmd->cmnd[k]) & 0xff );
-        printk("\n");
+/*
+ * modes in which the driver can operate 
+ */
+#define   PPA_AUTODETECT        0	/* Autodetect mode                */
+#define   PPA_NIBBLE            1	/* work in standard 4 bit mode    */
+#define   PPA_PS2               2	/* PS/2 byte mode         */
+#define   PPA_EPP_8             3	/* EPP mode, 8 bit                */
+#define   PPA_EPP_16            4	/* EPP mode, 16 bit               */
+#define   PPA_EPP_32            5	/* EPP mode, 32 bit               */
+#define   PPA_UNKNOWN           6	/* Just in case...                */
+
+static char *PPA_MODE_STRING[] =
+{
+	"Autodetect",
+	"SPP",
+	"PS/2",
+	"EPP 8 bit",
+	"EPP 16 bit",
+	"EPP 32 bit",
+	"Unknown"};
+
+typedef struct {
+	struct ppd *dev;	/* Parport device entry          */
+	int speed;		/* General PPA delay constant   */
+	int speed_fast;		/* Const for nibble/byte modes  */
+	int epp_speed;		/* Reset time period            */
+	int mode;		/* Transfer mode                */
+	int timeout;		/* Number of timeouts           */
+	int host;		/* Host number (for proc)       */
+	int abort_flag;		/* Abort flag                   */
+	int error_code;		/* Error code                   */
+	int ppa_failed;		/* Failure flag                 */
+	Scsi_Cmnd *cur_cmd;	/* Current queued command       */
+	void (*done) (Scsi_Cmnd *);	/* Done func for queuecommand   */
+	struct tq_struct ppa_tq;	/* Polling interupt stuff       */
+	struct wait_queue *ppa_wait_q;	/* Used for PnP stuff           */
+} ppa_struct;
+
+static void ppa_interrupt(void *data);
+/* I know that this is a mess but it works!! */
+#define NO_HOSTS 4
+static ppa_struct ppa_hosts[NO_HOSTS] =
+{
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL},
+  {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
+   {0, 0, ppa_interrupt, NULL}, NULL}
+};
+
+/* This is a global option */
+static int ppa_speed = -1;	/* Set to >0 to act as a global value */
+static int ppa_speed_fast = -1;	/* ditto.. */
+static int ppa_sg = SG_ALL;	/* enable/disable scatter-gather. */
+
+/* other options */
+#define   PPA_CAN_QUEUE         1	/* use "queueing" interface */
+#define   PPA_BURST_SIZE        512	/* block size for bulk transfers */
+#define   PPA_SELECT_TMO        5000	/* how long to wait for target ? */
+#define   PPA_SPIN_TMO          500000	/* ppa_wait loop limiter */
+
+#define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
+
+/* args to ppa_connect */
+#define CONNECT_EPP_MAYBE 1
+#define CONNECT_NORMAL  0
+
+#define PPA_BASE(x)	ppa_hosts[(x)].dev->port->base
+
+/* Port IO - Sorry Grant but I prefer the following symbols */
+#define r_dtr(x)        inb(PPA_BASE(x))
+#define r_str(x)        inb(PPA_BASE(x)+1)
+#define r_ctr(x)        inb(PPA_BASE(x)+2)
+#define r_epp(x)        inb(PPA_BASE(x)+4)
+#define r_fifo(x)       inb(PPA_BASE(x)+0x400)
+#define r_ecr(x)        inb(PPA_BASE(x)+0x402)
+
+#define w_dtr(x,y)      outb(y, PPA_BASE(x))
+#define w_str(x,y)      outb(y, PPA_BASE(x)+1)
+#define w_ctr(x,y)      outb(y, PPA_BASE(x)+2);\
+			udelay( ppa_hosts[(x)].speed)
+#define w_epp(x,y)      outb(y, PPA_BASE(x)+4)
+#define w_fifo(x,y)     outb(y, PPA_BASE(x)+0x400)
+#define w_ecr(x,y)      outb(y, PPA_BASE(x)+0x402)
+
+int ppa_wakeup(void *ref)
+{
+	ppa_struct *ppa_dev = (ppa_struct *) ref;
+
+	if (!ppa_dev->ppa_wait_q)
+		return 1;	/* Wake up whom ? */
+
+	/* Claim the Parport */
+	if (parport_claim(ppa_dev->dev))
+		return 1;	/* Shouldn't happen */
+
+	wake_up(&ppa_dev->ppa_wait_q);
+	return 0;
+}
+
+int ppa_release(struct Scsi_Host *host)
+{
+	int host_no = host->unique_id;
+
+	printk("Releasing ppa%i\n", host_no);
+	parport_unregister_device(ppa_hosts[host_no].dev);
+	return 0;
+}
+
+static int ppa_pb_claim(int host_no)
+{
+	if (parport_claim(ppa_hosts[host_no].dev)) {
+		sleep_on(&ppa_hosts[host_no].ppa_wait_q);
+		ppa_hosts[host_no].ppa_wait_q = NULL;
+
+		/* Check to see if we were aborted or reset */
+		if (ppa_hosts[host_no].dev->port->cad !=
+		    ppa_hosts[host_no].dev) {
+			printk("Abort detected on ppa%i\n", host_no);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void ppa_pb_release(int host_no)
+{
+	parport_release(ppa_hosts[host_no].dev);
+}
+
+
+/* Placed here so everyone knows what ecp_sync does.. */
+static void ecp_sync(int host_no)
+{
+	int i, r;
+
+	r = r_ecr(host_no);
+	if ((r & 0xe0) != 0x80)
+		return;
+
+	for (i = 0; i < 100; i++) {
+		r = r_ecr(host_no);
+		if (r & 0x01)
+			return;
+		udelay(5);
+	}
+
+	printk("ppa: ECP sync failed as data still present in FIFO.\n");
+}
+
+static inline void ppa_d_pulse(int host_no, char b)
+{
+	w_dtr(host_no, b);
+	w_ctr(host_no, 0xc);
+	w_ctr(host_no, 0xe);
+	w_ctr(host_no, 0xc);
+	w_ctr(host_no, 0x4);
+	w_ctr(host_no, 0xc);
+}
+
+static void ppa_disconnect(int host_no)
+{
+	ppa_d_pulse(host_no, 0);
+	ppa_d_pulse(host_no, 0x3c);
+	ppa_d_pulse(host_no, 0x20);
+	ppa_d_pulse(host_no, 0xf);
+
+	ppa_pb_release(host_no);
+}
+
+static inline void ppa_c_pulse(int host_no, char b)
+{
+	w_dtr(host_no, b);
+	w_ctr(host_no, 0x4);
+	w_ctr(host_no, 0x6);
+	w_ctr(host_no, 0x4);
+	w_ctr(host_no, 0xc);
+}
+
+static int ppa_connect(int host_no, int flag)
+{
+	int retv = ppa_pb_claim(host_no);
+
+	ppa_c_pulse(host_no, 0);
+	ppa_c_pulse(host_no, 0x3c);
+	ppa_c_pulse(host_no, 0x20);
+	if ((flag == CONNECT_EPP_MAYBE) &&
+	    IN_EPP_MODE(ppa_hosts[host_no].mode))
+		ppa_c_pulse(host_no, 0xcf);
+	else
+		ppa_c_pulse(host_no, 0x8f);
+
+	return retv;
+}
+
+static void ppa_do_reset(int host_no)
+{
+	/*
+	 * SCSI reset taken from ppa_init and checked with
+	 * Iomega document that Grant has (via email :(
+	 */
+	ppa_pb_claim(host_no);
+	ppa_disconnect(host_no);
+
+	ppa_connect(host_no, CONNECT_NORMAL);
+
+	w_ctr(host_no, 0x6);
+	w_ctr(host_no, 0x4);
+	w_dtr(host_no, 0x40);
+	w_ctr(host_no, 0x8);
+	udelay(50);
+	w_ctr(host_no, 0xc);
+
+	ppa_disconnect(host_no);
+}
+
+static char ppa_select(int host_no, int initiator, int target)
+{
+	char r;
+	int k;
+
+	r = r_str(host_no);	/* TODO */
+
+	w_dtr(host_no, (1 << target));
+	w_ctr(host_no, 0xe);
+	w_ctr(host_no, 0xc);
+	w_dtr(host_no, (1 << initiator));
+	w_ctr(host_no, 0x8);
+
+	k = 0;
+	while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
+		barrier();
+
+	if (k >= PPA_SELECT_TMO)
+		return 0;
+
+	return r;
+}
+
+static void ppa_fail(int host_no, int error_code)
+{
+	ppa_hosts[host_no].error_code = error_code;
+	ppa_hosts[host_no].ppa_failed = 1;
+	ppa_disconnect(host_no);
+}
+
+/*
+ * Wait for the high bit to be set.
+ * 
+ * In principle, this could be tied to an interrupt, but the adapter
+ * doesn't appear to be designed to support interrupts.  We spin on
+ * the 0x80 ready bit. 
+ */
+static char ppa_wait(int host_no)
+{
+	int k;
+	char r;
+
+	k = 0;
+	while (!((r = r_str(host_no)) & 0x80)
+	       && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
+		barrier();
+
+	/* check if we were interrupted */
+	if (ppa_hosts[host_no].abort_flag) {
+		if (ppa_hosts[host_no].abort_flag == 1)
+			ppa_fail(host_no, DID_ABORT);
+		else {
+			ppa_do_reset(host_no);
+			ppa_fail(host_no, DID_RESET);
+		}
+		return 0;
+	}
+	/* check if timed out */
+	if (k >= PPA_SPIN_TMO) {
+		ppa_fail(host_no, DID_TIME_OUT);
+		return 0;	/* command timed out */
+	}
+	/* 
+	 * return some status information.
+	 * Semantics: 0xc0 = ZIP wants more data
+	 *            0xd0 = ZIP wants to send more data
+	 *            0xf0 = end of transfer, ZIP is sending status
+	 */
+	return (r & 0xf0);
+}
+
+
+/* 
+ * This is based on a trace of what the Iomega DOS 'guest' driver does.
+ * I've tried several different kinds of parallel ports with guest and
+ * coded this to react in the same ways that it does.
+ * 
+ * The return value from this function is just a hint about where the
+ * handshaking failed.
+ * 
+ */
+static int ppa_init(int host_no)
+{
+	if (ppa_pb_claim(host_no))
+		return 1;
+	ppa_disconnect(host_no);
+
+	ppa_connect(host_no, CONNECT_NORMAL);
+
+	w_ctr(host_no, 0x6);
+	if ((r_str(host_no) & 0xf0) != 0xf0) {
+		ppa_pb_release(host_no);
+		return 2;
+	}
+	w_ctr(host_no, 0x4);
+	if ((r_str(host_no) & 0xf0) != 0x80) {
+		ppa_pb_release(host_no);
+		return 3;
+	}
+	/* This is a SCSI reset signal */
+	w_dtr(host_no, 0x40);
+	w_ctr(host_no, 0x8);
+	udelay(50);
+	w_ctr(host_no, 0xc);
+
+	ppa_disconnect(host_no);
+
+	return 0;
+}
+
+/* 
+ * check the epp status. After a EPP transfer, it should be true that
+ * 1) the TIMEOUT bit (SPP_STR.0) is clear
+ * 2) the READY bit (SPP_STR.7) is set
+ */
+static int ppa_check_epp_status(int host_no)
+{
+	char r;
+	r = r_str(host_no);
+
+	if (r & 1) {
+		/* EPP timeout, according to the PC87332 manual */
+		/* Semantics of clearing EPP timeout bit.
+		 * PC87332 - reading SPP_STR does it...
+		 * SMC     - write 1 to EPP timeout bit
+		 * Others  - (???) write 0 to EPP timeout bit
+		 */
+		w_str(host_no, r);
+		w_str(host_no, r & 0xfe);
+		ppa_hosts[host_no].timeout++;
+		printk("PPA: EPP timeout on port 0x%04x\n",
+		       PPA_BASE(host_no));
+		ppa_fail(host_no, DID_BUS_BUSY);
+		return 0;
+	}
+	if (!(r & 0x80)) {
+		ppa_fail(host_no, DID_ERROR);
+		return 0;
+	}
+	return 1;
+}
+
+static inline int ppa_force_epp_byte(int host_no, char x)
+{
+/* This routine forces a byte down the EPP data port whether the
+ * device is ready or not...
+ */
+	char r;
+
+	w_epp(host_no, x);
+
+	r = r_str(host_no);
+	if (!(r & 1))
+		return 1;
+
+	if (ppa_hosts[host_no].epp_speed > 0) {
+		/* EPP timeout, according to the PC87332 manual */
+		/* Semantics of clearing EPP timeout bit.
+		 * PC87332 - reading SPP_STR does it...
+		 * SMC     - write 1 to EPP timeout bit
+		 * Others  - (???) write 0 to EPP timeout bit
+		 */
+		w_str(host_no, r);
+		w_str(host_no, r & 0xfe);
+
+		/* Take a deep breath, count to 10 and then... */
+		udelay(ppa_hosts[host_no].epp_speed);
+
+		/* Second time around */
+		w_epp(host_no, x);
+
+		r = r_str(host_no);
+	}
+	if (r & 1) {
+		w_str(host_no, r);
+		w_str(host_no, r & 0xfe);
+		ppa_hosts[host_no].timeout++;
+		printk("PPA: warning: EPP timeout\n");
+		ppa_fail(host_no, DID_BUS_BUSY);
+		return 0;
+	} else
+		return 1;
+}
+
+static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+	int k;
+
+	w_ctr(host_no, 0x4);
+	for (k = 0; k < cmd->cmd_len; k++) {	/* send the command */
+		if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
+			return 0;
+	}
+	w_ctr(host_no, 0xc);
+	ecp_sync(host_no);
+	return 1;
+}
+
+static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+	int k;
+
+	w_ctr(host_no, 0xc);
+
+	for (k = 0; k < cmd->cmd_len; k++) {	/* send the command */
+		if (!ppa_wait(host_no))
+			return 0;
+		w_dtr(host_no, cmd->cmnd[k]);
+		w_ctr(host_no, 0xe);
+		w_ctr(host_no, 0xc);
+	}
+	return 1;
+}
+
+static int ppa_start(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+
+	/* 
+	 * by default, the command failed,
+	 * unless explicitly completed in ppa_completion().
+	 */
+	ppa_hosts[host_no].error_code = DID_ERROR;
+	ppa_hosts[host_no].abort_flag = 0;
+	ppa_hosts[host_no].ppa_failed = 0;
+
+	if (cmd->target == PPA_INITIATOR) {
+		ppa_hosts[host_no].error_code = DID_BAD_TARGET;
+		ppa_hosts[host_no].ppa_failed = 1;
+		return 0;
+	}
+	if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
+		return 0;
+
+	if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
+		ppa_fail(host_no, DID_NO_CONNECT);
+		return 0;
+	}
+	if (IN_EPP_MODE(ppa_hosts[host_no].mode))
+		return ppa_send_command_epp(cmd);
+	else
+		return ppa_send_command_normal(cmd);
+}
+
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol. 
+ */
+static inline int ppa_outs(int host_no, char *buffer, int len)
+{
+	int k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+	int r;
 #endif
 
-        return 1;
-}
-
-int     ppa_completion( Scsi_Cmnd * cmd )
-
-/* The bulk flag enables some optimisations in the data transfer loops,
-   it should be true for any command that transfers data in integral
-   numbers of sectors.
-
-   The driver appears to remain stable if we speed up the parallel port
-   i/o in this function, but not elsewhere.
-*/
-
-{       char    r, l, h, v;
-        int     dir, cnt, blen, fast, bulk;
-        char    *buffer;
-
-#ifdef PPA_DEBUG
-        int     k;
+	switch (ppa_hosts[host_no].mode) {
+	case PPA_NIBBLE:
+	case PPA_PS2:
+		/* 8 bit output, with a loop */
+		for (k = len; k; k--) {
+			w_dtr(host_no, *buffer++);
+			w_ctr(host_no, 0xe);
+			w_ctr(host_no, 0xc);
+		}
+		return 1;	/* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+	case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+		w_ctr(host_no, 0x4);
+		for (k = len; k; k -= 4) {
+			w_epp(host_no, *buffer++);
+			w_epp(host_no, *buffer++);
+			w_epp(host_no, *buffer++);
+			w_epp(host_no, *buffer++);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return 1;
 #endif
-
-        if (!(r = ppa_wait())) return 0;
-        v = cmd->cmnd[0];
-        bulk = ((v==READ_6)||(v==READ_10)||(v==WRITE_6)||(v==WRITE_10));
-        buffer = cmd->request_buffer;
-        blen = cmd->request_bufflen;
-        cnt = 0;  dir = 0;
-        if (r == (char) 0xc0) dir = 1;  /* d0 = read c0 = write f0 = status */
-
-        ppa_port_delay = ppa_speed_high;
-
-        while (r != (char) 0xf0) {
-                if (((r & 0xc0) != 0xc0 ) || (cnt >= blen)) {
-                        ppa_disconnect();
-                        ppa_error_code = DID_ERROR;
-                        return 0;
-                }
-                fast = bulk && ((blen - cnt) >= PPA_SECTOR_SIZE);
-                if (dir) do {
-                        out_p(0,buffer[cnt++]);
-                        out_p(2,0xe); out_p(2,0xc);
-                        if (!fast) break;
-                } while (cnt % PPA_SECTOR_SIZE);
-                else {  
-                        if (ppa_nybble) do {
-                                out_p(2,0x4); h = in_p(1); 
-                                out_p(2,0x6); l = in_p(1);
-                                v = ((l >> 4) & 0x0f) + (h & 0xf0);
-                                buffer[cnt++] = v;
-                                if (!fast) break;
-                        } while (cnt % PPA_SECTOR_SIZE);
-                        else do {
-                                out_p(2,0x25); v = in_p(0); out_p(2,0x27);
-                                buffer[cnt++] = v;
-                                if (!fast) break;
-                        } while (cnt % PPA_SECTOR_SIZE);
-                        if (!ppa_nybble) {
-                                out_p(2,0x5); out_p(2,0x4);
-                        }
-                        out_p(2,0xc);
-                }
-                if (!(r = ppa_wait())) return 0;
-        }
-
-        ppa_port_delay = ppa_speed_low;
-
-        out_p(2,0x4);            /* now read status byte */
-        h = in_p(1);
-        out_p(2,0x6);
-        l = in_p(1);
-        out_p(2,0xc);
-        r = ((l >> 4) & 0x0f) + (h & 0xf0);
-
-        out_p(2,0xe); out_p(2,0xc);
-        ppa_disconnect();
-
-#ifdef PPA_DEBUG
-        printk("PPA: status: %x, data[%d]: ",r & STATUS_MASK,cnt);
-        if (cnt > 12) cnt = 12;
-        for (k=0; k < cnt; k++)
-           printk("%3x",buffer[k] & 0xff );
-        printk("\n");   
+	case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+		w_ctr(host_no, 0x4);
+		for (k = len; k; k -= 2) {
+			w_epp(host_no, *buffer++);
+			w_epp(host_no, *buffer++);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return 1;
+#endif
+	case PPA_EPP_8:
+		w_ctr(host_no, 0x4);
+		for (k = len; k; k--) {
+			w_epp(host_no, *buffer++);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return 1;
+#else
+	case PPA_EPP_32:
+	case PPA_EPP_16:
+	case PPA_EPP_8:
+		w_ctr(host_no, 0x4);
+		switch (ppa_hosts[host_no].mode) {
+		case PPA_EPP_8:
+			outsb(PPA_BASE(host_no) + 0x04,
+			      buffer, len);
+			break;
+		case PPA_EPP_16:
+			outsw(PPA_BASE(host_no) + 0x04,
+			      buffer, len / 2);
+			break;
+		case PPA_EPP_32:
+			outsl(PPA_BASE(host_no) + 0x04,
+			      buffer, len / 4);
+			break;
+		}
+		k = ppa_check_epp_status(host_no);
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return k;
 #endif
 
-        return (r & STATUS_MASK);
-}
-
-/* deprecated synchronous interface */
-
-int     ppa_command( Scsi_Cmnd * cmd )
-
-{       int     s;
-
-        sti();
-        s = 0;
-        if (ppa_start(cmd))
-           if (ppa_wait()) 
-               s = ppa_completion(cmd);
-        return s + (ppa_error_code << 16);
-}
-
-/* pseudo-interrupt queueing interface */
-
-/* Since the PPA itself doesn't generate interrupts, we use
-   the scheduler's task queue to generate a stream of call-backs and
-   complete the request when the drive is ready.
-*/
-
-static void ppa_interrupt( void *data);
-
-static struct tq_struct ppa_tq = {0,0,ppa_interrupt,NULL};
-
-static void ppa_interrupt( void *data)
-
-{       Scsi_Cmnd *cmd;
-        void  (*done) (Scsi_Cmnd *);
-
-        cmd = ppa_current;
-        done = ppa_done;
-        if (!cmd) return;
-        
-        if (ppa_abort_flag) {
-                ppa_disconnect();
-                if(ppa_abort_flag == 1) cmd->result = DID_ABORT << 16;
-                else { ppa_do_reset();
-                       cmd->result = DID_RESET << 16;
-                }
-                ppa_current = 0;
-                done(cmd);
-                return;
-        }
-        if (!( in_p(1) & 0x80)) {
-                queue_task(&ppa_tq,&tq_scheduler);
-                return;
-        }
-        cmd->result = ppa_completion(cmd) + (ppa_error_code << 16);
-        ppa_current = 0;
-        done(cmd);
-        return;
-}
-
-int     ppa_queuecommand( Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-
-{       if (ppa_current) return 0;
-        sti();
-        ppa_current = cmd;
-        ppa_done = done;
-        if (!ppa_start(cmd)) {
-                cmd->result = ppa_error_code << 16;
-                ppa_current = 0;
-                done(cmd);
-                return 0;
-        }
-        queue_task(&ppa_tq,&tq_scheduler);
-        return 0;
-}
-
-int     ppa_detect( Scsi_Host_Template * host )
-
-{       struct  Scsi_Host       *hreg;
-        int     rs;
-
-        /* can we have the ports ? */
-
-        if (check_region(ppa_base,3)) {
-                printk("PPA: ports at 0x%3x are not available\n",ppa_base);
-                return 0;
-        }
-
-        /* attempt to initialise the controller */
-
-        ppa_port_delay = ppa_speed_low;
-
-        rs = ppa_init();
-        if (rs) {
-            printk("PPA: unable to initialise controller at 0x%x, error %d\n",
-                   ppa_base,rs);
-            return 0;
-        }
-
-        /* now the glue ... */
-
-        host->proc_dir = &proc_scsi_ppa;
-
-        request_region(ppa_base,3,"ppa");
-
-        host->can_queue = PPA_CAN_QUEUE;
-
-        hreg = scsi_register(host,0);
-        hreg->io_port = ppa_base;
-        hreg->n_io_port = 3;
-        hreg->dma_channel = -1;
-
-        sprintf(ppa_info_string,
-                "PPA driver version %s using %d-bit mode on port 0x%x.",
-                PPA_VERSION,8-ppa_nybble*4,ppa_base);
-        host->name = ppa_info_string;
-
-        return 1;       /* 1 host detected */
-}
-
-int     ppa_biosparam( Disk * disk, kdev_t dev, int ip[])
-
-/*  Apparently the the disk->capacity attribute is off by 1 sector 
-    for all disk drives.  We add the one here, but it should really
-    be done in sd.c.  Even if it gets fixed there, this will still
-    work.
-*/
-
-{       ip[0] = 0x40;
-        ip[1] = 0x20;
-        ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
-        if (ip[2] > 1024) {
-                ip[0] = 0xff;
-                ip[1] = 0x3f;
-                ip[2] = (disk->capacity +1) / (ip[0] * ip[1]);
-                if (ip[2] > 1023)
-                        ip[2] = 1023;
-        }
-        return 0;
-}
-
-int     ppa_abort( Scsi_Cmnd * cmd )
-
-{       ppa_abort_flag = 1;
-        return SCSI_ABORT_SNOOZE;
-}
+	default:
+		printk("PPA: bug in ppa_outs()\n");
+	}
+	return 0;
+}
+
+static inline int ppa_outb(int host_no, char d)
+{
+	int k;
+
+	switch (ppa_hosts[host_no].mode) {
+	case PPA_NIBBLE:
+	case PPA_PS2:
+		w_dtr(host_no, d);
+		w_ctr(host_no, 0xe);
+		w_ctr(host_no, 0xc);
+		return 1;	/* assume transfer went OK */
+
+	case PPA_EPP_8:
+	case PPA_EPP_16:
+	case PPA_EPP_32:
+		w_ctr(host_no, 0x4);
+		w_epp(host_no, d);
+		k = ppa_check_epp_status(host_no);
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return k;
+
+	default:
+		printk("PPA: bug in ppa_outb()\n");
+	}
+	return 0;
+}
+
+static inline int ppa_ins(int host_no, char *buffer, int len)
+{
+	int k, h, l;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+	int r;
+#endif
 
-int     ppa_reset( Scsi_Cmnd * cmd, unsigned int ignored )
+	switch (ppa_hosts[host_no].mode) {
+	case PPA_NIBBLE:
+		/* 4 bit input, with a loop */
+		for (k = len; k; k--) {
+			w_ctr(host_no, 0x4);
+			h = r_str(host_no);
+			w_ctr(host_no, 0x6);
+			l = r_str(host_no);
+			*buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
+		}
+		w_ctr(host_no, 0xc);
+		return 1;	/* assume transfer went OK */
+
+	case PPA_PS2:
+		/* 8 bit input, with a loop */
+		for (k = len; k; k--) {
+			w_ctr(host_no, 0x25);
+			*buffer++ = r_dtr(host_no);
+			w_ctr(host_no, 0x27);
+		}
+		w_ctr(host_no, 0x5);
+		w_ctr(host_no, 0x4);
+		w_ctr(host_no, 0xc);
+		return 1;	/* assume transfer went OK */
+
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
+	case PPA_EPP_32:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
+		w_ctr(host_no, 0x24);
+		for (k = len; k; k -= 4) {
+			*buffer++ = r_epp(host_no);
+			*buffer++ = r_epp(host_no);
+			*buffer++ = r_epp(host_no);
+			*buffer++ = r_epp(host_no);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0x2c);
+		ecp_sync(host_no);
+		return 1;
+#endif
+	case PPA_EPP_16:
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
+		w_ctr(host_no, 0x24);
+		for (k = len; k; k -= 2) {
+			*buffer++ = r_epp(host_no);
+			*buffer++ = r_epp(host_no);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0x2c);
+		ecp_sync(host_no);
+		return 1;
+#endif
+	case PPA_EPP_8:
+		w_ctr(host_no, 0x24);
+		for (k = len; k; k--) {
+			*buffer++ = r_epp(host_no);
+			r = ppa_check_epp_status(host_no);
+			if (!r)
+				return r;
+		}
+		w_ctr(host_no, 0x2c);
+		ecp_sync(host_no);
+		return 1;
+		break;
+#else
+	case PPA_EPP_8:
+	case PPA_EPP_16:
+	case PPA_EPP_32:
+		w_ctr(host_no, 0x24);
+		switch (ppa_hosts[host_no].mode) {
+		case PPA_EPP_8:
+			insb(PPA_BASE(host_no) + 0x04,
+			     buffer, len);
+			break;
+		case PPA_EPP_16:
+			insw(PPA_BASE(host_no) + 0x04,
+			     buffer, len / 2);
+			break;
+		case PPA_EPP_32:
+			insl(PPA_BASE(host_no) + 0x04,
+			     buffer, len / 4);
+			break;
+		}
+		k = ppa_check_epp_status(host_no);
+		w_ctr(host_no, 0x2c);
+		ecp_sync(host_no);
+		return k;
+#endif
 
-{       ppa_abort_flag = 2;
-        return SCSI_RESET_PUNT;
-}
+	default:
+		printk("PPA: bug in ppa_ins()\n");
+	}
+	return 0;
+}
+
+static int ppa_inb(int host_no, char *buffer)
+{
+	int h, l, k;
+
+	switch (ppa_hosts[host_no].mode) {
+	case PPA_NIBBLE:
+		/* 4 bit input */
+		w_ctr(host_no, 0x4);
+		h = r_str(host_no);
+		w_ctr(host_no, 0x6);
+		l = r_str(host_no);
+		*buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
+		w_ctr(host_no, 0xc);
+		return 1;	/* assume transfer went OK */
+
+	case PPA_PS2:
+		/* 8 bit input */
+		w_ctr(host_no, 0x25);
+		*buffer++ = r_dtr(host_no);
+		w_ctr(host_no, 0x27);
+		w_ctr(host_no, 0x5);
+		w_ctr(host_no, 0x4);
+		w_ctr(host_no, 0xc);
+		return 1;	/* assume transfer went OK */
+
+	case PPA_EPP_8:
+	case PPA_EPP_16:
+	case PPA_EPP_32:
+		w_ctr(host_no, 0x24);
+		*buffer = r_epp(host_no);
+		k = ppa_check_epp_status(host_no);
+		w_ctr(host_no, 0xc);
+		ecp_sync(host_no);
+		return k;
+
+	default:
+		printk("PPA: bug in ppa_inb()\n");
+	}
+	return 0;
+}
+
+/*
+ * The bulk flag enables some optimisations in the data transfer loops,
+ * it should be true for any command that transfers data in integral
+ * numbers of sectors.
+ * 
+ * The driver appears to remain stable if we speed up the parallel port
+ * i/o in this function, but not elsewhere.
+ */
+static int ppa_completion(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+
+	char r, l, h, v;
+	int dir, cnt, blen, fast, bulk, status;
+	char *buffer;
+	struct scatterlist *sl;
+	int current_segment, nsegment;
+
+	v = cmd->cmnd[0];
+	bulk = ((v == READ_6) ||
+		(v == READ_10) ||
+		(v == WRITE_6) ||
+		(v == WRITE_10));
+
+	/* code for scatter/gather: */
+	if (cmd->use_sg) {
+		/* if many buffers are available, start filling the first */
+		sl = (struct scatterlist *) cmd->request_buffer;
+		blen = sl->length;
+		buffer = sl->address;
+	} else {
+		/* else fill the only available buffer */
+		sl = NULL;
+		buffer = cmd->request_buffer;
+		blen = cmd->request_bufflen;
+	}
+	current_segment = 0;
+	nsegment = cmd->use_sg;
+
+	cnt = 0;
+
+	/* detect transfer direction */
+	dir = 0;
+	if (!(r = ppa_wait(host_no)))
+		return 0;
+	if (r == (char) 0xc0)
+		dir = 1;	/* d0 = read c0 = write f0 = status */
+
+	while (r != (char) 0xf0) {
+		if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
+			ppa_fail(host_no, DID_ERROR);
+			return 0;
+		}
+		/* determine if we should use burst I/O */
+		fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
+			((((long)buffer + cnt)) & 0x3) == 0);
+
+		if (fast) {
+			if (dir)
+				status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
+			else
+				status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
+			cnt += PPA_BURST_SIZE;
+		} else {
+			if (dir)
+				status = ppa_outb(host_no, buffer[cnt]);
+			else
+				status = ppa_inb(host_no, &buffer[cnt]);
+			cnt++;
+		}
+
+		if (!status || !(r = ppa_wait(host_no)))
+			return 0;
+
+		if (sl && cnt == blen) {
+			/* if scatter/gather, advance to the next segment */
+			if (++current_segment < nsegment) {
+				++sl;
+				blen = sl->length;
+				buffer = sl->address;
+				cnt = 0;
+			}
+			/*
+			 * the else case will be captured by the (cnt >= blen)
+			 * test above.
+			 */
+		}
+	}
+
+	/* read status and message bytes */
+	if (!ppa_inb(host_no, &l))	/* read status byte */
+		return 0;
+	if (!(ppa_wait(host_no)))
+		return 0;
+	if (!ppa_inb(host_no, &h))	/* read message byte */
+		return 0;
 
-const char      *ppa_info( struct Scsi_Host * host )
+	ppa_disconnect(host_no);
 
-{       return ppa_info_string;
+	ppa_hosts[host_no].error_code = DID_OK;
+	return (h << 8) | (l & STATUS_MASK);
 }
 
-#ifndef MODULE
-
-/* Command line parameters (for built-in driver):
-
-   Syntax:  ppa=base[,speed_high[,speed_low[,nybble]]]
-
-   For example:  ppa=0x378   or   ppa=0x378,0,3
-
-*/
-
-void    ppa_setup(char *str, int *ints)
+/* deprecated synchronous interface */
 
-{       if (ints[0] > 0) ppa_base = ints[1];
-        if (ints[0] > 1) ppa_speed_high = ints[2];
-        if (ints[0] > 2) ppa_speed_low = ints[3];
-        if (ints[0] > 3) ppa_nybble = ints[4];
-        if (ints[0] > 4) ppa_nybble = ints[5];
+int ppa_command(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+	int s;
+
+	sti();
+	s = 0;
+	if (ppa_start(cmd))
+		if (ppa_wait(host_no))
+			s = ppa_completion(cmd);
+	return s + (ppa_hosts[host_no].error_code << 16);
 }
 
-#else
+/* pseudo-interrupt queueing interface */
+/*
+ * Since the PPA itself doesn't generate interrupts, we use
+ * the scheduler's task queue to generate a stream of call-backs and
+ * complete the request when the drive is ready.
+ */
+static void ppa_interrupt(void *data)
+{
+	ppa_struct *tmp = (ppa_struct *) data;
+	Scsi_Cmnd *cmd = tmp->cur_cmd;
+	void (*done) (Scsi_Cmnd *) = tmp->done;
+	int host_no = cmd->host->unique_id;
+
+	if (!cmd) {
+		printk("PPA: bug in ppa_interrupt\n");
+		return;
+	}
+	/* First check for any errors that may of occured
+	 * Here we check for internal errors
+	 */
+	if (tmp->ppa_failed) {
+		printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
+		       tmp->error_code);
+		cmd->result = DID_ERROR << 16;
+		tmp->cur_cmd = 0;
+		done(cmd);
+		return;
+	}
+	/* Occasionally the mid level driver will abort a SCSI
+	 * command because we are taking to long, if this occurs
+	 * we should abort the command.
+	 */
+	if (tmp->abort_flag) {
+		ppa_disconnect(host_no);
+		if (tmp->abort_flag == 1)
+			cmd->result = DID_ABORT << 16;
+		else {
+			ppa_do_reset(host_no);
+			cmd->result = DID_RESET << 16;
+		}
+		tmp->cur_cmd = 0;
+		done(cmd);
+		return;
+	}
+	/* Check to see if the device is now free, if not
+	 * then throw this function onto the scheduler queue
+	 * to be called back in a jiffy.
+	 * (i386: 1 jiffy = 0.01 seconds)
+	 */
+	if (!(r_str(host_no) & 0x80)) {
+		tmp->ppa_tq.data = (void *) tmp;
+		queue_task(&tmp->ppa_tq, &tq_scheduler);
+		return;
+	}
+	/* Device is now free and no errors have occured so
+	 * it is safe to do the data phase
+	 */
+	cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
+	tmp->cur_cmd = 0;
+	done(cmd);
+	return;
+}
+
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+	int host_no = cmd->host->unique_id;
+
+	if (ppa_hosts[host_no].cur_cmd) {
+		printk("PPA: bug in ppa_queuecommand\n");
+		return 0;
+	}
+	sti();
+	ppa_hosts[host_no].cur_cmd = cmd;
+	ppa_hosts[host_no].done = done;
+
+	if (!ppa_start(cmd)) {
+		cmd->result = ppa_hosts[host_no].error_code << 16;
+		ppa_hosts[host_no].cur_cmd = 0;
+		done(cmd);
+		return 0;
+	}
+	ppa_interrupt(ppa_hosts + host_no);
+
+	return 0;
+}
+
+/*
+ * Apparently the the disk->capacity attribute is off by 1 sector 
+ * for all disk drives.  We add the one here, but it should really
+ * be done in sd.c.  Even if it gets fixed there, this will still
+ * work.
+ */
+int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+	ip[0] = 0x40;
+	ip[1] = 0x20;
+	ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+	if (ip[2] > 1024) {
+		ip[0] = 0xff;
+		ip[1] = 0x3f;
+		ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+		if (ip[2] > 1023)
+			ip[2] = 1023;
+	}
+	return 0;
+}
+
+int ppa_abort(Scsi_Cmnd * cmd)
+{
+	int host_no = cmd->host->unique_id;
+
+	ppa_hosts[host_no].abort_flag = 1;
+	ppa_hosts[host_no].error_code = DID_ABORT;
+	if (ppa_hosts[host_no].ppa_wait_q)
+		wake_up(&ppa_hosts[host_no].ppa_wait_q);
+
+	return SCSI_ABORT_SNOOZE;
+}
+
+int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
+{
+	int host_no = cmd->host->unique_id;
+
+	ppa_hosts[host_no].abort_flag = 2;
+	ppa_hosts[host_no].error_code = DID_RESET;
+	if (ppa_hosts[host_no].ppa_wait_q)
+		wake_up(&ppa_hosts[host_no].ppa_wait_q);
+
+	return SCSI_RESET_PUNT;
+}
+
+
+
+/***************************************************************************
+ *                   Parallel port probing routines                        *
+ ***************************************************************************/
 
-Scsi_Host_Template      driver_template = PPA;
 
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
 #include  "scsi_module.c"
-
 #endif
 
+/*
+ * Start of Chipset kludges
+ */
+
+int ppa_detect(Scsi_Host_Template * host)
+{
+	struct Scsi_Host *hreg;
+	int rs;
+	int ports;
+	int i, nhosts;
+	struct parport *pb = parport_enumerate();
+
+	printk("PPA driver version: %s\n", PPA_VERSION);
+	nhosts = 0;
+
+	for (i = 0; pb; i++, pb=pb->next) {
+		int modes;
+
+		/* transfer global values here */
+		if (ppa_speed >= 0)
+			ppa_hosts[i].speed = ppa_speed;
+		if (ppa_speed_fast >= 0)
+			ppa_hosts[i].speed_fast = ppa_speed_fast;
+
+		ppa_hosts[i].dev =
+		    parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+			      NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
+
+		/* Claim the bus so it remembers what we do to the control
+		 * registers. [ CTR and ECP ]
+		 */
+		ppa_pb_claim(i);
+		w_ctr(i, 0x0c);
+		modes = ppa_hosts[i].dev->port->modes;
+
+		ppa_hosts[i].mode = PPA_NIBBLE;
+		if (modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) {
+			ppa_hosts[i].mode = PPA_EPP_32;
+			printk("PPA: Parport [ EPP ]\n");
+		} else if (modes & PARPORT_MODE_ECP) {
+			w_ecr(i, 0x20);
+			ppa_hosts[i].mode = PPA_PS2;
+			printk("PPA: Parport [ ECP in PS2 submode ]\n");
+		} else if (modes & PARPORT_MODE_PS2) {
+			ppa_hosts[i].mode = PPA_PS2;
+			printk("PPA: Parport [ PS2 ]\n");
+		}
+		/* Done configuration */
+		ppa_pb_release(i);
+
+		rs = ppa_init(i);
+		if (rs) {
+			parport_unregister_device(ppa_hosts[i].dev);
+			continue;
+		}
+		/* now the glue ... */
+		switch (ppa_hosts[i].mode) {
+		case PPA_NIBBLE:
+		case PPA_PS2:
+			ports = 3;
+			break;
+		case PPA_EPP_8:
+		case PPA_EPP_16:
+		case PPA_EPP_32:
+			ports = 8;
+			break;
+		default:	/* Never gets here */
+			continue;
+		}
+
+		host->can_queue = PPA_CAN_QUEUE;
+		host->sg_tablesize = ppa_sg;
+		hreg = scsi_register(host, 0);
+		hreg->io_port = pb->base;
+		hreg->n_io_port = ports;
+		hreg->dma_channel = -1;
+		hreg->unique_id = i;
+		ppa_hosts[i]..host = hreg->host_no;
+		nhosts++;
+	}
+	if (nhosts == 0)
+		return 0;
+	else
+		return 1;	/* return number of hosts detected */
+}
+
+/* This is to give the ppa driver a way to modify the timings (and other
+ * parameters) by writing to the /proc/scsi/ppa/0 file.
+ * Very simple method really... (To simple, no error checking :( )
+ * Reason: Kernel hackers HATE having to unload and reload modules for
+ * testing...
+ * Also gives a method to use a script to obtain optimum timings (TODO)
+ */
+
+static int ppa_strncmp(const char *a, const char *b, int len)
+{
+	int loop;
+	for (loop = 0; loop < len; loop++)
+		if (a[loop] != b[loop])
+			return 1;
+
+	return 0;
+}
+
+static int ppa_proc_write(int hostno, char *buffer, int length)
+{
+	unsigned long x;
+	const char *inv_num = "ppa /proc entry passed invalid number\n";
+
+	if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
+		x = simple_strtoul(buffer + 15, NULL, 0);
+		if (x <= ppa_hosts[hostno].speed)
+			ppa_hosts[hostno].speed_fast = x;
+		else
+			printk(inv_num);
+		return length;
+	}
+	if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
+		x = simple_strtoul(buffer + 10, NULL, 0);
+		if (x >= ppa_hosts[hostno].speed_fast)
+			ppa_hosts[hostno].speed = x;
+		else
+			printk(inv_num);
+		return length;
+	}
+	if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
+		x = simple_strtoul(buffer + 10, NULL, 0);
+		ppa_hosts[hostno].epp_speed = x;
+		return length;
+	}
+	if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
+		x = simple_strtoul(buffer + 12, NULL, 0);
+		ppa_hosts[hostno].timeout = x;
+		return length;
+	}
+	if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+		x = simple_strtoul(buffer + 5, NULL, 0);
+		ppa_hosts[hostno].mode = x;
+		return length;
+	}
+	printk("ppa /proc: invalid variable\n");
+	return (-EINVAL);
+}
+
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+		  int length, int hostno, int inout)
+{
+	int i;
+	int size, len = 0;
+	off_t begin = 0;
+	off_t pos = 0;
+
+	for (i = 0; i < 4; i++)
+		if (ppa_hosts[i].host == hostno)
+			break;
+
+	if (inout)
+		return ppa_proc_write(i, buffer, length);
+
+	size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+	len += size;
+	pos = begin + len;
+	size = sprintf(buffer + len, "Parport  : %s\n",
+		       ppa_hosts[i].dev->port->name);
+	len += size;
+	pos = begin + len;
+
+	size = sprintf(buffer + len, "Mode    : %s\n",
+		       PPA_MODE_STRING[ppa_hosts[i].mode]);
+	len += size;
+	pos = begin + len;
+
+	size = sprintf(buffer + len, "\nTiming Parameters\n");
+	len += size;
+	pos = begin + len;
+
+	size = sprintf(buffer + len, "ppa_speed       %i\n",
+		       ppa_hosts[i].speed);
+	len += size;
+	pos = begin + len;
+
+	size = sprintf(buffer + len, "ppa_speed_fast  %i\n",
+		       ppa_hosts[i].speed_fast);
+	len += size;
+	pos = begin + len;
+
+	if (IN_EPP_MODE(ppa_hosts[i].mode)) {
+		size = sprintf(buffer + len, "epp_speed       %i\n",
+			       ppa_hosts[i].epp_speed);
+		len += size;
+		pos = begin + len;
+
+		size = sprintf(buffer + len, "\nInternal Counters\n");
+		len += size;
+		pos = begin + len;
+
+		size = sprintf(buffer + len, "epp_timeout     %i\n",
+			       ppa_hosts[i].timeout);
+		len += size;
+		pos = begin + len;
+	}
+	*start = buffer + (offset + begin);
+	len -= (offset - begin);
+	if (len > length)
+		len = length;
+	return len;
+}
 /* end of ppa.c */
diff -u --recursive --new-file v2.1.32/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.1.32/linux/drivers/scsi/ppa.h	Fri Dec 27 02:03:23 1996
+++ linux/drivers/scsi/ppa.h	Wed Apr  9 23:32:46 1997
@@ -1,44 +1,90 @@
+/*  Driver for the PPA3 parallel port SCSI HBA embedded in 
+ * the Iomega ZIP drive
+ * 
+ * (c) 1996     Grant R. Guenther  grant@torque.net
+ */
+
 #ifndef _PPA_H
 #define _PPA_H
 
-/*  Driver for the PPA3 parallel port SCSI HBA embedded in 
-    the Iomega ZIP drive
+#define   PPA_VERSION   "Curtin 1-08-BETA"
 
-	(c) 1996	Grant R. Guenther  grant@torque.net
-*/
+/* This driver reqires a 1.3.37 kernel or higher!! */
+
+/* Use the following to enable certain chipset support
+ * Default is PEDANTIC = 3
+ */
+
+#include <linux/config.h>
+
+#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
+#define CONFIG_SCSI_PPA_HAVE_PEDANTIC	3
+#endif
+#ifndef CONFIG_SCSI_PPA_EPP_TIME
+#define CONFIG_SCSI_PPA_EPP_TIME	64
+#endif
+
+/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+
+#include  <linux/stddef.h>
+#include  <linux/module.h>
+#include  <linux/kernel.h>
+#include  <linux/tqueue.h>
+#include  <linux/ioport.h>
+#include  <linux/delay.h>
+#include  <linux/proc_fs.h>
+#include  <linux/stat.h>
+#include  <linux/blk.h>
+
+#include  <asm/io.h>
+#include  "sd.h"
+#include  "hosts.h"
+#include  <linux/parport.h>
+/* batteries not included :-) */
 
 #define	PPA_INITIATOR	7
 
-int ppa_detect(Scsi_Host_Template * );
-const char * ppa_info(struct Scsi_Host *);
+int ppa_detect(Scsi_Host_Template *);
+const char *ppa_info(struct Scsi_Host *);
 int ppa_command(Scsi_Cmnd *);
-int ppa_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int ppa_abort(Scsi_Cmnd *);
 int ppa_reset(Scsi_Cmnd *, unsigned int);
-int ppa_biosparam(Disk *, kdev_t, int[]);
-
-#define PPA {			\
-	0,			\
-	0,			\
-	0,			\
-	0,		        \
-	0,			\
-	ppa_detect,		\
-	0,			\
-	ppa_info,		\
-	ppa_command, 		\
-	ppa_queuecommand,	\
-	ppa_abort,		\
-	ppa_reset,		\
-	0,			\
-	ppa_biosparam,		\
-	0,			\
-	PPA_INITIATOR,		\
-	SG_NONE,		\
-	1,			\
-	0,			\
-	0,			\
-	DISABLE_CLUSTERING	\
+int ppa_proc_info(char *, char **, off_t, int, int, int);
+int ppa_biosparam(Disk *, kdev_t, int*);
+int ppa_release(struct Scsi_Host *);
+
+#ifndef	MODULE
+#ifdef	PPA_CODE
+#define SKIP_PROC_DIR
+#endif
+#endif
+
+#ifndef SKIP_PROC_DIR
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#endif /* !PPA_CODE => hosts.c */
+
+#define PPA {	/* next */	 	0, \
+		/* usage_count */	0, \
+		/* proc_dir */		&proc_scsi_ppa, \
+		/* proc_info */		ppa_proc_info, \
+		/* name */		"Iomega ZIP/JAZ Traveller", \
+		/* detect */		ppa_detect, \
+		/* release */		ppa_release, \
+		/* info */		0, \
+		/* command */		ppa_command, \
+		/* queuecommand */	ppa_queuecommand, \
+		/* abort */		ppa_abort, \
+		/* reset */		ppa_reset, \
+		/* slave_attach */	0, \
+		/* bios_param */	ppa_biosparam, \
+		/* can_queue */		0, \
+		/* this_id */		PPA_INITIATOR, \
+		/* sg_tablesize */	SG_ALL, \
+		/* cmd_per_lun */	1, \
+		/* present */		0, \
+		/* unchecked_isa_dma */	0, \
+		/* use_clustering */	ENABLE_CLUSTERING \
 }
-
-#endif /* _PPA_H */
+#endif				/* _PPA_H */
diff -u --recursive --new-file v2.1.32/linux/fs/Config.in linux/fs/Config.in
--- v2.1.32/linux/fs/Config.in	Mon Apr  7 11:35:30 1997
+++ linux/fs/Config.in	Mon Apr  7 18:43:43 1997
@@ -18,12 +18,23 @@
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
   if [ "$CONFIG_NFS_FS" = "y" ]; then
-    define_bool CONFIG_SUNRPC y
-    define_bool CONFIG_LOCKD y
     bool '   Root file system on NFS' CONFIG_ROOT_NFS
     if [ "$CONFIG_ROOT_NFS" = "y" ]; then
       bool '      BOOTP support' CONFIG_RNFS_BOOTP
       bool '      RARP support' CONFIG_RNFS_RARP
+    fi
+  fi
+  tristate 'NFS server support' CONFIG_NFSD
+  if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
+    define_bool CONFIG_SUNRPC y
+    define_bool CONFIG_LOCKD y
+  else
+    if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" ]; then
+      define_bool CONFIG_SUNRPC m
+      define_bool CONFIG_LOCKD m
+    else
+      define_bool CONFIG_SUNRPC n
+      define_bool CONFIG_LOCKD n
     fi
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
diff -u --recursive --new-file v2.1.32/linux/fs/inode.c linux/fs/inode.c
--- v2.1.32/linux/fs/inode.c	Thu Mar 27 14:40:06 1997
+++ linux/fs/inode.c	Mon Apr  7 14:17:10 1997
@@ -173,6 +173,7 @@
 {
 	struct wait_queue * wait;
 
+	inode->i_count++;
 	truncate_inode_pages(inode, 0);
 	wait_on_inode(inode);
 	if (IS_WRITABLE(inode)) {
@@ -182,6 +183,7 @@
 	remove_inode_hash(inode);
 	remove_inode_free(inode);
 	wait = ((volatile struct inode *) inode)->i_wait;
+	inode->i_count--;
 	if (inode->i_count)
 		nr_free_inodes++;
 	memset(inode,0,sizeof(*inode));
diff -u --recursive --new-file v2.1.32/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.32/linux/fs/isofs/inode.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/isofs/inode.c	Tue Apr  8 11:58:19 1997
@@ -554,7 +554,7 @@
 	    if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
 	      {
 
-		printk("_isofs_bmap: block>= EOF(%d, %d)", block, 
+		printk("_isofs_bmap: block>= EOF(%d, %d)\n", block, 
 		       inode->i_size);
 	      }
 	    return 0;
diff -u --recursive --new-file v2.1.32/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- v2.1.32/linux/fs/lockd/clntproc.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/clntproc.c	Mon Apr  7 18:43:43 1997
@@ -322,7 +322,8 @@
 	int		status;
 
 	if (!host->h_monitored && nsm_monitor(host) < 0) {
-		printk(KERN_NOTICE "lockd: failed to monitor %s", host->h_name);
+		printk(KERN_NOTICE "lockd: failed to monitor %s\n",
+					host->h_name);
 		return -ENOLCK;
 	}
 
@@ -496,10 +497,10 @@
 		/* Everything's good */
 		break;
 	case NLM_LCK_DENIED_NOLOCKS:
-		dprintk("lockd: CANCEL failed (server has no locks)");
+		dprintk("lockd: CANCEL failed (server has no locks)\n");
 		goto retry_cancel;
 	default:
-		printk(KERN_NOTICE "lockd: weird return %d for CANCEL call",
+		printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
 			req->a_res.status);
 	}
 
diff -u --recursive --new-file v2.1.32/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.32/linux/fs/nfs/inode.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/nfs/inode.c	Mon Apr  7 18:43:43 1997
@@ -25,11 +25,12 @@
 #include <linux/locks.h>
 #include <linux/unistd.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
 #include <linux/nfs_fs.h>
 #include <linux/lockd/bind.h>
 
 #include <asm/system.h>
-# include <asm/uaccess.h>
+#include <asm/uaccess.h>
 
 #define NFSDBG_FACILITY		NFSDBG_VFS
 
@@ -50,6 +51,7 @@
 	NULL
 };
 
+struct rpc_stat			nfs_rpcstat = { &nfs_program };
 
 /*
  * The "read_inode" function doesn't actually do anything:
@@ -438,7 +440,7 @@
 init_nfs_fs(void)
 {
 #ifdef CONFIG_PROC_FS
-	rpcstat_register(&nfs_rpcstat);
+	rpc_proc_register(&nfs_rpcstat);
 #endif
         return register_filesystem(&nfs_fs_type);
 }
@@ -462,7 +464,7 @@
 cleanup_module(void)
 {
 #ifdef CONFIG_PROC_FS
-	rpcstat_unregister(&nfs_rpcstat);
+	rpc_proc_unregister("nfs");
 #endif
 	unregister_filesystem(&nfs_fs_type);
 	nfs_free_dircache();
diff -u --recursive --new-file v2.1.32/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- v2.1.32/linux/fs/nfs/nfs2xdr.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/nfs/nfs2xdr.c	Mon Apr  7 18:43:43 1997
@@ -625,25 +625,3 @@
 	nfs_version,
 	&nfs_rpcstat,
 };
-
-/*
- * RPC stats support
- */
-static int
-nfs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	return rpcstat_get_info(&nfs_rpcstat, buffer, start, offset, length);
-}
-
-static struct proc_dir_entry	proc_nfsclnt = {
-	0, 3, "nfs",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	6, &proc_net_inode_operations,
-	nfs_get_info
-};
-
-struct rpc_stat			nfs_rpcstat = {
-	NULL,			/* next */
-	&proc_nfsclnt,		/* /proc/net directory entry */
-	&nfs_program,		/* RPC program */
-};
diff -u --recursive --new-file v2.1.32/linux/fs/nfsd/stats.c linux/fs/nfsd/stats.c
--- v2.1.32/linux/fs/nfsd/stats.c	Mon Apr  7 11:35:31 1997
+++ linux/fs/nfsd/stats.c	Mon Apr  7 18:43:43 1997
@@ -1,18 +1,16 @@
 /*
- * nfsstat.c	procfs-based user access to knfsd statistics
+ * linux/fs/nfsd/stats.c
+ *
+ * procfs-based user access to knfsd statistics
+ *
+ * /proc/net/rpc/nfsd
  *
- * /proc/net/nfssrv
  * Format:
- *	net <packets> <udp> <tcp> <tcpconn>
- *	rpc <packets> <badfmt> <badclnt>
- *	auth <flavor> <creds> <upcalls> <badauth> <badverf> <authrej>
- *	fh  <hits> <misses> <avg_util> <stale> <cksum> <badcksum>
  *	rc <hits> <misses> <nocache>
- *	proto <version> <nrprocs>
- *	<calls> <time_msec>
- *	... (for each procedure and protocol version)
+ *			Statistsics for the reply cache
+ *	plus generic RPC stats (see net/sunrpc/stats.c)
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/kernel.h>
@@ -26,28 +24,13 @@
 #include <linux/nfsd/stats.h>
 
 struct nfsd_stats	nfsdstats;
-
-static int		nfsd_get_info(char *, char **, off_t, int, int);
-
-#ifndef PROC_NET_NFSSRV
-# define PROC_NET_NFSSRV	0
-#endif
-
-static struct proc_dir_entry proc_nfssrv = {
-	PROC_NET_NFSSRV, 4, "nfsd",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	6, &proc_net_inode_operations,
-	nfsd_get_info
-};
-
-struct svc_stat		nfsd_svcstats = {
-	NULL, &proc_nfssrv, &nfsd_program,
-};
+struct svc_stat		nfsd_svcstats = { &nfsd_program, };
 
 static int
-nfsd_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
+				int *eof, void *data)
 {
-	int			len;
+	int	len;
 
 	len = sprintf(buffer,
 		"rc %d %d %d\n",
@@ -55,38 +38,45 @@
 			nfsdstats.rcmisses,
 			nfsdstats.rcnocache);
 
+	/* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
+	*eof = 0;
+
 	/*
-	 * Append generic nfsd RPC statistics
+	 * Append generic nfsd RPC statistics if there's room for it.
 	 */
-	if (offset >= len) {
-		offset -= len;
-		len = svcstat_get_info(&nfsd_svcstats, buffer, start,
-					offset, length);
-#if 0
-	} else if (len < length) {
-		len = svcstat_get_info(&nfsd_svcstats, buffer + len, start,
-					offset - len, length - len);
-#endif
+	if (len <= offset) {
+		len = svc_proc_read(buffer, start, offset - len, count,
+				eof, data);
+		return len;
+	}
+
+	if (len < count) {
+		len += svc_proc_read(buffer + len, start, 0, count - len,
+				eof, data);
 	}
 
 	if (offset >= len) {
 		*start = buffer;
 		return 0;
 	}
+
 	*start = buffer + offset;
-	if ((len -= offset) > length)
-		len = length;
+	if ((len -= offset) > count)
+		return count;
 	return len;
 }
 
 void
 nfsd_stat_init(void)
 {
-	svcstat_register(&nfsd_svcstats);
+	struct proc_dir_entry	*ent;
+
+	if ((ent = svc_proc_register(&nfsd_svcstats)) != 0)
+		ent->read_proc = nfsd_proc_read;
 }
 
 void
 nfsd_stat_shutdown(void)
 {
-	svcstat_unregister(&nfsd_svcstats);
+	svc_proc_unregister("nfsd");
 }
diff -u --recursive --new-file v2.1.32/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v2.1.32/linux/fs/proc/procfs_syms.c	Mon Mar 17 14:54:31 1997
+++ linux/fs/proc/procfs_syms.c	Mon Apr  7 18:43:43 1997
@@ -14,6 +14,8 @@
 
 EXPORT_SYMBOL(proc_register);
 EXPORT_SYMBOL(proc_unregister);
+EXPORT_SYMBOL(create_proc_entry);
+EXPORT_SYMBOL(remove_proc_entry);
 EXPORT_SYMBOL(proc_root);
 EXPORT_SYMBOL(proc_get_inode);
 EXPORT_SYMBOL(in_group_p);
diff -u --recursive --new-file v2.1.32/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.1.32/linux/include/asm-i386/irq.h	Thu Mar 27 14:40:06 1997
+++ linux/include/asm-i386/irq.h	Mon Apr  7 14:02:43 1997
@@ -156,6 +156,18 @@
 	"call "SYMBOL_NAME_STR(smp_##x)"\n\t" \
 	"jmp ret_from_intr\n");
 
+#define BUILD_SMP_TIMER_INTERRUPT(x) \
+asmlinkage void x(struct pt_regs * regs); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(x) ":\n\t" \
+	SAVE_MOST \
+	"movl %esp,%eax\n\t" \
+	"pushl %eax\n\t" \
+	"call "SYMBOL_NAME_STR(smp_##x)"\n\t" \
+	"addl $4,%esp\n\t" \
+	RESTORE_MOST);
+
 #endif /* __SMP__ */
 
 #define BUILD_IRQ(chip,nr,mask) \
diff -u --recursive --new-file v2.1.32/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.32/linux/include/asm-i386/uaccess.h	Thu Feb  6 02:53:45 1997
+++ linux/include/asm-i386/uaccess.h	Wed Apr  9 23:31:13 1997
@@ -423,7 +423,7 @@
 
 #define __copy_from_user(to,from,n)			\
 	(__builtin_constant_p(n) ?			\
-	 __constant_copy_from_user_nockeck((to),(from),(n)) :	\
+	 __constant_copy_from_user_nocheck((to),(from),(n)) :	\
 	 __generic_copy_from_user_nocheck((to),(from),(n)))
 
 
diff -u --recursive --new-file v2.1.32/linux/include/linux/lp.h linux/include/linux/lp.h
--- v2.1.32/linux/include/linux/lp.h	Thu Nov 30 04:03:38 1995
+++ linux/include/linux/lp.h	Wed Apr  9 21:30:31 1997
@@ -78,19 +78,31 @@
 #define LP_TIMEOUT_INTERRUPT	(60 * HZ)
 #define LP_TIMEOUT_POLLED	(10 * HZ)
 
-#define LP_B(minor)	lp_table[(minor)].base		/* IO address */
 #define LP_F(minor)	lp_table[(minor)].flags		/* flags for busy, etc. */
-#define LP_S(minor)	inb_p(LP_B((minor)) + 1)	/* status port */
-#define LP_C(minor)	(lp_table[(minor)].base + 2)	/* control port */
 #define LP_CHAR(minor)	lp_table[(minor)].chars		/* busy timeout */
 #define LP_TIME(minor)	lp_table[(minor)].time		/* wait time */
 #define LP_WAIT(minor)	lp_table[(minor)].wait		/* strobe wait */
-#define LP_IRQ(minor)	lp_table[(minor)].irq		/* interrupt # */
+#define LP_IRQ(minor)	lp_table[(minor)].dev->port->irq /* interrupt # */
 							/* 0 means polled */
 #define LP_STAT(minor)	lp_table[(minor)].stats		/* statistics area */
-
 #define LP_BUFFER_SIZE 256
 
+#define LP_BASE(x)	lp_table[(x)].dev->port->base
+
+#define r_dtr(x)	inb(LP_BASE(x))
+#define r_str(x)	inb(LP_BASE(x)+1)
+#define r_ctr(x)	inb(LP_BASE(x)+2)
+#define r_epp(x)	inb(LP_BASE(x)+4)
+#define r_fifo(x)	inb(LP_BASE(x)+0x400)
+#define r_ecr(x)	inb(LP_BASE(x)+0x402)
+
+#define w_dtr(x,y)	outb((y), LP_BASE(x))
+#define w_str(x,y)	outb((y), LP_BASE(x)+1)
+#define w_ctr(x,y)	outb((y), LP_BASE(x)+2)
+#define w_epp(x,y)	outb((y), LP_BASE(x)+4)
+#define w_fifo(x,y)	outb((y), LP_BASE(x)+0x400)
+#define w_ecr(x,y)	outb((y), LP_BASE(x)+0x402)
+
 struct lp_stats {
 	unsigned long chars;
 	unsigned long sleeps;
@@ -101,8 +113,7 @@
 };
 
 struct lp_struct {
-	int base;
-	unsigned int irq;
+	struct ppd *dev;
 	int flags;
 	unsigned int chars;
 	unsigned int time;
diff -u --recursive --new-file v2.1.32/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.1.32/linux/include/linux/parport.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/parport.h	Wed Apr  9 21:30:31 1997
@@ -0,0 +1,213 @@
+/* $Id: parport.h,v 1.1.2.5 1997/03/29 21:08:31 phil Exp $ */
+
+#ifndef _PARPORT_H_
+#define _PARPORT_H_
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+
+/* Maximum of 8 ports per machine */
+#define PARPORT_MAX  8 
+
+/* Type classes for Plug-and-Play probe */
+
+typedef enum {
+	PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
+	PARPORT_CLASS_PRINTER,
+	PARPORT_CLASS_MODEM,
+	PARPORT_CLASS_NET,
+	PARPORT_CLASS_HDC,              /* Hard disk controller */
+	PARPORT_CLASS_PCMCIA,
+	PARPORT_CLASS_MEDIA,            /* Multimedia device */
+	PARPORT_CLASS_FDC,              /* Floppy disk controller */
+	PARPORT_CLASS_PORTS,
+	PARPORT_CLASS_SCANNER,
+	PARPORT_CLASS_DIGCAM,
+	PARPORT_CLASS_OTHER,            /* Anything else */
+	PARPORT_CLASS_UNSPEC            /* No CLS field in ID */
+} parport_device_class;
+
+struct parport_device_info {
+	parport_device_class class;
+	char *mfr;
+	char *model;
+	char *cmdset;
+	char *description;
+};
+
+/* Definitions for parallel port sharing */
+
+/* Forward declare some stuff so we can use mutually circular structures */
+struct ppd;
+struct parport;
+
+/* Each device can have two callback functions:
+ *  1) a preemption function, called by the resource manager to request
+ *     that the driver relinquish control of the port.  The driver should
+ *     return zero if it agrees to release the port, and nonzero if it 
+ *     refuses.  Do not call parport_release() - the kernel will do this
+ *     implicitly.
+ *
+ *  2) a wake-up function, called by the resource manager to tell drivers
+ *     that the port is available to be claimed.  If a driver wants to use
+ *     the port, it should call parport_claim() here.  The return value from
+ *     this function is ignored.
+ */
+typedef int (*callback_func) (void *);
+
+/* This is an ordinary kernel IRQ handler routine.
+ * The dev_id field (void *) will point the the port structure
+ * associated with the interrupt request (to allow IRQ sharing)
+ * Please make code IRQ sharing as this function may be called
+ * when it isn't meant for you...
+ */
+typedef void (*irq_handler_func) (int, void *, struct pt_regs *);
+
+/* A parallel port device */
+struct ppd {
+	char *name;
+	struct parport *port;	/* The port this is associated with */
+	callback_func preempt;	/* preemption function */
+	callback_func wakeup;	/* kick function */
+	void *private;
+	irq_handler_func irq_func;
+	int flags;
+	unsigned char ctr;	/* SPP CTR register */
+	unsigned char ecr;	/* ECP ECR register */
+	struct ppd *next;
+	struct ppd *prev;
+};
+
+/* A parallel port */
+struct parport {
+	unsigned int base;	/* base address */
+	unsigned int size;	/* IO extent */
+	char *name;
+	int irq;		/* interrupt (or -1 for none) */
+	int dma;
+	unsigned int modes;
+	struct ppd *devices;
+	struct ppd *cad;	/* port owner */
+	struct ppd *lurker;
+	unsigned int ctr;	/* SPP CTR register */
+	unsigned int ecr;	/* ECP ECR register */
+	struct parport *next;
+        unsigned int flags; 
+	struct proc_dir_entry *proc_dir;
+	struct parport_device_info probe_info; 
+};
+
+/* parport_register_port registers a new parallel port at the given address (if
+ * one does not already exist) and returns a pointer to it.  This entails
+ * claiming the I/O region, IRQ and DMA.
+ * NULL is returned if initialisation fails. 
+ */
+struct parport *parport_register_port(unsigned long base, int irq, int dma);
+
+/* parport_in_use returns nonzero if there are devices attached to a port. */
+#define parport_in_use(x)  ((x)->devices != NULL)
+
+/* parport_destroy blows away a parallel port.  This fails if any devices are
+ * registered.
+ */
+void parport_destroy(struct parport *);
+
+/* parport_enumerate returns a pointer to the linked list of all the ports
+ * in this machine.
+ */
+struct parport *parport_enumerate(void);
+
+/* parport_register_device declares that a device is connected to a port, and 
+ * tells the kernel all it needs to know.  
+ * pf is the preemption function (may be NULL for a transient driver)
+ * kf is the wake-up function (may be NULL for a transient driver)
+ * irq_func is the interrupt handler (may be NULL for no interrupts)
+ * Only one lurking driver can be used on a given port. 
+ * handle is a user pointer that gets handed to callback functions. 
+ */
+struct ppd *parport_register_device(struct parport *port, const char *name,
+				    callback_func pf, callback_func kf,
+				    irq_handler_func irq_func, int flags,
+				    void *handle);
+
+/* parport_deregister causes the kernel to forget about a device */
+void parport_unregister_device(struct ppd *dev);
+
+/* parport_claim tries to gain ownership of the port for a particular driver.
+ * This may fail (return non-zero) if another driver is busy.  If this
+ * driver has registered an interrupt handler, it will be enabled. 
+ */
+int parport_claim(struct ppd *dev);
+
+/* parport_release reverses a previous parport_claim.  This can never fail, 
+ * though the effects are undefined (except that they are bad) if you didn't
+ * previously own the port.  Once you have released the port you should make
+ * sure that neither your code nor the hardware on the port tries to initiate
+ * any communication without first re-claiming the port.
+ * If you mess with the port state (enabling ECP for example) you should
+ * clean up before releasing the port. 
+ */
+void parport_release(struct ppd *dev);
+
+/* The "modes" entry in parport is a bit field representing the following
+ * modes.
+ * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
+ * 100% compatible with EPP.
+ */
+#define PARPORT_MODE_SPP	        0x0001
+#define PARPORT_MODE_PS2		0x0002
+#define PARPORT_MODE_EPP		0x0004
+#define PARPORT_MODE_ECP		0x0008
+#define PARPORT_MODE_ECPEPP		0x0010
+#define PARPORT_MODE_ECR		0x0020  /* ECR Register Exists */
+#define PARPORT_MODE_ECPPS2		0x0040
+
+/* Flags used to identify what a device does
+ */
+#define PARPORT_DEV_TRAN	        0x0000
+#define PARPORT_DEV_LURK	        0x0001
+
+#define PARPORT_FLAG_COMA		1
+
+extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
+extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
+				   char);
+
+/* Prototypes from parport_procfs */
+extern int parport_proc_register(struct parport *pp);
+extern void parport_proc_unregister(struct parport *pp);
+
+/* Prototypes from parport_ksyms.c */
+extern void dec_parport_count(void);
+extern void inc_parport_count(void);
+
+extern int parport_probe(struct parport *port, char *buffer, int len);
+extern void parport_probe_one(struct parport *port);
+
+/* Primitive port access functions */
+extern inline void parport_w_ctrl(struct parport *port, int val) 
+{
+	outb(val, port->base+2);
+}
+
+extern inline int parport_r_ctrl(struct parport *port)
+{
+	return inb(port->base+2);
+}
+
+extern inline void parport_w_data(struct parport *port, int val)
+{
+	outb(val, port->base);
+}
+
+extern inline int parport_r_data(struct parport *port)
+{
+	return inb(port->base);
+}
+
+extern inline int parport_r_status(struct parport *port)
+{
+	return inb(port->base+1);
+}
+
+#endif /* _PARPORT_H_ */
diff -u --recursive --new-file v2.1.32/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.32/linux/include/linux/proc_fs.h	Thu Mar 27 14:40:10 1997
+++ linux/include/linux/proc_fs.h	Wed Apr  9 23:31:26 1997
@@ -48,7 +48,8 @@
 	PROC_RTC,
 	PROC_LOCKS,
 	PROC_ZORRO,
-	PROC_SLABINFO
+	PROC_SLABINFO,
+	PROC_PARPORT
 };
 
 enum pid_directory_inos {
diff -u --recursive --new-file v2.1.32/linux/include/linux/scc.h linux/include/linux/scc.h
--- v2.1.32/linux/include/linux/scc.h	Wed Oct 30 22:05:23 1996
+++ linux/include/linux/scc.h	Tue Apr  8 11:37:47 1997
@@ -1,4 +1,4 @@
-/* $Id: scc.h,v 1.28 1996/10/30 20:01:15 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */
 
 #ifndef	_SCC_H
 #define	_SCC_H
@@ -14,55 +14,56 @@
 
 /* DEV ioctl() commands */
 
-#define SIOCSCCRESERVED (SIOCDEVPRIVATE+0)
-#define SIOCSCCCFG	(SIOCDEVPRIVATE+1)
-#define SIOCSCCINI	(SIOCDEVPRIVATE+2)
-#define SIOCSCCCHANINI	(SIOCDEVPRIVATE+3)
-#define SIOCSCCSMEM	(SIOCDEVPRIVATE+4)
-#define SIOCSCCGKISS	(SIOCDEVPRIVATE+5)
-#define SIOCSCCSKISS	(SIOCDEVPRIVATE+6)
-#define SIOCSCCGSTAT	(SIOCDEVPRIVATE+7)
+enum SCC_ioctl_cmds {
+	SIOCSCCRESERVED = SIOCDEVPRIVATE,
+	SIOCSCCCFG,
+	SIOCSCCINI,
+	SIOCSCCCHANINI,
+	SIOCSCCSMEM,
+	SIOCSCCGKISS,
+	SIOCSCCSKISS,
+	SIOCSCCGSTAT,
+	SIOCSCCCAL
+};
 
 /* magic number */
 
 #define SCC_MAGIC	0x8530		/* ;-) */
 
-/* KISS state machine */
-
-#define	KISS_IDLE	0
-#define KISS_DATA	1
-#define KISS_ESCAPE	2
-#define KISS_RXFRAME	3
-
 /* Device parameter control (from WAMPES) */
 
-#define	PARAM_TXDELAY	1
-#define	PARAM_PERSIST	2
-#define	PARAM_SLOTTIME	3
-#define	PARAM_TXTAIL	4
-#define	PARAM_FULLDUP	5
-#define PARAM_SOFTDCD	6	/* was: PARAM_HW */
-#define PARAM_MUTE	7	/* ??? */
-#define PARAM_DTR       8
-#define PARAM_RTS	9
-#define PARAM_SPEED     10
-#define PARAM_ENDDELAY	11	/* ??? */
-#define PARAM_GROUP     12
-#define PARAM_IDLE      13
-#define PARAM_MIN       14
-#define	PARAM_MAXKEY	15
-#define PARAM_WAIT      16
-#define PARAM_MAXDEFER	17
-#define PARAM_TX        18
-#define PARAM_HWEVENT	31
-#define PARAM_RETURN	255	/* reset kiss mode */
+enum L1_params {
+	PARAM_DATA,
+	PARAM_TXDELAY,
+	PARAM_PERSIST,
+	PARAM_SLOTTIME,
+	PARAM_TXTAIL,
+	PARAM_FULLDUP,
+	PARAM_SOFTDCD,		/* was: PARAM_HW */
+	PARAM_MUTE,		/* ??? */
+	PARAM_DTR,
+	PARAM_RTS,
+	PARAM_SPEED,
+	PARAM_ENDDELAY,		/* ??? */
+	PARAM_GROUP,
+	PARAM_IDLE,
+	PARAM_MIN,
+	PARAM_MAXKEY,
+	PARAM_WAIT,
+	PARAM_MAXDEFER,
+	PARAM_TX,
+	PARAM_HWEVENT = 31,
+	PARAM_RETURN = 255	/* reset kiss mode */
+};
 
 /* fulldup parameter */
 
-#define KISS_DUPLEX_HALF	0	/* normal CSMA operation */
-#define KISS_DUPLEX_FULL	1	/* fullduplex, key down trx after transmission */
-#define KISS_DUPLEX_LINK	2	/* fullduplex, key down trx after 'idletime' sec */
-#define KISS_DUPLEX_OPTIMA	3	/* fullduplex, let the protocol layer control the hw */
+enum FULLDUP_modes {
+	KISS_DUPLEX_HALF,	/* normal CSMA operation */
+	KISS_DUPLEX_FULL,	/* fullduplex, key down trx after transmission */
+	KISS_DUPLEX_LINK,	/* fullduplex, key down trx after 'idletime' sec */
+	KISS_DUPLEX_OPTIMA	/* fullduplex, let the protocol layer control the hw */
+};
 
 /* misc. parameters */
 
@@ -71,9 +72,11 @@
 
 /* HWEVENT parameter */
 
-#define HWEV_DCD_ON	0
-#define HWEV_DCD_OFF	1
-#define HWEV_ALL_SENT	2
+enum HWEVENT_opts {
+	HWEV_DCD_ON,
+	HWEV_DCD_OFF,
+	HWEV_ALL_SENT
+};
 
 /* channel grouping */
 
@@ -82,20 +85,26 @@
 
 /* Tx/Rx clock sources */
 
-#define CLK_DPLL	0	/* normal halfduplex operation */
-#define CLK_EXTERNAL	1	/* external clocking (G3RUH/DF9IC modems) */
-#define CLK_DIVIDER	2	/* Rx = DPLL, Tx = divider (fullduplex with */
-				/* modems without clock regeneration */
+enum CLOCK_sources {
+	CLK_DPLL,	/* normal halfduplex operation */
+	CLK_EXTERNAL,	/* external clocking (G3RUH/DF9IC modems) */
+	CLK_DIVIDER,	/* Rx = DPLL, Tx = divider (fullduplex with */
+			/* modems without clock regeneration */
+	CLK_BRG		/* experimental fullduplex mode with DPLL/BRG for */
+			/* MODEMs without clock recovery */
+};
 
 /* Tx state */
 
-#define TXS_IDLE	0	/* Transmitter off, no data pending */
-#define TXS_BUSY	1	/* waiting for permission to send / tailtime */
-#define TXS_ACTIVE	2	/* Transmitter on, sending data */
-#define TXS_NEWFRAME	3	/* reset CRC and send (next) frame */
-#define TXS_IDLE2	4	/* Transmitter on, no data pending */
-#define TXS_WAIT	5	/* Waiting for Mintime to expire */
-#define TXS_TIMEOUT	6	/* We had a transmission timeout */
+enum TX_state {
+	TXS_IDLE,	/* Transmitter off, no data pending */
+	TXS_BUSY,	/* waiting for permission to send / tailtime */
+	TXS_ACTIVE,	/* Transmitter on, sending data */
+	TXS_NEWFRAME,	/* reset CRC and send (next) frame */
+	TXS_IDLE2,	/* Transmitter on, no data pending */
+	TXS_WAIT,	/* Waiting for Mintime to expire */
+	TXS_TIMEOUT	/* We had a transmission timeout */
+};
 
 typedef unsigned long io_port;	/* type definition for an 'io port address' */
 
@@ -123,7 +132,6 @@
 	unsigned int bufsize;	/* used buffersize */
 };
 
-
 struct scc_modem {
 	long speed;		/* Line speed, bps */
 	char clocksrc;		/* 0 = DPLL, 1 = external, 2 = divider */
@@ -159,11 +167,14 @@
 	unsigned int bufsize;
 };
 
+struct scc_calibrate {
+	unsigned int time;
+	unsigned char pattern;
+};
 
 #ifdef __KERNEL__
 
-#define TX_ON		1	/* command for scc_key_trx() */
-#define TX_OFF		0	/* dto */
+enum {TX_OFF, TX_ON};	/* command for scc_key_trx() */
 
 /* Vector masks in RR2B */
 
@@ -173,7 +184,6 @@
 #define RXINT		0x04
 #define SPINT		0x06
 
-
 #ifdef SCC_DELAY
 #define Inb(port)	inb_p(port)
 #define Outb(port, val)	outb_p(val, port)
@@ -210,7 +220,7 @@
 	int init;			/* channel exists? */
 
 	struct device *dev;		/* link to device control structure */
-	struct enet_statistics dev_stat;/* device statistics */
+	struct net_device_stats dev_stat;/* device statistics */
 
 	char brand;			/* manufacturer of the board */
 	long clock;			/* used clock */
diff -u --recursive --new-file v2.1.32/linux/include/linux/sunrpc/stats.h linux/include/linux/sunrpc/stats.h
--- v2.1.32/linux/include/linux/sunrpc/stats.h	Mon Apr  7 11:35:32 1997
+++ linux/include/linux/sunrpc/stats.h	Wed Apr  9 23:34:09 1997
@@ -12,8 +12,6 @@
 #include <linux/proc_fs.h>
 
 struct rpc_stat {
-	struct rpc_stat *	next;
-	struct proc_dir_entry *	entry;
 	struct rpc_program *	program;
 
 	unsigned int		netcnt,
@@ -28,8 +26,6 @@
 };
 
 struct svc_stat {
-	struct svc_stat *	next;
-	struct proc_dir_entry *	entry;
 	struct svc_program *	program;
 
 	unsigned int		netcnt,
@@ -42,18 +38,18 @@
 				rpcbadclnt;
 };
 
-void		rpcstat_init(void);
-void		rpcstat_exit(void);
+void			rpc_proc_init(void);
+void			rpc_proc_exit(void);
 
-void		rpcstat_register(struct rpc_stat *);
-void		rpcstat_unregister(struct rpc_stat *);
-int		rpcstat_get_info(struct rpc_stat *, char *, char **,
-					off_t, int);
-void		rpcstat_zero_info(struct rpc_program *);
-void		svcstat_register(struct svc_stat *);
-void		svcstat_unregister(struct svc_stat *);
-int		svcstat_get_info(struct svc_stat *, char *, char **,
-					off_t, int);
-void		svcstat_zero_info(struct svc_program *);
+struct proc_dir_entry *	rpc_proc_register(struct rpc_stat *);
+void			rpc_proc_unregister(const char *);
+int			rpc_proc_read(char *, char **, off_t, int,
+					int *, void *);
+void			rpc_proc_zero(struct rpc_program *);
+struct proc_dir_entry *	svc_proc_register(struct svc_stat *);
+void			svc_proc_unregister(const char *);
+int			svc_proc_read(char *, char **, off_t, int,
+					int *, void *);
+void			svc_proc_zero(struct svc_program *);
 
 #endif /* _LINUX_SUNRPC_STATS_H */
diff -u --recursive --new-file v2.1.32/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v2.1.32/linux/include/linux/swapctl.h	Thu Feb  6 02:54:09 1997
+++ linux/include/linux/swapctl.h	Wed Apr  9 23:31:26 1997
@@ -31,17 +31,6 @@
 typedef struct swap_control_v5 swap_control_t;
 extern swap_control_t swap_control;
 
-typedef struct kswapd_control_v1
-{
-	unsigned int	maxpages;
-	unsigned int	pages_buff;
-	unsigned int	pages_shm;
-	unsigned int	pages_mmap;
-	unsigned int	pages_swap;
-} kswapd_control_v1;
-typedef kswapd_control_v1 kswapd_control_t;
-extern kswapd_control_t kswapd_ctl;
-
 typedef struct swapstat_v1
 {
 	unsigned int	wakeups;
diff -u --recursive --new-file v2.1.32/linux/init/main.c linux/init/main.c
--- v2.1.32/linux/init/main.c	Mon Apr  7 11:35:32 1997
+++ linux/init/main.c	Wed Apr  9 21:30:31 1997
@@ -79,7 +79,9 @@
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
+#ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
+#endif
 extern void eth_setup(char *str, int *ints);
 extern void xd_setup(char *str, int *ints);
 #ifdef CONFIG_BLK_DEV_EZ
@@ -107,7 +109,9 @@
 extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void wd7000_setup(char *str, int *ints);
+#ifdef NOTDEF
 extern void ppa_setup(char *str, int *ints);
+#endif
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
 extern void reboot_setup(char *str, int *ints);
@@ -201,6 +205,12 @@
 #ifdef CONFIG_WDT
 extern void wdt_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_PNP_PARPORT
+extern void parport_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_PLIP
+extern void plip_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -380,7 +390,7 @@
 #ifdef CONFIG_SCSI_7000FASST
 	{ "wd7000=", wd7000_setup},
 #endif
-#ifdef CONFIG_SCSI_PPA
+#ifdef NOTDEF /* CONFIG_SCSI_PPA */
         { "ppa=", ppa_setup },
 #endif
 #ifdef CONFIG_SCSI_IBMMCA
@@ -481,6 +491,12 @@
 #endif
 #ifdef CONFIG_WDT
 	{ "wdt=", wdt_setup },
+#endif
+#ifdef CONFIG_PNP_PARPORT
+	{ "parport=", parport_setup },
+#endif
+#ifdef CONFIG_PLIP
+	{ "plip=", plip_setup },
 #endif
 	{ 0, 0 }
 };
diff -u --recursive --new-file v2.1.32/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.32/linux/kernel/exit.c	Thu Mar 27 14:40:12 1997
+++ linux/kernel/exit.c	Mon Apr  7 11:17:31 1997
@@ -577,9 +577,9 @@
 
 NORET_TYPE void do_exit(long code)
 {
-	if (0 && intr_count) {
+	if (in_interrupt()) {
+		local_irq_count[smp_processor_id()] = 0;	/* Not really correct */
 		printk("Aiee, killing interrupt handler\n");
-		intr_count = 0;
 	}
 fake_volatile:
 	acct_process(code);
diff -u --recursive --new-file v2.1.32/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.32/linux/kernel/fork.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/fork.c	Wed Apr  9 21:25:44 1997
@@ -134,9 +134,11 @@
 		tsk->cmin_flt = tsk->cmaj_flt = 0;
 		tsk->nswap = tsk->cnswap = 0;
 		if (new_page_tables(tsk))
-			return -1;
+			goto free_mm;
 		if (dup_mmap(mm)) {
 			free_page_tables(mm);
+free_mm:
+			kfree(mm);
 			return -1;
 		}
 		return 0;
diff -u --recursive --new-file v2.1.32/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.32/linux/kernel/printk.c	Mon Apr  7 11:35:32 1997
+++ linux/kernel/printk.c	Tue Apr  8 09:41:34 1997
@@ -176,6 +176,7 @@
 	va_list args;
 	int i;
 	char *msg, *p, *buf_end;
+	int line_feed;
 	static signed char msg_level = -1;
 	long flags;
 
@@ -202,6 +203,7 @@
 				msg += 3;
 			msg_level = p[1] - '0';
 		}
+		line_feed = 0;
 		for (; p < buf_end; p++) {
 			log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p;
 			if (log_size < LOG_BUF_LEN)
@@ -211,18 +213,20 @@
 				log_start &= LOG_BUF_LEN-1;
 			}
 			logged_chars++;
-			if (*p == '\n')
+			if (*p == '\n') {
+				line_feed = 1;
 				break;
+			}
 		}
 		if (msg_level < console_loglevel && console_drivers) {
 			struct console *c = console_drivers;
 			while(c) {
 				if (c->write)
-					c->write(msg, p - msg + 1);
+					c->write(msg, p - msg + line_feed);
 				c = c->next;
 			}
 		}
-		if (*p == '\n')
+		if (line_feed)
 			msg_level = -1;
 	}
 	__restore_flags(flags);
diff -u --recursive --new-file v2.1.32/linux/kernel/resource.c linux/kernel/resource.c
--- v2.1.32/linux/kernel/resource.c	Thu Mar 27 14:40:12 1997
+++ linux/kernel/resource.c	Mon Apr  7 13:48:08 1997
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 #include <linux/ioport.h>
 
-#define IOTABLE_SIZE 64
+#define IOTABLE_SIZE 128
 
 typedef struct resource_entry_t {
 	u_long from, num;
diff -u --recursive --new-file v2.1.32/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.32/linux/kernel/sched.c	Fri Apr  4 08:52:26 1997
+++ linux/kernel/sched.c	Tue Apr  8 22:11:27 1997
@@ -155,7 +155,7 @@
 		return;
 	}
 #endif
-	if (p == &init_task) {
+	if (!p->pid) {
 		static int nr = 0;
 		if (nr < 5) {
 			nr++;
@@ -307,6 +307,10 @@
 
 	need_resched = 0;
 	this_cpu = smp_processor_id();
+	if (local_irq_count[this_cpu]) {
+		printk("Scheduling in interrupt\n");
+		*(char *)0 = 0;
+	}
 	prev = current;
 	release_kernel_lock(prev, this_cpu, lock_depth);
 	if (bh_active & bh_mask)
@@ -317,8 +321,16 @@
 
 	/* move an exhausted RR process to be last.. */
 	if (!prev->counter && prev->policy == SCHED_RR) {
-		prev->counter = prev->priority;
-		move_last_runqueue(prev);
+		if (prev->pid) {
+			prev->counter = prev->priority;
+			move_last_runqueue(prev);
+		} else {
+			static int count = 5;
+			if (count) {
+				count--;
+				printk("Moving pid 0 last\n");
+			}
+		}
 	}
 	timeout = 0;
 	switch (prev->state) {
@@ -1240,6 +1252,27 @@
 	run_timer_list();
 }
 
+/*
+ * It is up to the platform where it does the profiling: in the
+ * global timer interrupt, or in a special interrupt handler.
+ *
+ * by default it's done in the global timer interrupt.
+ */
+
+static void default_do_profile (struct pt_regs * regs)
+{
+	if (prof_buffer && current->pid) {
+		extern int _stext;
+		unsigned long ip = instruction_pointer(regs);
+		ip -= (unsigned long) &_stext;
+		ip >>= prof_shift;
+		if (ip < prof_len)
+			prof_buffer[ip]++;
+	}
+}
+
+void (*do_profile)(struct pt_regs *) = default_do_profile;
+
 void do_timer(struct pt_regs * regs)
 {
 	(*(unsigned long *)&jiffies)++;
@@ -1247,14 +1280,8 @@
 	mark_bh(TIMER_BH);
 	if (!user_mode(regs)) {
 		lost_ticks_system++;
-		if (prof_buffer && current->pid) {
-			extern int _stext;
-			unsigned long ip = instruction_pointer(regs);
-			ip -= (unsigned long) &_stext;
-			ip >>= prof_shift;
-			if (ip < prof_len)
-				prof_buffer[ip]++;
-		}
+		if (do_profile)
+			do_profile(regs);
 	}
 	if (tq_timer)
 		mark_bh(TQUEUE_BH);
diff -u --recursive --new-file v2.1.32/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.32/linux/kernel/sys.c	Mon Apr  7 11:35:32 1997
+++ linux/kernel/sys.c	Mon Apr  7 14:24:54 1997
@@ -698,10 +698,12 @@
 	 */
 	if (tbuf) 
 	{
-		if(put_user(current->utime,&tbuf->tms_utime)||
-		   put_user(current->stime,&tbuf->tms_stime) ||
-		   put_user(current->cutime,&tbuf->tms_cutime) ||
-		   put_user(current->cstime,&tbuf->tms_cstime))
+		/* ?? use copy_to_user() */
+		if(!access_ok(VERIFY_READ, tbuf, sizeof(struct tms)) ||
+		   __put_user(current->utime,&tbuf->tms_utime)||
+		   __put_user(current->stime,&tbuf->tms_stime) ||
+		   __put_user(current->cutime,&tbuf->tms_cutime) ||
+		   __put_user(current->cstime,&tbuf->tms_cstime))
 			return -EFAULT;
 	}
 	return jiffies;
@@ -943,25 +945,19 @@
 	lock_kernel();
 	if (!name)
 		goto out;
-	error = copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
-	if (!error)
-		error = put_user(0,name->sysname+__OLD_UTS_LEN);
-	if (!error)
-		error = copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
-	if (!error)
-		error = put_user(0,name->nodename+__OLD_UTS_LEN);
-	if (!error)
-		error = copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
-	if (!error)
-		error = put_user(0,name->release+__OLD_UTS_LEN);
-	if (!error)
-		error = copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
-	if (!error)
-		error = put_user(0,name->version+__OLD_UTS_LEN);
-	if (!error)
-		error = copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
-	if (!error)
-		error = put_user(0,name->machine+__OLD_UTS_LEN);
+	if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+		goto out;
+  
+	error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
+	error -= __put_user(0,name->sysname+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
+	error -= __put_user(0,name->nodename+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN);
+	error -= __put_user(0,name->release+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN);
+	error -= __put_user(0,name->version+__OLD_UTS_LEN);
+	error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN);
+	error = __put_user(0,name->machine+__OLD_UTS_LEN);
 	error = error ? -EFAULT : 0;
 out:
 	unlock_kernel();
diff -u --recursive --new-file v2.1.32/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.32/linux/kernel/sysctl.c	Mon Apr  7 11:35:32 1997
+++ linux/kernel/sysctl.c	Tue Apr  8 09:43:01 1997
@@ -183,10 +183,6 @@
 static ctl_table vm_table[] = {
 	{VM_SWAPCTL, "swapctl", 
 	 &swap_control, sizeof(swap_control_t), 0600, NULL, &proc_dointvec},
-	{VM_KSWAPD, "kswapd", 
-	 &kswapd_ctl, sizeof(kswapd_ctl), 0600, NULL, &proc_dointvec},
-	{VM_SWAPOUT, "kswapd-interval",
-	 &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec},
 	{VM_FREEPG, "freepages", 
 	 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
 	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
diff -u --recursive --new-file v2.1.32/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.32/linux/mm/vmscan.c	Sun Jan 26 02:07:50 1997
+++ linux/mm/vmscan.c	Tue Apr  8 09:39:27 1997
@@ -5,6 +5,8 @@
  *
  *  Swap reorganised 29.12.95, Stephen Tweedie.
  *  kswapd added: 7.1.96  sct
+ *  Removed kswapd_ctl limits, and swap out as many pages as needed
+ *  to bring the system back to free_pages_high: 2.4.97, Rik van Riel.
  *  Version: $Id: vmscan.c,v 1.21 1997/01/06 06:54:03 davem Exp $
  */
 
@@ -49,12 +51,6 @@
  */
 static int kswapd_awake = 0;
 
-/*
- * sysctl-modifiable parameters to control the aggressiveness of the
- * page-searching within the kswapd page recovery daemon.
- */
-kswapd_control_t kswapd_ctl = {4, -1, -1, -1, -1};
-
 static void init_swap_timer(void);
 
 /*
@@ -409,8 +405,16 @@
 		interruptible_sleep_on(&kswapd_wait);
 		kswapd_awake = 1;
 		swapstats.wakeups++;
-		/* Do the background pageout: */
-		for (i=0; i < kswapd_ctl.maxpages; i++)
+		/* Do the background pageout: 
+		 * We now only swap out as many pages as needed.
+		 * When we are truly low on memory, we swap out
+		 * synchronously (WAIT == 1).  -- Rik.
+		 */
+		while(nr_free_pages < min_free_pages)
+			try_to_free_page(GFP_KERNEL, 0, 1);
+		while((nr_free_pages + nr_async_pages) < free_pages_low)
+			try_to_free_page(GFP_KERNEL, 0, 1);
+		while((nr_free_pages + nr_async_pages) < free_pages_high)
 			try_to_free_page(GFP_KERNEL, 0, 0);
 	}
 }
@@ -436,12 +440,16 @@
 		want_wakeup = 1;
 	}
 
-	if (want_wakeup) {
-		if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
+	if (want_wakeup) { 
+		if (!kswapd_awake) {
 			wake_up(&kswapd_wait);
 			need_resched = 1;
 		}
-		next_swap_jiffies = jiffies + swapout_interval;
+		/* low on memory, we need to start swapping soon */
+		if(last_wakeup_low) 
+			next_swap_jiffies = jiffies;
+		else  
+			next_swap_jiffies = jiffies + swapout_interval;
 	}
 	timer_active |= (1<<SWAP_TIMER);
 }
diff -u --recursive --new-file v2.1.32/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.32/linux/net/core/dev.c	Fri Apr  4 08:52:26 1997
+++ linux/net/core/dev.c	Wed Apr  9 21:30:31 1997
@@ -85,6 +85,9 @@
 #ifdef CONFIG_NET_RADIO
 #include <linux/wireless.h>
 #endif	/* CONFIG_NET_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
 
 /*
  *	The list of devices, that are able to output.
@@ -1627,6 +1630,9 @@
 #endif
 #if defined(CONFIG_LAPBETHER)
 	lapbeth_init();
+#endif
+#if defined(CONFIG_PLIP)
+	plip_init();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
diff -u --recursive --new-file v2.1.32/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.32/linux/net/ipv4/proc.c	Thu Mar 27 14:40:15 1997
+++ linux/net/ipv4/proc.c	Wed Apr  9 21:30:31 1997
@@ -172,6 +172,11 @@
 		       raw_prot.inuse, raw_prot.highestinuse);
 	len += sprintf(buffer+len,"PAC: inuse %d highest %d\n",
 		       packet_prot.inuse, packet_prot.highestinuse);
+	if (offset >= len)
+	{
+		*start = buffer;
+		return 0;
+	}
 	*start = buffer + offset;
 	len -= offset;
 	if (len > length)
diff -u --recursive --new-file v2.1.32/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.32/linux/net/ipv4/tcp_ipv4.c	Fri Apr  4 08:52:28 1997
+++ linux/net/ipv4/tcp_ipv4.c	Tue Apr  8 21:16:24 1997
@@ -654,7 +654,7 @@
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 
-	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source);
 
 	if (sk == NULL)
 		return;
diff -u --recursive --new-file v2.1.32/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.32/linux/net/ipv4/udp.c	Fri Apr  4 08:52:28 1997
+++ linux/net/ipv4/udp.c	Tue Apr  8 21:16:24 1997
@@ -436,7 +436,7 @@
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 
-	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+	sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source);
 	if (sk == NULL)
 	  	return;	/* No socket for error */
 
diff -u --recursive --new-file v2.1.32/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.32/linux/net/ipv6/tcp_ipv6.c	Thu Mar 27 14:40:17 1997
+++ linux/net/ipv6/tcp_ipv6.c	Tue Apr  8 21:16:25 1997
@@ -516,7 +516,7 @@
 	int err;
 	int opening;
 
-	sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
+	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
 
 	if (sk == NULL)
 		return;
diff -u --recursive --new-file v2.1.32/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.32/linux/net/ipv6/udp.c	Thu Mar 27 14:40:17 1997
+++ linux/net/ipv6/udp.c	Tue Apr  8 21:16:25 1997
@@ -379,7 +379,7 @@
 	
 	uh = (struct udphdr *) buff;
 
-	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
+	sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
    
 	if (sk == NULL) {
 		printk(KERN_DEBUG "icmp for unkown sock\n");
diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.32/linux/net/sunrpc/clnt.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/clnt.c	Mon Apr  7 18:43:43 1997
@@ -746,7 +746,7 @@
 #ifdef RPC_DEBUG
 	rpc_register_sysctl();
 #endif
-	rpcstat_init();
+	rpc_proc_init();
 	return 0;
 }
 
@@ -756,6 +756,6 @@
 #ifdef RPC_DEBUG
 	rpc_unregister_sysctl();
 #endif
-	rpcstat_exit();
+	rpc_proc_exit();
 }
 #endif
diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/stats.c linux/net/sunrpc/stats.c
--- v2.1.32/linux/net/sunrpc/stats.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/stats.c	Mon Apr  7 18:43:43 1997
@@ -1,14 +1,15 @@
 /*
  * linux/net/sunrpc/stats.c
  *
- * procfs-based user access to RPC statistics
+ * procfs-based user access to generic RPC statistics. The stats files
+ * reside in /proc/net/rpc.
  *
- * Everything is complicated by the fact that procfs doesn't pass the
- * proc_dir_info struct in the call to get_info. We need our own 
- * inode_ops for /proc/net/rpc (we want to have a write op for zeroing
- * the current stats, anyway).
+ * The read routines assume that the buffer passed in is just big enough.
+ * If you implement an RPC service that has its own stats routine which
+ * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
+ * limit.
  *
- * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/kernel.h>
@@ -19,83 +20,16 @@
 
 #define RPCDBG_FACILITY	RPCDBG_MISC
 
-/*
- * Generic stats object (same for clnt and svc stats).
- * Must agree with first two fields of either.
- */
-struct stats {
-	struct stats *		next;
-	struct proc_dir_entry *	entry;
-};
-
-/* Code disabled until updated to new dynamic /proc code */
-#if 0
-
-static struct stats *		rpc_stats = NULL;
-static struct stats *		svc_stats = NULL;
-
-static struct proc_dir_entry	proc_rpc = {
-	0, 3, "rpc",
-	S_IFDIR | S_IRUGO | S_IXUGO, 1, 0, 0,
-	0, NULL,
-	NULL, NULL,
-	NULL,
-	NULL, NULL,
-};
+static struct proc_dir_entry	*proc_net_rpc = 0;
 
 /*
- * Register/unregister a stats file
+ * Get RPC client stats
  */
-static void
-stats_register(struct stats **q, struct stats *p)
-{
-	dprintk("RPC: registering /proc/net/rpc/%s\n",
-				p->entry->name);
-	/* return; */
-	if (p->entry->low_ino)
-		return;
-	p->next = *q;
-	*q = p;
-	proc_register_dynamic(&proc_rpc, p->entry);
-}
-
-static void
-stats_unregister(struct stats **q, struct stats *p)
-{
-	dprintk("RPC: unregistering /proc/net/rpc/%s\n",
-				p->entry->name);
-	/* return; */
-	if (!p->entry->low_ino)
-		return;
-	while (*q) {
-		if (*q == p) {
-			*q = p->next;
-			proc_unregister(&proc_rpc, p->entry->low_ino);
-			return;
-		}
-		q = &((*q)->next);
-	}
-}
-
-/*
- * Client stats handling
- */
-void
-rpcstat_register(struct rpc_stat *statp)
-{
-	stats_register(&rpc_stats, (struct stats *) statp);
-}
-
-void
-rpcstat_unregister(struct rpc_stat *statp)
-{
-	stats_unregister(&rpc_stats, (struct stats *) statp);
-}
-
 int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
-			char **start, off_t offset, int length)
+rpc_proc_read(char *buffer, char **start, off_t offset, int count,
+				int *eof, void *data)
 {
+	struct rpc_stat	*statp = (struct rpc_stat *) data;
 	struct rpc_program *prog = statp->program;
 	struct rpc_version *vers;
 	int		len, i, j;
@@ -125,34 +59,24 @@
 
 	if (offset >= len) {
 		*start = buffer;
+		*eof = 1;
 		return 0;
 	}
 	*start = buffer + offset;
-	len -= offset;
-	if (len > length)
-		len = length;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
 	return len;
 }
 
 /*
- * Server stats handling
+ * Get RPC server stats
  */
-void
-svcstat_register(struct svc_stat *statp)
-{
-	stats_register(&svc_stats, (struct stats *) statp);
-}
-
-void
-svcstat_unregister(struct svc_stat *statp)
-{
-	stats_unregister(&svc_stats, (struct stats *) statp);
-}
-
 int
-svcstat_get_info(struct svc_stat *statp, char *buffer,
-			char **start, off_t offset, int length)
+svc_proc_read(char *buffer, char **start, off_t offset, int count,
+				int *eof, void *data)
 {
+	struct svc_stat *statp	= (struct svc_stat *) data;
 	struct svc_program *prog = statp->program;
 	struct svc_procedure *proc;
 	struct svc_version *vers;
@@ -183,85 +107,69 @@
 
 	if (offset >= len) {
 		*start = buffer;
+		*eof = 1;
 		return 0;
 	}
 	*start = buffer + offset;
-	if ((len -= offset) > length)
-		len = length;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
 	return len;
 }
 
 /*
- * Register /proc/net/rpc
- */
-void
-rpcstat_init(void)
-{
-	dprintk("RPC: registering /proc/net/rpc\n");
-	proc_rpc.ops = proc_net.ops;	/* cheat */
-	proc_register_dynamic(&proc_net, &proc_rpc);
-}
-
-/*
- * Unregister /proc/net/rpc
+ * Register/unregister RPC proc files
  */
-void
-rpcstat_exit(void)
+static inline struct proc_dir_entry *
+do_register(const char *name, void *data, int issvc)
 {
-	while (rpc_stats)
-		stats_unregister(&rpc_stats, rpc_stats);
-	while (svc_stats)
-		stats_unregister(&svc_stats, svc_stats);
-	dprintk("RPC: unregistering /proc/net/rpc\n");
-	proc_unregister(&proc_net, proc_rpc.low_ino);
-}
+	struct proc_dir_entry	*ent;
 
-#else
+	dprintk("RPC: registering /proc/net/rpc/%s\n", name);
+	ent = create_proc_entry(name, 0, proc_net_rpc);
+	ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
+	ent->data = data;
 
-/* Various dummy functions */
-
-int
-rpcstat_get_info(struct rpc_stat *statp, char *buffer,
-			char **start, off_t offset, int length)
-{
-	return 0;
-}
-
-int
-svcstat_get_info(struct svc_stat *statp, char *buffer,
-			char **start, off_t offset, int length)
-{
-	return 0;
+	return ent;
 }
 
-void
-rpcstat_register(struct rpc_stat *statp)
+struct proc_dir_entry *
+rpc_proc_register(struct rpc_stat *statp)
 {
+	return do_register(statp->program->name, statp, 0);
 }
 
 void
-rpcstat_unregister(struct rpc_stat *statp)
+rpc_proc_unregister(const char *name)
 {
+	remove_proc_entry(name, proc_net_rpc);
 }
 
-void
-svcstat_register(struct svc_stat *statp)
+struct proc_dir_entry *
+svc_proc_register(struct svc_stat *statp)
 {
+	return do_register(statp->program->pg_name, statp, 1);
 }
 
 void
-svcstat_unregister(struct svc_stat *statp)
+svc_proc_unregister(const char *name)
 {
+	remove_proc_entry(name, proc_net_rpc);
 }
 
 void
-rpcstat_init(void)
+rpc_proc_init(void)
 {
+	dprintk("RPC: registering /proc/net/rpc\n");
+	if (!proc_net_rpc)
+		proc_net_rpc = create_proc_entry("net/rpc", S_IFDIR, 0);
 }
 
 void
-rpcstat_exit(void)
+rpc_proc_exit(void)
 {
+	dprintk("RPC: unregistering /proc/net/rpc\n");
+	if (proc_net_rpc)
+		remove_proc_entry("net/rpc", 0);
+	proc_net_rpc = 0;
 }
-
-#endif
diff -u --recursive --new-file v2.1.32/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c
--- v2.1.32/linux/net/sunrpc/sunrpc_syms.c	Mon Apr  7 11:35:33 1997
+++ linux/net/sunrpc/sunrpc_syms.c	Mon Apr  7 18:43:44 1997
@@ -10,8 +10,6 @@
 #include <linux/config.h>
 #include <linux/module.h>
 
-#ifdef CONFIG_MODULES
-
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
@@ -75,12 +73,12 @@
 
 /* RPC statistics */
 #ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(rpcstat_register);
-EXPORT_SYMBOL(rpcstat_unregister);
-EXPORT_SYMBOL(rpcstat_get_info);
-EXPORT_SYMBOL(svcstat_register);
-EXPORT_SYMBOL(svcstat_unregister);
-EXPORT_SYMBOL(svcstat_get_info);
+EXPORT_SYMBOL(rpc_proc_register);
+EXPORT_SYMBOL(rpc_proc_unregister);
+EXPORT_SYMBOL(rpc_proc_read);
+EXPORT_SYMBOL(svc_proc_register);
+EXPORT_SYMBOL(svc_proc_unregister);
+EXPORT_SYMBOL(svc_proc_read);
 #endif
 
 /* Generic XDR */
@@ -101,5 +99,3 @@
 EXPORT_SYMBOL(nfs_debug);
 EXPORT_SYMBOL(nfsd_debug);
 EXPORT_SYMBOL(nlm_debug);
-
-#endif /* CONFIG_MODULES */
diff -u --recursive --new-file v2.1.32/linux/scripts/Configure linux/scripts/Configure
--- v2.1.32/linux/scripts/Configure	Mon Mar 17 14:54:36 1997
+++ linux/scripts/Configure	Wed Apr  9 23:29:44 1997
@@ -46,6 +46,9 @@
 #
 # 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
 # expressions for GNU expr since version 1.15 and up use \? and \+.
+#
+# 300397 Phil Blundell (pjb27@cam.ac.uk) - added support for "limint", 
+# allow dep_tristate to take a list of dependencies rather than just one.
 
 #
 # Make sure we're really running bash.
@@ -224,22 +227,35 @@
 
 #
 # dep_tristate processes a tristate argument that depends upon
-# another option.  If the option we depend upon is a module,
-# then the only allowable options are M or N.  If Y, then
+# another option or options.  If any of the options we depend upon is a
+# module, then the only allowable options are M or N.  If all are Y, then
 # this is a normal tristate.  This is used in cases where modules
 # are nested, and one module requires the presence of something
 # else in the kernel.
 #
-#	tristate question define default
+#	tristate question define default ...
 #
 function dep_tristate () {
 	old=$(eval echo "\${$2}")
 	def=${old:-'n'}
-	if [ "$3" = "n" ]; then
-		define_bool "$2" "n"
-	elif [ "$3" = "y" ]; then
-		tristate "$1" "$2"
-	else
+	ques=$1
+	var=$2
+	need_module=0
+	shift 2
+	while [ $# -gt 0 ]; do
+	  case "$1" in
+ 	    n)
+	      define_bool "$var" "n"
+	      return
+	      ;;
+	    m)
+	      need_module=1
+	      ;;
+	  esac
+	  shift
+	done
+
+	if [ $need_module = 1 ]; then
 	   if [ "$CONFIG_MODULES" = "y" ]; then
 		case "$def" in
 		 "y" | "m") defprompt="M/n/?"
@@ -249,11 +265,11 @@
 		      ;;
 		esac
 		while :; do
-		  readln "$1 ($2) [$defprompt] " "$def" "$old"
+		  readln "$ques ($var) [$defprompt] " "$def" "$old"
 		  case "$ans" in
-		      [nN] | [nN]o )  define_bool "$2" "n"
+		      [nN] | [nN]o )  define_bool "$var" "n"
 				      break ;;
-		      [mM] )          define_bool "$2" "m"
+		      [mM] )          define_bool "$var" "m"
 				      break ;;
 		      [yY] | [yY]es ) echo 
    echo "  This answer is not allowed, because it is not consistent with"
@@ -263,11 +279,13 @@
    echo "  as a module as well (with M) or leave it out altogether (N)."
 				      echo
 				      ;;
-		      * )             help "$2"
+		      * )             help "$var"
 				      ;;
 		  esac
 		done
 	   fi
+	else
+	   tristate "$ques" "$var"
 	fi
 }
 
@@ -283,23 +301,34 @@
 }
 
 #
-# int processes an integer argument
+# int processes an integer argument with optional limits
 #
-#	int question define default
+#	int question define default [min max]
 #
 function int () {
 	old=$(eval echo "\${$2}")
 	def=${old:-$3}
+	if [ $# -gt 3 ]; then
+	  min=$4
+	else
+	  min=1
+	fi
+	if [ $# -gt 4 ]; then
+	  max=$5
+	else
+	  max=10000000     # !!
+	fi
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
-	    define_int "$2" "$ans"
+	  if expr \( \( $ans + 0 \) \>= $min \) \& \( $ans \<= $max \) >/dev/null 2>&1 ; then
+            define_int "$2" "$ans"
 	    break
-	  else
+          else
 	    help "$2"
-	  fi
+          fi
 	done
 }
+
 #
 # define_hex sets the value of a hexadecimal argument
 #
diff -u --recursive --new-file v2.1.32/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.1.32/linux/scripts/Menuconfig	Mon Mar 17 14:54:36 1997
+++ linux/scripts/Menuconfig	Wed Apr  9 21:59:27 1997
@@ -21,6 +21,9 @@
 #
 # Please send comments / questions / bug fixes to roadcapw@cfw.com
 #
+# 070497 Bernhard Kaindl (bkaindl@netway.at) - get default values for
+# new bool, tristate and dep_tristate parameters from the defconfig file.
+# new configuration parameters are marked with '(NEW)' as in make config.
 #----------------------------------------------------------------------------
 
 
@@ -40,9 +43,21 @@
 #
 set -h +o posix
 
+#
+# Converts "# xxx is not..." to xxx=n
+#
+parse_config () {
+	sed -e 's/# \(.*\) is not.*/\1=n/'
+}
 
-
-
+#
+# Parses the defconfig file to set the default for a new parameter.
+#
+function get_def () {
+	parse_config < arch/$ARCH/defconfig | grep "^$1=" > /tmp/conf.$$
+	. /tmp/conf.$$
+	rm /tmp/conf.$$
+}
 
 #
 # Load the functions used by the config.in files.
@@ -56,6 +71,19 @@
 load_functions () {
 
 #
+# Macro for setting the x and info varibles. get's default from defconfig
+# file if it's a new parameter.
+#
+function set_x () {
+	eval x=\$$1
+	if [ -z "$x" ]; then
+		get_def "$1"
+		eval x=\${$1:-'n'} INFO_$1="' (NEW)'"
+	fi
+	eval info="\$INFO_$1"
+}
+
+#
 # Additional comments
 #
 function comment () {
@@ -75,14 +103,14 @@
 # which calls our local bool function.
 #
 function bool () {
-	eval $2=\${$2:-'n'}  x=\$$2
+	set_x "$2"
 
 	case $x in
 	y|m)	flag="*" ;;
 	n)	flag=" " ;;
 	esac
 
-	echo -ne "'$2' '[$flag] $1' " >>MCmenu
+	echo -ne "'$2' '[$flag] $1$info' " >>MCmenu
 
 	echo -e "function $2 () { l_bool '$2' \"\$1\" ;}\n" >>MCradiolists
 }
@@ -98,7 +126,7 @@
 	then
 		bool "$1" "$2"
 	else
-		eval $2=\${$2:-'n'}  x=\$$2
+		set_x "$2"
 	
 		case $x in
 		y) flag="*" ;;
@@ -106,7 +134,7 @@
 		*) flag=" " ;;
 		esac
 	
-		echo -ne "'$2' '<$flag> $1' " >>MCmenu
+		echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
 	
 		echo -e "
 		function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists
@@ -323,14 +351,14 @@
 # Same as bool() except options are (Module/No)
 #
 function mod_bool () {
-	eval $2=\${$2:-'n'}  x=\$$2
+	set_x "$2"
 
 	case $x in
 	y|m) flag='M' ;;
 	*)   flag=' ' ;;
 	esac
 
-	echo -ne "'$2' '<$flag> $1' " >>MCmenu
+	echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
 
 	echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
 }
@@ -882,31 +910,45 @@
 		  --infobox "Saving your kernel configuration..."  3 40
 
 	#
+	# Macro for setting the newval varible. get's default from defconfig
+	# file if it's a new parameter and it has not been shown yet.
+	#
+	function set_newval () {
+		eval newval=\$$1
+		if [ -z "$newval" ]; then
+			get_def "$1"
+			eval newval=\${$1:-'n'}
+		fi
+	}
+
+	#
 	# Now, let's redefine the configuration functions for final
 	# output to the config files.
 	#
 	# Nested function definitions, YIPEE!
 	#
 	function bool () {
-		eval define_bool "$2" "\${$2:-n}"
+		set_newval "$2"
+		eval define_bool "$2" "$newval"
 	}
 
 	function tristate () {
-		eval define_bool "$2" "\${$2:-n}"
+		set_newval "$2"
+		eval define_bool "$2" "$newval"
 	}
 
 	function dep_tristate () {
-		eval x=\${$2:-n}
+		set_newval "$2"
 
 		if eval [ "_$3" = "_m" ]
 		then
-			if [ "$x" = "y" ]
+			if [ "$newval" = "y" ]
 			then
-				x="m"
+				newval="m"
 			fi
 		fi
 
-		define_bool "$2" "$x"
+		define_bool "$2" "$newval"
 	}
 
 	function int () {
