diff -u --recursive --new-file v1.3.85/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.85/linux/Documentation/Configure.help	Mon Apr  8 19:01:41 1996
+++ linux/Documentation/Configure.help	Tue Apr  9 14:58:18 1996
@@ -422,6 +422,12 @@
 ### Add info about Platform2000, EB164
 ###
 
+Is it really a true XL
+CONFIG_ALPHA_XL
+  If your Avanti Machine is of type XL (a.k.a. "Windows NT Dream
+  Machine") (as opposed to Mustang (AS200), M3 (AS250) or Avanti
+  (AS400)), say Y, otherwise N.
+
 Limit memory to low 16MB
 CONFIG_MAX_16M
   This is for some buggy motherboards which cannot properly deal with
@@ -710,13 +716,18 @@
   traffic from one of the local computers and destined for an outside
   host is changed by your box so that it appears to come from you),
   you'll have to say Y here and also to IP firewalling and IP
-  masquerading below. You should say Y here also if you want to
+  masquerading below. You should also say Y here if you want to
   configure your box as a SLIP (the protocol for sending internet
   traffic over telephone lines) or PPP (a better SLIP) server for
   other people to dial into and your box is connected to a local
   network at the same time. You would then most likely use proxy-ARP
   (Address Resolution Protocol), explained in the Proxy-Arp mini howto
-  on sunsite in /pub/Linux/docs/HOWTO/mini. If unsure, say N.
+  on sunsite in /pub/Linux/docs/HOWTO/mini. You also need to say Y
+  here if you want to run mrouted in order to do multicast routing as
+  used on the MBONE (a high bandwidth network on top of the internet
+  which carries audio and video broadcasts) for example. In this case,
+  say Y to "IP: multicasting" and "IP: multicast routing" as well. If
+  unsure, say N.
 
 IP: multicasting
 CONFIG_IP_MULTICAST
@@ -842,9 +853,12 @@
 IP: multicast routing(in progress)
 CONFIG_IP_MROUTE
   This is used if you want your machine to act as a router for IP
-  packets that have several destination addresses. Information about
-  the multicast capabilities of the various network cards is contained
-  in drivers/net/README.multicast. If you haven't heard about it, you
+  packets that have several destination addresses. It is needed on the
+  MBONE, a high bandwidth network on top of the internet which carries
+  audio and video broadcasts. In order to do that, you would most
+  likely run the program mrouted. Information about the multicast
+  capabilities of the various network cards is contained in
+  drivers/net/README.multicast. If you haven't heard about it, you
   don't need it.
 
 PC/TCP compatibility mode
@@ -1032,6 +1046,13 @@
   useful if some other computer on your local network has a direct
   amateur radio connection.
 
+Bridging (test)
+CONFIG_BRIDGE
+  Enables bridge mode support in the box. This allows you to use a Linux
+  box (suitably configured) as an ethernet bridge including 802.1 spanning
+  tree support. This is in test. There are a small batch of changes before
+  final release but it should work nicely.
+
 Kernel/User network link driver(ALPHA)
 CONFIG_NETLINK
   This driver allows for two-way communication between certain parts
@@ -1132,6 +1153,9 @@
   determine the mapping used under the other operating systems (e.g.
   DOS), and set these parameters to the determined values, or if the
   disk has no valid partition table, to an optimal value.
+###
+### What are the advantages/disadvantages? What is a safe value?
+###
 
 AdvanSys SCSI support
 CONFIG_SCSI_ADVANSYS
@@ -1197,6 +1221,11 @@
   You might also want to read the SCSI-HOWTO, available via anonymous
   ftp from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
  
+DTC3180/3280 SCSI support
+CONFIG_SCSI_DTC3280
+  This is support for DTC 3180/3280 SCSI Host Adaptors.  It does not
+  use IRQ's.  It does not support parity on the SCSI bus.
+
 EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support
 CONFIG_SCSI_EATA_DMA
   This is support for a SCSI host adaptor. Please read the
@@ -1284,6 +1313,18 @@
   = code which can be inserted in and removed from the running kernel
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
+
+always negotiate synchronous transfers
+CONFIG_SCSI_NCR53C7xx_sync
+  In general, this is good; however, it is a bit dangerous since there
+  are some broken SCSI devices out there. Take your chances. Safe bet
+  is N.
+
+allow FAST-SCSI [10MHz]
+CONFIG_SCSI_NCR53C7xx_FAST
+  This will enable 10MHz FAST-SCSI transfers with your host
+  adaptor. Some systems have problems with that speed, so it's safest
+  to say N here.
  
 Always IN2000 SCSI support (test release)
 CONFIG_SCSI_IN2000
@@ -1606,10 +1647,10 @@
   that has one of the programs lynx, netscape or Mosaic.) To use frame
   relay, you need supporting hardware (FRAD) and certain programs from
   the net-tools package as explained in
-  Documentation/framerelay.txt. This driver is also available as a
-  module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt.
+  Documentation/networking/framerelay.txt. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile
+  it as a module, say M here and read Documentation/modules.txt.
 
 Max open DLCI
 CONFIG_DLCI_COUNT 
@@ -2579,20 +2620,17 @@
   about 27 kB. This filesystem is also available as a module ( = code
   which can be inserted in and removed from the running kernel
   whenever you want). If you want to compile it as a module, say M
-  here and read Documentation/modules.txt. If you don't know what all
-  this is about, say N.
+  here and read Documentation/modules.txt. If you configure a diskless
+  machine which will mount its root filesystem over nfs, you cannot
+  compile this driver as a module. If you don't know what all this is
+  about, say N.
 
 Root file system on NFS
 CONFIG_ROOT_NFS
   If you want your Linux box to mount its whole root filesystem from
   some other computer over the net via NFS (presumably because your
-  box doesn't have a harddisk), say Y here. You will then have to
-  specify the directory that should be remotely mounted with the
-  "root" kernel command line option at boot time. See the
-  documentation of your boot loader (lilo or loadlin) about how to
-  pass options to the kernel. The lilo procedure is also explained in
-  the SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) Most people say N here.
+  box doesn't have a harddisk), say Y. Read Documentation/nfsroot.txt
+  for details. Most people say N here.
 
 BOOTP support
 CONFIG_RNFS_BOOTP
@@ -2604,7 +2642,8 @@
   booting Linux and does BOOTP itself, providing all necessary
   information on the kernel command line, you can say N here.
   If unsure, say Y. Note that in case you want to use BOOTP, a BOOTP
-  server must be operating on your network.
+  server must be operating on your network. Read
+  Documentation/nfsroot.txt for details.
 
 RARP support
 CONFIG_RNFS_RARP
@@ -2613,7 +2652,8 @@
   of your computer to be discovered automatically using the RARP
   protocol (an older protocol which is being obsoleted by BOOTP), say
   Y here. Note that in case you want to use RARP, a RARP server must be
-  operating on your network.
+  operating on your network. Read Documentation/nfsroot.txt for
+  details. 
 
 ISO9660 cdrom filesystem support
 CONFIG_ISO9660_FS
@@ -2713,6 +2753,14 @@
   serial mice, modems and similar devices connecting to the standard
   serial ports.
 
+Digiboard PC/X Support
+CONFIG_DIGI
+  This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards
+  that give you many serial ports. You would need something like this
+  to connect more than two modems to your linux box, for instance in
+  order to become a BBS. If you have a card like that, say Y here and
+  read the file Documentation/digiboard.txt.
+
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a driver for a card that gives you many serial ports. You
@@ -2898,7 +2946,7 @@
   compliant APM BIOS, you want to say N.  However, on the NEC Versa M
   series notebooks, it is necessary to say Y because of a BIOS bug.
 
-Enable APM features
+Enable APM at boot time
 CONFIG_APM_DO_ENABLE
   Enable APM features at boot time.  From page 36 of the APM BIOS
   specification: "When disabled, the APM BIOS does not automatically
@@ -3139,4 +3187,6 @@
 # LocalWords:  ibp md ARCnet ether encap NDIS arcether ODI Amigas AmiTCP NetBSD
 # LocalWords:  initrd tue util DES funet des OnNet BIOSP smc Travan Iomega CMS
 # LocalWords:  FC DC dc PPA IOMEGA's ppa RNFS FMV Fujitsu ARPD arpd loran layes
-# LocalWords:  FRAD indiana framerelay DLCI DCLIs Sangoma SDLA
+# LocalWords:  FRAD indiana framerelay DLCI DCLIs Sangoma SDLA mrouted sync sec
+# LocalWords:  Starmode Metricom MosquitoNet mosquitonet kbit nfsroot Digiboard
+# LocalWords:  DIGI Xe Xeve digiboard UMISC touchscreens
diff -u --recursive --new-file v1.3.85/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt
--- v1.3.85/linux/Documentation/mandatory.txt	Mon Apr  8 19:01:41 1996
+++ linux/Documentation/mandatory.txt	Wed Apr 10 08:41:08 1996
@@ -60,25 +60,39 @@
 Available implementations
 -------------------------
 
-I originally intended to base my implementation on SunOS, only to find out that
-the implementation in SunOS 4.1.1 (the latest version that will work on my Sun
-3/80 at home) is completely hopeless.
-
-For one thing, calls to open() for a file fail with EAGAIN if another process
-holds a mandatory lock on the file. However, processes already holding open
-file descriptors can carry on using them. Wierd!
+I have considered the implementations of mandatory locking available with
+SunOS 4.1.x, Solaris 2.x and HP-UX 9.x.
 
-In addition, SunOS doesn't seem to honour the O_NONBLOCK (O_NDELAY) flag for
-mandatory locks, so reads and writes to locked files always block when they
-should probably return EAGAIN.
+Generally I have tried to make the most sense out of the behaviour exhibited
+by these three reference systems. There are many anomalies.
+
+Originally I wrote (about SunOS):
+ "For one thing, calls to open() for a file fail with EAGAIN if another
+  process holds a mandatory lock on the file. However, processes already
+  holding open file descriptors can carry on using them. Wierd!"
+
+Well, all my reference systems do it, so I decided to go with the flow.
+My gut feeling is that only calls to open() and creat() with O_TRUNC should be
+rejected, as these are the only ones that try to modify the file contents as
+part of the open() call.
+
+HP-UX even disallows open() with O_TRUNC for a file with advisory locks, not
+just mandatory locks. That to me contravenes POSIX.1.
+
+mmap() is another interesting case. All the operating systems mentioned
+prevent mandatory locks from being applied to an mmap()'ed file, but  HP-UX
+also disallows advisory locks for such a file.
 
-I found some test code online, which I think comes from one of Stevens' UNIX
-programming books, that confirmed what I considered to be be the "obvious"
-semantics, described below. I shall attempt to run my test programs on other
-platforms to verify my implementation.
+My opinion is that only MAP_SHARED mappings should be immune from locking, and
+then only from mandatory locks - that is what is currently implemented.
 
-Personally I feel that this is such an esoteric area that these semantics are
-just as valid as any others, so long as the main points seem to agree.
+SunOS is so hopeless that it doesn't even honour the O_NONBLOCK flag for
+mandatory locks, so reads and writes to locked files always block when they
+should return EAGAIN.
+
+I'm afraid that this is such an esoteric area that the semantics described
+below are just as valid as any others, so long as the main points seem to
+agree. 
 
 Semantics
 ---------
@@ -99,13 +113,24 @@
    unless a process has opened the file with the O_NONBLOCK flag in which case
    the system call will return immediately with the error status EAGAIN.
 
+4. Calls to open() or creat() on a file that has any mandatory locks owned
+   by other processes will be rejected with the error status EAGAIN.
+
+5. Attempts to apply a mandatory lock to a file that is memory mapped and
+   shared (via mmap() with MAP_SHARED) will be rejected with the error status
+   EAGAIN.
+
+6. Attempts to create a shared memory map of a file (via mmap() with MAP_SHARED)
+   that has any mandatory locks in effect will be rejected with the error status
+   EAGAIN.
+
 Which system calls are affected?
 --------------------------------
 
 Those which modify a file's contents, not just the inode. That gives read(),
-write(), readv(), writev(), truncate() and ftruncate(). truncate() and
-ftruncate() are considered to be "write" actions for the purposes of mandatory
-locking.
+write(), readv(), writev(), open(), creat(), mmap(), truncate() and
+ftruncate(). truncate() and ftruncate() are considered to be "write" actions
+for the purposes of mandatory locking.
 
 The affected region is usually defined as stretching from the current position
 for the total number of bytes read or written. For the truncate calls it is
@@ -124,3 +149,4 @@
 havoc if they lock crucial files. The way around it is to change the file
 permissions (remove the setgid bit) before trying to read or wite to it.
 Of course, that might be a bit tricky if the system is hung :-(
+
diff -u --recursive --new-file v1.3.85/linux/Documentation/svga.txt linux/Documentation/svga.txt
--- v1.3.85/linux/Documentation/svga.txt	Wed Mar 27 08:19:28 1996
+++ linux/Documentation/svga.txt	Tue Apr  9 16:55:01 1996
@@ -1,4 +1,4 @@
-			Video Mode Selection Support 2.6
+			Video Mode Selection Support 2.7
 	      (c) 1995, 1996 Martin Mares, <mj@k332.feld.cvut.cz>
 --------------------------------------------------------------------------------
 
@@ -103,8 +103,10 @@
 	(as presented to INT 10, function 00) increased by 0x0100. You can
 	use any mode numbers even if not shown on the menu.
 
-   0x0200 to 0x04ff - VESA BIOS modes. The ID is a VESA mode ID increased by
-	0x0200. All VESA modes should be autodetected and shown on the menu.
+   0x0200 to 0x08ff - VESA BIOS modes. The ID is a VESA mode ID increased by
+	0x0100. All VESA modes should be autodetected and shown on the menu.
+
+   0x0900 to 0x09ff - Video7 special modes. Set by calling INT 0x10, AX=0x6f05.
 
    0x0f00 to 0x0fff - special modes (they are set by various tricks -- usually
 	by modifying one of the standard modes). Currently available:
@@ -113,6 +115,9 @@
 	0x0f02	VGA 80x43 (VGA switched to 350 scanlines with a 8-point font)
 	0x0f03	VGA 80x28 (standard VGA scans, but 14-point font)
 	0x0f04	leave current video mode
+	0x0f05	VGA 80x30 (480 scans, 16-point font)
+	0x0f06	VGA 80x34 (480 scans, 14-point font)
+	0x0f07	VGA 80x60 (480 scans, 8-point font)
 
    0x1000 to 0x7fff - modes specified by resolution. The code has a "0xRRCC"
 	form where RR is a number of rows and CC is a number of columns.
@@ -148,7 +153,7 @@
 -- in this case turn this switch off to see the rest.
 
    CONFIG_VIDEO_RETAIN - enables retaining of screen contents when switching
-video modes. Works only with some boot loaders leaving enough room for the
+video modes. Works only with some boot loaders which leave enough room for the
 buffer.
 
    CONFIG_VIDEO_LOCAL - enables inclusion of "local modes" in the list. The
@@ -225,3 +230,14 @@
 2.5 (19-Mar-96)	Fixed a VESA mode scanning bug introduced in 2.4.
 2.6 (25-Mar-96)	Some VESA BIOS errors not reported -- it fixes error reports on
 		several cards with broken VESA code (e.g., ATI VGA).
+2.7 (09-Apr-96)	- Accepted all VESA modes in range 0x100 to 0x7ff, because some
+		  cards use very strange mode numbers.
+		- Added Realtek VGA modes (thanks to Gonzalo Tornaria).
+		- Hardware testing order slightly changed, tests based on ROM
+		  contents done as first.
+		- Added support for special Video7 mode switching functions
+		  (thanks to Tom Vander Aa).
+		- Added 480-scanline modes (especially useful for notebooks,
+		  original version written by hhanemaa@cs.ruu.nl, patched by
+		  Jeff Chua, rewritten by me).
+		- Screen store/restore fixed.
diff -u --recursive --new-file v1.3.85/linux/Makefile linux/Makefile
--- v1.3.85/linux/Makefile	Mon Apr  8 19:01:41 1996
+++ linux/Makefile	Tue Apr  9 14:58:26 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 85
+SUBLEVEL = 86
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.85/linux/arch/i386/boot/video.S linux/arch/i386/boot/video.S
--- v1.3.85/linux/arch/i386/boot/video.S	Wed Mar 27 08:19:28 1996
+++ linux/arch/i386/boot/video.S	Tue Apr  9 16:55:01 1996
@@ -1,5 +1,5 @@
 !
-!	Display adapter & video mode setup, version 2.6 (25-Mar-96)
+!	Display adapter & video mode setup, version 2.7 (09-Apr-96)
 !
 !	Copyright (C) 1995, 1996 Martin Mares <mj@k332.feld.cvut.cz>
 !	Based on the original setup.S code (C) Linus Torvalds
@@ -32,6 +32,8 @@
 #define VIDEO_FIRST_BIOS 0x0100
 ! VESA BIOS video modes (VESA number + 0x0200)
 #define VIDEO_FIRST_VESA 0x0200
+! Video7 special modes (BIOS number + 0x0900)
+#define VIDEO_FIRST_V7 0x0900
 ! Special video modes
 #define VIDEO_FIRST_SPECIAL 0x0f00
 #define VIDEO_80x25 0x0f00
@@ -39,7 +41,10 @@
 #define VIDEO_80x43 0x0f02
 #define VIDEO_80x28 0x0f03
 #define VIDEO_CURRENT_MODE 0x0f04
-#define VIDEO_LAST_SPECIAL 0x0f05
+#define VIDEO_80x30 0x0f05
+#define VIDEO_80x34 0x0f06
+#define VIDEO_80x60 0x0f07
+#define VIDEO_LAST_SPECIAL 0x0f08
 ! Video modes given by resolution
 #define VIDEO_FIRST_RESOLUTION 0x1000
 
@@ -152,20 +157,22 @@
 	seg	fs
 	mov	[PARAM_FONT_POINTS],ax	! (valid only on EGA/VGA)
 
-	cmpb	[def_mode],#0	! Default mode -- force sane values
+	mov	ax,[force_size]	! Forced size?
+	or	ax,ax
 	jz	mopar1
 	seg	fs
-	movb	[PARAM_VIDEO_COLS],#80
-mopar2:	seg	fs
-	movb	[PARAM_VIDEO_LINES],#25
+	mov	[PARAM_VIDEO_COLS],ah
+	seg	fs
+	mov	[PARAM_VIDEO_LINES],al
 	ret
 
-mopar1:	cmpb	[adapter],#0	! If we are on CGA/MDA/HGA, the screen must
+mopar1:	mov	al,#25
+	cmpb	[adapter],#0	! If we are on CGA/MDA/HGA, the screen must
 	jz	mopar2		! have 25 lines.
 	seg	gs		! On EGA/VGA, use the EGA+ BIOS variable
 	mov	al,[0x484]	! containing maximal line number.
 	inc	al
-	seg	fs
+mopar2:	seg	fs
 	movb	[PARAM_VIDEO_LINES],al
 	ret
 
@@ -317,6 +324,18 @@
 lmdef:	ret
 
 !
+! Additional parts of mode_set... (relative jumps, you know)
+!
+
+setv7:				! Video7 extended modes
+	DO_STORE
+	sub	bh,#VIDEO_FIRST_V7>>8
+	mov	ax,#0x6f05
+	int	0x10
+	stc
+	ret
+
+!
 ! Aliases for backward compatibility.
 !
 
@@ -344,6 +363,8 @@
 	jnc	setres
 	cmp	ah,#VIDEO_FIRST_SPECIAL>>8
 	jz	setspc
+	cmp	ah,#VIDEO_FIRST_V7>>8
+	jz	setv7
 	cmp	ah,#VIDEO_FIRST_VESA>>8
 	jnc	setvesa
 	or	ah,ah
@@ -453,13 +474,16 @@
 	.word	set_80x43
 	.word	set_80x28
 	.word	set_current
+	.word	set_80x30
+	.word	set_80x34
+	.word	set_80x60
 
 !
 ! Set the 80x25 mode. If already set, do nothing.
 !
 
 set_80x25:
-	incb	[def_mode]	! Signal "we use default mode"
+	mov	[force_size],#0x5019	! Override possibly broken BIOS vars
 use_80x25:
 	mov	ah,#0x0f	! Get current mode ID
 	int	0x10
@@ -513,7 +537,7 @@
 set_80x28:
 	DO_STORE
 	call	use_80x25	! The base is 80x25
-	mov	ax,#0x1111	! Use 9x14 font
+set14:	mov	ax,#0x1111	! Use 9x14 font
 	xor	bl,bl
 	int	0x10
 	mov	ah,#0x01	! Define cursor (scan lines 11 to 12)
@@ -536,6 +560,69 @@
 	int	0x10
 	jmp	set_8pt		! Use 8-pixel font
 
+!
+! Set the 80x30 mode (all VGA's). 480 scanlines, 16-pixel font.
+!
+
+set_80x30:
+	call	use_80x25	! Start with real 80x25
+	DO_STORE
+	mov	dx,#0x3cc	! Get CRTC port
+	in	al,dx
+	mov	dl,#0xd4
+	ror	al,#1		! Mono or color?
+	jc	set48a
+	mov	dl,#0xb4
+set48a:	mov	ax,#0x0c11	! Vertical sync end (also unlocks CR0-7)
+ 	call	outidx
+	mov	ax,#0x0b06	! Vertical total
+ 	call	outidx
+	mov	ax,#0x3e07	! (Vertical) overflow
+ 	call	outidx
+	mov	ax,#0xea10	! Vertical sync start
+ 	call	outidx
+	mov	ax,#0xdf12	! Vertical display end
+	call	outidx
+	mov	ax,#0xe715	! Vertical blank start
+ 	call	outidx
+	mov	ax,#0x0416	! Vertical blank end
+ 	call	outidx
+	push	dx
+	mov	dl,#0xcc	! Misc output register (read)
+ 	in	al,dx
+ 	mov	dl,#0xc2	! (write)
+ 	and	al,#0x0d	! Preserve clock select bits and color bit
+ 	or	al,#0xe2	! Set correct sync polarity
+ 	out	dx,al
+	pop	dx
+	mov	[force_size],#0x501e
+	stc			! That's all.
+	ret
+
+!
+! Set the 80x34 mode (all VGA's). 480 scans, 14-pixel font.
+!
+
+set_80x34:
+	call	set_80x30	! Set 480 scans
+	call	set14		! And 14-pt font
+	mov	ax,#0xdb12	! VGA vertical display end
+	mov	[force_size],#0x5022
+setvde:	call	outidx
+	stc
+	ret
+
+!
+! Set the 80x60 mode (all VGA's). 480 scans, 8-pixel font.
+!
+
+set_80x60:
+	call	set_80x30	! Set 480 scans
+	call	set_8pt		! And 8-pt font
+	mov	ax,#0xdf12	! VGA vertial display end
+	mov	[force_size],#0x503c
+	jmp	setvde
+
 #ifdef CONFIG_VIDEO_RETAIN
 
 !
@@ -549,12 +636,10 @@
 	jz	stsr
 	push	ax
 	push	bx
-	mov	al,[def_mode]		! "Default mode" flag overriden
-	push	ax
-	movb	[def_mode],#0
+	push	[force_size]		! Don't force specific size
+	mov	[force_size],#0
 	call	mode_params		! Obtain params of current mode
-	pop	ax
-	mov	[def_mode],al
+	pop	[force_size]
 
 	seg	fs
 	mov	ah,[PARAM_VIDEO_LINES]
@@ -564,7 +649,7 @@
 	mul	ah
 	mov	cx,ax			! CX=number of characters to store
 	add	ax,ax			! Calculate image size
-	add	ax,modelist+1024+4
+	add	ax,#modelist+1024+4
 	cmp	ax,[heap_end_ptr]
 	jnc	sts1			! Unfortunately, out of memory
 
@@ -681,12 +766,10 @@
 	jmp	mtabe
 mtab1x:	jmp	mtab1
 
-mtabv:	mov	eax,#VIDEO_8POINT + 0x50320000	! The 80x50 mode (VGA only)
-	stosd
-	mov	eax,#VIDEO_80x43 + 0x502b0000	! The 80x43 mode (VGA only)
-	stosd
-	mov	eax,#VIDEO_80x28 + 0x501c0000	! The 80x28 mode (VGA only)
-	stosd
+mtabv:	lea	si,vga_modes			! All modes for std VGA
+	mov	cx,#12
+	rep
+	movsw
 
 	cmpb	[scanning],#0			! Mode scan requested?
 	jz	mscan1
@@ -737,6 +820,22 @@
 mtab1:	lea	si,modelist	! Returning: SI=mode list, DI=list end
 ret0:	ret
 
+! Modes usable on all standard VGAs
+
+vga_modes:
+	.word	VIDEO_8POINT
+	.word	0x5032		! 80x50
+	.word	VIDEO_80x43
+	.word	0x502b		! 80x43
+	.word	VIDEO_80x28
+	.word	0x501c		! 80x28
+	.word	VIDEO_80x30
+	.word	0x501e		! 80x30
+	.word	VIDEO_80x34
+	.word	0x5022		! 80x34
+	.word	VIDEO_80x60
+	.word	0x503c		! 80x60
+
 !
 ! Detect VESA modes.
 !
@@ -768,8 +867,8 @@
 	cmp	ax,#0x0080	! Check validity of mode ID
 	jc	vesa2
 	or	ah,ah		! Valid ID's are 0x0000-0x007f
-	jz	vesae		! and 0x0100-0x02ff.
-	cmp	ax,#0x0300
+	jz	vesae		! and 0x0100-0x07ff.
+	cmp	ax,#0x0800
 	jnc	vesae
 vesa2:	push	cx
 	mov	cx,ax		! Get mode information structure
@@ -901,7 +1000,7 @@
 	or	bp,bp
 	jz	dosvga
 	mov	si,bp		! Found, copy the modes
-	mov	ah,#0x01
+	mov	ah,[svga_prefix]
 cpsvga:	lodsb
 	or	al,al
 	jz	didsv
@@ -920,20 +1019,21 @@
 !
 
 svga_table:
-	.word	s3_md, s3_test
 	.word	ati_md, ati_test
+	.word	oak_md, oak_test
+	.word	paradise_md, paradise_test
+	.word	realtek_md, realtek_test
+	.word	s3_md, s3_test
 	.word	chips_md, chips_test
+	.word	video7_md, video7_test
 	.word	cirrus5_md, cirrus5_test
 	.word	cirrus6_md, cirrus6_test
 	.word	cirrus1_md, cirrus1_test
 	.word	ahead_md, ahead_test
 	.word	everex_md, everex_test
 	.word	genoa_md, genoa_test
-	.word	oak_md, oak_test
-	.word	paradise_md, paradise_test
 	.word	trident_md, trident_test
 	.word	tseng_md, tseng_test
-	.word	video7_md, video7_test
 	.word	0
 
 !
@@ -1426,7 +1526,7 @@
 	out	dx,al
 	cmp	ah,#0x55
  	je	istsen
-	xor	bp,bp
+isnot:	xor	bp,bp
 istsen:	ret
 
 tseng_md:
@@ -1472,9 +1572,9 @@
 	mov	al,#0x55
 	xor	al,#0xea
 	cmp	al,bh
-	je	isvid7
-	xor	bp,bp
-isvid7:	ret
+	jne	isnot
+	movb	[svga_prefix],#VIDEO_FIRST_V7>>8	! Use special mode switching
+	ret
 
 video7_md:
 	.byte	0x40, 0x2b, 0x50
@@ -1487,6 +1587,30 @@
 	.ascii	"Video 7"
 	.byte	0
 
+! Realtek VGA
+
+realtek_test:
+	lea	si,idrtvga
+	mov	di,#0x45
+	mov	cx,#0x0b
+	repe
+	cmpsb
+	je	isrt
+	xor	bp,bp
+isrt:	ret
+
+idrtvga:	.ascii	"REALTEK VGA"
+
+realtek_md:
+	.byte	0x1a, 0x3c, 0x50
+	.byte	0x1b, 0x19, 0x84
+	.byte	0x1c, 0x1e, 0x84
+	.byte	0x1d, 0x2b, 0x84
+	.byte	0x1e, 0x3c, 0x84
+	.byte	0
+	.ascii	"REALTEK"
+	.byte	0
+
 #endif	/* CONFIG_VIDEO_SVGA */
 
 !
@@ -1606,13 +1730,14 @@
 ! Variables:
 
 adapter:	.byte	0	! Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA
-def_mode:	.byte	0	! "Default mode selected" flag
 mt_end:		.word	0	! End of video mode table if built
 edit_buf:	.space	6	! Line editor buffer
 card_name:	.word	0	! Pointer to adapter name
 scanning:	.byte	0	! Performing mode scan
 do_restore:	.byte	0	! Screen contents altered during mode change
+svga_prefix:	.byte	VIDEO_FIRST_BIOS>>8	! Default prefix for BIOS modes
 video_segment:	.word	0xb800	! Video memory segment
+force_size:	.word	0	! Use this size instead of the one in BIOS vars
 
 ! Messages:
 
diff -u --recursive --new-file v1.3.85/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.85/linux/arch/i386/defconfig	Wed Apr  3 16:06:55 1996
+++ linux/arch/i386/defconfig	Tue Apr  9 15:02:14 1996
@@ -74,6 +74,7 @@
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
 # CONFIG_AX25 is not set
+# CONFIG_BRIDGE is not set
 # CONFIG_NETLINK is not set
 
 #
@@ -89,6 +90,7 @@
 # CONFIG_SLIP is not set
 # CONFIG_PPP is not set
 # CONFIG_STRIP is not set
+# CONFIG_WIC is not set
 # CONFIG_SCC is not set
 # CONFIG_PLIP is not set
 # CONFIG_EQUALIZER is not set
diff -u --recursive --new-file v1.3.85/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v1.3.85/linux/arch/i386/mm/init.c	Mon Apr  8 19:01:41 1996
+++ linux/arch/i386/mm/init.c	Wed Apr 10 16:11:56 1996
@@ -153,8 +153,13 @@
 	pg_dir = swapper_pg_dir;
 	while (address < end_mem) {
 #ifdef USE_PENTIUM_MM
-		if (address + 4*1024*1024 <= end_mem &&
-		    (x86_capability & 8)) {
+		/*
+		 * 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) {
 #ifdef GAS_KNOWS_CR4
 			__asm__("movl %%cr4,%%eax\n\t"
 				"orl $16,%%eax\n\t"
diff -u --recursive --new-file v1.3.85/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.85/linux/drivers/block/ide.c	Mon Apr  8 19:01:42 1996
+++ linux/drivers/block/ide.c	Tue Apr  9 14:36:32 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.36  Mar 30, 1996
+ *  linux/drivers/block/ide.c	Version 5.37  Apr 6, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -224,6 +224,7 @@
  * Version 5.35		cosmetic changes
  *			fix cli() problem in try_to_identify()
  * Version 5.36		fixes to optional PCMCIA support
+ * Version 5.37		don't use DMA when "noautotune" is specified
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -2207,9 +2208,10 @@
 					drive->media = ide_tape;
 					drive->present = 1;
 					drive->removeable = 1;
-					if (HWIF(drive)->dmaproc != NULL &&
-					    !HWIF(drive)->dmaproc(ide_dma_check, drive))
-						printk(", DMA");
+					if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
+						if (!HWIF(drive)->dmaproc(ide_dma_check, drive))
+							printk(", DMA");
+					}
 					printk("\n");
 				}
 				else {
@@ -2309,7 +2311,7 @@
 		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
 			drive->special.b.set_multmode = 1;
 	}
-	if (HWIF(drive)->dmaproc != NULL) {	/* hwif supports DMA? */
+	if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
 		if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
 			printk(", DMA");
 	}
diff -u --recursive --new-file v1.3.85/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v1.3.85/linux/drivers/block/triton.c	Tue Apr  2 13:32:19 1996
+++ linux/drivers/block/triton.c	Tue Apr  9 14:36:32 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.09  Mar 31, 1996
+ *  linux/drivers/block/triton.c	Version 1.10  Apr 3, 1996
  *
  *  Copyright (c) 1995-1996  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -86,7 +86,8 @@
  *	  cannot improve slow media.  Both DMA and PIO peak around 3.5MB/sec.
  *
  *   Maxtor 71260AT (1204Meg w/256kB buffer), DMA mword0/sword2, PIO mode3.
- *	- works with DMA, giving 3-4MB/sec performance, about the same as mode3.
+ *	- works with DMA, on some systems (but not always on others, eg. Dell),
+ *	giving 3-4MB/sec performance, about the same as mode3.
  *
  * If you have any drive models to add, email your results to:  mlord@pobox.com
  * Keep an eye on /var/adm/messages for "DMA disabled" messages.
@@ -121,7 +122,6 @@
  * known to work fine with this interface under Linux.
  */
 const char *good_dma_drives[] = {"Micropolis 2112A",
-				 /* "Maxtor 71260 AT", known-bad! */
 				 "CONNER CTMA 4000"};
 
 /*
@@ -379,8 +379,8 @@
 {
 	int rc = 0, h;
 	int dma_enabled = 0;
-	unsigned short bmiba, pcicmd;
-	unsigned int timings;
+	unsigned short pcicmd;
+	unsigned int bmiba, timings;
 
 	printk("ide: 430FX (Triton) on PCI bus %d function %d\n", bus, fn);
 	/*
@@ -400,20 +400,24 @@
 		 */
 		int try_again = 1;
 		do {
-			if ((rc = pcibios_read_config_word(bus, fn, 0x20, &bmiba)))
+			if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba)))
 				goto quit;
 			bmiba &= 0xfff0;	/* extract port base address */
 			if (bmiba) {
 				dma_enabled = 1;
+				break;
 			} else {
-				printk("ide: BM-DMA base register is invalid (BIOS problem)\n");
-				if (inb(DEFAULT_BMIBA) == 0xff) {
-					printk("ide: setting BM-DMA base register to 0x%04x\n", DEFAULT_BMIBA);
-					if ((rc = pcibios_write_config_word(bus, fn, 0x20, DEFAULT_BMIBA)))
-						goto quit;
-				}
+				printk("ide: BM-DMA base register is invalid (0x%04x, PnP BIOS problem)\n", bmiba);
+				if (inb(DEFAULT_BMIBA) != 0xff || !try_again)
+					break;
+				printk("ide: setting BM-DMA base register to 0x%04x\n", DEFAULT_BMIBA);
+				if ((rc = pcibios_write_config_word(bus, fn, 0x04, pcicmd&~1)))
+					goto quit;
+				rc = pcibios_write_config_dword(bus, fn, 0x20, DEFAULT_BMIBA|1);
+				if (pcibios_write_config_word(bus, fn, 0x04, pcicmd|5) || rc)
+					goto quit;
 			}
-		} while (!dma_enabled && try_again--);
+		} while (try_again--);
 	}
 
 	/*
diff -u --recursive --new-file v1.3.85/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.85/linux/drivers/net/Config.in	Mon Apr  8 19:01:43 1996
+++ linux/drivers/net/Config.in	Tue Apr  9 14:36:30 1996
@@ -12,6 +12,7 @@
   comment 'CCP compressors for PPP are only built as modules.'
 fi
 tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
+tristate 'WIC (Radio IP bridge)' CONFIG_WIC
 tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 tristate 'PLIP (parallel port) support' CONFIG_PLIP
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
diff -u --recursive --new-file v1.3.85/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.85/linux/drivers/net/Makefile	Tue Apr  2 13:32:20 1996
+++ linux/drivers/net/Makefile	Tue Apr  9 14:36:30 1996
@@ -63,6 +63,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_WIC),y)
+L_OBJS += wic.o
+else
+  ifeq ($(CONFIG_WIC),m)
+  M_OBJS += wic.o
+  endif
+endif
+
 ifeq ($(CONFIG_SMC9194),y)
 L_OBJS += smc9194.o
 else
diff -u --recursive --new-file v1.3.85/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v1.3.85/linux/drivers/net/dlci.c	Mon Mar 25 10:06:39 1996
+++ linux/drivers/net/dlci.c	Tue Apr  9 14:36:30 1996
@@ -5,7 +5,7 @@
  *		interfaces.  Requires 'dlcicfg' program to create usable 
  *		interfaces, the initial one, 'dlci' is for IOCTL use only.
  *
- * Version:	@(#)dlci.c	0.10	23 Mar 1996
+ * Version:	@(#)dlci.c	0.15	31 Mar 1996
  *
  * Author:	Mike McLagan <mike.mclagan@linux.org>
  *
@@ -41,7 +41,7 @@
 #include <net/sock.h>
 
 static const char *devname = "dlci";
-static const char *version = "DLCI driver v0.10, 23 Mar 1996, mike.mclagan@linux.org";
+static const char *version = "DLCI driver v0.15, 31 Mar 1996, mike.mclagan@linux.org";
 
 static struct device *open_dev[CONFIG_DLCI_COUNT];
 
@@ -68,9 +68,13 @@
    }
 
    if (i == sizeof(basename) / sizeof(char *))
+      return(-EMLINK);
+
+   basename[i] = kmalloc(strlen(name) + 1, GFP_KERNEL);
+   if (!basename[i])
       return(-ENOMEM);
 
-   basename[i] = (char *) name;
+   strcpy(basename[i], name);
 
    return(0);
 }
@@ -89,22 +93,23 @@
    if (i == sizeof(basename) / sizeof(char *))
       return(-EINVAL);
 
+   kfree(basename[i]);
    basename[i] = NULL;
 
    return(0);
 }
 
 /* 
-   these encapsulate the RFC 1490 requirements as well as 
-   deal with packet transmission and reception, working with
-   the upper network layers 
+ * these encapsulate the RFC 1490 requirements as well as 
+ * deal with packet transmission and reception, working with
+ * the upper network layers 
  */
 
 static int dlci_header(struct sk_buff *skb, struct device *dev, 
                            unsigned short type, void *daddr, void *saddr, 
                            unsigned len)
 {
-   struct fradhdr    hdr;
+   struct frhdr      hdr;
    struct dlci_local *dlp;
    unsigned          hlen;
    char              *dest;
@@ -115,8 +120,8 @@
    switch(type)
    {
       case ETH_P_IP:
-         hdr.pad = FRAD_P_IP;
-         hlen = sizeof(hdr.control) + sizeof(hdr.pad);
+         hdr.IP_NLPID = FRAD_P_IP;
+         hlen = sizeof(hdr.control) + sizeof(hdr.IP_NLPID);
          break;
 
       /* feel free to add other types, if necessary */
@@ -142,11 +147,11 @@
 static void dlci_receive(struct sk_buff *skb, struct device *dev)
 {
    struct dlci_local *dlp;
-   struct fradhdr    *hdr;
+   struct frhdr      *hdr;
    int               process, header;
 
    dlp = dev->priv;
-   hdr = (struct fradhdr *) skb->data;
+   hdr = (struct frhdr *) skb->data;
    process = 0;
    header = 0;
    skb->dev = dev;
@@ -157,7 +162,7 @@
       dlp->stats.rx_errors++;
    }
    else
-      switch(hdr->pad)
+      switch(hdr->IP_NLPID)
       {
          case FRAD_P_PADDING:
             if (hdr->NLPID != FRAD_P_SNAP)
@@ -175,13 +180,13 @@
             }
 
             /* at this point, it's an EtherType frame */
-            header = sizeof(struct fradhdr);
+            header = sizeof(struct frhdr);
             skb->protocol = htons(hdr->PID);
             process = 1;
             break;
 
          case FRAD_P_IP:
-            header = sizeof(hdr->control) + sizeof(hdr->pad);
+            header = sizeof(hdr->control) + sizeof(hdr->IP_NLPID);
             skb->protocol = htons(ETH_P_IP);
             process = 1;
             break;
@@ -230,14 +235,25 @@
       printk(KERN_WARNING "%s: transmitter access conflict.\n", dev->name);
    else
    {
-      ret = dlp->slave->hard_start_xmit(skb, dlp->slave); 
-      if (ret)
-         dlp->stats.tx_errors++;
-      else 
-         dlp->stats.tx_packets++;
+      ret = dlp->slave->hard_start_xmit(skb, dlp->slave);
+      switch (ret)
+      {
+         case DLCI_RET_OK:
+            dlp->stats.tx_packets++;
+            break;
 
-      /* per Alan Cox, always return 0, let the slave free the packet */
+         case DLCI_RET_ERR:
+            dlp->stats.tx_errors++;
+            break;
+
+         case DLCI_RET_DROP:
+            dlp->stats.tx_dropped++;
+            break;
+      }
+
+      /* Alan Cox recommends always returning 0, and always freeing the packet */
       ret = 0;
+      dev_kfree_skb(skb, FREE_WRITE);
       dev->tbusy = 0;
    }
 
@@ -257,6 +273,10 @@
    if (err)
       return(err);
 
+   err = verify_area(VERIFY_WRITE, new, sizeof(*new));
+   if (err)
+      return(err);
+
    memcpy_fromfs(&dlci, new, sizeof(dlci));
 
    /* validate slave device */
@@ -293,7 +313,7 @@
       return(-ENOMEM);
 
    memset(master, 0, sizeof(*master));
-   master->name = kmalloc(strlen(buf), GFP_KERNEL);
+   master->name = kmalloc(strlen(buf) + 1, GFP_KERNEL);
 
    if (!master->name)
    {
@@ -502,6 +522,7 @@
    err = (*flp->deactivate)(dlp->slave, dev);
 
    dev->start = 0;
+   dev->tbusy = 1;
 
    return 0;
 }
@@ -540,14 +561,25 @@
 
    dev->type            = ARPHRD_DLCI;
    dev->family          = AF_INET;
-   dev->hard_header_len = sizeof(struct fradhdr);
+   dev->hard_header_len = sizeof(struct frhdr);
    dev->pa_alen         = sizeof(unsigned long);
-
+   dev->addr_len        = sizeof(short);
    memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
 
+   dev->pa_addr         = 0;
+   dev->pa_dstaddr      = 0;
+   dev->pa_brdaddr      = 0;
+   dev->pa_mask         = 0;
+
    for (i = 0; i < DEV_NUMBUFFS; i++) 
       skb_queue_head_init(&dev->buffs[i]);
 
+   if (strcmp(dev->name, devname) == 0)
+   {
+      dev->type = 0xFFFF;
+      dev->family = AF_UNSPEC;
+   }
+
    return(0);
 }
 
@@ -567,7 +599,7 @@
 }
 
 #ifdef MODULE
-static struct device dlci = {"dlci", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, dlci_init, };
+static struct device dlci = {devname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, dlci_init, };
 
 int init_module(void)
 {
@@ -577,32 +609,7 @@
 
 void cleanup_module(void)
 {
-   struct dlci_local   *dlp;
-   struct frad_local   *flp;
-   int                 i;
-
    unregister_netdev(&dlci);
-
-   for(i=0;i<CONFIG_DLCI_COUNT;i++)
-      if (open_dev[i])
-      {
-         dlp = open_dev[i]->priv;
-         flp = open_dev[i]->slave->priv;
-
-         if (open_dev[i]->start)
-         {
-            if (flp->deactivate)
-               (*flp->deactivate)(open_dev[i]->slave, open_dev[i]);
-            open_dev[i]->start = 0;
-         }
-
-         (*flp->deassoc)(open_dev[i]->slave, open_dev[i]);
-         kfree(open_dev[i]->priv);
-         kfree(open_dev[i]->name);
-         kfree(open_dev[i]);
-         open_dev[i] = NULL;
-      }
-
    if (dlci.priv)
       kfree(dlci.priv);
 }
diff -u --recursive --new-file v1.3.85/linux/drivers/net/eepro.c linux/drivers/net/eepro.c
--- v1.3.85/linux/drivers/net/eepro.c	Fri Mar  1 07:50:44 1996
+++ linux/drivers/net/eepro.c	Wed Apr 10 08:38:52 1996
@@ -1,8 +1,8 @@
 /* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
 /*
-	Written 1994, 1995 by Bao C. Ha.
+	Written 1994, 1995,1996 by Bao C. Ha.
 
-	Copyright (C) 1994, 1995 by Bao C. Ha.
+	Copyright (C) 1994, 1995,1996 by Bao C. Ha.
 
 	This software may be used and distributed
 	according to the terms of the GNU Public License,
@@ -24,9 +24,13 @@
 	If you have a problem of not detecting the 82595 during a
 	reboot (warm reset), disable the FLASH memory should fix it.
 	This is a compatibility hardware problem.
-	
+
 	Versions:
 
+	0.08	Implement 32-bit I/O for the 82595TX and 82595FX
+		based lan cards.  Disable full-duplex mode if TPE
+		is not used.  (BCH, 4/8/96)
+
 	0.07a	Fix a stat report which counts every packet as a
 		heart-beat failure. (BCH, 6/3/95)
 
@@ -52,7 +56,7 @@
 */
 
 static const char *version =
-	"eepro.c: v0.07a 6/5/95 Bao C. Ha (bao@saigon.async.com)\n";
+	"eepro.c: v0.08 4/8/96 Bao C. Ha (bao.ha@srs.gov)\n";
 
 #include <linux/module.h>
 
@@ -69,8 +73,9 @@
 	provided by Donald Becker.  I also borrowed the EEPROM routine from
 	Donald Becker's 82586 driver.
 
-	Datasheet for the Intel 82595. It provides just enough info that 
-	the casual reader might think that it documents the i82595.
+	Datasheet for the Intel 82595 (including the TX and FX version). It 
+	provides just enough info that the casual reader might think that it 
+	documents the i82595.
 
 	The User Manual for the 82595.  It provides a lot of the missing
 	information.
@@ -112,6 +117,11 @@
 /* The number of low I/O ports used by the ethercard. */
 #define EEPRO_IO_EXTENT	16
 
+/* Different 82595 chips */
+#define	LAN595		0
+#define	LAN595TX	1
+#define	LAN595FX	2
+
 /* Information that need to be kept for each board. */
 struct eepro_local {
 	struct enet_statistics stats;
@@ -121,6 +131,9 @@
 	unsigned tx_end;   /* end of the transmit chain (plus 1) */
 	int eepro;	/* a flag, TRUE=1 for the EtherExpress Pro/10,
 			   FALSE = 0 for other 82595-based lan cards. */
+	int version;	/* a flag to indicate if this is a TX or FX
+				   version of the 82595 chip. */
+	int stepping;
 };
 
 /* The station (ethernet) address prefix, used for IDing the board. */
@@ -219,11 +232,13 @@
 #define	TX_MASK		0x04
 #define	EXEC_MASK	0x08
 #define	ALL_MASK	0x0f
+#define	IO_32_BIT	0x10
 #define	RCV_BAR		0x04	/* The following are word (16-bit) registers */
 #define	RCV_STOP	0x06
 #define	XMT_BAR		0x0a
 #define	HOST_ADDRESS_REG	0x0c
 #define	IO_PORT		0x0e
+#define	IO_PORT_32_BIT	0x0c
 
 /* Bank 1 registers */
 #define	REG1	0x01
@@ -245,6 +260,9 @@
 #define	REG3		0x03
 #define	TPE_BIT		0x04
 #define	BNC_BIT		0x20
+#define	REG13		0x0d
+#define	FDX		0x00
+#define	A_N_ENABLE	0x02
 	
 #define	I_ADD_REG0	0x04
 #define	I_ADD_REG1	0x05
@@ -497,7 +515,7 @@
 static int
 eepro_open(struct device *dev)
 {
-	unsigned short temp_reg;
+	unsigned short temp_reg, old8, old9;
 	int i, ioaddr = dev->base_addr;
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
 
@@ -524,6 +542,12 @@
 
 	outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 	temp_reg = inb(ioaddr + EEPROM_REG);
+
+	lp->stepping = temp_reg >> 5;	/* Get the stepping number of the 595 */
+	
+	if (net_debug > 3)
+		printk("The stepping of the 82595 is %d\n", lp->stepping);
+
 	if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
 		outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
 	for (i=0; i < 6; i++) 
@@ -569,6 +593,40 @@
 
 	/* Initialize XMT */
 	outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR); 
+
+	/* Check for the i82595TX and i82595FX */
+	old8 = inb(ioaddr + 8);
+	outb(~old8, ioaddr + 8);
+	if ((temp_reg = inb(ioaddr + 8)) == old8) {
+		if (net_debug > 3)
+			printk("i82595 detected!\n");
+		lp->version = LAN595;
+	}
+	else {
+		lp->version = LAN595TX;
+		outb(old8, ioaddr + 8);
+		old9 = inb(ioaddr + 9);
+		outb(~old9, ioaddr + 9);
+		if ((temp_reg = inb(ioaddr + 9)) == ~old9) {
+			enum iftype { AUI=0, BNC=1, TPE=2 };
+			if (net_debug > 3)
+				printk("i82595FX detected!\n");
+			lp->version = LAN595FX;
+			outb(old9, ioaddr + 9);
+			if (dev->if_port != TPE) {	/* Hopefully, this will fix the
+							problem of using Pentiums and
+							pro/10 w/ BNC. */
+				outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+				temp_reg = inb(ioaddr + REG13);
+				/* disable the full duplex mode since it is not
+				applicable with the 10Base2 cable. */
+				outb(temp_reg & ~(FDX | A_N_ENABLE), REG13);
+				outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
+			}
+		}
+		else if (net_debug > 3)
+			printk("i82595TX detected!\n");
+	}
 	
 	outb(SEL_RESET_CMD, ioaddr);
 	/* We are supposed to wait for 2 us after a SEL_RESET */
@@ -954,19 +1012,19 @@
 		   service routines. */
 		outb(ALL_MASK, ioaddr + INT_MASK_REG);
 
-		if (((((length + 1) >> 1) << 1) + 2*XMT_HEADER) 
+		if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) 
 			>= tx_available)   /* No space available ??? */
 			continue;
 
 		last = lp->tx_end;
-		end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+		end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
 
 		if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
 			if ((RAM_SIZE - last) <= XMT_HEADER) {	
 			/* Arrrr!!!, must keep the xmt header together,
 			  several days were lost to chase this one down. */
 				last = RCV_RAM;
-				end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+				end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
 			}	
 			else end = RCV_RAM + (end - RAM_SIZE);
 		}
@@ -976,7 +1034,15 @@
 		outw(0, ioaddr + IO_PORT);
 		outw(end, ioaddr + IO_PORT);
 		outw(length, ioaddr + IO_PORT);
-		outsw(ioaddr + IO_PORT, buf, (length + 1) >> 1);
+
+		if (lp->version == LAN595)
+			outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1);
+		else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
+			unsigned short temp = inb(ioaddr + INT_MASK_REG);
+			outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
+			outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2);
+			outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
+		}
 
 		if (lp->tx_start != lp->tx_end) { 
 			/* update the next address and the chain bit in the 
@@ -1046,7 +1112,7 @@
 			struct sk_buff *skb;
 
 			rcv_size &= 0x3fff;
-			skb = dev_alloc_skb(rcv_size+2);
+			skb = dev_alloc_skb(rcv_size+5);
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
@@ -1055,7 +1121,15 @@
 			skb->dev = dev;
 			skb_reserve(skb,2);
 
-			insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 1) >> 1);
+			if (lp->version == LAN595)
+				insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1);
+			else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
+				unsigned short temp = inb(ioaddr + INT_MASK_REG);
+				outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
+				insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2);
+				outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
+			}
+
 	
 			skb->protocol = eth_type_trans(skb,dev);	
 			netif_rx(skb);
diff -u --recursive --new-file v1.3.85/linux/drivers/net/wic.c linux/drivers/net/wic.c
--- v1.3.85/linux/drivers/net/wic.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/wic.c	Tue Apr  9 14:36:30 1996
@@ -0,0 +1,1454 @@
+/* $Id: wic.c,v 1.0 1995/02/11 10:26:05 hayes Exp $ */
+/* WIC: A parallel port "network" driver for Linux. */
+/* based on the plip network driver */
+
+char *version = "NET3 WIC version 0.9 hayes@netplumbing.com";
+
+/*
+  Sources:
+	Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
+	"parallel.asm" parallel port packet driver and from the plip.c
+	parallel networking linux driver from the 1.2.13 Linux
+	distribution.
+
+  The packet is encapsulated as if it were ethernet.
+
+*/
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/if_ether.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/lp.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_wic.h>
+
+#include <linux/tqueue.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <string.h>
+
+#define NET_DEBUG 1
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+unsigned int net_debug = NET_DEBUG;
+
+/* Connection time out = WIC_TRIGGER_WAIT * WIC_DELAY_UNIT usec */
+#define WIC_TRIGGER_WAIT	 500
+
+/* Nibble time out = WIC_NIBBLE_WAIT * WIC_DELAY_UNIT usec */
+#define WIC_NIBBLE_WAIT        3000
+
+#define PAR_DATA(dev)		((dev)->base_addr+0)
+#define PAR_STATUS(dev)		((dev)->base_addr+1)
+#define PAR_CONTROL(dev)	((dev)->base_addr+2)
+
+/* Bottom halfs */
+void wic_kick_bh(struct device *dev);
+void wic_bh(struct device *dev);
+
+/* Interrupt handler */
+void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs);
+
+/* Functions for DEV methods */
+int wic_rebuild_header(void *buff, struct device *dev,
+			       unsigned long raddr, struct sk_buff *skb);
+int wic_tx_packet(struct sk_buff *skb, struct device *dev);
+int wic_open(struct device *dev);
+int wic_close(struct device *dev);
+struct enet_statistics *wic_get_stats(struct device *dev);
+int wic_config(struct device *dev, struct ifmap *map);
+int wic_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+int send_cmd(struct device *dev, unsigned char *cmd, char len);
+int recv_cmd_resp(struct device *dev, unsigned char *cmd);
+int send_byte(struct device *dev, unsigned char c);
+int get_byte(struct device *dev, unsigned char *c);
+int ack_resp(struct device *dev);
+int check_bfr(struct device *dev);
+void wic_reset(struct device *dev);
+void wic_set_multicast_list(struct device *dev, int num_addrs, 
+		void *addrs);
+
+#define LOOPCNT 30000	
+unsigned char tog = 3;
+unsigned char save = 0;
+
+enum wic_connection_state {
+	WIC_CN_NONE=0,
+	WIC_CN_RECEIVE,
+	WIC_CN_SEND,
+	WIC_CN_CLOSING,
+	WIC_CN_ERROR
+};
+
+enum wic_packet_state {
+	WIC_PK_DONE=0,
+	WIC_PK_TRIGGER,
+	WIC_PK_LENGTH_LSB,
+	WIC_PK_LENGTH_MSB,
+	WIC_PK_DATA,
+	WIC_PK_CHECKSUM
+};
+
+enum wic_nibble_state {
+	WIC_NB_BEGIN,
+	WIC_NB_1,
+	WIC_NB_2,
+};
+
+struct wic_local {
+	enum wic_packet_state state;
+	enum wic_nibble_state nibble;
+	union {
+		struct {
+#if defined(LITTLE_ENDIAN)
+			unsigned char lsb;
+			unsigned char msb;
+#elif defined(BIG_ENDIAN)
+			unsigned char msb;
+			unsigned char lsb;
+#else
+#error	"Please fix the endianness defines in <asm/byteorder.h>"
+#endif						
+		} b;
+		unsigned short h;
+	} length;
+	unsigned short byte;
+	unsigned char  checksum;
+	unsigned char  data;
+	struct sk_buff *skb;
+};
+
+struct net_local {
+	struct enet_statistics enet_stats;
+	struct tq_struct immediate;
+	struct tq_struct deferred;
+	struct wic_local snd_data;
+	struct wic_local rcv_data;
+	unsigned long  trigger;
+	unsigned long  nibble;
+	enum wic_connection_state connection;
+	unsigned short timeout_count;
+	char is_deferred;
+	int (*orig_rebuild_header)(void *eth, struct device *dev,
+				   unsigned long raddr, struct sk_buff *skb);
+};
+
+/* Entry point of WIC driver.
+   Probe the hardware, and register/initialize the driver. */
+int
+wic_init(struct device *dev)
+{
+	struct net_local *nl;
+	struct wicconf wc;
+	int i;
+
+	/* Check region before the probe */
+	if (check_region(PAR_DATA(dev), 3) < 0)
+		return -ENODEV;
+	
+	/* Check that there is something at base_addr. */
+	outb(0, PAR_DATA(dev));
+	udelay(1000);
+	if (inb(PAR_DATA(dev)) != 0)
+		return -ENODEV;
+
+	printk("%s\n",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);
+		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);
+	}
+
+	request_region(PAR_DATA(dev), 3, dev->name);
+
+	/* Fill in the generic fields of the device structure. */
+	ether_setup(dev);
+
+	/* Then, override parts of it */
+	dev->hard_start_xmit	= wic_tx_packet;
+	dev->open		= wic_open;
+	dev->stop		= wic_close;
+	dev->get_stats 		= wic_get_stats;
+	dev->set_config		= wic_config;
+	dev->do_ioctl		= wic_ioctl;
+	dev->mtu		= 1514;
+	dev->set_multicast_list = wic_set_multicast_list;
+	dev->flags	        = IFF_BROADCAST | IFF_RUNNING | IFF_NOTRAILERS;
+
+	/* get the MAC address from the controller */
+	wc.len = 1;
+	wc.pcmd = WIC_GETNET;
+	check_bfr(dev);
+	send_cmd(dev, (unsigned char *)&wc, 1);
+	wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+	while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
+		wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+	
+	printk("%s:MAC address: ",dev->name);	
+	for (i=0; i < ETH_ALEN ; i++) {
+		dev->dev_addr[i] = wc.data[i];
+		printk("%2x ",dev->dev_addr[i]);
+	}
+	printk("\n");
+
+	/* Set the private structure */
+	dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return EAGAIN;
+	memset(dev->priv, 0, sizeof(struct net_local));
+	nl = (struct net_local *) dev->priv;
+
+	nl->orig_rebuild_header = dev->rebuild_header;
+	dev->rebuild_header 	= wic_rebuild_header;
+
+	/* Initialize constants */
+	nl->trigger	= WIC_TRIGGER_WAIT;
+	nl->nibble	= WIC_NIBBLE_WAIT;
+
+	/* Initialize task queue structures */
+	nl->immediate.next = &tq_last;
+	nl->immediate.sync = 0;
+	nl->immediate.routine = (void *)(void *)wic_bh;
+	nl->immediate.data = dev;
+
+	nl->deferred.next = &tq_last;
+	nl->deferred.sync = 0;
+	nl->deferred.routine = (void *)(void *)wic_kick_bh;
+	nl->deferred.data = dev;
+
+	return 0;
+}
+
+/* Bottom half handler for the delayed request.
+   This routine is kicked by do_timer().
+   Request `wic_bh' to be invoked. */
+void
+wic_kick_bh(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+
+	if (nl->is_deferred) {
+		queue_task(&nl->immediate, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+/* Forward declarations of internal routines */
+int wic_none(struct device *, struct net_local *,
+		     struct wic_local *, struct wic_local *);
+int wic_receive_packet(struct device *, struct net_local *,
+			       struct wic_local *, struct wic_local *);
+int wic_send_packet(struct device *, struct net_local *,
+			    struct wic_local *, struct wic_local *);
+int wic_connection_close(struct device *, struct net_local *,
+				 struct wic_local *, struct wic_local *);
+int wic_error(struct device *, struct net_local *,
+		      struct wic_local *, struct wic_local *);
+int wic_bh_timeout_error(struct device *dev, struct net_local *nl,
+				 struct wic_local *snd,
+				 struct wic_local *rcv,
+				 int error);
+
+#define OK        0
+#define TIMEOUT   1
+#define ERROR     2
+
+typedef int (*wic_func)(struct device *dev, struct net_local *nl,
+			 struct wic_local *snd, struct wic_local *rcv);
+
+wic_func connection_state_table[] =
+{
+	wic_none,
+	wic_receive_packet,
+	wic_send_packet,
+	wic_connection_close,
+	wic_error
+};
+
+void 
+wic_set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+	struct wicconf wc;
+	struct wic_net *wn;
+	
+	disable_irq(dev->irq);
+	save &= 0xef; /* disable */
+	outb(save, PAR_CONTROL(dev));
+	
+	wc.len = 1;
+	wc.pcmd = WIC_GETNET;
+	check_bfr(dev);
+	tog = 3;
+	send_cmd(dev, (unsigned char *)&wc, 1);
+	wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+	while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
+		wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+	wn = (struct wic_net *)&wc.data;
+	switch (num_addrs) {
+		case -1:	/* promiscuous mode */
+			wn->mode |= (NET_MODE_ME | NET_MODE_BCAST | 
+				NET_MODE_MCAST | NET_MODE_PROM);
+	printk("%s: Setting promiscuous mode\n", dev->name);
+			break;
+		default:	/* my address and bcast addresses */
+			wn->mode &= ~(NET_MODE_PROM | NET_MODE_MCAST);
+			wn->mode |= (NET_MODE_ME | NET_MODE_BCAST);
+			/* break; */
+	}
+	wc.len = 23;
+	wc.pcmd = WIC_SETNET;
+	check_bfr(dev);
+	tog = 3;
+	send_cmd(dev, (unsigned char *)&wc, wc.len);
+
+	save |= 0x10; /* enable */
+	outb(save, PAR_CONTROL(dev));
+	enable_irq(dev->irq);
+	return;
+}
+
+/* Bottom half handler of WIC. */
+void
+wic_bh(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct wic_local *snd = &nl->snd_data;
+	struct wic_local *rcv = &nl->rcv_data;
+	wic_func f;
+	int r;
+
+	nl->is_deferred = 0;
+	f = connection_state_table[nl->connection];
+	if ((r = (*f)(dev, nl, snd, rcv)) != OK
+	    && (r = wic_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
+		nl->is_deferred = 1;
+		queue_task(&nl->deferred, &tq_timer);
+	}
+}
+
+int
+wic_bh_timeout_error(struct device *dev, struct net_local *nl,
+		      struct wic_local *snd, struct wic_local *rcv,
+		      int error)
+{
+	unsigned char c0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (nl->connection == WIC_CN_SEND) {
+
+		if (error != ERROR) { /* Timeout */
+			nl->timeout_count++;
+			if ((snd->state == WIC_PK_TRIGGER
+			     && nl->timeout_count <= 10)
+			    || nl->timeout_count <= 3) {
+				restore_flags(flags);
+				/* Try again later */
+				return TIMEOUT;
+			}
+			c0 = inb(PAR_STATUS(dev));
+			printk("%s: transmit timeout(%d,%02x)\n",
+			       dev->name, snd->state, c0);
+		}
+		nl->enet_stats.tx_errors++;
+		nl->enet_stats.tx_aborted_errors++;
+	} else if (nl->connection == WIC_CN_RECEIVE) {
+		if (rcv->state == WIC_PK_TRIGGER) {
+			/* Transmission was interrupted. */
+			restore_flags(flags);
+			return OK;
+		}
+		if (error != ERROR) { /* Timeout */
+			if (++nl->timeout_count <= 3) {
+				restore_flags(flags);
+				/* Try again later */
+				return TIMEOUT;
+			}
+			c0 = inb(PAR_STATUS(dev));
+			printk("%s: receive timeout(%d,%02x)\n",
+			       dev->name, rcv->state, c0);
+		}
+		nl->enet_stats.rx_dropped++;
+	}
+	rcv->state = WIC_PK_DONE;
+	if (rcv->skb) {
+		rcv->skb->free = 1;
+		kfree_skb(rcv->skb, FREE_READ);
+		rcv->skb = NULL;
+	}
+	snd->state = WIC_PK_DONE;
+	if (snd->skb) {
+		snd->skb->free = 1;
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		snd->skb = NULL;
+	}
+#if (0)
+	disable_irq(dev->irq);
+	save &= 0xef; /* disable */
+	outb(save, PAR_CONTROL(dev));
+	dev->tbusy = 1;
+	outb(0x00, PAR_DATA(dev));
+#endif /* (0) */
+	nl->connection = WIC_CN_ERROR;
+	restore_flags(flags);
+
+	return TIMEOUT;
+}
+
+int
+wic_none(struct device *dev, struct net_local *nl,
+	  struct wic_local *snd, struct wic_local *rcv)
+{
+	return OK;
+}
+
+/* WIC_RECEIVE --- receive a byte(two nibbles)
+   Returns OK on success, TIMEOUT on timeout */
+inline int
+wic_receive(unsigned short nibble_timeout, unsigned short status_addr,
+	     enum wic_nibble_state *ns_p, unsigned char *data_p)
+{
+unsigned int cx;
+
+	cx = LOOPCNT;
+	while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) {
+		if (--cx == 0) {
+			return TIMEOUT;
+		}
+	}
+	*data_p = inb(status_addr-1);
+	tog ^= 0x01;
+	outb(tog| save, status_addr+1);
+	return OK;
+}
+
+/* WIC_RECEIVE_PACKET --- receive a packet */
+int
+wic_receive_packet(struct device *dev, struct net_local *nl,
+		    struct wic_local *snd, struct wic_local *rcv)
+{
+	unsigned short status_addr = PAR_STATUS(dev);
+	unsigned short nibble_timeout = nl->nibble;
+	unsigned char *lbuf;
+	unsigned char junk;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	switch (rcv->state) {
+	case WIC_PK_TRIGGER:
+		disable_irq(dev->irq);
+		save &= 0xef; /* disable */
+		outb(save, PAR_CONTROL(dev));
+		
+		dev->interrupt = 0;
+
+		tog &= 0xfe;
+		ack_resp(dev);
+		if (net_debug > 2)
+			printk("%s: receive start\n", dev->name);
+		rcv->state = WIC_PK_LENGTH_LSB;
+		rcv->nibble = WIC_NB_BEGIN;
+
+	case WIC_PK_LENGTH_LSB:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_LENGTH_LSB\n", dev->name);
+		if (snd->state != WIC_PK_DONE) {
+			if (wic_receive(nl->trigger, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb)) {
+				/* collision, here dev->tbusy == 1 */
+				rcv->state = WIC_PK_DONE;
+				nl->is_deferred = 1;
+				nl->connection = WIC_CN_SEND;
+				restore_flags(flags);
+				queue_task(&nl->deferred, &tq_timer);
+				save |= 0x10; /* enable */
+				outb(save, PAR_CONTROL(dev));
+				enable_irq(dev->irq);
+				return OK;
+			}
+		} else {
+			if (wic_receive(nibble_timeout, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb)) {
+				restore_flags(flags);
+				return TIMEOUT;
+			}
+		}
+		rcv->state = WIC_PK_LENGTH_MSB;
+
+	case WIC_PK_LENGTH_MSB:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
+		if (wic_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &rcv->length.b.msb)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
+			printk("%s: bad packet size %d.\n", dev->name, rcv->length.h);
+			restore_flags(flags);
+			return ERROR;
+		}
+		/* Malloc up new buffer. */
+		rcv->skb = dev_alloc_skb(rcv->length.h);
+		if (rcv->skb == NULL) {
+			printk("%s: Memory squeeze.\n", dev->name);
+			restore_flags(flags);
+			return ERROR;
+		}
+		skb_put(rcv->skb,rcv->length.h);
+		rcv->skb->dev = dev;
+		
+		rcv->state = WIC_PK_DATA;
+		rcv->byte = 0;
+		rcv->checksum = 0;
+		
+		/* sequence numbers */
+		if (net_debug > 2)
+			printk("%s: WIC_PK_SEQ\n", dev->name);
+		if (wic_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &junk)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		if (wic_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &junk)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+
+	case WIC_PK_DATA:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_DATA: length %i\n", dev->name, 
+				rcv->length.h);
+		lbuf = rcv->skb->data;
+		do {
+			if (wic_receive(nibble_timeout, status_addr, 
+					 &rcv->nibble, &lbuf[rcv->byte])) {
+				restore_flags(flags);
+				return TIMEOUT;
+			}
+		} while (++rcv->byte < (rcv->length.h - 4));
+
+		/* receive pad byte */
+		if (rcv->length.h & 0x01)
+			wic_receive(nibble_timeout, status_addr, 
+					 &rcv->nibble, &lbuf[rcv->byte]);
+		
+		do {
+			rcv->checksum += lbuf[--rcv->byte];
+		} while (rcv->byte);
+
+		rcv->state = WIC_PK_CHECKSUM;
+
+	case WIC_PK_CHECKSUM:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_CHECKSUM\n", dev->name);
+		if (wic_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &junk)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		outb(0, PAR_DATA(dev));
+		rcv->state = WIC_PK_DONE;
+
+	case WIC_PK_DONE:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_DONE\n", dev->name);
+		/* Inform the upper layer for the arrival of a packet. */
+		netif_rx(rcv->skb);
+		nl->enet_stats.rx_packets++;
+		rcv->skb = NULL;
+		if (net_debug > 2)
+			printk("%s: receive end\n", dev->name);
+
+		/* Close the connection. */
+		if (snd->state != WIC_PK_DONE) {
+			nl->connection = WIC_CN_SEND;
+			restore_flags(flags);
+			queue_task(&nl->immediate, &tq_immediate);
+			save |= 0x10; /* enable */
+			outb(save, PAR_CONTROL(dev));
+			enable_irq(dev->irq);
+			return OK;
+		} else {
+			nl->connection = WIC_CN_NONE;
+			restore_flags(flags);
+			save |= 0x10; /* enable */
+			outb(save, PAR_CONTROL(dev));
+			enable_irq(dev->irq);
+			return OK;
+		}
+	}
+	restore_flags(flags);
+	return OK;
+}
+
+/* WIC_SEND --- send a byte (two nibbles) 
+   Returns OK on success, TIMEOUT when timeout    */
+inline int
+wic_send(unsigned short nibble_timeout, unsigned short data_addr,
+	  enum wic_nibble_state *ns_p, unsigned char data)
+{
+unsigned int cx;
+
+	cx = LOOPCNT;
+	while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) {
+		if (--cx == 0) {
+			return -TIMEOUT;
+		}
+	}
+	outb(data, data_addr);
+	outb(tog | save, data_addr+2);
+	tog ^= 0x01;
+	return OK;
+}
+
+/* WIC_SEND_PACKET --- send a packet */
+int
+wic_send_packet(struct device *dev, struct net_local *nl,
+		 struct wic_local *snd, struct wic_local *rcv)
+{
+	unsigned short data_addr = PAR_DATA(dev);
+	unsigned short nibble_timeout = nl->nibble;
+	unsigned char *lbuf;
+	unsigned int cx;
+	unsigned int pad = 2;
+	unsigned long flags;
+
+	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
+		printk("%s: send skb lost\n", dev->name);
+		snd->state = WIC_PK_DONE;
+		snd->skb = NULL;
+		save |= 0x10; /* enable */
+		outb(save, PAR_CONTROL(dev));
+		enable_irq(dev->irq);
+		return ERROR;
+	}
+
+	save_flags(flags);	
+	cli();
+	switch (snd->state) {
+	case WIC_PK_TRIGGER:
+	
+		if (nl->connection == WIC_CN_RECEIVE) {
+			/* interrupted */
+			nl->enet_stats.collisions++;
+			restore_flags(flags);
+			if (net_debug > 1)
+				printk("%s: collision.\n", dev->name);
+			save |= 0x10; /* enable */
+			outb(save, PAR_CONTROL(dev));
+			enable_irq(dev->irq);
+			return OK;
+		}
+		
+		disable_irq(dev->irq);
+		save &= 0xef; /* disable */
+		outb(save, PAR_CONTROL(dev));
+		
+		/* interrupt controller */
+		tog = 3;
+		outb(0x06 | save, PAR_CONTROL(dev));
+			
+		cx = LOOPCNT;
+		while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
+			if (--cx == 0) {
+				restore_flags(flags);
+				return TIMEOUT;
+			}
+			if (cx == 10)
+				outb(0x02, PAR_CONTROL(dev));
+		}
+		
+		if (net_debug > 2)
+			printk("%s: send start\n", dev->name);
+		snd->state = WIC_PK_LENGTH_LSB;
+		snd->nibble = WIC_NB_BEGIN;
+		nl->timeout_count = 0;
+
+	case WIC_PK_LENGTH_LSB:
+		if (snd->length.h & 0x01)
+			pad = 3;
+		else
+			pad = 2;
+		snd->length.h += (4 + pad); /* len + seq + data + pad */
+		if (net_debug > 2)
+			printk("%s: WIC_PK_LENGTH_LSB: length = %i\n", 
+				dev->name, snd->length.h);
+
+		if (wic_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.lsb)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		snd->state = WIC_PK_LENGTH_MSB;
+
+	case WIC_PK_LENGTH_MSB:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
+		if (wic_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.msb)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		snd->state = WIC_PK_DATA;
+		snd->byte = 0;
+		snd->checksum = 0;
+
+	case WIC_PK_DATA:
+		/* adjust length back to data only */
+		snd->length.h -= (4 + pad); /* len + seq + data + pad */
+		/* send 2 byte sequence number */
+		if (net_debug > 2)
+			printk("%s: WIC_SEQ\n", dev->name);
+		if (wic_send(nibble_timeout, data_addr,
+			      &snd->nibble, 0)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}
+		if (wic_send(nibble_timeout, data_addr,
+			      &snd->nibble, 0)) {
+			restore_flags(flags);
+			return TIMEOUT;
+		}	
+		if (net_debug > 2)
+			printk("%s: WIC_PK_DATA\n", dev->name);
+
+		do {
+			if (wic_send(nibble_timeout, data_addr,
+				      &snd->nibble, lbuf[snd->byte])) {
+				restore_flags(flags);
+				return TIMEOUT;
+			}
+		}
+		while (++snd->byte < snd->length.h);
+		
+		do
+			snd->checksum += lbuf[--snd->byte];
+		while (snd->byte);
+
+		snd->state = WIC_PK_CHECKSUM;
+
+	case WIC_PK_CHECKSUM:
+		/* send pad bytes */
+		if (net_debug > 2)
+			printk("%s: WIC_PK_PAD: %i bytes\n", 
+				dev->name, pad);
+		while(pad--)
+			if (wic_send(nibble_timeout, data_addr,
+			      	&snd->nibble, 0)) {
+				restore_flags(flags);
+				return TIMEOUT;
+			}
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		nl->enet_stats.tx_packets++;
+		snd->state = WIC_PK_DONE;
+
+	case WIC_PK_DONE:
+		if (net_debug > 2)
+			printk("%s: WIC_PK_DONE\n", dev->name);
+		/* Close the connection */
+		outb (0x00, PAR_DATA(dev));
+		outb(save, PAR_CONTROL(dev));
+		
+		snd->skb = NULL;
+		if (net_debug > 2)
+			printk("%s: send end\n", dev->name);
+		nl->connection = WIC_CN_CLOSING;
+		nl->is_deferred = 1;
+		restore_flags(flags);
+		queue_task(&nl->deferred, &tq_timer);
+		save |= 0x10; /* enable */
+		outb(save, PAR_CONTROL(dev));
+		enable_irq(dev->irq);
+		return OK;
+	}
+	restore_flags(flags);
+	return OK;
+}
+
+int
+wic_connection_close(struct device *dev, struct net_local *nl,
+		      struct wic_local *snd, struct wic_local *rcv)
+{
+unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (nl->connection == WIC_CN_CLOSING) {
+		nl->connection = WIC_CN_NONE;
+		dev->tbusy = 0;
+		mark_bh(NET_BH);
+	}
+	restore_flags(flags);
+	return OK;
+}
+
+/* WIC_ERROR --- wait till other end settled */
+int
+wic_error(struct device *dev, struct net_local *nl,
+	   struct wic_local *snd, struct wic_local *rcv)
+{
+	unsigned char status;
+
+	status = inb(PAR_STATUS(dev));
+	if ((status & 0xf8) == 0x80) {
+		if (net_debug > 2)
+			printk("%s: reset interface.\n", dev->name);
+		nl->connection = WIC_CN_NONE;
+		dev->tbusy = 0;
+		dev->interrupt = 0;
+		save |= 0x10; /* enable */
+		outb(save, PAR_CONTROL(dev));
+		enable_irq(dev->irq);
+		mark_bh(NET_BH);
+	} else {
+		nl->is_deferred = 1;
+		queue_task(&nl->deferred, &tq_timer);
+	}
+
+	return OK;
+}
+
+/* Handle the parallel port interrupts. */
+void
+wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs)
+{
+	struct device *dev = (struct device *) irq2dev_map[irq];
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct wic_local *rcv = &nl->rcv_data;
+	unsigned long flags;
+
+	if (dev == NULL) {
+		printk ("wic_interrupt: irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (dev->interrupt) {
+		return;
+	}
+
+	if (check_bfr(dev) < 0) {
+		return;
+	}
+	
+	dev->interrupt = 1;
+	if (net_debug > 3)
+		printk("%s: interrupt.\n", dev->name);
+
+	save_flags(flags);
+	cli();
+	switch (nl->connection) {
+	case WIC_CN_CLOSING:
+		dev->tbusy = 0;
+	case WIC_CN_NONE:
+	case WIC_CN_SEND:
+		dev->last_rx = jiffies;
+		rcv->state = WIC_PK_TRIGGER;
+		nl->connection = WIC_CN_RECEIVE;
+		nl->timeout_count = 0;
+		restore_flags(flags);
+		queue_task(&nl->immediate, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		break;
+
+	case WIC_CN_RECEIVE:
+		printk("%s: receive interrupt when receiving packet\n", dev->name);
+		restore_flags(flags);
+		break;
+
+	case WIC_CN_ERROR:
+		printk("%s: receive interrupt in error state\n", dev->name);
+		restore_flags(flags);
+		break;
+	}
+}
+
+int
+wic_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+		    struct sk_buff *skb)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct ethhdr *eth = (struct ethhdr *)buff;
+	int i;
+
+	if ((dev->flags & IFF_NOARP)==0)
+		return nl->orig_rebuild_header(buff, dev, dst, skb);
+
+	if (eth->h_proto != htons(ETH_P_IP)) {
+		printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+		return 0;
+	}
+
+	for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+		eth->h_dest[i] = 0xfc;
+	memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
+	return 0;
+}
+
+int
+wic_tx_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct wic_local *snd = &nl->snd_data;
+	unsigned long flags;
+
+	if (dev->tbusy)
+		return 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;
+	}
+
+	if (skb->len > dev->mtu) {
+		printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+		dev->tbusy = 0;
+		return 0;
+	}
+
+	if (net_debug > 2)
+		printk("%s: send request\n", dev->name);
+
+	save_flags(flags);
+	cli();
+	dev->trans_start = jiffies;
+	snd->skb = skb;
+	snd->length.h = skb->len;
+	snd->state = WIC_PK_TRIGGER;
+	if (nl->connection == WIC_CN_NONE) {
+		nl->connection = WIC_CN_SEND;
+		nl->timeout_count = 0;
+	}
+	restore_flags(flags);
+	queue_task(&nl->immediate, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	return 0;
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine gets exclusive access to the parallel port by allocating
+   its IRQ line.
+ */
+int
+wic_open(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	unsigned long flags;
+
+	if (dev->irq == 0) {
+		printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
+		return -EAGAIN;
+	}
+	save_flags(flags);
+	cli();
+	check_bfr(dev);
+	if (request_irq(dev->irq , wic_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;
+	restore_flags(flags);
+
+	save |= 0x10; /* enable */
+	outb(save, PAR_CONTROL(dev));
+	/* Initialize the state machine. */
+	nl->rcv_data.state = nl->snd_data.state = WIC_PK_DONE;
+	nl->rcv_data.skb = nl->snd_data.skb = NULL;
+	nl->connection = WIC_CN_NONE;
+	nl->is_deferred = 0;
+
+	dev->interrupt = 0;
+	dev->start = 1;
+	dev->tbusy = 0;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* The inverse routine to wic_open (). */
+int
+wic_close(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct wic_local *snd = &nl->snd_data;
+	struct wic_local *rcv = &nl->rcv_data;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+	cli();
+	free_irq(dev->irq, NULL);
+	irq2dev_map[dev->irq] = NULL;
+	nl->is_deferred = 0;
+	nl->connection = WIC_CN_NONE;
+	sti();
+	outb(0x00, PAR_DATA(dev));
+
+	snd->state = WIC_PK_DONE;
+	if (snd->skb) {
+		snd->skb->free = 1;
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		snd->skb = NULL;
+	}
+	rcv->state = WIC_PK_DONE;
+	if (rcv->skb) {
+		rcv->skb->free = 1;
+		kfree_skb(rcv->skb, FREE_READ);
+		rcv->skb = NULL;
+	}
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+struct enet_statistics *
+wic_get_stats(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct enet_statistics *r = &nl->enet_stats;
+
+	return r;
+}
+
+int
+wic_config(struct device *dev, struct ifmap *map)
+{
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	if (map->base_addr != (unsigned long)-1
+	    && map->base_addr != dev->base_addr)
+		printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+
+	if (map->irq != (unsigned char)-1)
+		dev->irq = map->irq;
+	return 0;
+}
+
+int
+wic_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct wicconf wc;
+	int err;
+	char len = 0;
+	unsigned long flags;
+
+	err=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(struct wicconf));
+	if (err)
+		return err;
+	memcpy_fromfs(&wc, rq->ifr_data, sizeof(struct wicconf));
+	switch(wc.pcmd) {
+		case WIC_AYT:
+			strcpy(wc.data, version);
+			wc.len = strlen(wc.data);
+			memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
+			/* return 0; */
+			break;
+		case WIC_RESET:
+			wic_reset(dev);
+			return(0);
+			/* break; */
+		case WIC_SETSN:
+			len = 17;
+			break;
+		case WIC_SETPS:
+			len = 3;
+			break;
+		case WIC_SETAF:
+		case WIC_SETGPF:
+			len = 2;
+			break;
+		case WIC_SETNET:
+			len = 23;
+			break;
+		case WIC_SETSYS:
+			len = 15;
+			break;
+		case WIC_GETVERH:
+		case WIC_GETNL:
+		case WIC_GETSN:
+		case WIC_CLRSTATS:
+		case WIC_GETSTATS:
+		case WIC_GETVERM:
+		case WIC_GETNET:
+		case WIC_GETSYS:
+			len = 1;
+			break;	
+		default:
+			return -EOPNOTSUPP;
+	}
+
+	/* Wait for lock to free */
+      	while (set_bit(0, (void *)&dev->tbusy) != 0); 
+	save_flags(flags);
+	cli();
+
+	disable_irq(dev->irq);
+	save &= 0xef; /* disable */
+	outb(save, PAR_CONTROL(dev));
+	err = check_bfr(dev);
+	tog = 3;
+	err = send_cmd(dev, (unsigned char *)&wc, len);
+
+	if (wc.pcmd & 0x40) {	/* response */
+		len = (char)recv_cmd_resp(dev, wc.data);
+		while ((len == 1) && (wc.data[0] == 0x7)) { /* controller int */
+			len = (char)recv_cmd_resp(dev, wc.data);
+		}
+		save |= 0x10; /* enable */
+		outb(save, PAR_CONTROL(dev));
+		enable_irq(dev->irq);
+		wc.len = (len <0) ? 0 : len;
+		memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
+	} else {
+		save |= 0x10; /* enable */
+		outb(save, PAR_CONTROL(dev));
+		enable_irq(dev->irq);
+	}
+	restore_flags(flags);
+
+	outb(0, PAR_DATA(dev));
+	dev->tbusy = 0;
+	return 0;
+}
+
+int
+get_byte(struct device *dev, unsigned char *c)
+{
+unsigned int cx;
+
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0x08) != ((tog << 3)&0x08)) {
+		if (--cx == 0) {
+			return(-TIMEOUT);
+		}
+	}
+	/* receive a byte of data */
+	*c = inb(PAR_DATA(dev));
+	tog ^= 0x01;
+	/* ack reception of data */
+	outb(tog| save, PAR_CONTROL(dev));
+	return OK;
+}
+
+int
+ack_resp(struct device *dev)
+{
+unsigned int cx;
+	
+	outb(save | 0x27, PAR_CONTROL(dev));
+
+	/* wait for controller to remove interrupt [Ack(low), Busy(low)] */
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0xc0) != 0x80) {
+		if (--cx == 0) {
+			return -TIMEOUT;
+		}
+	}
+	
+	outb(save | 0x22, PAR_CONTROL(dev));
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0x08) == 0x08) {
+		if (--cx == 0) {
+			return TIMEOUT;
+		}
+	}
+	tog |= 0x20;
+	tog &= 0xfe;
+	return OK;
+}
+
+void
+wic_reset(struct device *dev)
+{
+unsigned char stat;
+
+	stat = inb(PAR_CONTROL(dev));
+	outb(0, PAR_DATA(dev));
+	outb(stat | 0x08, PAR_CONTROL(dev));
+	outb(stat & 0xf7, PAR_CONTROL(dev));
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	tog = 3;
+	save = 0;
+	return;
+}
+
+int
+check_bfr(struct device *dev)
+{
+unsigned char c0, l;
+
+	if ((inb(PAR_STATUS(dev)) & 0xc8) == 0x48) {
+		save |= 0x80;
+		outb(0x23| save, PAR_CONTROL(dev));
+		ack_resp(dev);
+		get_byte(dev, &l);	/* len */
+		while (l--) {
+			get_byte(dev, &c0);
+		}
+		get_byte(dev, &c0);
+		save &=0x7f;
+		outb(0, PAR_DATA(dev));
+		return -l;
+	} else
+	return (0);
+}
+
+
+int
+recv_cmd_resp(struct device *dev, unsigned char *buf)
+{
+unsigned char cksum = 0;
+int err;
+unsigned char c0 = 0;
+int len;
+int savelen;
+unsigned int cx;
+int i;
+
+	tog &= 0xfe;
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0xc8) != 0x48) {
+		if (--cx == 0) {
+			/* clear Busy */
+			outb(0, PAR_DATA(dev));
+			printk("rcv_cmd_resp: timeout\n");
+			return -TIMEOUT;
+		}
+	}
+	
+	/* acknowledge the interrupt */
+	i = ack_resp(dev);
+
+	/* get length */
+	err = get_byte(dev, &c0);
+	if (err < 0) {
+		printk("get_byte1: failed\n");
+		return(err);
+	}
+	len = c0;
+	savelen = len;
+
+	/* get data */
+	while(len--) {
+		err = get_byte(dev, &c0);
+		if (err < 0) {
+			printk("get_byte2: failed\n");
+			return(err);
+		}
+		outb(0, PAR_DATA(dev));	
+		*buf = c0;
+		cksum += c0;
+		buf++;
+	}	
+	/* get cksum */
+	err = get_byte(dev, &c0);
+	if (err < 0) {
+		printk("get_byte3: failed\n");
+		return(err);
+	}
+	if (cksum != c0) {
+		printk("cksum failed\n");
+		return(-3);
+	}
+	/* get trailing byte, if any... */
+	get_byte(dev, &c0);
+	return(savelen);
+}	
+
+int
+send_byte(struct device *dev, unsigned char c)
+{
+unsigned int cx;
+
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog<<7) & 0x80)) {
+		if (--cx == 0) {
+			return(-TIMEOUT);
+		}
+	}
+	outb(c, PAR_DATA(dev));
+	outb(save |tog, PAR_CONTROL(dev));
+	tog ^= 0x01;
+	return OK;
+}
+
+
+int
+send_cmd(struct device *dev, unsigned char *cmd, char len)
+{
+unsigned char cksum = 0;
+int err = 0;
+unsigned int cx;
+
+	/* interrupt controller */
+	outb(save | 0x04, PAR_CONTROL(dev));
+	/* wait for ACK */
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
+		if (--cx == 0) 
+			return -TIMEOUT;
+		if (cx == 10)
+			outb(0x02, PAR_CONTROL(dev));
+	}
+	/* cmd coming... */
+	outb(save | 0x02, PAR_CONTROL(dev));
+	/* send length byte */
+	err = send_byte(dev, (unsigned char)len);
+	
+	/* send data */
+	while (len--) {
+		err = send_byte(dev, *cmd);	
+		if (err < 0) {
+			return err;
+		}
+		cksum += *cmd;
+		cmd++;
+	}
+	
+	/* send cksum byte */
+	err = send_byte(dev, cksum);	
+	if (err < 0)
+		return err;
+
+	cx = LOOPCNT;
+	while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog <<7)&0x80)) {
+		if (--cx == 0) 
+			return -TIMEOUT;
+	}
+	save |= 0x80;
+	outb(save | 0x23, PAR_CONTROL(dev));
+	outb(0, PAR_DATA(dev));
+	return OK;	
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+struct device dev_wic0 = 
+{
+	"wic0" /*"wic"*/,
+	0, 0, 0, 0,		/* memory */
+	0x3BC, 5,		/* base, irq */
+	0, 0, 0, NULL, wic_init 
+};
+
+struct device dev_wic1 = 
+{
+	"wic1" /*"wic"*/,
+	0, 0, 0, 0,		/* memory */
+	0x378, 7,		/* base, irq */
+	0, 0, 0, NULL, wic_init 
+};
+
+struct device dev_wic2 = 
+{
+	"wic2" /*"wic"*/,
+	0, 0, 0, 0,		/* memory */
+	0x278, 2,		/* base, irq */
+	0, 0, 0, NULL, wic_init 
+};
+
+int
+init_module(void)
+{
+	int devices=0;
+
+	if (register_netdev(&dev_wic0) != 0)
+		devices++;
+	if (register_netdev(&dev_wic1) != 0)
+		devices++;
+	if (register_netdev(&dev_wic2) != 0)
+		devices++;
+	if (devices == 0)
+		return -EIO;
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	if (dev_wic0.priv) {
+		unregister_netdev(&dev_wic0);
+		release_region(PAR_DATA(&dev_wic0), 3);
+		kfree_s(dev_wic0.priv, sizeof(struct net_local));
+		dev_wic0.priv = NULL;
+	}
+	if (dev_wic1.priv) {
+		unregister_netdev(&dev_wic1);
+		release_region(PAR_DATA(&dev_wic1), 3);
+		kfree_s(dev_wic1.priv, sizeof(struct net_local));
+		dev_wic1.priv = NULL;
+	}
+	if (dev_wic2.priv) {
+		unregister_netdev(&dev_wic2);
+		release_region(PAR_DATA(&dev_wic2), 3);
+		kfree_s(dev_wic2.priv, sizeof(struct net_local));
+		dev_wic2.priv = NULL;
+	}
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c wic.c"
+ * End:
+ */
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.85/linux/drivers/scsi/Config.in	Mon Apr  8 19:01:43 1996
+++ linux/drivers/scsi/Config.in	Tue Apr  9 14:25:24 1996
@@ -22,6 +22,7 @@
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
+dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
 dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v1.3.85/linux/drivers/scsi/Makefile	Thu Mar 14 12:57:45 1996
+++ linux/drivers/scsi/Makefile	Tue Apr  9 14:25:37 1996
@@ -251,6 +251,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_DTC3280),y)
+L_OBJS += dtc.o
+else
+  ifeq ($(CONFIG_SCSI_DTC3280),m)
+  M_OBJS += dtc.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_ULTRASTOR),y)
 L_OBJS += ultrastor.o
 else
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v1.3.85/linux/drivers/scsi/NCR5380.c	Fri Mar 22 09:40:30 1996
+++ linux/drivers/scsi/NCR5380.c	Tue Apr  9 14:25:37 1996
@@ -31,6 +31,12 @@
 
 /*
  * $Log: NCR5380.c,v $
+ * Revision 1.7  1996/3/2	Ray Van Tassle (rayvt@comm.mot.com)
+ * added proc_info
+ * added support needed for DTC 3180/3280
+ * fixed a couple of bugs
+ *
+
  * Revision 1.5  1994/01/19  09:14:57  drew
  * Fixed udelay() hack that was being used on DATAOUT phases
  * instead of a proper wait for the final handshake.
@@ -203,6 +209,9 @@
  * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
  * 	transceivers. 
  *
+ * DONT_USE_INTR - if defined, never use interrupts, even if we probe or
+ *	override-configure an IRQ.
+ *
  * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
  *	bytes at a time.  Since interrupts are disabled by default during
  *	these transfers, we might need this to give reasonable interrupt
@@ -242,8 +251,8 @@
  * USLEEP_POLL - amount of time, in jiffies, to poll
  *
  * These macros MUST be defined :
- * NCR5380_local_declare() - declare any local variables needed for your transfer
- *	routines.
+ * NCR5380_local_declare() - declare any local variables needed for your
+ * 	transfer routines.
  *
  * NCR5380_setup(instance) - initialize any local variables needed from a given
  *	instance of the host adapter for NCR5380_{read,write,pread,pwrite}
@@ -279,6 +288,7 @@
  * NCR5380_queue_command
  * NCR5380_reset
  * NCR5380_abort
+ * NCR5380_proc_info
  *
  * to be the global entry points into the specific driver, ie 
  * #define NCR5380_queue_command t128_queue_command.
@@ -724,10 +734,9 @@
  */
 
 static void NCR5380_print_status (struct Scsi_Host *instance) {
-    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
-	instance->hostdata;
-    Scsi_Cmnd *ptr;
-
+   char pr_bfr[256];
+   char *start;
+   int len;
 
     printk("NCR5380 : coroutine is%s running.\n",
 	main_running ? "" : "n't");
@@ -737,27 +746,144 @@
     NCR5380_print_phase (instance);
 #endif
 
-    cli();
-    if (!hostdata->connected) {
-	printk ("scsi%d: no currently connected command\n",
-	    instance->host_no);
-    } else {
-	print_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected);
-    }
+   len = NCR5380_proc_info(pr_bfr, &start, 0, sizeof(pr_bfr),
+			instance->host_no, 0);
+   pr_bfr[len] = 0;
+   printk("\n%s\n", pr_bfr);
+  }
 
-    printk ("scsi%d: issue_queue\n", instance->host_no);
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
 
-    for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; 
-	ptr = (Scsi_Cmnd *) ptr->host_scribble) 
-	print_Scsi_Cmnd (ptr);
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+static
+char *lprint_command (unsigned char *cmd, char *pos, char *buffer, int len);
+static
+char *lprint_opcode(int opcode, char *pos, char *buffer, int length);
 
-    printk ("scsi%d: disconnected_queue\n", instance->host_no);
+#ifndef NCR5380_proc_info
+static
+#endif
+int NCR5380_proc_info (
+     char *buffer, char **start,off_t offset,
+     int length,int hostno,int inout)
+{
+   char *pos = buffer;
+   struct Scsi_Host *instance;
+   struct NCR5380_hostdata *hostdata;
+   Scsi_Cmnd *ptr;
+
+   for (instance = first_instance; instance &&
+        instance->host_no != hostno; instance=instance->next)
+        ;
+   if (!instance)
+      return(-ESRCH);
+   hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+   if (inout) { /* Has data been written to the file ? */
+#ifdef DTC_PUBLIC_RELEASE
+        dtc_wmaxi = dtc_maxi = 0;
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+        pas_wmaxi = pas_maxi = 0;
+#endif
+      return(-ENOSYS);  /* Currently this is a no-op */
+   }
+   SPRINTF("NCR5380 core release=%d.   ", NCR5380_PUBLIC_RELEASE);
+   if (((struct NCR5380_hostdata *)instance->hostdata)->flags & FLAG_NCR53C400)
+        SPRINTF("ncr53c400 release=%d.  ", NCR53C400_PUBLIC_RELEASE);
+#ifdef DTC_PUBLIC_RELEASE
+   SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE);
+#endif
+#ifdef T128_PUBLIC_RELEASE
+   SPRINTF("T128 release %d", T128_PUBLIC_RELEASE);
+#endif
+#ifdef GENERIC_NCR5380_PUBLIC_RELEASE
+   SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
+#endif
+
+   SPRINTF("\nBase Addr: 0x%05X    ", (int)instance->base);
+   SPRINTF("io_port: %04x      ", (int)instance->io_port);
+   if (instance->irq == IRQ_NONE)
+      SPRINTF("IRQ: None.\n");
+   else
+      SPRINTF("IRQ: %d.\n", instance->irq);
+
+#ifdef DTC_PUBLIC_RELEASE
+   SPRINTF("Highwater I/O busy_spin_counts -- write: %d  read: %d\n",
+        dtc_wmaxi, dtc_maxi);
+#endif
+#ifdef PAS16_PUBLIC_RELEASE
+   SPRINTF("Highwater I/O busy_spin_counts -- write: %d  read: %d\n",
+        pas_wmaxi, pas_maxi);
+#endif
+    cli();
+    SPRINTF("NCR5380 : coroutine is%s running.\n", main_running ? "" : "n't");
+    if (!hostdata->connected)
+      SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
+   else
+      pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+        pos, buffer, length);
+   SPRINTF("scsi%d: issue_queue\n", instance->host_no);
+   for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
+        ptr = (Scsi_Cmnd *) ptr->host_scribble)
+        pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+
+   SPRINTF("scsi%d: disconnected_queue\n", instance->host_no);
+   for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+        ptr = (Scsi_Cmnd *) ptr->host_scribble)
+        pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+
+   sti();
+   *start=buffer;
+  if (pos - buffer < offset)
+    return 0;
+  else if (pos - buffer - offset < length)
+    return pos - buffer - offset;
+  return length;
+}
 
-    for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; 
-	ptr = (Scsi_Cmnd *) ptr->host_scribble) 
-	print_Scsi_Cmnd (ptr);
-    
-    sti();
+static 
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length) {
+   SPRINTF("scsi%d : destination target %d, lun %d\n",
+        cmd->host->host_no, cmd->target, cmd->lun);
+   SPRINTF("        command = ");
+   pos = lprint_command (cmd->cmnd, pos, buffer, length);
+   return (pos);
+}
+
+static 
+char *lprint_command (unsigned char *command,
+     char *pos, char *buffer, int length) {
+   int i, s;
+   pos = lprint_opcode(command[0], pos, buffer, length);
+   for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+      SPRINTF("%02x ", command[i]);
+   SPRINTF("\n");
+   return(pos);
+}
+
+static 
+char *lprint_opcode(int opcode, char *pos, char *buffer, int length) {
+   SPRINTF("%2d (0x%02x)", opcode, opcode);
+   return(pos);
 }
 
 
@@ -926,7 +1052,6 @@
 
 #if (NDEBUG & NDEBUG_NO_WRITE)
     switch (cmd->cmnd[0]) {
-    case WRITE:
     case WRITE_6:
     case WRITE_10:
 	printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
@@ -1152,6 +1277,7 @@
     main_running = 0;
 }
 
+#ifndef DONT_USE_INTR
 /*
  * Function : void NCR5380_intr (int irq)
  * 
@@ -1263,6 +1389,7 @@
 	    } /* if (instance->irq == irq) */
     } while (!done);
 }
+#endif
 
 #ifdef NCR5380_STATS
 static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
@@ -2071,6 +2198,9 @@
     return 0;
 #else /* defined(REAL_DMA_POLL) */
     if (p & SR_IO) {
+#ifdef DMA_WORKS_RIGHT
+        foo = NCR5380_pread(instance, d, c);
+#else
 	int diff = 1;
 	if (hostdata->flags & FLAG_NCR53C400) {
 	    diff=0;
@@ -2106,8 +2236,12 @@
 		d[c - 1] = NCR5380_read(INPUT_DATA_REG);
 	    }
 	}
+#endif
     } else {
-	int timeout;
+#ifdef DMA_WORKS_RIGHT
+        foo = NCR5380_pwrite(instance, d, c);
+#else
+        int timeout;
 #if (NDEBUG & NDEBUG_C400_PWRITE)
 	printk("About to pwrite %d bytes\n", c);
 #endif
@@ -2164,6 +2298,7 @@
 	    udelay (5);
 #endif
 	}
+#endif
     }
 
     NCR5380_write(MODE_REG, MR_BASE);
@@ -2316,6 +2451,10 @@
 		    !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
 		    cmd->SCp.this_residual && !(cmd->SCp.this_residual % 
 		    transfersize)) {
+                    /* Limit transfers to 32K, for xx400 & xx406
+                     * pseudoDMA that transfers in 128 bytes blocks. */
+                    if (transfersize > 32*1024)
+                         transfersize = 32*1024;
 #endif
 		    len = transfersize;
 		    if (NCR5380_transfer_dma(instance, &phase,
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h
--- v1.3.85/linux/drivers/scsi/NCR5380.h	Wed Mar 20 10:50:02 1996
+++ linux/drivers/scsi/NCR5380.h	Tue Apr  9 14:25:37 1996
@@ -7,7 +7,7 @@
  * 	drew@colorado.edu
  *      +1 (303) 666-5836
  *
- * DISTRIBUTION RELEASE 6
+ * DISTRIBUTION RELEASE 7
  *
  * For more information, please consult 
  *
@@ -28,7 +28,7 @@
 #ifndef NCR5380_H
 #define NCR5380_H
 
-#define NCR5380_PUBLIC_RELEASE 6
+#define NCR5380_PUBLIC_RELEASE 7
 #define NCR53C400_PUBLIC_RELEASE 2
 
 #define NDEBUG_ARBITRATION	0x1
@@ -284,7 +284,9 @@
 #endif
 static void NCR5380_init (struct Scsi_Host *instance, int flags);
 static void NCR5380_information_transfer (struct Scsi_Host *instance);
+#ifndef DONT_USE_INTR
 static void NCR5380_intr (int irq, void *dev_id, struct pt_regs * regs);
+#endif
 static void NCR5380_main (void);
 static void NCR5380_print_options (struct Scsi_Host *instance);
 static void NCR5380_print_phase (struct Scsi_Host *instance);
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/README.dtc3x80 linux/drivers/scsi/README.dtc3x80
--- v1.3.85/linux/drivers/scsi/README.dtc3x80	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/README.dtc3x80	Tue Apr  9 14:25:37 1996
@@ -0,0 +1,44 @@
+REAME file for the Linux DTC3180/3280 scsi driver.
+by Ray Van Tassle (rayvt@comm.mot.com)  March 1996
+Based on the generic & core NCR5380 code by Drew Eckhard
+
+SCSI device driver for the DTC 3180/3280.
+Data Technology Corp---a division of Qume.
+
+The 3280 has a standard floppy interface.
+The 3180 does not.  Otherwise, they are identical.
+The DTC3x80 does not support DMA but it does have Pseudo-DMA which is
+supported by the driver.
+It's DTC406 scsi chip is supposedly compatible with the NCR 53C400.
+It is memory mapped, uses an IRQ, but no dma or io-port.  There is 
+internal DMA, between SCSI bus and an on-chip 128-byte buffer.  Double
+buffering is done automagically by the chip.
+Data is transferred between the on-chip buffer and CPU/RAM via
+memory moves.
+
+The driver detects the possible memory addresses (jumper selectable):
+	CC00, DC00, C800, and D800
+The possible IRQ's (jumper selectable) are:
+	IRQ 10, 11, 12, 15
+Parity is supported by the chip, but not by this driver.
+Information can be obtained from /proc/scsi/dtc3c80/N.
+
+Note on interrupts:
+The documentation says that it can be set to interrupt whenever the
+on-chip buffer needs CPU attention.  I couldn't get this to work.
+So the driver polls for data-ready in the pseudo-DMA transfer routine.
+The interrupt support routines in the NCR3280.c core modules handle
+scsi disconnect/reconnect, and this (mostly) works.
+However.....
+I have tested it with 4 totally different hard drives (both SCSI-1 
+and SCSI-2), and one CDROM drive.
+Interrupts works great for all but one specific hard drive.  For this one,
+the driver will eventually hang in the transfer state.
+I have tested with: "dd bs=4k count=2k of=/dev/null if=/dev/sdb".  It
+reads ok for a while, then hangs.  After beating my head against this for a
+couple of weeks, getting nowhere, I give up.
+So.....This driver does NOT use interrupts, even if you have the card 
+jumpered to an IRQ.  Probably nobody will ever care.
+Nor will I when the $380 2.5GB IDE drives hit the market in early 1996!
+
+
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/dtc.c linux/drivers/scsi/dtc.c
--- v1.3.85/linux/drivers/scsi/dtc.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/dtc.c	Tue Apr  9 14:25:37 1996
@@ -0,0 +1,400 @@
+
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define DONT_USE_INTR
+#define UNSAFE		/* Leave interrupts enabled during pseudo-dma I/O */
+#define xNDEBUG (NDEBUG_INTR+NDEBUG_RESELECTION+\
+		 NDEBUG_SELECTION+NDEBUG_ARBITRATION)
+#define DMA_WORKS_RIGHT
+
+
+/*
+ * DTC 3180/3280 driver, by
+ *	Ray Van Tassle	rayvt@comm.mot.com
+ *
+ *	taken from ...
+ *	Trantor T128/T128F/T228 driver by...
+ *
+ * 	Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 1.
+ *
+ * For more information, please consult 
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+*/
+
+/*
+ * Options : 
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ *      for commands that return with a CHECK CONDITION status. 
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. 
+ *		You probably want this.
+ *
+ * The card is detected and initialized in one of several ways : 
+ * 1.  Autoprobe (default) - since the board is memory mapped, 
+ *     a BIOS signature is scanned for to locate the registers.
+ *     An interrupt is triggered to autoprobe for the interrupt
+ *     line.
+ *
+ * 2.  With command line overrides - dtc=address,irq may be 
+ *     used on the LILO command line to override the defaults.
+ * 
+*/
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 0
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include <asm/io.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "dtc.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+#include<linux/string.h>
+
+struct proc_dir_entry proc_scsi_dtc = {
+   PROC_SCSI_T128, 7, "dtc3x80",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+     };
+
+
+static struct override {
+   unsigned char *address;
+   int irq;
+} overrides
+#ifdef OVERRIDE
+[] = OVERRIDE;
+#else
+[4] = {{NULL, IRQ_AUTO}, {NULL, IRQ_AUTO}, {NULL, IRQ_AUTO},
+     {NULL, IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+   unsigned char *address;
+   int noauto;
+} bases[] = {{(unsigned char *) 0xcc000, 0}, {(unsigned char *) 0xc8000, 0},
+{(unsigned char *) 0xdc000, 0}, {(unsigned char *) 0xd8000, 0}};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static const struct signature {
+   const char *string;
+   int offset;
+} signatures[] = { {"DATA TECHNOLOGY CORPORATION BIOS", 0x25}, };
+
+#define NO_SIGNATURES (sizeof (signatures) /  sizeof (struct signature))
+
+/*
+ * Function : dtc_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+*/
+
+void dtc_setup(char *str, int *ints) {
+   static int commandline_current = 0;
+   int i;
+   if (ints[0] != 2)
+      printk("dtc_setup: usage dtc=address,irq\n");
+   else
+      if (commandline_current < NO_OVERRIDES) {
+      overrides[commandline_current].address = (unsigned char *) ints[1];
+      overrides[commandline_current].irq = ints[2];
+      for (i = 0; i < NO_BASES; ++i)
+	 if (bases[i].address == (unsigned char *) ints[1]) {
+	 bases[i].noauto = 1;
+	 break;
+      }
+      ++commandline_current;
+   }
+}
+
+/* 
+ * Function : int dtc_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes DTC 3180/3280 controllers
+ *	that were autoprobed, overridden on the LILO command line, 
+ *	or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ * 
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+*/
+
+
+int dtc_detect(Scsi_Host_Template * tpnt) {
+   static int current_override = 0, current_base = 0;
+   struct Scsi_Host *instance;
+   unsigned char *base;
+   int sig, count;
+
+   tpnt->proc_dir = &proc_scsi_dtc;
+   tpnt->proc_info = &dtc_proc_info;
+
+   for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+      base = NULL;
+
+      if (overrides[current_override].address)
+	 base = overrides[current_override].address;
+      else
+	 for (; !base && (current_base < NO_BASES); ++current_base) {
+#if (DTCDEBUG & DTCDEBUG_INIT)
+	 printk("scsi : probing address %08x\n", (unsigned int) bases[current_base].address);
+#endif
+	 for (sig = 0; sig < NO_SIGNATURES; ++sig)
+	    if (!bases[current_base].noauto && !memcmp
+	      (bases[current_base].address + signatures[sig].offset,
+	      signatures[sig].string, strlen(signatures[sig].string))) {
+	    base = bases[current_base].address;
+#if (DTCDEBUG & DTCDEBUG_INIT)
+	    printk("scsi-dtc : detected board.\n");
+#endif
+	    break;
+	 }
+      }
+
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+      printk("scsi-dtc : base = %08x\n", (unsigned int) base);
+#endif
+
+      if (!base)
+	 break;
+
+      instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+      instance->base = base;
+
+      NCR5380_init(instance, 0);
+
+      NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR); /* Enable int's */
+      if (overrides[current_override].irq != IRQ_AUTO)
+	 instance->irq = overrides[current_override].irq;
+      else
+	 instance->irq = NCR5380_probe_irq(instance, DTC_IRQS);
+
+#ifndef DONT_USE_INTR
+/* With interrupts enabled, it will sometimes hang when doing heavy
+ * reads. So better not enable them until I finger it out. */
+      if (instance->irq != IRQ_NONE)
+	 if (request_irq(instance->irq, dtc_intr, SA_INTERRUPT, "dtc")) {
+	 printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+	      instance->host_no, instance->irq);
+	 instance->irq = IRQ_NONE;
+      }
+
+      if (instance->irq == IRQ_NONE) {
+	 printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+	 printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+      }
+#else
+      if (instance->irq != IRQ_NONE)
+	 printk("scsi%d : interrupts not used. Might as well not jumper it.\n",
+		instance->host_no);
+      instance->irq = IRQ_NONE;
+#endif
+#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
+      printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+      printk("scsi%d : at 0x%05X", instance->host_no, (int)instance->base);
+      if (instance->irq == IRQ_NONE)
+	 printk (" interrupts disabled");
+      else
+	 printk (" irq %d", instance->irq);
+      printk(" options CAN_QUEUE=%d  CMD_PER_LUN=%d release=%d",
+	   CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE);
+      NCR5380_print_options(instance);
+      printk("\n");
+
+      ++current_override;
+      ++count;
+   }
+   return count;
+}
+
+/*
+ * Function : int dtc_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for 
+ *	the specified device / size.
+ * 
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ *	major / minor, ip[] = {heads, sectors, cylinders}  
+ *
+ * Returns : always 0 (success), initializes ip
+ *	
+*/
+
+/* 
+ * XXX Most SCSI boards use this mapping, I could be incorrect.  Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+*/
+
+int dtc_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+   int size = disk->capacity;
+
+   ip[0] = 64;
+   ip[1] = 32;
+   ip[2] = size >> 11;
+   return 0;
+}
+
+/****************************************************************
+ * Function : int NCR5380_pread (struct Scsi_Host *instance, 
+ *	unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, reads len bytes to 
+ *	dst
+ * 
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog 
+ * 	timeout.
+*/
+
+static int dtc_maxi = 0;
+static int dtc_wmaxi = 0;
+
+static inline int NCR5380_pread (struct Scsi_Host *instance,
+     unsigned char *dst, int len)
+     {
+   unsigned char *d = dst;
+   int i;	/* For counting time spent in the poll-loop */
+   NCR5380_local_declare();
+   NCR5380_setup(instance);
+
+   i = 0;
+   NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+   NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+   if (instance->irq == IRQ_NONE)
+      NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ);
+   else
+      NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE);
+   NCR5380_write(DTC_BLK_CNT, len >> 7);        /* Block count */
+   rtrc(1);
+   while (len > 0) {
+      rtrc(2);
+      while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+	 ++i;
+      rtrc(3);
+      memcpy(d, (char *)(base + DTC_DATA_BUF), 128);
+      d += 128;
+      len -= 128;
+      rtrc(7);	/*** with int's on, it sometimes hangs after here.
+		 * Looks like something makes HBNR go away. */
+   }
+   rtrc(4);
+   while ( !(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+      ++i;
+   NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+   rtrc(0);
+   NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+   if (i > dtc_maxi)
+      dtc_maxi = i;
+   return(0);
+}
+
+/****************************************************************
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance, 
+ *	unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ *	src
+ * 
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog 
+ * 	timeout.
+*/
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance,
+     unsigned char *src, int len) {
+   int i;
+   NCR5380_local_declare();
+   NCR5380_setup(instance);
+
+   NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+   NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
+   /* set direction (write) */
+   if (instance->irq == IRQ_NONE)
+      NCR5380_write(DTC_CONTROL_REG, 0);
+   else
+      NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR);
+   NCR5380_write(DTC_BLK_CNT, len >> 7);        /* Block count */
+   for (i = 0; len > 0; ++i) {
+      rtrc(5);
+      /* Poll until the host buffer can accept data. */
+      while (NCR5380_read(DTC_CONTROL_REG) & CSR_HOST_BUF_NOT_RDY)
+	 ++i;
+      rtrc(3);
+      memcpy((char *)(base + DTC_DATA_BUF), src, 128);
+      src += 128;
+      len -= 128;
+   }
+   rtrc(4);
+   while ( !(NCR5380_read(DTC_CONTROL_REG) & D_CR_ACCESS))
+      ++i;
+   rtrc(6);
+   /* Wait until the last byte has been sent to the disk */
+   while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+      ++i;
+   rtrc(7);
+   /* Check for parity error here. fixme. */
+   NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
+   rtrc(0);
+   if (i > dtc_wmaxi)
+      dtc_wmaxi = i;
+   return (0);
+}
+
+#include "NCR5380.c"
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = DTC3x80;
+
+#include "scsi_module.c"
+#endif
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/dtc.h linux/drivers/scsi/dtc.h
--- v1.3.85/linux/drivers/scsi/dtc.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/dtc.h	Tue Apr  9 14:25:37 1996
@@ -0,0 +1,169 @@
+/*
+ * DTC controller, taken from T128 driver by...
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 1. 
+ *
+ * For more information, please consult 
+ *
+ * 
+ * 
+ * and 
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+#ifndef DTC3280_H
+#define DTC3280_H
+
+#define DTC_PUBLIC_RELEASE 1
+
+/*#define DTCDEBUG 0x1*/
+#define DTCDEBUG_INIT	0x1
+#define DTCDEBUG_TRANSFER 0x2
+
+/*
+ * The DTC3180 & 3280 boards are memory mapped.
+ * 
+ */
+
+/*
+ */ 
+/* Offset from DTC_5380_OFFSET */
+#define DTC_CONTROL_REG		0x100	/* rw */
+#define D_CR_ACCESS		0x80	/* ro set=can access 3280 registers */
+#define CSR_DIR_READ		0x40	/* rw direction, 1 = read 0 = write */
+
+#define CSR_RESET              0x80    /* wo  Resets 53c400 */
+#define CSR_5380_REG           0x80    /* ro  5380 registers can be accessed */
+#define CSR_TRANS_DIR          0x40    /* rw  Data transfer direction */
+#define CSR_SCSI_BUFF_INTR     0x20    /* rw  Enable int on transfer ready */
+#define CSR_5380_INTR          0x10    /* rw  Enable 5380 interrupts */
+#define CSR_SHARED_INTR        0x08    /* rw  Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY   0x04    /* ro  Host buffer not ready */
+#define CSR_SCSI_BUF_RDY       0x02    /* ro  SCSI buffer ready */
+#define CSR_GATED_5380_IRQ     0x01    /* ro  Last block xferred */
+#define CSR_INT_BASE (CSR_SCSI_BUFF_INTR | CSR_5380_INTR)
+
+
+#define DTC_BLK_CNT		0x101   /* rw 
+					 * # of 128-byte blocks to transfer */
+
+
+#define D_CR_ACCESS             0x80    /* ro set=can access 3280 registers */
+
+#define DTC_SWITCH_REG		0x3982	/* ro - DIP switches */
+#define DTC_RESUME_XFER		0x3982	/* wo - resume data xfer 
+					   * after disconnect/reconnect*/
+
+#define DTC_5380_OFFSET		0x3880	/* 8 registers here, see NCR5380.h */
+
+/*!!!! for dtc, it's a 128 byte buffer at 3900 !!! */
+#define DTC_DATA_BUF		0x3900  /* rw 128 bytes long */
+
+
+#ifndef ASM
+int dtc_abort(Scsi_Cmnd *);
+int dtc_biosparam(Disk *, kdev_t, int*);
+int dtc_detect(Scsi_Host_Template *);
+int dtc_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int dtc_reset(Scsi_Cmnd *);
+int dtc_proc_info (char *buffer, char **start, off_t offset,
+		   int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32 
+#endif
+
+/* 
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define DTC3x80 {NULL, NULL, NULL, NULL, \
+	"DTC 3180/3280 ", dtc_detect, NULL,  \
+	NULL,							\
+	NULL, dtc_queue_command, dtc_abort, dtc_reset, NULL, 	\
+	dtc_biosparam, 						\
+	/* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,			\
+	/* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+    volatile unsigned char *base
+
+#define NCR5380_local_declare() \
+    volatile unsigned char *base
+
+#define NCR5380_setup(instance) \
+    base = (volatile unsigned char *) (instance)->base
+
+#define DTC_address(reg) (base + DTC_5380_OFFSET + reg)
+
+#define dbNCR5380_read(reg)                                              \
+    (rval=*(DTC_address(reg)), \
+     (((unsigned char) printk("DTC : read register %d at addr %08x is: %02x\n"\
+    , (reg), (int)DTC_address(reg), rval)), rval ) )
+
+#define dbNCR5380_write(reg, value) do {                                  \
+    printk("DTC : write %02x to register %d at address %08x\n",         \
+            (value), (reg), (int)DTC_address(reg));     \
+    *(DTC_address(reg)) = (value);} while(0)
+
+
+#if !(DTCDEBUG & DTCDEBUG_TRANSFER) 
+#define NCR5380_read(reg) (*(DTC_address(reg)))
+#define NCR5380_write(reg, value) (*(DTC_address(reg)) = (value))
+#else
+#define NCR5380_read(reg) (*(DTC_address(reg)))
+#define xNCR5380_read(reg)						\
+    (((unsigned char) printk("DTC : read register %d at address %08x\n"\
+    , (reg), DTC_address(reg))), *(DTC_address(reg)))
+
+#define NCR5380_write(reg, value) do {					\
+    printk("DTC : write %02x to register %d at address %08x\n", 	\
+	    (value), (reg), (int)DTC_address(reg));	\
+    *(DTC_address(reg)) = (value);		} while(0)
+#endif
+
+#define NCR5380_intr dtc_intr
+#define NCR5380_queue_command dtc_queue_command
+#define NCR5380_abort dtc_abort
+#define NCR5380_reset dtc_reset
+#define NCR5380_proc_info dtc_proc_info 
+
+/* 15 12 11 10
+   1001 1100 0000 0000 */
+
+#define DTC_IRQS 0x9c00
+
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* DTC3280_H */
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h
--- v1.3.85/linux/drivers/scsi/g_NCR5380.h	Wed Mar 20 10:50:02 1996
+++ linux/drivers/scsi/g_NCR5380.h	Tue Apr  9 14:25:37 1996
@@ -47,6 +47,8 @@
 int generic_NCR5380_release_resources(struct Scsi_Host *);
 int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int generic_NCR5380_reset(Scsi_Cmnd *);
+int notyet_generic_proc_info (char *buffer ,char **start, off_t offset,
+                     int length, int hostno, int inout);
 const char* generic_NCR5380_info(struct Scsi_Host *);
 #ifdef BIOSPARAM
 int generic_NCR5380_biosparam(Disk *, kdev_t, int *);
@@ -72,7 +74,7 @@
 	generic_NCR5380_proc_info,					\
 	"Generic NCR5380/NCR53C400 Scsi Driver", 			\
 	generic_NCR5380_detect, generic_NCR5380_release_resources,	\
-	generic_NCR5380_info, NULL,					\
+	(void *)generic_NCR5380_info, NULL,				\
 	generic_NCR5380_queue_command, generic_NCR5380_abort, 		\
 	generic_NCR5380_reset, NULL,					\
 	NCR5380_BIOSPARAM,						\
@@ -151,6 +153,7 @@
 #define NCR5380_reset generic_NCR5380_reset
 #define NCR5380_pread generic_NCR5380_pread
 #define NCR5380_pwrite generic_NCR5380_pwrite
+#define NCR5380_proc_info notyet_generic_proc_info
 
 #define BOARD_NCR5380	0
 #define BOARD_NCR53C400	1
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.85/linux/drivers/scsi/hosts.c	Thu Mar 14 12:57:46 1996
+++ linux/drivers/scsi/hosts.c	Tue Apr  9 14:25:37 1996
@@ -105,6 +105,10 @@
 #include "t128.h"
 #endif
 
+#ifdef CONFIG_SCSI_DTC3280
+#include "dtc.h"
+#endif
+
 #ifdef CONFIG_SCSI_NCR53C7xx
 #include "53c7,8xx.h"
 #endif
@@ -137,6 +141,7 @@
 #include "scsi_debug.h"
 #endif
 
+
 /*
 static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.c,v 1.3 1993/09/24 12:21:00 drew Exp drew $";
 */
@@ -218,6 +223,9 @@
 #endif
 #ifdef CONFIG_SCSI_T128
     TRANTOR_T128,
+#endif
+#ifdef CONFIG_SCSI_DTC3280
+        DTC3x80,
 #endif
 #ifdef CONFIG_SCSI_NCR53C7xx
     NCR53c7xx,
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c
--- v1.3.85/linux/drivers/scsi/pas16.c	Fri Mar  1 07:50:53 1996
+++ linux/drivers/scsi/pas16.c	Tue Apr  9 14:25:38 1996
@@ -126,6 +126,9 @@
     PROC_SCSI_PAS16, 5, "pas16",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
+static int pas_maxi = 0;
+static int pas_wmaxi = 0;
+ 
 
 int scsi_irq_translate[] =
 	{ 0,  0,  1,  2,  3,  4,  5,  6, 0,  0,  7,  8,  9,  0, 10, 11 };
@@ -180,7 +183,22 @@
 		    * START_DMA_INITIATOR_RECEIVE_REG wo
 		    */
     };
-
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+#if 1
+#define rtrc(i) {inb(0x3da); outb(0x31, 0x3c0); outb((i), 0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
 
 
 /*
@@ -357,6 +375,7 @@
     int  count;
 
     tpnt->proc_dir = &proc_scsi_pas16;
+    tpnt->proc_info = &pas16_proc_info;
 
     for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
 	io_port = 0;
@@ -400,6 +419,7 @@
 	else 
 	    instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
 
+                instance->irq = IRQ_NONE;  /*****temp****/
 	if (instance->irq != IRQ_NONE) 
 	    if (request_irq(instance->irq, pas16_intr, SA_INTERRUPT, "pas16", NULL)) {
 		printk("scsi%d : IRQ%d not free, interrupts disabled\n", 
@@ -492,8 +512,10 @@
     register unsigned short reg = (unsigned short) (instance->io_port + 
 	P_DATA_REG_OFFSET);
     register i = len;
+    int ii = 0;
 
-    while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) );
+    while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
+	 ++ii;
 
     insb( reg, d, i );
 
@@ -502,8 +524,10 @@
 	printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
 	    instance->host_no);
 	return -1;
-    } else
-	return 0;
+    }
+   if (ii > pas_maxi)
+      pas_maxi = ii;
+    return 0;
 }
 
 /*
@@ -524,8 +548,10 @@
     register unsigned char *s = src;
     register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
     register i = len;
+    int ii = 0;
 
-    while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) );
+    while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
+	 ++ii;
  
     outsb( reg, s, i );
 
@@ -534,8 +560,10 @@
 	printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
 	    instance->host_no);
 	return -1;
-    } else 
-	return 0;
+    }
+    if (ii > pas_maxi)
+	 pas_wmaxi = ii;
+    return 0;
 }
 
 #include "NCR5380.c"
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/pas16.h linux/drivers/scsi/pas16.h
--- v1.3.85/linux/drivers/scsi/pas16.h	Thu Dec 14 08:16:52 1995
+++ linux/drivers/scsi/pas16.h	Tue Apr  9 14:25:38 1996
@@ -119,6 +119,8 @@
 int pas16_detect(Scsi_Host_Template *);
 int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int pas16_reset(Scsi_Cmnd *);
+int pas16_proc_info (char *buffer ,char **start, off_t offset,
+		     int length, int hostno, int inout);
 
 #ifndef NULL
 #define NULL 0
@@ -182,6 +184,7 @@
 #define NCR5380_queue_command pas16_queue_command
 #define NCR5380_abort pas16_abort
 #define NCR5380_reset pas16_reset
+#define NCR5380_proc_info pas16_proc_info
 
 /* 15 14 12 10 7 5 3 
    1101 0100 1010 1000 */
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.85/linux/drivers/scsi/sd.c	Mon Mar 18 13:15:00 1996
+++ linux/drivers/scsi/sd.c	Tue Apr  9 14:25:38 1996
@@ -1157,18 +1157,26 @@
 	}
     {
 	/*
-	 * The msdos fs need to know the hardware sector size
+	 * The msdos fs needs to know the hardware sector size
 	 * So I have created this table. See ll_rw_blk.c
 	 * Jacques Gelinas (Jacques@solucorp.qc.ca)
 	 */
-	int m;
+	int m, mb;
+        int sz_quot, sz_rem;
 	int hard_sector = rscsi_disks[i].sector_size;
-	/* There is 16 minor allocated for each devices */
+	/* There are 16 minors allocated for each major device */
 	for (m=i<<4; m<((i+1)<<4); m++){
 	    sd_hardsizes[m] = hard_sector;
 	}
-	printk ("SCSI Hardware sector size is %d bytes on device sd%c\n",
-		hard_sector,i+'a');
+        mb = (hard_sector * rscsi_disks[i].capacity) / (1024*1024);
+        /* sz = div(m/100, 10);  this seems to not be in the libr */
+        m = (mb + 50) / 100;
+        sz_quot = m / 10;
+        sz_rem = m - (10 * sz_quot);
+	printk ("SCSI device sd%c: hdwr sector= %d bytes."
+               " Sectors= %d [%d MB] [%d.%1d GB]\n",
+		i+'a', hard_sector, rscsi_disks[i].capacity, 
+                mb, sz_quot, sz_rem);
     }
 	if(rscsi_disks[i].sector_size == 1024)
 	    rscsi_disks[i].capacity <<= 1;  /* Change into 512 byte sectors */
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v1.3.85/linux/drivers/scsi/t128.c	Fri Mar  1 07:50:54 1996
+++ linux/drivers/scsi/t128.c	Tue Apr  9 14:25:38 1996
@@ -204,6 +204,7 @@
     int sig, count;
 
     tpnt->proc_dir = &proc_scsi_t128;
+    tpnt->proc_info = &t128_proc_info;
 
     for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
 	base = NULL;
diff -u --recursive --new-file v1.3.85/linux/drivers/scsi/t128.h linux/drivers/scsi/t128.h
--- v1.3.85/linux/drivers/scsi/t128.h	Thu Dec 14 08:16:52 1995
+++ linux/drivers/scsi/t128.h	Tue Apr  9 14:25:38 1996
@@ -96,6 +96,8 @@
 int t128_detect(Scsi_Host_Template *);
 int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int t128_reset(Scsi_Cmnd *);
+int t128_proc_info (char *buffer, char **start, off_t offset,
+		   int length, int hostno, int inout);
 
 #ifndef NULL
 #define NULL 0
@@ -127,7 +129,7 @@
 
 #endif
 
-#ifndef(HOSTS_C)
+#ifndef HOSTS_C
 
 #define NCR5380_implementation_fields \
     volatile unsigned char *base
@@ -159,6 +161,7 @@
 #define NCR5380_queue_command t128_queue_command
 #define NCR5380_abort t128_abort
 #define NCR5380_reset t128_reset
+#define NCR5380_proc_info t128_proc_info
 
 /* 15 14 12 10 7 5 3 
    1101 0100 1010 1000 */
diff -u --recursive --new-file v1.3.85/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v1.3.85/linux/drivers/sound/Readme.cards	Tue Apr  2 13:32:21 1996
+++ linux/drivers/sound/Readme.cards	Tue Apr  9 08:21:57 1996
@@ -219,7 +219,8 @@
 Audio Excell DSP16 
 	Support for this card was written by Riccardo Faccetti
 	(riccardo@cdc8g5.cdc.polimi.it). See aedsp16.c for more info.
-	(This driver is not functional in version 3.5 of this driver).
+	(This driver is not functional in version 3.5 of this driver. A
+	patch should be made available during April 96 (sunsite.unc.edu)).
 	
 Crystal CS4232 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
 	many PC motherboards (Compaq, HP, Intel, ...)
@@ -835,6 +836,7 @@
 ESS ES1688 and ES688 'AudioDrive' based cards
 ---------------------------------------------
 
+Support for these two ESS chips is embedded in the SB Pro driver.
 Configure these cards just like SB Pro. Enable the 'SB MPU401 MIDI port'
 if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode
 so you don't need to enable it (the driver uses normal SB MIDI automaticly
@@ -887,6 +889,12 @@
 
 Cards not supported yet
 =======================
+
+Please check which version of sound driver you are using before 
+complaining that your card is not supported. It's possible that you are 
+using a driver version which was released months before your card was
+introduced. Driver's release date is listed after it's version number
+in "cat /dev/sndstat" printout and in file linux/drivers/sound/soundvers.h.
 
 First of all. There is an easy way to make most soundcards to work
 with Linux. Just use the DOS based driver to initialize the card
diff -u --recursive --new-file v1.3.85/linux/fs/locks.c linux/fs/locks.c
--- v1.3.85/linux/fs/locks.c	Mon Apr  8 19:01:45 1996
+++ linux/fs/locks.c	Wed Apr 10 08:41:08 1996
@@ -58,15 +58,16 @@
  *  once we've checked for blocking and deadlocking.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 03, 1996.
  *
- *  NOTE:
- *  Starting to look at mandatory locks - using SunOS as a model.
- *  Probably a configuration option because mandatory locking can cause
- *  all sorts  of chaos with runaway processes.
- *
  *  Initial implementation of mandatory locks. SunOS turned out to be
  *  a rotten model, so I implemented the "obvious" semantics.
  *  See 'linux/Documentation/mandatory.txt' for details.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
+ *
+ *  Don't allow mandatory locks on mmap()'ed files. Added simple functions to
+ *  check if a file has mandatory locks, used by mmap(), open() and creat() to
+ *  see if system call should be rejected. Ref. HP-UX/SunOS/Solaris Reference
+ *  Manual, Section 2.
+ *  Andy Walker (andy@lysaker.kvaerner.no), April 09, 1996.
  */
 
 #include <linux/malloc.h>
@@ -195,7 +196,7 @@
 	    (flock.l_type == F_SHLCK))
 		return (-EINVAL);
 
-	if (!posix_make_lock(filp, &file_lock, &flock))
+	if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 
 	for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
@@ -227,6 +228,7 @@
 	struct file *filp;
 	struct file_lock file_lock;
 	struct flock flock;
+	struct inode *inode;
 
 	/*
 	 * Get arguments and validate them ...
@@ -239,6 +241,21 @@
 	if (error)
 		return (error);
 	
+	if (!(inode = filp->f_inode))
+		return (-EINVAL);
+	
+	/* Don't allow mandatory locks on files that may be memory mapped
+	 * and shared.
+	 */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && inode->i_mmap) {
+		struct vm_area_struct *vma = inode->i_mmap;
+		do {
+			if (vma->vm_flags & VM_MAYSHARE)
+				return (-EAGAIN);
+			vma = vma->vm_next_share;
+		} while (vma != inode->i_mmap);
+	}
+
 	memcpy_fromfs(&flock, l, sizeof(flock));
 	if (!posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
@@ -246,16 +263,16 @@
 	switch (flock.l_type) {
 	case F_RDLCK :
 		if (!(filp->f_mode & 1))
-			return -EBADF;
+			return (-EBADF);
 		break;
 	case F_WRLCK :
 		if (!(filp->f_mode & 2))
-			return -EBADF;
+			return (-EBADF);
 		break;
 	case F_SHLCK :
 	case F_EXLCK :
 		if (!(filp->f_mode & 3))
-			return -EBADF;
+			return (-EBADF);
 		break;
 	case F_UNLCK :
 		break;
@@ -288,21 +305,44 @@
 	return;
 }
 
-int locks_verify(int read_write, struct inode *inode, struct file *filp,
-		 unsigned int offset, unsigned int count)
+int locks_verify_locked(struct inode *inode)
 {
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
 	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
-		return (locks_locked_mandatory(read_write, inode, filp,
-					       offset, count));
+		return (locks_mandatory_locked(inode));
+	return (0);
+}
+
+int locks_mandatory_locked(struct inode *inode)
+{
+	struct file_lock *fl;
+
+	/* Search the lock list for this inode for any POSIX locks.
+	 */
+	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (fl->fl_flags == F_POSIX && fl->fl_owner != current)
+			return (-EAGAIN);
+	}
+	return (0);
+}
+
+int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
+		      unsigned int offset, unsigned int count)
+{
+	/* Candidates for mandatory locking have the setgid bit set
+	 * but no group execute bit -  an otherwise meaningless combination.
+	 */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return (locks_mandatory_area(read_write, inode, filp, offset,
+					     count));
 	return (0);
 }
 	
-int locks_locked_mandatory(int read_write, struct inode *inode,
-			   struct file *filp, unsigned int offset,
-			   unsigned int count)
+int locks_mandatory_area(int read_write, struct inode *inode,
+			 struct file *filp, unsigned int offset,
+			 unsigned int count)
 {
 	struct file_lock *fl;
 
@@ -352,9 +392,6 @@
 			   struct flock *l)
 {
 	off_t start;
-
-	if (!filp->f_inode)	/* just in case */
-		return (0);
 
 	switch (l->l_type) {
 	case F_RDLCK :
diff -u --recursive --new-file v1.3.85/linux/fs/namei.c linux/fs/namei.c
--- v1.3.85/linux/fs/namei.c	Wed Feb  7 08:55:39 1996
+++ linux/fs/namei.c	Wed Apr 10 08:41:08 1996
@@ -385,6 +385,13 @@
 		iput(dir);
 		return error;
 	}
+	/* SunOS, Solaris 2.x and HPUX all deny open() on
+	 * an existing file with mandatory locks.
+	 */
+	if ((error = locks_verify_locked(inode)) != 0) {
+		iput(inode);
+		return error;
+	}
 	error = follow_link(dir,inode,flag,mode,&inode);
 	if (error)
 		return error;
@@ -430,6 +437,17 @@
 			iput(inode);
 			return error;
 		}
+#if 0
+		/*
+		 * In my opinion the mandatory lock check should really be
+		 * here. Only O_TRUNC calls can modify the file contents -
+		 * but none of the commercial OS'es seem to do it this way.
+		 */
+		if ((error = locks_verify_locked(inode)) != 0) {
+			iput(inode);
+			return error;
+		}
+#endif
 		if (inode->i_sb && inode->i_sb->dq_op)
 			inode->i_sb->dq_op->initialize(inode, -1);
 			
diff -u --recursive --new-file v1.3.85/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.85/linux/fs/nfs/nfsroot.c	Mon Apr  8 19:01:45 1996
+++ linux/fs/nfs/nfsroot.c	Tue Apr  9 14:36:30 1996
@@ -834,7 +834,8 @@
 
 	/* Extract basic fields */
 	myaddr.sin_addr.s_addr = recv_bootp->your_ip;
-	server.sin_addr.s_addr = recv_bootp->server_ip;
+	if (server.sin_addr.s_addr==INADDR_NONE)
+		server.sin_addr.s_addr = recv_bootp->server_ip;
 
 	/* Parse extensions */
 	if (recv_bootp->vendor_area[0] == 99 &&	/* Check magic cookie */
diff -u --recursive --new-file v1.3.85/linux/fs/open.c linux/fs/open.c
--- v1.3.85/linux/fs/open.c	Mon Apr  8 19:01:45 1996
+++ linux/fs/open.c	Wed Apr 10 08:41:08 1996
@@ -107,9 +107,9 @@
 		iput(inode);
 		return error;
 	}
-	error = locks_verify(FLOCK_VERIFY_WRITE, inode, NULL,
-			     length < inode->i_size ? length : inode->i_size,
-			     abs(inode->i_size - length));
+	error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
+				  length < inode->i_size ? length : inode->i_size,
+				  abs(inode->i_size - length));
 	if (error)
 		return error;
 	error = do_truncate(inode, length);
@@ -132,9 +132,9 @@
 		return -EACCES;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
-	error = locks_verify(FLOCK_VERIFY_WRITE, inode, file,
-			     length < inode->i_size ? length : inode->i_size,
-			     abs(inode->i_size - length));
+	error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
+				  length < inode->i_size ? length : inode->i_size,
+				  abs(inode->i_size - length));
 	if (error)
 		return error;
 	return do_truncate(inode, length);
diff -u --recursive --new-file v1.3.85/linux/fs/read_write.c linux/fs/read_write.c
--- v1.3.85/linux/fs/read_write.c	Mon Apr  8 19:01:45 1996
+++ linux/fs/read_write.c	Wed Apr 10 08:41:08 1996
@@ -114,7 +114,7 @@
 		return -EINVAL;
 	if (!count)
 		return 0;
-	error = locks_verify(FLOCK_VERIFY_READ,inode,file,file->f_pos,count);
+	error = locks_verify_area(FLOCK_VERIFY_READ,inode,file,file->f_pos,count);
 	if (error)
 		return error;
 	error = verify_area(VERIFY_WRITE,buf,count);
@@ -138,7 +138,7 @@
 		return -EINVAL;
 	if (!count)
 		return 0;
-	error = locks_verify(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
+	error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
 	if (error)
 		return error;
 	error = verify_area(VERIFY_READ,buf,count);
@@ -227,8 +227,8 @@
 			return retval;
 	}
 
-	retval = locks_verify(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
-			      inode, file, file->f_pos, tot_len);
+	retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+				   inode, file, file->f_pos, tot_len);
 	if (retval)
 		return retval;
 
diff -u --recursive --new-file v1.3.85/linux/fs/super.c linux/fs/super.c
--- v1.3.85/linux/fs/super.c	Mon Apr  8 19:01:45 1996
+++ linux/fs/super.c	Tue Apr  9 08:13:42 1996
@@ -582,10 +582,14 @@
 		iput(inode);
 		return -ENXIO;
 	}
-	if (!(retval = do_umount(dev,0)) && dev != ROOT_DEV) {
-		blkdev_release (inode);
-		if (MAJOR(dev) == UNNAMED_MAJOR)
-			put_unnamed_dev(dev);
+	retval = do_umount(dev,0);
+	if (!retval) {
+		fsync_dev(dev);
+		if (dev != ROOT_DEV) {
+			blkdev_release (inode);
+			if (MAJOR(dev) == UNNAMED_MAJOR)
+				put_unnamed_dev(dev);
+		}
 	}
 	if (inode != &dummy_inode)
 		iput(inode);
diff -u --recursive --new-file v1.3.85/linux/include/asm-i386/segment.h linux/include/asm-i386/segment.h
--- v1.3.85/linux/include/asm-i386/segment.h	Fri Feb 16 08:23:32 1996
+++ linux/include/asm-i386/segment.h	Tue Apr  9 10:35:29 1996
@@ -85,9 +85,9 @@
     __asm__ volatile
 	("	cld
 		push %%es
-		movw %%fs,%%cx
-		movw %%cx,%%es
+		push %%fs
 		cmpl $3,%0
+		pop %%es
 		jbe 1f
 		movl %%edi,%%ecx
 		negl %%ecx
diff -u --recursive --new-file v1.3.85/linux/include/asm-i386/string.h linux/include/asm-i386/string.h
--- v1.3.85/linux/include/asm-i386/string.h	Sat Mar 23 19:45:51 1996
+++ linux/include/asm-i386/string.h	Wed Apr 10 15:36:07 1996
@@ -406,6 +406,24 @@
 			*(unsigned long *)to = *(const unsigned long *)from;
 			*(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
 			return to;
+		case 12:
+			*(unsigned long *)to = *(const unsigned long *)from;
+			*(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+			*(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+			return to;
+		case 16:
+			*(unsigned long *)to = *(const unsigned long *)from;
+			*(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+			*(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+			*(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+			return to;
+		case 20:
+			*(unsigned long *)to = *(const unsigned long *)from;
+			*(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+			*(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+			*(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+			*(4+(unsigned long *)to) = *(4+(const unsigned long *)from);
+			return to;
 	}
 #define COMMON(x) \
 __asm__("cld\n\t" \
diff -u --recursive --new-file v1.3.85/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.85/linux/include/linux/fs.h	Mon Apr  8 19:01:46 1996
+++ linux/include/linux/fs.h	Wed Apr 10 16:40:24 1996
@@ -354,19 +354,29 @@
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
-extern int locks_locked_mandatory(int read_write, struct inode *inode,
-				  struct file *filp, unsigned int offset,
-				  unsigned int count);
-extern inline int locks_verify(int read_write, struct inode *inode,
-			       struct file *filp, unsigned int offset,
-			       unsigned int count)
+extern int locks_mandatory_locked(struct inode *inode);
+extern inline int locks_verify_locked(struct inode *inode)
 {
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
 	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
-		return (locks_locked_mandatory(read_write, inode, filp,
-					       offset, count));
+		return (locks_mandatory_locked(inode));
+	return (0);
+}
+extern int locks_mandatory_area(int read_write, struct inode *inode,
+				struct file *filp, unsigned int offset,
+				unsigned int count);
+extern inline int locks_verify_area(int read_write, struct inode *inode,
+				    struct file *filp, unsigned int offset,
+				    unsigned int count)
+{
+	/* Candidates for mandatory locking have the setgid bit set
+	 * but no group execute bit -  an otherwise meaningless combination.
+	 */
+	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+		return (locks_mandatory_area(read_write, inode, filp, offset,
+					     count));
 	return (0);
 }
 
diff -u --recursive --new-file v1.3.85/linux/include/linux/if_frad.h linux/include/linux/if_frad.h
--- v1.3.85/linux/include/linux/if_frad.h	Mon Mar 25 08:58:22 1996
+++ linux/include/linux/if_frad.h	Tue Apr  9 14:36:30 1996
@@ -6,7 +6,7 @@
  *		code, including it's RFC1490 encapsulation along side the current
  *		implementation for the Sangoma cards.
  *
- * Version:	@(#)if_ifrad.h	0.10	23 Mar 96
+ * Version:	@(#)if_ifrad.h	0.15	31 Mar 96
  *
  * Author:	Mike McLagan <mike.mclagan@linux.org>
  *
@@ -33,7 +33,11 @@
 #define DLCI_GET_CONF	(SIOCDEVPRIVATE + 2)
 #define DLCI_SET_CONF	(SIOCDEVPRIVATE + 3)
 
-/* These are related to the Sangoma FRAD */
+/* 
+ * These are related to the Sangoma SDLA and should remain in order. 
+ * Code within the SDLA module is based on the specifics of this 
+ * structure.  Change at your own peril.
+ */
 struct dlci_conf {
    short flags;
    short CIR_fwd;
@@ -48,6 +52,8 @@
    short Tc_bwd;
    short Tf_max;
    short Tb_max;
+
+/* add any new fields here above is a mirror of sdla_dlci_conf */
 };
 
 #define DLCI_GET_SLAVE	(SIOCDEVPRIVATE + 4)
@@ -59,6 +65,10 @@
 
 #define DLCI_VALID_FLAGS	0x000B
 
+/* FRAD driver uses these to indicate what it did with packet */
+#define DLCI_RET_OK		0x00
+#define DLCI_RET_ERR		0x01
+#define DLCI_RET_DROP		0x02
 
 /* defines for the actual Frame Relay hardware */
 #define FRAD_GET_CONF	(SIOCDEVPRIVATE)
@@ -66,6 +76,11 @@
 
 #define FRAD_LAST_IOCTL	FRAD_SET_CONF
 
+/*
+ * Based on the setup for the Sangoma SDLA.  If changes are 
+ * necessary to this structure, a routine will need to be 
+ * added to that module to copy fields.
+ */
 struct frad_conf 
 {
    short station;
@@ -85,7 +100,7 @@
    short Bc_bwd;
    short Be_bwd;
 
-/* Add new fields here, above is a mirror of the sangoma_conf */
+/* Add new fields here, above is a mirror of the sdla_conf */
 
 };
 
@@ -106,14 +121,19 @@
 
 #ifdef __KERNEL__
 
-struct fradhdr
+/* these are the fields of an RFC 1490 header */
+struct frhdr
 {
-   /* these are the fields of an RFC 1490 header               */
-   unsigned char  control;
-   unsigned char  pad;		/* for IP packets, this can be the NLPID */
-   unsigned char  NLPID;
-   unsigned char  OUI[3];
-   unsigned short PID;
+   unsigned char  control	__attribute__((packed));
+
+   /* for IP packets, this can be the NLPID */
+   unsigned char  pad		__attribute__((packed)); 
+
+   unsigned char  NLPID		__attribute__((packed));
+   unsigned char  OUI[3]	__attribute__((packed));
+   unsigned short PID		__attribute__((packed));
+
+#define IP_NLPID pad 
 };
 
 /* see RFC 1490 for the definition of the following */
@@ -139,12 +159,15 @@
 struct frad_local
 {
    struct enet_statistics stats;
-   struct timer_list timer;
 
    /* devices which this FRAD is slaved to */
    struct device     *master[CONFIG_DLCI_MAX];
    short             dlci[CONFIG_DLCI_MAX];
 
+   struct frad_conf  config;
+   int               configured;	/* has this device been configured */
+   int               initialized;	/* mem_start, port, irq set ? */
+
    /* callback functions */
    int               (*activate)(struct device *, struct device *);
    int               (*deactivate)(struct device *, struct device *);
@@ -152,12 +175,11 @@
    int               (*deassoc)(struct device *, struct device *);
    int               (*dlci_conf)(struct device *, struct device *, int get);
 
-   int               initialized;	/* mem_start, port, irq set ? */
-   int               configured;	/* has this device been configured */
+   /* fields that are used by the Sangoma SDLA cards */
+   struct timer_list timer;
    int               type;		/* adapter type */
    int               state;		/* state of the S502/8 control latch */
    int               buffer;		/* current buffer for S508 firmware */
-   struct frad_conf  config;
 };
 
 int register_frad(const char *name);
diff -u --recursive --new-file v1.3.85/linux/include/linux/if_wic.h linux/include/linux/if_wic.h
--- v1.3.85/linux/include/linux/if_wic.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/if_wic.h	Tue Apr  9 14:36:30 1996
@@ -0,0 +1,102 @@
+#ifndef _LINUX_IF_WIC_H
+#define _LINUX_IF_WIC_H
+
+#include <linux/sockios.h>
+
+#define	SIOCDEVWIC	SIOCDEVPRIVATE
+
+struct wicconf
+{
+	unsigned char 	pcmd;
+	unsigned char	data[120];
+	unsigned char	len;
+};
+
+/* WIC host to controller commands */
+
+#define WIC_AYT		0x10	/* test dki */
+#define WIC_RESET	0x11	/* reset controller */
+#define WIC_SETSN	0x21	/* set station name */
+#define WIC_SETPS	0x22	/* set power saving mode */
+#define WIC_SETAF	0x23	/* set announce filter */
+#define WIC_SETGPF	0x24	/* set GPSP filter */
+#define WIC_GETVERH	0x61	/* get interface controller version */
+#define WIC_GETNL	0x62	/* get neighbor list */
+#define WIC_GETSN	0x65	/* get station name */
+#define WIC_CLRSTATS	0x83	/* clear controller statistics */
+#define WIC_SETNET	0x84	/* set network configuration */
+#define WIC_SETSYS	0x85	/* set system configuration */
+#define WIC_GETSTATS	0xc1	/* get statistics */
+#define WIC_GETVERM	0xc3	/* get MAC version */
+#define WIC_GETNET	0xc4	/* get network configuration */
+#define WIC_GETSYS	0xc5	/* get system configuration */
+
+/*
+ * structure used for the GETNET/SETNET command
+ */
+
+struct wic_net {
+	unsigned char ula[6];		/* ula of interface */
+	unsigned char mode;		/* operating mode */
+#define NET_MODE_ME		0x01	/* receive my ula */
+#define NET_MODE_BCAST		0x02	/* receive bcasts */
+#define NET_MODE_MCAST		0x04	/* receive mcasts */
+#define NET_MODE_PROM		0x08	/* promiscuous */
+#define NET_MODE_HC		0x10	/* is a hop coordinator */
+#define NET_MODE_HC_VALID	0x20	/* hc addres is valid */
+#define NET_MODE_HCAP		0x40	/* hc is also ap */
+#define NET_MODE_HC_KNOWN	0x80	/* hc is known */
+	unsigned char rts_lo;		/* rts threshhold */
+	unsigned char rts_hi;		/* rts threshhold */
+	unsigned char retry;		/* retry limit */
+	unsigned char hc_ula[6];	/* ula of hc */
+	unsigned char key[4];		/* network key */
+	unsigned char dsl;		/* direct send limit */
+	unsigned char res1;		/* reserved */
+};
+
+/*
+ * structure used for the GETSYS/SETSYS command 
+ */
+
+struct wic_sys {
+	unsigned char mode;		/* set operating mode */
+#define SYS_MODE_ANT_DIV	0x00	/* use antenna diversity */
+#define SYS_MODE_ANT_1		0x01	/* use ant 1 for tx */
+#define SYS_MODE_ANT_2		0x02	/* use ant 2 for tx */
+#define SYS_MODE_HC_LOCK	0x04	/* lock onto current hc */
+#define SYS_MODE_DEBUG		0x08	/* upload failed frames */
+#define SYS_MODE_IAM_AP		0x10	/* I am AP */
+#define SYS_MODE_IAM_HC		0x20	/* I am HC */
+#define SYS_MODE_USE_SKIP	0x40	/* use skipping mechanism */
+#define SYS_MODE_AUTO		0x80	/* station is in auto mode */
+	unsigned char switches;		/* radio/controller switches */
+#define SYS_SWITCH_STDBY	0x01	/* switch radio to standby */
+#define SYS_SWITCH_TXRX		0x02	/* 1 = tx, manual mode only */
+#define SYS_SWITCH_PA		0x04	/* 1 = enable PA on radio */
+#define SYS_SWITCH_PWR		0x10	/* 1 = hi, 0 = lo power output */
+#define SYS_SWITCH_RES1		0x20	/* reserved, must be 0 */
+#define SYS_SWITCH_LIGHTS	0x40	/* light for tx & rx */
+#define SYS_SWITCH_LIGHTS_HC	0x80	/* light for rx while coordinated */
+	unsigned char hop_min;		/* hop range */
+	unsigned char hop_max;		/* hop range */
+	unsigned char pre_len;		/* preamble length (bytes) */
+	unsigned char pre_match;	/* valid preamble match (bytes) */
+	unsigned char mod;		/* data mod: 1 = 8:1, 0 = none */
+	unsigned char cca_mode;		/* cca flags */
+#define CCA_PKT_DET_BSY		0x01	/* busy if packet is detected */
+#define CCA_VIRT_CARR		0x02	/* use virtual carrier */
+#define CCA_RSSI_BSY		0x04	/* busy if rssi > thresshold */
+#define CCA_DATA_BSY		0x08	/* busy if valid data > XXX usec */
+	unsigned char dwell_hi;		/* dwell time */
+	unsigned char dwell_lo;		/* dwell time */
+	unsigned char hc_timeout;	/* HC timeout */
+	unsigned char rssi;		/* rssi threshhold */
+	unsigned char hc_rssi;		/* rssi of last hc frame */
+	unsigned char hc_rssi_chan;	/* channel of hc rssi value */
+};
+
+
+#endif /* _LINUX_IF_WIC_H */
+
+
diff -u --recursive --new-file v1.3.85/linux/include/linux/ip.h linux/include/linux/ip.h
--- v1.3.85/linux/include/linux/ip.h	Thu Jan  4 08:13:45 1996
+++ linux/include/linux/ip.h	Tue Apr  9 14:36:30 1996
@@ -109,5 +109,4 @@
 	/*The options start here. */
 };
 
-
 #endif	/* _LINUX_IP_H */
diff -u --recursive --new-file v1.3.85/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.3.85/linux/include/linux/ip_fw.h	Sun Mar 31 00:13:18 1996
+++ linux/include/linux/ip_fw.h	Wed Apr 10 16:46:03 1996
@@ -124,6 +124,7 @@
 #define IP_FW_ZERO		(IP_FW_BASE_CTL+4)
 #define IP_FW_POLICY		(IP_FW_BASE_CTL+5)
 #define IP_FW_CHECK		(IP_FW_BASE_CTL+6)
+#define IP_FW_MASQ_TIMEOUTS	(IP_FW_BASE_CTL+7)
 
 #define IP_FW_INSERT_FWD	(IP_FW_INSERT | (IP_FW_FWD << IP_FW_SHIFT))
 #define IP_FW_APPEND_FWD	(IP_FW_APPEND | (IP_FW_FWD << IP_FW_SHIFT))
@@ -166,6 +167,12 @@
 	struct in_addr fwp_via;			/* interface address */
 	char           fwp_vianame[IFNAMSIZ];	/* interface name */
 };
+
+/*
+ * timeouts for ip masquerading
+ */
+
+struct ip_fw_masq;
 
 /*
  *	Main firewall chains definitions and global var's definitions.
diff -u --recursive --new-file v1.3.85/linux/include/linux/sdla.h linux/include/linux/sdla.h
--- v1.3.85/linux/include/linux/sdla.h	Mon Mar 25 08:58:22 1996
+++ linux/include/linux/sdla.h	Tue Apr  9 14:36:31 1996
@@ -5,7 +5,7 @@
  *
  *		Global definitions for the Frame relay interface.
  *
- * Version:	@(#)if_ifrad.h	0.10	23 Mar 96
+ * Version:	@(#)if_ifrad.h	0.15	31 Mar 96
  *
  * Author:	Mike McLagan <mike.mclagan@linux.org>
  *
@@ -64,12 +64,15 @@
 #define SDLA_CONF_ADDR			0x0010
 #define SDLA_S502A_NMIADDR		0x0066
 #define SDLA_CODE_BASEADDR		0x0100
+#define SDLA_WINDOW_SIZE		0x2000
+#define SDLA_ADDR_MASK			0x1FFF
 
 /* largest handlable block of data */
 #define SDLA_MAX_DATA			4080
 #define SDLA_MAX_MTU			4072	/* MAX_DATA - sizeof(fradhdr) */
 #define SDLA_MAX_DLCI			24
 
+/* this should be the same as frad_conf */
 struct sdla_conf {
    short station;
    short config;
@@ -89,7 +92,8 @@
    short Be_bwd;
 };
 
-struct sdla_dlci {
+/* this should be the same as dlci_conf */
+struct sdla_dlci_conf {
    short config;
    short CIR_fwd;
    short Bc_fwd;
@@ -97,8 +101,6 @@
    short CIR_bwd;
    short Bc_bwd;
    short Be_bwd; 
-
-/* these are part of the status READ */
    short Tc_fwd;
    short Tc_bwd;
    short Tf_max;
@@ -203,7 +205,7 @@
 #define SDLA_RET_NO_DATA		0x05
 #define SDLA_RET_BUF_OVERSIZE		0x06
 #define SDLA_RET_CIR_OVERFLOW		0x07
-#define SDLA_RET_NO_BUFF		0x08
+#define SDLA_RET_NO_BUFS		0x08
 #define SDLA_RET_TIMEOUT		0x0A
 #define SDLA_RET_MODEM			0x10
 #define SDLA_RET_CHANNEL_OFF		0x11
diff -u --recursive --new-file v1.3.85/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.85/linux/include/linux/skbuff.h	Mon Apr  8 19:01:46 1996
+++ linux/include/linux/skbuff.h	Wed Apr 10 16:40:32 1996
@@ -94,6 +94,7 @@
   			lock,			/* Are we locked ?				*/
   			localroute,		/* Local routing asserted for this frame	*/
   			pkt_type,		/* Packet class					*/
+  			pkt_bridged,		/* Tracker for bridging 			*/
   			ip_summed;		/* Driver fed us an IP checksum			*/
 #define PACKET_HOST		0		/* To us					*/
 #define PACKET_BROADCAST	1		/* To all					*/
diff -u --recursive --new-file v1.3.85/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.3.85/linux/include/linux/sockios.h	Sun Mar 24 13:33:16 1996
+++ linux/include/linux/sockios.h	Tue Apr  9 14:36:31 1996
@@ -50,10 +50,11 @@
 #define SIOCGIFHWADDR	0x8927		/* Get hardware address		*/
 #define SIOCGIFSLAVE	0x8929		/* Driver slaving support	*/
 #define SIOCSIFSLAVE	0x8930
-/* begin multicast support change */
-#define SIOCADDMULTI  0x8931
-#define SIOCDELMULTI  0x8932
-/* end multicast support change */
+#define SIOCADDMULTI	0x8931		/* Multicast address lists	*/
+#define SIOCDELMULTI	0x8932
+
+#define SIOCGIFBR	0x8940		/* Bridging support		*/
+#define SIOCSIFBR	0x8941		/* Set bridging options 	*/
 
 /* ARP cache control calls. */
 #define OLD_SIOCDARP	0x8950		/* old delete ARP table entry	*/
diff -u --recursive --new-file v1.3.85/linux/include/net/br.h linux/include/net/br.h
--- v1.3.85/linux/include/net/br.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/br.h	Tue Apr  9 14:36:31 1996
@@ -0,0 +1,270 @@
+/*
+ * Constants and structure definitions for the bridging code
+ */
+
+#if !defined(One)
+#define Zero    0
+#define One	1
+#endif  /* !defined(One) */
+
+#if !defined(TRUE)
+#define FALSE   0
+#define TRUE	1
+#endif /* !defined(TRUE) */
+
+/** port states. **/
+#define Disabled	0			  /* (4.4 5)	 */
+#define Listening	1			  /* (4.4.2)	 */
+#define Learning	2			  /* (4.4.3)	 */
+#define Forwarding	3			  /* (4 4 4)	 */
+#define Blocking	4			  /* (4.4.1)	 */
+
+#define No_of_ports 8
+/* arbitrary choice, to allow the code below to compile */
+
+#define All_ports (No_of_ports + 1)
+
+/*
+ * We time out our entries in the FDB after this many seconds.
+ */
+#define FDB_TIMEOUT	300
+
+/*
+ * the following defines are the initial values used when the 
+ * bridge is booted.  These may be overridden when this bridge is
+ * not the root bridge.  These are the reccomended default values 
+ * from the 802.1d specification.
+ */
+#define BRIDGE_MAX_AGE		20
+#define BRIDGE_HELLO_TIME	2
+#define BRIDGE_FORWARD_DELAY	15
+#define HOLD_TIME		1
+
+#define Default_path_cost 10
+
+/*
+ * minimum increment possible to avoid underestimating age, allows for BPDU
+ * transmission time
+ */
+#define Message_age_increment 1
+
+#define No_port 0
+/*
+ * reserved value for Bridge's root port parameter indicating no root port,
+ * used when Bridge is the root - also used to indicate the source when
+ * a frame is being generated by a higher layer protocol on this host
+ */
+
+/** Configuration BPDU Parameters (4.5.1) **/
+
+typedef struct {
+	union {
+		struct {
+			unsigned short priority;
+			unsigned char ula[6];
+		} p_u;
+		unsigned int id[2];
+	} bi;
+} bridge_id_t;
+
+#define BRIDGE_PRIORITY	bi.p_u.priority
+#define BRIDGE_ID_ULA	bi.p_u.ula
+#define BRIDGE_ID	bi.id
+
+typedef struct {
+	unsigned short	protocol_id;	
+	unsigned char	protocol_version_id;
+	unsigned char   type;
+	unsigned char   flags;
+#define TOPOLOGY_CHANGE		0x01
+#define TOPOLOGY_CHANGE_ACK	0x80
+	bridge_id_t      root_id;		  /* (4.5.1.1)	 */
+	unsigned int     root_path_cost;	  /* (4.5.1.2)	 */
+	bridge_id_t      bridge_id;		  /* (4.5.1.3)	 */
+	unsigned short   port_id;		  /* (4.5.1.4)	 */
+	unsigned short   message_age;		  /* (4.5.1.5)	 */
+	unsigned short   max_age;		  /* (4.5.1.6)	 */
+	unsigned short   hello_time;		  /* (4.5.1.7)	 */
+	unsigned short   forward_delay;		  /* (4.5.1.8)	 */
+} Config_bpdu;
+
+
+/** Topology Change Notification BPDU Parameters (4.5.2) **/
+
+typedef struct {
+	unsigned short	protocol_id;	
+	unsigned char	protocol_version_id;
+	unsigned char   type;
+} Tcn_bpdu;
+
+#define BPDU_TYPE_CONFIG	0
+#define BPDU_TYPE_TOPO_CHANGE	128
+
+/** Bridge Parameters (4.5.3) **/
+typedef struct {
+	bridge_id_t      designated_root;	  /* (4.5.3.1)	 */
+	unsigned int     root_path_cost;	  /* (4.5.3.2)	 */
+	unsigned int 	 root_port;		  /* (4.5.3.3)	 */
+	unsigned short   max_age;		  /* (4.5.3.4)	 */
+	unsigned short   hello_time;		  /* (4.5.3.5)	 */
+	unsigned short   forward_delay;		  /* (4.5.3.6)	 */
+	bridge_id_t      bridge_id;		  /* (4.5.3.7)	 */
+	unsigned short   bridge_max_age;	  /* (4.5.3.8)	 */
+	unsigned short   bridge_hello_time;	  /* (4.5.3.9)	 */
+	unsigned short   bridge_forward_delay;	  /* (4.5.3.10)	 */
+	unsigned int     topology_change_detected; /* (4.5.3.11) */
+	unsigned int     topology_change;	  /* (4.5.3.12)	 */
+	unsigned short   topology_change_time;	  /* (4.5.3.13)	 */
+	unsigned short   hold_time;		  /* (4.5.3.14)	 */
+	unsigned int     top_change;
+	unsigned int     top_change_detected;
+} Bridge_data;
+
+/** Port Parameters (4.5.5) **/
+typedef struct {
+	unsigned short   port_id;		  /* (4.5.5.1)	 */
+	unsigned int     state;			  /* (4.5.5.2)	 */
+	unsigned int     path_cost;		  /* (4.5.5.3)	 */
+	bridge_id_t      designated_root;	  /* (4.5.5.4)	 */
+	unsigned int     designated_cost;	  /* (4.5.5.5)	 */
+	bridge_id_t      designated_bridge;	  /* (4.5.5.6)	 */
+	unsigned short   designated_port;	  /* (4.5.5.7)	 */
+	unsigned int     top_change_ack;	  /* (4.5.5.8)	 */
+	unsigned int     config_pending;	  /* (4.5.5.9)	 */
+	struct device *dev;	
+	struct fdb *fdb;	/* head of per port fdb chain */
+} Port_data;
+
+
+
+/** types to support timers for this pseudo-implementation. **/
+typedef struct {
+	unsigned int     active;			  /* timer in use. */
+	unsigned int     value;			  /* current value of timer,
+						   * counting up. */
+} Timer;
+
+struct fdb {
+	unsigned char ula[6];
+	unsigned char pad[2];
+	unsigned short port;
+	unsigned int timer;
+	unsigned int flags;
+#define FDB_ENT_VALID	0x01
+/* AVL tree of all addresses, sorted by address */
+	short fdb_avl_height;
+	struct fdb *fdb_avl_left;
+	struct fdb *fdb_avl_right;
+/* linked list of addresses for each port */
+	struct fdb *fdb_next;
+};
+
+#define IS_BRIDGED	0x2e
+
+struct br_stat {
+	unsigned int flags;
+	Bridge_data bridge_data;
+	Port_data port_data[No_of_ports];
+};
+
+/* defined flags for br_stat.flags */
+#define BR_UP		0x0001	/* bridging enabled */
+#define BR_DEBUG	0x0002	/* debugging enabled */
+
+struct br_cf {
+	unsigned int cmd;
+	unsigned int arg1;
+	unsigned int arg2;
+};
+
+/* defined cmds */
+#define	BRCMD_BRIDGE_ENABLE	1
+#define	BRCMD_BRIDGE_DISABLE	2
+#define	BRCMD_PORT_ENABLE	3	/* arg1 = port */
+#define	BRCMD_PORT_DISABLE	4	/* arg1 = port */
+#define	BRCMD_SET_BRIDGE_PRIORITY	5	/* arg1 = priority */
+#define	BRCMD_SET_PORT_PRIORITY	6	/* arg1 = port, arg2 = priority */
+#define	BRCMD_SET_PATH_COST	7	/* arg1 = port, arg2 = cost */
+#define	BRCMD_DISPLAY_FDB	8	/* arg1 = port */
+#define	BRCMD_ENABLE_DEBUG	9
+#define	BRCMD_DISABLE_DEBUG	10
+
+/* prototypes of all bridging functions... */
+
+void transmit_config(int port_no);
+int root_bridge(void);
+int supersedes_port_info(int port_no, Config_bpdu *config);
+void record_config_information(int port_no, Config_bpdu *config);
+void record_config_timeout_values(Config_bpdu *config);
+void config_bpdu_generation(void);
+int designated_port(int port_no);
+void reply(int port_no);
+void transmit_tcn(void);
+void configuration_update(void);
+void root_selection(void);
+void designated_port_selection(void);
+void become_designated_port(int port_no);
+void port_state_selection(void);
+void make_forwarding(int port_no);
+void topology_change_detection(void);
+void topology_change_acknowledged(void);
+void acknowledge_topology_change(int port_no);
+void make_blocking(int port_no);
+void set_port_state(int port_no, int state);
+void received_config_bpdu(int port_no, Config_bpdu *config);
+void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn);
+void hello_timer_expiry(void);
+void message_age_timer_expiry(int port_no);
+void forward_delay_timer_expiry(int port_no);
+int designated_for_some_port(void);
+void tcn_timer_expiry(void);
+void topology_change_timer_expiry(void);
+void hold_timer_expiry(int port_no);
+void br_init(void);
+void br_init_port(int port_no);
+void enable_port(int port_no);
+void disable_port(int port_no);
+void set_bridge_priority(bridge_id_t *new_bridge_id);
+void set_port_priority(int port_no, unsigned short new_port_id);
+void set_path_cost(int port_no, unsigned short path_cost);
+void start_hello_timer(void);
+void stop_hello_timer(void);
+int hello_timer_expired(void);
+void start_tcn_timer(void);
+void stop_tcn_timer(void);
+int tcn_timer_expired(void);
+void start_topology_change_timer(void);
+void stop_topology_change_timer(void);
+int topology_change_timer_expired(void);
+void start_message_age_timer(int port_no, unsigned short message_age);
+void stop_message_age_timer(int port_no);
+int message_age_timer_expired(int port_no);
+void start_forward_delay_timer(int port_no);
+void stop_forward_delay_timer(int port_no);
+int forward_delay_timer_expired(int port_no);
+void start_hold_timer(int port_no);
+void stop_hold_timer(int port_no);
+int hold_timer_expired(int port_no);
+
+struct fdb *br_avl_find_addr(unsigned char addr[6]);
+int br_avl_insert (struct fdb * new_node);
+int br_avl_remove (struct fdb * node_to_delete);
+
+int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
+int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
+int find_port(struct device *dev);
+int br_flood(struct sk_buff *skb, int port);
+int br_drop(struct sk_buff *skb);
+int br_learn(struct sk_buff *skb, int port);	/* 3.8 */
+
+int br_receive_frame(struct sk_buff *skb);	/* 3.5 */
+int br_tx_frame(struct sk_buff *skb);
+int br_ioctl(unsigned int cmd, void *arg);
+
+void free_fdb(struct fdb *);
+struct fdb *get_fdb(void);
+
+/* externs */
+
+extern struct br_stat br_stats;
+
diff -u --recursive --new-file v1.3.85/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- v1.3.85/linux/include/net/ip_masq.h	Sun Mar 31 00:13:18 1996
+++ linux/include/net/ip_masq.h	Wed Apr 10 16:46:03 1996
@@ -56,6 +56,18 @@
 };
 
 /*
+ *	timeout values
+ */
+
+struct ip_fw_masq {
+        int tcp_timeout;
+        int tcp_fin_timeout;
+        int udp_timeout;
+};
+
+extern struct ip_fw_masq *ip_masq_expire;
+
+/*
  *	[0]: UDP free_ports
  *	[1]: TCP free_ports
  */
diff -u --recursive --new-file v1.3.85/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.85/linux/include/net/tcp.h	Mon Apr  8 19:01:46 1996
+++ linux/include/net/tcp.h	Wed Apr 10 16:45:30 1996
@@ -149,7 +149,7 @@
 extern void tcp_send_fin(struct sock *sk);
 extern void tcp_send_synack(struct sock *, struct sock *, struct sk_buff *);
 extern void tcp_send_skb(struct sock *, struct sk_buff *);
-extern void tcp_send_ack(u32, u32, struct sock *sk, struct tcphdr *th, u32);
+extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
 	  struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl);
 
diff -u --recursive --new-file v1.3.85/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.85/linux/mm/filemap.c	Mon Apr  8 19:01:46 1996
+++ linux/mm/filemap.c	Wed Apr 10 16:33:02 1996
@@ -425,6 +425,7 @@
 	if (read) {
 		error = read;
 
+#ifdef WE_SHOULD_DO_SOME_EXTRA_CHECKS
 		/*
 		 * Start some extra read-ahead if we haven't already
 		 * read ahead enough..
@@ -434,6 +435,7 @@
 			ra_pos += PAGE_SIZE;
 		}
 		run_task_queue(&tq_disk);
+#endif
 
 		filp->f_pos = pos;
 		filp->f_reada = ra_pos;
diff -u --recursive --new-file v1.3.85/linux/mm/mmap.c linux/mm/mmap.c
--- v1.3.85/linux/mm/mmap.c	Fri Mar 22 14:07:14 1996
+++ linux/mm/mmap.c	Wed Apr 10 08:41:08 1996
@@ -73,6 +73,11 @@
 		case MAP_SHARED:
 			if ((prot & PROT_WRITE) && !(file->f_mode & 2))
 				return -EACCES;
+			/*
+			 * make sure there are no mandatory locks on the file.
+			 */
+			if (locks_verify_locked(file->f_inode))
+				return -EAGAIN;
 			/* fall through */
 		case MAP_PRIVATE:
 			if (!(file->f_mode & 1))
diff -u --recursive --new-file v1.3.85/linux/net/802/Makefile linux/net/802/Makefile
--- v1.3.85/linux/net/802/Makefile	Tue Apr  2 13:32:23 1996
+++ linux/net/802/Makefile	Tue Apr  9 14:36:31 1996
@@ -15,12 +15,12 @@
 endif
 
 ifdef CONFIG_IPX
-O_OBJS += p8022.o psnap.o p8022tr.o
+OX_OBJS += p8022.o psnap.o p8022tr.o
 endif
 
 ifdef CONFIG_ATALK
 ifndef CONFIG_IPX
-O_OBJS += p8022.o psnap.o p8022tr.o
+OX_OBJS += p8022.o psnap.o p8022tr.o
 endif
 endif
 
diff -u --recursive --new-file v1.3.85/linux/net/Config.in linux/net/Config.in
--- v1.3.85/linux/net/Config.in	Mon Mar 25 08:58:23 1996
+++ linux/net/Config.in	Tue Apr  9 14:36:31 1996
@@ -11,7 +11,7 @@
 fi
 comment ' '
 tristate 'The IPX protocol' CONFIG_IPX
-if [ ! "$CONFIG_IPX" = "n" ]; then
+if [ "$CONFIG_IPX" != "n" ]; then
   bool 'Full internal IPX network' CONFIG_IPX_INTERN
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
@@ -20,6 +20,7 @@
   bool 'AX.25 over Ethernet' CONFIG_BPQETHER
   bool 'Amateur Radio NET/ROM' CONFIG_NETROM
 fi
+bool 'Bridging (test)' CONFIG_BRIDGE
 bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK
 if [ "$CONFIG_NETLINK" = "y" ]; then
   bool 'Routing messages' CONFIG_RTNETLINK
diff -u --recursive --new-file v1.3.85/linux/net/Makefile linux/net/Makefile
--- v1.3.85/linux/net/Makefile	Tue Apr  2 13:32:23 1996
+++ linux/net/Makefile	Tue Apr  9 14:36:31 1996
@@ -8,12 +8,16 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 MOD_SUB_DIRS := ipv4
-ALL_SUB_DIRS := 802 ax25 core ethernet ipv4 ipx unix appletalk netrom
+ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipx unix appletalk netrom 
 SUB_DIRS     := 802 core ethernet unix
 MOD_LIST_NAME := NET_MISC_MODULES
 
 ifeq ($(CONFIG_INET),y)
 SUB_DIRS += ipv4
+endif
+
+ifeq ($(CONFIG_BRIDGE),y)
+SUB_DIRS += bridge
 endif
 
 ifeq ($(CONFIG_IPX),y)
diff -u --recursive --new-file v1.3.85/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.85/linux/net/appletalk/ddp.c	Mon Apr  8 19:01:46 1996
+++ linux/net/appletalk/ddp.c	Tue Apr  9 14:36:31 1996
@@ -1143,40 +1143,19 @@
 
 	MOD_INC_USE_COUNT;
 
-	sk->dead=0;
-	sk->next=NULL;
-	sk->broadcast=0;
 	sk->no_check=0;		/* Checksums on by default */
 	sk->allocation=GFP_KERNEL;
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
 	sk->pair=NULL;
-	sk->wmem_alloc=0;
-	sk->rmem_alloc=0;
-	sk->users=0;
-	sk->proc=0;
 	sk->priority=1;
-	sk->shutdown=0;
-	sk->prot=NULL;	/* So we use default free mechanisms */
-	sk->broadcast=0;
-	sk->err=0;
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
-	sk->send_head=NULL;
 	skb_queue_head_init(&sk->back_log);
 	sk->state=TCP_CLOSE;
 	sk->socket=sock;
 	sk->type=sock->type;
-	sk->debug=0;
 	
-	sk->protinfo.af_at.src_net=0;
-	sk->protinfo.af_at.src_node=0;
-	sk->protinfo.af_at.src_port=0;
-	
-	sk->protinfo.af_at.dest_net=0;
-	sk->protinfo.af_at.dest_node=0;
-	sk->protinfo.af_at.dest_port=0;
-
 	sk->mtu=DDP_MAXSZ;
 	
 	if(sock!=NULL)
diff -u --recursive --new-file v1.3.85/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.85/linux/net/ax25/af_ax25.c	Mon Apr  8 19:01:46 1996
+++ linux/net/ax25/af_ax25.c	Tue Apr  9 14:36:31 1996
@@ -447,7 +447,7 @@
 				ax25->digipeat = NULL;
 			}
 		
-			kfree_s(ax25->sk, sizeof(*ax25->sk));
+			sk_free(ax25->sk);
 			kfree_s(ax25, sizeof(*ax25));
 		}
 	} else {
@@ -1105,25 +1105,12 @@
 	sk->socket        = sock;
 	sk->type          = sock->type;
 	sk->protocol      = protocol;
-	sk->dead          = 0;
 	sk->next          = NULL;
-	sk->broadcast     = 0;
 	sk->allocation	  = GFP_KERNEL;
 	sk->rcvbuf        = SK_RMEM_MAX;
 	sk->sndbuf        = SK_WMEM_MAX;
-	sk->wmem_alloc    = 0;
-	sk->rmem_alloc    = 0;
-	sk->users         = 0;
-	sk->debug         = 0;
-	sk->destroy       = 0;
-	sk->prot          = NULL;	/* So we use default free mechanisms */
-	sk->err           = 0;
-	sk->localroute    = 0;
-	sk->send_head     = NULL;
 	sk->state         = TCP_CLOSE;
-	sk->shutdown      = 0;
 	sk->priority      = SOPRI_NORMAL;
-	sk->ack_backlog   = 0;
 	sk->mtu           = AX25_MTU;	/* 256 */
 	sk->zapped        = 1;
 
@@ -1176,26 +1163,14 @@
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->back_log);
 
-	sk->dead        = 0;
 	sk->next        = NULL;
 	sk->priority    = osk->priority;
-	sk->broadcast   = 0;
 	sk->protocol    = osk->protocol;
 	sk->rcvbuf      = osk->rcvbuf;
 	sk->sndbuf      = osk->sndbuf;
-	sk->wmem_alloc  = 0;
-	sk->rmem_alloc  = 0;
-	sk->users       = 0;
-	sk->ack_backlog = 0;
-	sk->destroy     = 0;
-	sk->prot        = NULL;	/* So we use default free mechanisms */
-	sk->err         = 0;
-	sk->localroute  = 0;
-	sk->send_head   = NULL;
 	sk->debug       = osk->debug;
 	sk->state       = TCP_ESTABLISHED;
 	sk->window      = osk->window;
-	sk->shutdown    = 0;
 	sk->mtu         = osk->mtu;
 	sk->sleep       = osk->sleep;
 	sk->zapped      = osk->zapped;
diff -u --recursive --new-file v1.3.85/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.85/linux/net/ax25/ax25_subr.c	Tue Apr  2 13:32:23 1996
+++ linux/net/ax25/ax25_subr.c	Tue Apr  9 14:36:31 1996
@@ -496,7 +496,7 @@
 
 	skbq = (struct sk_buff *) list->next;
 
-	while (skbq != list) {
+	while (skbq != (struct sk_buff *)list) {
 		if (skb->sk == skbq->sk)
 			count++;
 		skbq = skbq->next;
diff -u --recursive --new-file v1.3.85/linux/net/bridge/Makefile linux/net/bridge/Makefile
--- v1.3.85/linux/net/bridge/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/net/bridge/Makefile	Tue Apr  9 14:36:31 1996
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux TCP/IP (INET) layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := bridge.o
+O_OBJS	 := br.o br_tree.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+tar:
+		tar -cvf /dev/f1 .
diff -u --recursive --new-file v1.3.85/linux/net/bridge/br.c linux/net/bridge/br.c
--- v1.3.85/linux/net/bridge/br.c	Thu Jan  1 02:00:00 1970
+++ linux/net/bridge/br.c	Wed Apr 10 08:52:39 1996
@@ -0,0 +1,1561 @@
+/*
+ *	Linux NET3 Bridge Support
+ *
+ *	Originally by John Hayes (Network Plumbing).
+ *	Minor hacks to get it to run with 1.3.x by Alan Cox <Alan.Cox@linux.org>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	Fixes:
+ *
+ *	Todo:
+ *		Don't bring up devices automatically. Start ports disabled
+ *	and use a netlink notifier so a daemon can maintain the bridge
+ *	port group (could we also do multiple groups ????).
+ *		A nice /proc file interface.
+ *		Put the path costs in the port info and devices
+ *		Put the bridge port number in the device structure for speed.
+ *	
+ */
+ 
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <net/br.h>
+
+static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
+static void br_tick(unsigned long arg);
+int br_forward(struct sk_buff *skb, int port);	/* 3.7 */
+int br_port_cost(struct device *dev);	/* 4.10.2 */
+void br_bpdu(struct sk_buff *skb); /* consumes skb */
+int br_tx_frame(struct sk_buff *skb);
+int br_cmp(unsigned int *a, unsigned int *b);
+
+unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+Bridge_data     bridge_info;			  /* (4.5.3)	 */
+Port_data       port_info[All_ports];		  /* (4.5.5)	 */
+Config_bpdu     config_bpdu[All_ports];
+Tcn_bpdu        tcn_bpdu[All_ports];
+Timer           hello_timer;			  /* (4.5.4.1)	 */
+Timer           tcn_timer;			  /* (4.5.4.2)	 */
+Timer           topology_change_timer;		  /* (4.5.4.3)	 */
+Timer           message_age_timer[All_ports];	  /* (4.5.6.1)	 */
+Timer           forward_delay_timer[All_ports];	  /* (4.5.6.2)	 */
+Timer           hold_timer[All_ports];		  /* (4.5.6.3)	 */
+
+/* entries timeout after this many seconds */
+unsigned int fdb_aging_time = FDB_TIMEOUT; 
+
+struct br_stat br_stats;
+
+static struct timer_list tl; /* for 1 second timer... */
+
+/*
+ * the following structure is required so that we receive
+ * event notifications when network deviced are enabled and
+ * disabled (ifconfig up and down).
+ */
+static struct notifier_block br_dev_notifier={
+	br_device_event,
+	NULL,
+	0
+};
+
+/** Elements of Procedure (4.6) **/
+
+/*
+ * this section of code was gratiously borrowed from the IEEE 802.1d
+ * specification section 4.9.1 starting on pg 69.  It has been
+ * modified somewhat to fit within out framework and structure.  It
+ * implements the spanning tree algorithm that is the heart of the
+ * 802.1d bridging protocol.
+ */
+
+void
+transmit_config(int port_no)			  /* (4.6.1)	 */
+{
+	if (hold_timer[port_no].active) {	  /* (4.6.1.3.1)	 */
+		port_info[port_no].config_pending = TRUE;	/* (4.6.1.3.1)	 */
+	} else {				  /* (4.6.1.3.2)	 */
+		config_bpdu[port_no].type = BPDU_TYPE_CONFIG;
+		config_bpdu[port_no].root_id = bridge_info.designated_root;
+		/* (4.6.1.3.2(1)) */
+		config_bpdu[port_no].root_path_cost = bridge_info.root_path_cost;
+		/* (4.6.1.3.2(2)) */
+		config_bpdu[port_no].bridge_id = bridge_info.bridge_id;
+		/* (4.6.1.3.2(3)) */
+		config_bpdu[port_no].port_id = port_info[port_no].port_id;
+		/*
+		 * (4.6.1.3.2(4))
+		 */
+		if (root_bridge()) {
+			config_bpdu[port_no].message_age = Zero;	/* (4.6.1.3.2(5)) */
+		} else {
+			config_bpdu[port_no].message_age
+				= message_age_timer[bridge_info.root_port].value
+				+ Message_age_increment;	/* (4.6.1.3.2(6)) */
+		}
+
+		config_bpdu[port_no].max_age = bridge_info.max_age;	/* (4.6.1.3.2(7)) */
+		config_bpdu[port_no].hello_time = bridge_info.hello_time;
+		config_bpdu[port_no].forward_delay = bridge_info.forward_delay;
+		config_bpdu[port_no].flags = 0;
+		config_bpdu[port_no].flags |=
+			port_info[port_no].top_change_ack ? TOPOLOGY_CHANGE_ACK : 0;
+		/* (4.6.1.3.2(8)) */
+		port_info[port_no].top_change_ack = 0;
+		/* (4.6.1.3.2(8)) */
+		config_bpdu[port_no].flags |=
+			bridge_info.top_change ? TOPOLOGY_CHANGE : 0;
+		/* (4.6.1.3.2(9)) */
+
+		send_config_bpdu(port_no, &config_bpdu[port_no]);
+		port_info[port_no].config_pending = FALSE;	/* (4.6.1.3.2(10)) */
+		start_hold_timer(port_no);	  /* (4.6.1.3.2(11)) */
+	}
+}
+
+int
+root_bridge(void)
+{
+	return (br_cmp(bridge_info.designated_root.BRIDGE_ID,
+		 bridge_info.bridge_id.BRIDGE_ID)?FALSE:TRUE);
+}
+
+int
+supersedes_port_info(int port_no, Config_bpdu *config)	  /* (4.6.2.2)	 */
+{
+	return (
+		(br_cmp(config->root_id.BRIDGE_ID,
+		 port_info[port_no].designated_root.BRIDGE_ID) < 0)	/* (4.6.2.2.1)	 */ 
+		||
+		((br_cmp(config->root_id.BRIDGE_ID,
+		  port_info[port_no].designated_root.BRIDGE_ID) == 0
+		  )
+		 &&
+		 ((config->root_path_cost
+		   < port_info[port_no].designated_cost	/* (4.6.2.2.2)	 */
+		   )
+		  ||
+		  ((config->root_path_cost
+		    == port_info[port_no].designated_cost
+		    )
+		   &&
+		   ((br_cmp(config->bridge_id.BRIDGE_ID,
+		     port_info[port_no].designated_bridge.BRIDGE_ID) < 0	/* (4.6.2.2.3)    */
+		     )
+		    ||
+		    ((br_cmp(config->bridge_id.BRIDGE_ID,
+		      port_info[port_no].designated_bridge.BRIDGE_ID) == 0
+		      )				  /* (4.6.2.2.4)	 */
+		     &&
+		     ((br_cmp(config->bridge_id.BRIDGE_ID,
+			bridge_info.bridge_id.BRIDGE_ID) != 0
+		       )			  /* (4.6.2.2.4(1)) */
+		      ||
+		      (config->port_id <=
+		       port_info[port_no].designated_port
+		       )			  /* (4.6.2.2.4(2)) */
+		      ))))))
+		);
+}
+
+void
+record_config_information(int port_no, Config_bpdu *config)	  /* (4.6.2)	 */
+{
+	port_info[port_no].designated_root = config->root_id;	/* (4.6.2.3.1)   */
+	port_info[port_no].designated_cost = config->root_path_cost;
+	port_info[port_no].designated_bridge = config->bridge_id;
+	port_info[port_no].designated_port = config->port_id;
+	start_message_age_timer(port_no, config->message_age);	/* (4.6.2.3.2)   */
+}
+
+void
+record_config_timeout_values(Config_bpdu *config)		  /* (4.6.3)	 */
+{
+	bridge_info.max_age = config->max_age;	  /* (4.6.3.3)	 */
+	bridge_info.hello_time = config->hello_time;
+	bridge_info.forward_delay = config->forward_delay;
+	if (config->flags & TOPOLOGY_CHANGE)
+		bridge_info.top_change = 1;
+}
+
+void
+config_bpdu_generation(void)
+{						  /* (4.6.4)	 */
+	int             port_no;
+	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.6.4.3) */
+		if (designated_port(port_no)	  /* (4.6.4.3)	 */
+				&&
+				(port_info[port_no].state != Disabled)
+			) {
+			transmit_config(port_no); /* (4.6.4.3)	 */
+		}				  /* (4.6.1.2)	 */
+	}
+}
+
+int
+designated_port(int port_no)
+{
+	return ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
+		 bridge_info.bridge_id.BRIDGE_ID) == 0
+		 )
+		&&
+		(port_info[port_no].designated_port
+		 == port_info[port_no].port_id
+		 )
+		);
+}
+
+void
+reply(int port_no)					  /* (4.6.5)	 */
+{
+	transmit_config(port_no);		  /* (4.6.5.3)	 */
+}
+
+void
+transmit_tcn(void)
+{						  /* (4.6.6)	 */
+	int             port_no;
+
+	port_no = bridge_info.root_port;
+	tcn_bpdu[port_no].type = BPDU_TYPE_TOPO_CHANGE;
+	send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]);	/* (4.6.6.3)     */
+}
+
+void
+configuration_update(void)	/* (4.6.7) */
+{
+	root_selection();			  /* (4.6.7.3.1)	 */
+	/* (4.6.8.2)	 */
+	designated_port_selection();		  /* (4.6.7.3.2)	 */
+	/* (4.6.9.2)	 */
+}
+
+void
+root_selection(void)
+{						  /* (4.6.8) */
+	int             root_port;
+	int             port_no;
+	root_port = No_port;
+	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.6.8.3.1) */
+		if (((!designated_port(port_no))
+		     &&
+		     (port_info[port_no].state != Disabled)
+		     &&
+		(br_cmp(port_info[port_no].designated_root.BRIDGE_ID,
+			bridge_info.bridge_id.BRIDGE_ID) < 0)
+			)
+				&&
+				((root_port == No_port)
+				 ||
+				 (br_cmp(port_info[port_no].designated_root.BRIDGE_ID,
+				  port_info[root_port].designated_root.BRIDGE_ID) < 0
+				  )
+				 ||
+				 ((br_cmp(port_info[port_no].designated_root.BRIDGE_ID,
+				   port_info[root_port].designated_root.BRIDGE_ID) == 0
+				   )
+				  &&
+				  (((port_info[port_no].designated_cost
+				     + port_info[port_no].path_cost
+				     )
+				    ==
+				    (port_info[root_port].designated_cost
+				     + port_info[root_port].path_cost
+				     )		  /* (4.6.8.3.1(2)) */
+				    )
+				   ||
+				   (((port_info[port_no].designated_cost
+				      + port_info[port_no].path_cost
+				      )
+				     ==
+				     (port_info[root_port].designated_cost
+				      + port_info[root_port].path_cost
+				      )
+				     )
+				    &&
+				    ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
+				    port_info[root_port].designated_bridge.BRIDGE_ID) < 0
+				      )		  /* (4.6.8.3.1(3)) */
+				     ||
+				     ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
+				   port_info[root_port].designated_bridge.BRIDGE_ID) == 0
+				       )
+				      &&
+				      ((port_info[port_no].designated_port
+				      < port_info[root_port].designated_port
+					)	  /* (4.6.8.3.1(4)) */
+				       ||
+				       ((port_info[port_no].designated_port
+				      = port_info[root_port].designated_port
+					 )
+					&&
+					(port_info[port_no].port_id
+					 < port_info[root_port].port_id
+					 )	  /* (4.6.8.3.1(5)) */
+					))))))))) {
+			root_port = port_no;
+		}
+	}
+	bridge_info.root_port = root_port;	  /* (4.6.8.3.1)	 */
+
+	if (root_port == No_port) {		  /* (4.6.8.3.2)	 */
+		bridge_info.designated_root = bridge_info.bridge_id;
+		/* (4.6.8.3.2(1)) */
+		bridge_info.root_path_cost = Zero;/* (4.6.8.3.2(2)) */
+	} else {				  /* (4.6.8.3.3)	 */
+		bridge_info.designated_root = port_info[root_port].designated_root;
+		/* (4.6.8.3.3(1)) */
+		bridge_info.root_path_cost = (port_info[root_port].designated_cost
+					    + port_info[root_port].path_cost
+			);			  /* (4.6.8.3.3(2)) */
+	}
+}
+
+void
+designated_port_selection(void)
+{						  /* (4.6.9)	 */
+	int             port_no;
+
+	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.6.9.3)	 */
+		if (designated_port(port_no)	  /* (4.6.9.3.1)	 */
+				||
+				(
+				 br_cmp(port_info[port_no].designated_root.BRIDGE_ID,
+				 bridge_info.designated_root.BRIDGE_ID) != 0
+				 )
+				||
+				(bridge_info.root_path_cost
+				 < port_info[port_no].designated_cost
+				 )		  /* (4.6.9.3.3)	 */
+				||
+				((bridge_info.root_path_cost
+				  == port_info[port_no].designated_cost
+				  )
+				 &&
+				 ((br_cmp(bridge_info.bridge_id.BRIDGE_ID,
+				   port_info[port_no].designated_bridge.BRIDGE_ID) < 0
+				   )		  /* (4.6.9.3.4)	 */
+				  ||
+				  ((br_cmp(bridge_info.bridge_id.BRIDGE_ID,
+				    port_info[port_no].designated_bridge.BRIDGE_ID) == 0
+				    )
+				   &&
+				   (port_info[port_no].port_id
+				    <= port_info[port_no].designated_port
+				    )		  /* (4.6.9.3.5)	 */
+				   )))) {
+			become_designated_port(port_no);	/* (4.6.10.3.2.2) */
+		}
+	}
+}
+
+void
+become_designated_port(int port_no)
+{						  /* (4.6.10)	 */
+
+	/* (4.6.10.3.1) */
+	port_info[port_no].designated_root = bridge_info.designated_root;
+	/* (4.6.10.3.2) */
+	port_info[port_no].designated_cost = bridge_info.root_path_cost;
+	/* (4.6.10.3.3) */
+	port_info[port_no].designated_bridge = bridge_info.bridge_id;
+	/* (4.6.10.3.4) */
+	port_info[port_no].designated_port = port_info[port_no].port_id;
+}
+
+void
+port_state_selection(void)
+{						  /* (4.6.11) */
+	int             port_no;
+	for (port_no = One; port_no <= No_of_ports; port_no++) {
+		if (port_no == bridge_info.root_port) {	/* (4.6.11.3.1) */
+			port_info[port_no].config_pending = FALSE;	/* (4.6.11.3~1(1)) */
+			port_info[port_no].top_change_ack = 0;
+			make_forwarding(port_no); /* (4.6.11.3.1(2)) */
+		} else if (designated_port(port_no)) {	/* (4.6.11.3.2) */
+			stop_message_age_timer(port_no);	/* (4.6.11.3.2(1)) */
+			make_forwarding(port_no); /* (4.6.11.3.2(2)) */
+		} else {			  /* (4.6.11.3.3) */
+			port_info[port_no].config_pending = FALSE;	/* (4.6.11.3.3(1)) */
+			port_info[port_no].top_change_ack = 0;
+			make_blocking(port_no);	  /* (4.6.11.3.3(2)) */
+		}
+	}
+
+}
+
+void
+make_forwarding(int port_no)
+{						  /* (4.6.12) */
+	if (port_info[port_no].state == Blocking) {	/* (4.6.12.3) */
+		set_port_state(port_no, Listening);	/* (4.6.12.3.1) */
+		start_forward_delay_timer(port_no);	/* (4.6.12.3.2) */
+	}
+}
+
+void 
+topology_change_detection(void)
+{						  /* (4.6.14)       */
+	if (root_bridge()) {			  /* (4.6.14.3.1)   */
+		bridge_info.top_change = 1;
+		start_topology_change_timer();	  /* (4.6.14.3.1(2)) */
+	} else if (!(bridge_info.top_change_detected)) {
+		transmit_tcn();			  /* (4.6.14.3.2(1)) */
+		start_tcn_timer();		  /* (4.6.14.3.2(2)) */
+	}
+	bridge_info.top_change = 1;
+}
+
+void
+topology_change_acknowledged(void)
+{						  /* (4.6.15) */
+	bridge_info.top_change_detected = 0;
+	stop_tcn_timer();			  /* (4.6.15.3.2) */
+}
+
+void
+acknowledge_topology_change(int port_no)
+{						  /* (4.6.16) */
+	port_info[port_no].top_change_ack = 1;
+	transmit_config(port_no);		  /* (4.6.16.3.2) */
+}
+
+void
+make_blocking(int port_no)				  /* (4.6.13)	 */
+{
+
+	if ((port_info[port_no].state != Disabled)
+			&&
+			(port_info[port_no].state != Blocking)
+	/* (4.6.13.3)	 */
+		) {
+		if ((port_info[port_no].state == Forwarding)
+				||
+				(port_info[port_no].state == Learning)
+			) {
+			topology_change_detection();	/* (4.6.13.3.1) */
+			/* (4.6.14.2.3)	 */
+		}
+		set_port_state(port_no, Blocking);/* (4.6.13.3.2) */
+		stop_forward_delay_timer(port_no);/* (4.6.13.3.3) */
+	}
+}
+
+void
+set_port_state(int port_no, int state)
+{
+	port_info[port_no].state = state;
+}
+
+void
+received_config_bpdu(int port_no, Config_bpdu *config)		  /* (4.7.1)	 */
+{
+	int         root;
+
+	root = root_bridge();
+	if (port_info[port_no].state != Disabled) {
+		if (supersedes_port_info(port_no, config)) {	/* (4.7.1.1)	 *//* (4.
+								 * 6.2.2)	 */
+			record_config_information(port_no, config);	/* (4.7.1.1.1)	 */
+			/* (4.6.2.2)	 */
+			configuration_update();	  /* (4.7.1.1.2)	 */
+			/* (4.6.7.2.1)	 */
+			port_state_selection();	  /* (4.7.1.1.3)	 */
+			/* (4.6.11.2.1)	 */
+			if ((!root_bridge()) && root) {	/* (4.7.1.1.4)	 */
+				stop_hello_timer();
+				if (bridge_info.top_change_detected) {	/* (4.7.1.1.5~ */
+					stop_topology_change_timer();
+					transmit_tcn();	/* (4.6.6.1)	 */
+					start_tcn_timer();
+				}
+			}
+			if (port_no == bridge_info.root_port) {
+				record_config_timeout_values(config);	/* (4.7.1.1.6)	 */
+				/* (4.6.3.2)	 */
+				config_bpdu_generation();	/* (4.6.4.2.1)	 */
+				if (config->flags & TOPOLOGY_CHANGE_ACK) {	/* (4.7.1.1.7)    */
+					topology_change_acknowledged();	/* (4.6.15.2)	 */
+				}
+			}
+		} else if (designated_port(port_no)) {	/* (4.7.1.2)	 */
+			reply(port_no);		  /* (4.7.1.2.1)	 */
+			/* (4.6.5.2)	 */
+		}
+	}
+}
+
+void
+received_tcn_bpdu(int port_no, Tcn_bpdu *tcn)			  /* (4.7.2)	 */
+{
+	if (port_info[port_no].state != Disabled) {
+		if (designated_port(port_no)) {
+			topology_change_detection();	/* (4.7.2.1)	 */
+			/* (4.6.14.2.1)	 */
+			acknowledge_topology_change(port_no);	/* (4.7.2.2)	 */
+		}				  /* (4.6.16.2)	 */
+	}
+}
+
+void
+hello_timer_expiry(void)
+{						  /* (4.7.3)	 */
+	config_bpdu_generation();		  /* (4.6.4.2.2)	 */
+	start_hello_timer();
+}
+
+void
+message_age_timer_expiry(int port_no)		  /* (4.7.4)	 */
+{
+	int         root;
+	root = root_bridge();
+
+	become_designated_port(port_no);	  /* (4.7.4.1)	 */
+	/* (4.6.10.2.1)	 */
+	configuration_update();			  /* (4.7.4.2)	 */
+	/* (4.6.7.2.2)	 */
+	port_state_selection();			  /* (4.7.4.3)	 */
+	/* (4.6.11.2.2)	 */
+	if ((root_bridge()) && (!root)) {	  /* (4.7.4.4)	 */
+
+		bridge_info.max_age = bridge_info.bridge_max_age;	/* (4.7.4.4.1)    */
+		bridge_info.hello_time = bridge_info.bridge_hello_time;
+		bridge_info.forward_delay = bridge_info.bridge_forward_delay;
+		topology_change_detection();	  /* (4.7.4.4.2)	 */
+		/* (4.6.14.2.4)	 */
+		stop_tcn_timer();		  /* (4.7.4.4.3)	 */
+		config_bpdu_generation();	  /* (4.7.4.4.4)	 */
+		/* (4.6.4.4.3)	 */
+		start_hello_timer();
+	}
+}
+
+void
+forward_delay_timer_expiry(int port_no)		  /* (4.7.5)	 */
+{
+	if (port_info[port_no].state == Listening) {	/* (4.7.5.1)	 */
+		set_port_state(port_no, Learning);	/* (4.7.5.1.1)	 */
+		start_forward_delay_timer(port_no);	/* (4.7.5.1.2)	 */
+	} else if (port_info[port_no].state == Learning) {	/* (4.7.5.2) */
+		set_port_state(port_no, Forwarding);	/* (4.7.5.2.1) */
+		if (designated_for_some_port()) { /* (4.7.5.2.2) */
+			topology_change_detection();	/* (4.6.14.2.2) */
+
+		}
+	}
+}
+
+int
+designated_for_some_port(void)
+{
+	int             port_no;
+
+
+	for (port_no = One; port_no <= No_of_ports; port_no++) {
+		if ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
+				bridge_info.bridge_id.BRIDGE_ID) == 0)
+			) {
+			return (TRUE);
+		}
+	}
+	return (FALSE);
+}
+
+void
+tcn_timer_expiry(void)
+{						  /* (4.7.6)	 */
+	transmit_tcn();				  /* (4.7.6.1)	 */
+	start_tcn_timer();			  /* (4.7.6.2)	 */
+}
+
+void
+topology_change_timer_expiry(void)
+{						  /* (4.7.7)	 */
+	bridge_info.top_change_detected = 0;
+	bridge_info.top_change = 0;
+	  /* (4.7.7.2)	 */
+}
+
+void
+hold_timer_expiry(int port_no)			  /* (4.7.8)	 */
+{
+	if (port_info[port_no].config_pending) {
+		transmit_config(port_no);	  /* (4.7.8.1)	 */
+	}					  /* (4.6.1.2.3)	 */
+}
+
+void
+br_init(void)
+{						  /* (4.8.1)	 */
+	int             port_no;
+
+	bridge_info.designated_root = bridge_info.bridge_id;	/* (4.8.1.1)	 */
+	bridge_info.root_path_cost = Zero;
+	bridge_info.root_port = No_port;
+
+	bridge_info.bridge_max_age = BRIDGE_MAX_AGE;
+	bridge_info.bridge_hello_time = BRIDGE_HELLO_TIME;
+	bridge_info.bridge_forward_delay = BRIDGE_FORWARD_DELAY;
+	bridge_info.hold_time = HOLD_TIME;
+
+	bridge_info.max_age = bridge_info.bridge_max_age;	/* (4.8.1.2)	 */
+	bridge_info.hello_time = bridge_info.bridge_hello_time;
+	bridge_info.forward_delay = bridge_info.bridge_forward_delay;
+
+	bridge_info.top_change_detected = 0;
+	bridge_info.top_change = 0;
+	stop_tcn_timer();
+	stop_topology_change_timer();
+	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.1.4) */
+		br_init_port(port_no);
+		disable_port(port_no);
+	}
+	port_state_selection();			  /* (4.8.1.5)	 */
+	config_bpdu_generation();		  /* (4.8.1.6)	 */
+	
+	/* initialize system timer */
+	tl.expires = HZ;	/* 1 second */
+	tl.function = br_tick;
+	add_timer(&tl);
+
+	register_netdevice_notifier(&br_dev_notifier);
+	br_stats.flags = BR_UP | BR_DEBUG;	/* enable bridge */
+	start_hello_timer();
+}
+
+void
+br_init_port(int port_no)
+{
+	become_designated_port(port_no);	  /* (4.8.1.4.1) */
+	set_port_state(port_no, Blocking);	  /* (4.8.1.4.2)    */
+	port_info[port_no].top_change_ack = 0;
+	port_info[port_no].config_pending = FALSE;/* (4.8.1.4.4)	 */
+	stop_message_age_timer(port_no);	  /* (4.8.1.4.5)	 */
+	stop_forward_delay_timer(port_no);	  /* (4.8.1.4.6)	 */
+	stop_hold_timer(port_no);		  /* (4.8.1.4.7)	 */
+}
+
+void
+enable_port(int port_no)				  /* (4.8.2)	 */
+{
+	br_init_port(port_no);
+	port_state_selection();			  /* (4.8.2.7)	 */
+}						  /* */
+
+void
+disable_port(int port_no)				  /* (4.8.3)	 */
+{
+	int         root;
+
+	root = root_bridge();
+	become_designated_port(port_no);	  /* (4.8.3.1)	 */
+	set_port_state(port_no, Disabled);	  /* (4.8.3.2)	 */
+	port_info[port_no].top_change_ack = 0;
+	port_info[port_no].config_pending = FALSE;/* (4.8.3.4)	 */
+	stop_message_age_timer(port_no);	  /* (4.8.3.5)	 */
+	stop_forward_delay_timer(port_no);	  /* (4.8.3.6)	 */
+	configuration_update();
+	port_state_selection();			  /* (4.8.3.7)	 */
+	if ((root_bridge()) && (!root)) {	  /* (4.8.3.8)	 */
+		bridge_info.max_age = bridge_info.bridge_max_age;	/* (4.8.3.8.1)    */
+		bridge_info.hello_time = bridge_info.bridge_hello_time;
+		bridge_info.forward_delay = bridge_info.bridge_forward_delay;
+		topology_change_detection();	  /* (4.8.3.8.2)    */
+		stop_tcn_timer();		  /* (4.8.3.8.3)    */
+		config_bpdu_generation();	  /* (4.8.3.8.4)    */
+		start_hello_timer();
+	}
+}
+
+
+void
+set_bridge_priority(bridge_id_t *new_bridge_id)		  /* (4.8.4)	 */
+{
+
+	int         root;
+	int             port_no;
+	root = root_bridge();
+	for (port_no = One; port_no <= No_of_ports; port_no++) {	/* (4.8.4.2) */
+		if (designated_port(port_no)) {
+			port_info[port_no].designated_bridge = *new_bridge_id;
+		}
+	}
+
+	bridge_info.bridge_id = *new_bridge_id;	  /* (4.8.4.3)	 */
+	configuration_update();			  /* (4.8.4.4)	 */
+	port_state_selection();			  /* (4.8.4.5)	 */
+	if ((root_bridge()) && (!root)) {	  /* (4.8.4.6)	 */
+		bridge_info.max_age = bridge_info.bridge_max_age;	/* (4.8.4.6.1)    */
+		bridge_info.hello_time = bridge_info.bridge_hello_time;
+		bridge_info.forward_delay = bridge_info.bridge_forward_delay;
+		topology_change_detection();	  /* (4.8.4.6.2)    */
+		stop_tcn_timer();		  /* (4.8.4.6.3)    */
+		config_bpdu_generation(),	  /* (4.8.4.6.4)    */
+		start_hello_timer();
+	}
+}
+
+void
+set_port_priority(int port_no, unsigned short new_port_id)		  /* (4.8.5)	 */
+{
+	if (designated_port(port_no)) {		  /* (4.8.5.2)	 */
+		port_info[port_no].designated_port = new_port_id;
+	}
+	port_info[port_no].port_id = new_port_id; /* (4.8.5.3)	 */
+	if ((br_cmp(bridge_info.bridge_id.BRIDGE_ID,
+	     port_info[port_no].designated_bridge.BRIDGE_ID) == 0
+	     )
+			&&
+			(port_info[port_no].port_id
+			 < port_info[port_no].designated_port
+
+			 )
+		) {
+		become_designated_port(port_no);  /* (4.8.5.4.1) */
+		port_state_selection();		  /* (4.8.5.4.2) */
+	}
+}
+
+void
+set_path_cost(int port_no, unsigned short path_cost)		  /* (4.8.6)	 */
+{
+	port_info[port_no].path_cost = path_cost; /* (4.8.6.1)	 */
+	configuration_update();			  /* (4.8.6.2)	 */
+	port_state_selection();			  /* (4.8.6.3)	 */
+}
+
+static void
+br_tick(unsigned long arg)
+{
+	int             port_no;
+
+	if (hello_timer_expired()) {
+		hello_timer_expiry();
+	}
+	if (tcn_timer_expired()) {
+		tcn_timer_expiry();
+	}
+	if (topology_change_timer_expired()) {
+		topology_change_timer_expiry();
+	}
+	for (port_no = One; port_no <= No_of_ports; port_no++) {
+		if (forward_delay_timer_expired(port_no)) {
+			forward_delay_timer_expiry(port_no);
+		}
+		if (message_age_timer_expired(port_no)) {
+			message_age_timer_expiry(port_no);
+		}
+		if (hold_timer_expired(port_no)) {
+			hold_timer_expiry(port_no);
+		}
+	}
+	/* call me again sometime... */
+	tl.expires = HZ;	/* 1 second */
+	tl.function = br_tick;
+	add_timer(&tl);
+}
+
+void
+start_hello_timer(void)
+{
+	hello_timer.value = 0;
+	hello_timer.active = TRUE;
+}
+
+void
+stop_hello_timer(void)
+{
+	hello_timer.active = FALSE;
+}
+
+int
+hello_timer_expired(void)
+{
+	if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) {
+		hello_timer.active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+void
+start_tcn_timer(void)
+{
+	tcn_timer.value = 0;
+	tcn_timer.active = TRUE;
+}
+
+void
+stop_tcn_timer(void)
+{
+	tcn_timer.active = FALSE;
+}
+
+int
+tcn_timer_expired(void)
+{
+	if (tcn_timer.active && (++tcn_timer.value >=
+				 bridge_info.bridge_hello_time)) {
+		tcn_timer.active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
+
+}
+
+void
+start_topology_change_timer(void)
+{
+	topology_change_timer.value = 0;
+	topology_change_timer.active = TRUE;
+}
+
+void
+stop_topology_change_timer(void)
+{
+	topology_change_timer.active = FALSE;
+}
+
+int
+topology_change_timer_expired(void)
+{
+	if (topology_change_timer.active
+			&& (++topology_change_timer.value
+			    >= bridge_info.topology_change_time
+			    )) {
+		topology_change_timer.active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+void
+start_message_age_timer(int port_no, unsigned short message_age)
+{
+	message_age_timer[port_no].value = message_age;
+	message_age_timer[port_no].active = TRUE;
+}
+
+void
+stop_message_age_timer(int port_no)
+{
+	message_age_timer[port_no].active = FALSE;
+}
+
+int
+message_age_timer_expired(int port_no)
+{
+	if (message_age_timer[port_no].active &&
+	      (++message_age_timer[port_no].value >= bridge_info.max_age)) {
+		message_age_timer[port_no].active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+void
+start_forward_delay_timer(int port_no)
+{
+	forward_delay_timer[port_no].value = 0;
+	forward_delay_timer[port_no].active = TRUE;
+}
+
+void
+stop_forward_delay_timer(int port_no)
+{
+	forward_delay_timer[port_no].active = FALSE;
+}
+
+int
+forward_delay_timer_expired(int port_no)
+{
+		if (forward_delay_timer[port_no].active &&
+				(++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) {
+			forward_delay_timer[port_no].active = FALSE;
+			return (TRUE);
+		}
+		return (FALSE);
+}
+
+void
+start_hold_timer(int port_no)
+{
+	hold_timer[port_no].value = 0;
+	hold_timer[port_no].active = TRUE;
+}
+
+void
+stop_hold_timer(int port_no)
+{
+	hold_timer[port_no].active = FALSE;
+}
+
+
+int
+hold_timer_expired(int port_no)
+{
+	if (hold_timer[port_no].active &&
+		   (++hold_timer[port_no].value >= bridge_info.hold_time)) {
+		hold_timer[port_no].active = FALSE;
+		return (TRUE);
+	}
+	return (FALSE);
+
+}
+
+int
+send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+{
+struct sk_buff *skb;
+struct device *dev = port_info[port_no].dev;
+int size;
+unsigned long flags;
+	
+	if (port_info[port_no].state == Disabled) {
+		printk("send_config_bpdu: port %i not valid\n",port_no);
+		return(-1);
+		}
+	if (br_stats.flags & BR_DEBUG)
+		printk("send_config_bpdu: ");
+	/*
+	 * create and send the message
+	 */
+	size = sizeof(Config_bpdu) + dev->hard_header_len;
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (skb == NULL) {
+		printk("send_config_bpdu: no skb available\n");
+		return(-1);
+		}
+	skb->dev = dev;
+	skb->free = 1;
+	skb->h.eth = (struct ethhdr *)skb_put(skb, size);
+	memcpy(skb->h.eth->h_dest, bridge_ula, ETH_ALEN);
+	memcpy(skb->h.eth->h_source, dev->dev_addr, ETH_ALEN);
+	if (br_stats.flags & BR_DEBUG)
+		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
+			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+			port_no,
+			skb->h.eth->h_source[0],
+			skb->h.eth->h_source[1],
+			skb->h.eth->h_source[2],
+			skb->h.eth->h_source[3],
+			skb->h.eth->h_source[4],
+			skb->h.eth->h_source[5],
+			skb->h.eth->h_dest[0],
+			skb->h.eth->h_dest[1],
+			skb->h.eth->h_dest[2],
+			skb->h.eth->h_dest[3],
+			skb->h.eth->h_dest[4],
+			skb->h.eth->h_dest[5]);
+	skb->h.eth->h_proto = htonl(0x8038);	/* XXX verify */
+
+	skb->h.raw += skb->dev->hard_header_len;
+	memcpy(skb->h.raw, config_bpdu, sizeof(Config_bpdu));
+
+	/* won't get bridged again... */
+	skb->pkt_bridged = IS_BRIDGED;
+	skb->arp = 1;	/* do not resolve... */
+	skb->h.raw = skb->data + ETH_HLEN;
+	save_flags(flags);
+	cli();
+	skb_queue_tail(dev->buffs, skb);
+	restore_flags(flags);
+	return(0);
+}
+
+int
+send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
+{
+struct sk_buff *skb;
+struct device *dev = port_info[port_no].dev;
+int size;
+unsigned long flags;
+	
+	if (port_info[port_no].state == Disabled) {
+		printk("send_tcn_bpdu: port %i not valid\n",port_no);
+		return(-1);
+		}
+	if (br_stats.flags & BR_DEBUG)
+		printk("send_tcn_bpdu: ");
+	size = sizeof(Tcn_bpdu) + dev->hard_header_len;
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (skb == NULL) {
+		printk("send_tcn_bpdu: no skb available\n");
+		return(-1);
+		}
+	skb->dev = dev;
+	skb->free = 1;
+	skb->h.eth = (struct ethhdr *)skb_put(skb,size);
+	memcpy(skb->h.eth->h_dest, bridge_ula, ETH_ALEN);
+	memcpy(skb->h.eth->h_source, dev->dev_addr, ETH_ALEN);
+	if (br_stats.flags & BR_DEBUG)
+		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
+			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+			port_no,
+			skb->h.eth->h_source[0],
+			skb->h.eth->h_source[1],
+			skb->h.eth->h_source[2],
+			skb->h.eth->h_source[3],
+			skb->h.eth->h_source[4],
+			skb->h.eth->h_source[5],
+			skb->h.eth->h_dest[0],
+			skb->h.eth->h_dest[1],
+			skb->h.eth->h_dest[2],
+			skb->h.eth->h_dest[3],
+			skb->h.eth->h_dest[4],
+			skb->h.eth->h_dest[5]);
+	skb->h.eth->h_proto = 0x8038;	/* XXX verify */
+
+	skb->h.raw += skb->dev->hard_header_len;
+	memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
+
+	/* mark that's we've been here... */
+	skb->pkt_bridged = IS_BRIDGED;
+	skb->arp = 1;	/* do not resolve... */
+	skb->h.raw = skb->data + ETH_HLEN;
+	save_flags(flags);
+	cli();
+	skb_queue_tail(dev->buffs, skb);
+	restore_flags(flags);
+	return(0);
+}
+
+static int 
+br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
+{
+	struct device *dev = ptr;
+	int i;
+
+	/* check for loopback devices */
+	if (dev->flags & IFF_LOOPBACK)
+		return(NOTIFY_DONE);
+
+	switch (event) {
+	case NETDEV_DOWN:
+		if (br_stats.flags & BR_DEBUG)
+			printk("br_device_event: NETDEV_DOWN...\n");
+		/* find our device and mark it down */
+		for (i = One; i <= No_of_ports; i++) {
+			if (port_info[i].dev == dev) {
+				disable_port(i);
+				return NOTIFY_DONE;
+				break;
+			}
+		}
+		break;
+	case NETDEV_UP:
+		if (br_stats.flags & BR_DEBUG)
+			printk("br_device_event: NETDEV_UP...\n");
+		/* Only handle ethernet ports */
+		if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
+			return NOTIFY_DONE;
+		/* look up an unused device and enable it */
+		for (i = One; i <= No_of_ports; i++) {
+			if ((port_info[i].dev == (struct device *)0) ||
+				(port_info[i].dev == dev)) {
+				port_info[i].dev = dev;
+				enable_port(i);
+				set_path_cost(i, br_port_cost(dev));
+				set_port_priority(i, 128); 
+				port_info[i].port_id = i;
+				/* set bridge addr from 1st device addr */
+				if ((bridge_info.bridge_id.BRIDGE_ID[0] == 0) &&
+						(bridge_info.bridge_id.BRIDGE_ID[1] == 0)) {
+					memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6);
+					bridge_info.bridge_id.BRIDGE_PRIORITY = port_info[i].port_id;
+					set_bridge_priority(&bridge_info.bridge_id);
+				}	
+				make_forwarding(i);
+				return NOTIFY_DONE;
+				break;
+			}
+		}
+		break;
+	default:
+		printk("br_device_event: unknown event [%x]\n",
+			(unsigned int)event);
+	}
+	return NOTIFY_DONE;
+}
+
+/*
+ * following routine is called when a frame is received
+ * from an interface, it returns 1 when it consumes the
+ * frame, 0 when it does not
+ */
+
+int
+br_receive_frame(struct sk_buff *skb)	/* 3.5 */
+{
+	int port;
+	
+	if (br_stats.flags & BR_DEBUG)
+		printk("br_receive_frame: ");
+	/* sanity */
+	if (!skb) {
+		printk("no skb!\n");
+		return(1);
+	}
+	/* check for loopback */
+	if (skb->dev->flags & IFF_LOOPBACK)
+		return(0);
+
+	port = find_port(skb->dev);
+	skb->h.raw = skb->data;
+	if (br_stats.flags & BR_DEBUG)
+		printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
+			dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+			port,
+			skb->h.eth->h_source[0],
+			skb->h.eth->h_source[1],
+			skb->h.eth->h_source[2],
+			skb->h.eth->h_source[3],
+			skb->h.eth->h_source[4],
+			skb->h.eth->h_source[5],
+			skb->h.eth->h_dest[0],
+			skb->h.eth->h_dest[1],
+			skb->h.eth->h_dest[2],
+			skb->h.eth->h_dest[3],
+			skb->h.eth->h_dest[4],
+			skb->h.eth->h_dest[5]);
+
+	if (!port) {
+		printk("\nbr_receive_frame: no port!\n");
+		return(0);
+	}
+
+	switch (port_info[port].state) {
+		case Learning:
+			(void) br_learn(skb, port);	/* 3.8 */
+			/* fall through */
+		case Listening:
+			/* process BPDUs */
+			if (memcmp(skb->h.eth->h_dest, bridge_ula, 6) == 0) {
+				br_bpdu(skb);
+				return(1); /* br_bpdu consumes skb */
+			}
+			/* fall through */
+		case Blocking:
+			/* fall through */
+		case Disabled:
+			/* should drop frames, but for now, we let
+			 * them get passed up to the next higher layer
+			return(br_drop(skb));	
+			 */
+			return(0);	/* pass frame up stack */
+			break;
+		case Forwarding:
+			(void) br_learn(skb, port);	/* 3.8 */
+			/* process BPDUs */
+			if (memcmp(skb->h.eth->h_dest, bridge_ula, 
+					ETH_ALEN) == 0) {
+				printk("frame bpdu processor for me!!!\n");
+				br_bpdu(skb);
+				return(1); /* br_bpdu consumes skb */
+			}
+			/* is frame for me? */	
+			if (memcmp(skb->h.eth->h_dest, 
+					port_info[port].dev->dev_addr, 
+					ETH_ALEN) == 0) {
+				return(0);	/* pass frame up our stack (this will */
+						/* happen in net_bh() in dev.c) */
+			}
+			/* ok, forward this frame... */
+			return(br_forward(skb, port));
+		default:
+			printk("br_receive_frame: port [%i] unknown state [%i]\n",
+				port, port_info[port].state);
+			return(0);	/* pass frame up stack? */
+	}
+}
+
+/*
+ * the following routine is called to transmit frames from the host
+ * stack.  it returns 1 when it consumes the frame and
+ * 0 when it does not.
+ */
+
+int
+br_tx_frame(struct sk_buff *skb)	/* 3.5 */
+{
+	int port;
+	
+	/* sanity */
+	if (!skb) {
+		printk("br_tx_frame: no skb!\n");
+		return(0);
+	}
+	/* check for loopback */
+	if (skb->dev->flags & IFF_LOOPBACK)
+		return(0);
+
+	skb->h.raw = skb->data;
+	port = 0;	/* an impossible port */	
+	if (br_stats.flags & BR_DEBUG)
+		printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
+	  		dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
+			port,
+			skb->h.eth->h_source[0],
+			skb->h.eth->h_source[1],
+			skb->h.eth->h_source[2],
+			skb->h.eth->h_source[3],
+			skb->h.eth->h_source[4],
+			skb->h.eth->h_source[5],
+			skb->h.eth->h_dest[0],
+			skb->h.eth->h_dest[1],
+			skb->h.eth->h_dest[2],
+			skb->h.eth->h_dest[3],
+			skb->h.eth->h_dest[4],
+			skb->h.eth->h_dest[5]);
+	return(br_forward(skb, port));
+}
+
+/*
+ * this routine returns 0 when it learns (or updates) from the
+ * frame, and -1 if the frame is simply discarded due to port
+ * state or lack of resources...
+ */
+
+int
+br_learn(struct sk_buff *skb, int port)	/* 3.8 */
+{
+	struct fdb *f;
+
+	switch (port_info[port].state) {
+		case Listening:
+		case Blocking:
+		case Disabled:
+		default:
+			return(-1);
+			/* break; */
+		case Learning:
+		case Forwarding:
+			/* don't keep group addresses in the tree */
+			if (skb->h.eth->h_source[0] & 0x01)
+				return(-1);
+			
+			f = (struct fdb *)kmalloc(sizeof(struct fdb), 
+				GFP_ATOMIC);
+
+			if (!f) {
+				printk("br_learn: unable to malloc fdb\n");
+				return(-1);
+			}
+			f->port = port;	/* source port */
+			memcpy(f->ula, skb->h.eth->h_source, 6);
+			f->timer = CURRENT_TIME;
+			f->flags = FDB_ENT_VALID;
+			/*
+			 * add entity to AVL tree.  If entity already
+			 * exists in the tree, update the fields with
+			 * what we have here.
+		  	 */
+			if (br_avl_insert(f) == 0) { /* update */
+				kfree(f);
+				return(0);
+			}
+			/* add to head of port chain */
+			f->fdb_next = port_info[port].fdb;
+			port_info[port].fdb = f;
+			return(0);
+			/* break */
+	}
+}
+
+/*
+ * this routine always consumes the frame
+ */
+
+int
+br_drop(struct sk_buff *skb)
+{
+	kfree_skb(skb, 0);
+	return(1);
+}
+
+/*
+ * this routine returns 1 if it consumes the frame, 0
+ * if not...
+ */
+
+int
+br_forward(struct sk_buff *skb, int port)	/* 3.7 */
+{
+struct fdb *f;
+unsigned long flags;
+	
+	/*
+   	 * flood all ports with frames destined for a group
+	 * address.  If frame came from above, drop it,
+	 * otherwise it will be handled in br_receive_frame()
+	 * Multicast frames will also need to be seen
+	 * by our upper layers.
+	 */	
+	if (skb->h.eth->h_dest[0] & 0x01) {	/* group address */
+		br_flood(skb, port);
+		if (port == 0) 			/* locally generated */
+			return(br_drop(skb));
+		return(0);
+	} else {
+		/* locate port to forward to */
+		f = br_avl_find_addr(skb->h.eth->h_dest);
+		if (!f | !(f->flags & FDB_ENT_VALID)) {
+		 	/* not found; flood all ports */
+			br_flood(skb, port);
+			return(br_drop(skb));
+		}
+		if (port_info[f->port].state == Forwarding) {
+			/* has entry expired? */
+			if (f->timer + fdb_aging_time < CURRENT_TIME) {
+				/* timer expired, invalidate entry */
+				f->flags &= ~FDB_ENT_VALID;
+				if (br_stats.flags & BR_DEBUG)
+					printk("fdb entry expired...\n");
+				br_flood(skb, port);
+				return(br_drop(skb));
+			}
+			/* mark that's we've been here... */
+			skb->pkt_bridged = IS_BRIDGED;
+			
+			/*
+			 * if the frame is originating in this host,
+			 * we may need to resolve the outgoing address
+			if (port != 0)
+				skb->arp = 1;
+			 */
+		
+			/* reset the skb->ip pointer */	
+			skb->h.raw = skb->data + ETH_HLEN;
+
+			/* we must unlock the skb before requeueing it... */
+			if (skb_device_locked(skb))
+				skb_device_unlock(skb);
+
+			save_flags(flags);	/* enter critical section */
+			cli();
+			skb_queue_head(port_info[f->port].dev->buffs, skb);
+			restore_flags(flags);	/* exit critical section */
+			return(1);	/* skb has been consumed */
+		} else {
+			return(br_drop(skb));
+		}
+	}
+}
+
+/*
+ * this routine sends a copy of the frame to all forwarding ports
+ * with the exception of the port given.  This routine never
+ * consumes the original frame.
+ */
+	
+int
+br_flood(struct sk_buff *skb, int port)
+{
+int i;
+struct sk_buff *nskb;
+unsigned long flags;
+
+	for (i = One; i <= No_of_ports; i++) {
+		if (i == port)
+			continue;
+		if (port_info[i].state == Forwarding) {
+			nskb = skb_clone(skb, GFP_ATOMIC);
+			/* mark that's we've been here... */
+			nskb->pkt_bridged = IS_BRIDGED;
+			/*
+			 * if the frame is originating in this host,
+			 * we may need to resolve the outgoing address
+			if (port != 0)
+				nskb->arp = 1;
+			 */
+			nskb->h.raw = nskb->data + ETH_HLEN;
+			save_flags(flags);
+			cli();
+			skb_queue_tail(port_info[i].dev->buffs, nskb);
+			restore_flags(flags);
+		}
+	}
+	return(0);
+}
+
+int
+find_port(struct device *dev)
+{
+int i;
+
+	for (i = One; i <= No_of_ports; i++)
+		if ((port_info[i].dev == dev) && 
+			(port_info[i].state != Disabled))
+			return(i);
+	return(0);
+}
+
+int
+br_port_cost(struct device *dev)	/* 4.10.2 */
+{
+	if (strncmp(dev->name, "eth", 3) == 0)	/* ethernet */
+		return(100);
+	if (strncmp(dev->name, "wic", 3) == 0)	/* wic */
+		return(1600);
+	if (strncmp(dev->name, "plip",4) == 0) /* plip */
+		return (1600);
+	return(100);	/* default */
+}
+
+/*
+ * this routine always consumes the skb 
+ */
+
+void
+br_bpdu(struct sk_buff *skb) /* consumes skb */
+{
+Tcn_bpdu *bpdu;
+int port;
+
+	port = find_port(skb->dev);
+	if (port == 0) {	/* unknown port */
+		br_drop(skb);
+		return;
+	}
+		
+	bpdu = (Tcn_bpdu *)skb->data + ETH_HLEN;
+	switch (bpdu->type) {
+		case BPDU_TYPE_CONFIG:
+			received_config_bpdu(port, (Config_bpdu *)bpdu);
+			break;
+		case BPDU_TYPE_TOPO_CHANGE:
+			received_tcn_bpdu(port, bpdu);
+			break;
+		default:
+			printk("br_bpdu: received unknown bpdu, type = %i\n",
+				bpdu->type);
+			/* break; */
+	}
+	br_drop(skb);
+}
+
+int
+br_ioctl(unsigned int cmd, void *arg)
+{
+	int err;
+	struct br_cf bcf;
+	int i;
+
+	switch(cmd)
+	{
+		case SIOCGIFBR:	/* get bridging control blocks */
+			err = verify_area(VERIFY_WRITE, arg, 
+				sizeof(struct br_stat));
+			if(err)
+				return err;
+			memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data));
+			memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports);
+			memcpy_tofs(arg, &br_stats, sizeof(struct br_stat));
+			return(0);
+		case SIOCSIFBR:
+			if (!suser())
+				return -EPERM;
+			err = verify_area(VERIFY_READ, arg, 
+				sizeof(struct br_cf));
+			if(err)
+				return err;
+			memcpy_fromfs(&bcf, arg, sizeof(struct br_cf));
+			switch (bcf.cmd) {
+				case BRCMD_BRIDGE_ENABLE:
+					if (br_stats.flags & BR_UP)
+						return(-EALREADY);	
+					printk("br: enabling bridging function\n");
+					register_netdevice_notifier(&br_dev_notifier);
+					br_stats.flags |= BR_UP;	/* enable bridge */
+					start_hello_timer();
+					break;
+				case BRCMD_BRIDGE_DISABLE:
+					if (!(br_stats.flags & BR_UP))
+						return(-EALREADY);	
+					printk("br: disabling bridging function\n");
+					unregister_netdevice_notifier(&br_dev_notifier);
+					br_stats.flags &= ~BR_UP;	/* disable bridge */
+					stop_hello_timer();
+					for (i = One; i <= No_of_ports; i++)
+						if (port_info[i].state != Disabled)
+							disable_port(i);
+					break;
+				case BRCMD_PORT_ENABLE:
+					if (port_info[bcf.arg1].dev == 0)
+						return(-EINVAL);
+					if (port_info[bcf.arg1].state != Disabled)
+						return(-EALREADY);
+					printk("br: enabling port %i\n",bcf.arg1);
+					enable_port(bcf.arg1);
+					break;
+				case BRCMD_PORT_DISABLE:
+					if (port_info[bcf.arg1].dev == 0)
+						return(-EINVAL);
+					if (port_info[bcf.arg1].state == Disabled)
+						return(-EALREADY);
+					printk("br: disabling port %i\n",bcf.arg1);
+					disable_port(bcf.arg1);
+					break;
+				case BRCMD_SET_BRIDGE_PRIORITY:
+					set_bridge_priority((bridge_id_t *)&bcf.arg1);
+					break;
+				case BRCMD_SET_PORT_PRIORITY:
+					if (port_info[bcf.arg1].dev == 0)
+						return(-EINVAL);
+					set_port_priority(bcf.arg1, bcf.arg2);
+					break;
+				case BRCMD_SET_PATH_COST:
+					if (port_info[bcf.arg1].dev == 0)
+						return(-EINVAL);
+					set_path_cost(bcf.arg1, bcf.arg2);
+					break;
+				case BRCMD_ENABLE_DEBUG:
+					br_stats.flags |= BR_DEBUG;
+					break;
+				case BRCMD_DISABLE_DEBUG:
+					br_stats.flags &= ~BR_DEBUG;
+					break;
+				default:
+					return -EINVAL;
+			}
+			return(0);
+		default:
+			return -EINVAL;
+	}
+	/*NOTREACHED*/
+	return 0;
+}
+
+int br_cmp(unsigned int *a, unsigned int *b)
+{
+	int i;	
+	for (i=0; i<2; i++) {
+		if (a[i] == b[i])
+			continue;
+		if (a[i] < b[i])
+			return(1);
+		if (a[i] > b[i])
+			return(-1);
+	}
+	return(0);
+}
+		
diff -u --recursive --new-file v1.3.85/linux/net/bridge/br_tree.c linux/net/bridge/br_tree.c
--- v1.3.85/linux/net/bridge/br_tree.c	Thu Jan  1 02:00:00 1970
+++ linux/net/bridge/br_tree.c	Tue Apr  9 14:36:31 1996
@@ -0,0 +1,403 @@
+/*
+ * this code is derived from the avl functions in mmap.c
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/skbuff.h>
+
+#include <net/br.h>
+#define _DEBUG_AVL
+
+/*
+ * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search
+ * from O(n) to O(log n), where n is the number of ULAs.
+ * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>.
+ * Taken from mmap.c, extensively modified by John Hayes 
+ * <hayes@netplumbing.com>
+ */
+
+struct fdb fdb_head;
+struct fdb *fhp = &fdb_head;
+struct fdb **fhpp = &fhp;
+static int fdb_inited = 0;
+
+int addr_cmp(unsigned char *a1, unsigned char *a2);
+static void printk_avl (struct fdb * tree);
+
+/*
+ * fdb_head is the AVL tree corresponding to fdb
+ * or, more exactly, its root.
+ * A fdb has the following fields:
+ *   fdb_avl_left     left son of a tree node
+ *   fdb_avl_right    right son of a tree node
+ *   fdb_avl_height   1+max(heightof(left),heightof(right))
+ * The empty tree is represented as NULL.
+ */
+ 
+#ifndef avl_br_empty
+#define avl_br_empty	(struct fdb *) NULL
+#endif
+
+/* Since the trees are balanced, their height will never be large. */
+#define avl_maxheight	127
+#define heightof(tree)	((tree) == avl_br_empty ? 0 : (tree)->fdb_avl_height)
+/*
+ * Consistency and balancing rules:
+ * 1. tree->fdb_avl_height == 1+max(heightof(tree->fdb_avl_left),heightof(tree->fdb_avl_right))
+ * 2. abs( heightof(tree->fdb_avl_left) - heightof(tree->fdb_avl_right) ) <= 1
+ * 3. foreach node in tree->fdb_avl_left: node->fdb_avl_key <= tree->fdb_avl_key,
+ *    foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key.
+ */
+
+int
+fdb_init(void)
+{
+	fdb_head.fdb_avl_height = 0;
+	fdb_head.fdb_avl_left = (struct fdb *)0;
+	fdb_head.fdb_avl_right = (struct fdb *)0;
+	fdb_inited = 1;
+	return(0);
+}
+
+struct fdb *
+br_avl_find_addr(unsigned char addr[6])
+{
+	struct fdb * result = NULL;
+	struct fdb * tree;
+
+	if (!fdb_inited)
+		fdb_init();
+#if (DEBUG_AVL)
+	printk("searching for ula %02x:%02x:%02x:%02x:%02x:%02x\n",
+		addr[0],
+		addr[1],
+		addr[2],
+		addr[3],
+		addr[4],
+		addr[5]);
+#endif /* DEBUG_AVL */
+	for (tree = &fdb_head ; ; ) {
+		if (tree == avl_br_empty) {
+#if (DEBUG_AVL)
+			printk("search failed, returning node 0x%x\n", (unsigned int)result);
+#endif /* DEBUG_AVL */
+			return result;
+		}
+
+#if (DEBUG_AVL)
+		printk("node 0x%x: checking ula %02x:%02x:%02x:%02x:%02x:%02x\n",
+			(unsigned int)tree,
+			tree->ula[0],
+			tree->ula[1],
+			tree->ula[2],
+			tree->ula[3],
+			tree->ula[4],
+			tree->ula[5]);
+#endif /* DEBUG_AVL */
+		if (addr_cmp(addr, tree->ula) == 0) {
+#if (DEBUG_AVL)
+			printk("found node 0x%x\n", (unsigned int)tree);
+#endif /* DEBUG_AVL */
+			return tree;
+		}
+		if (addr_cmp(addr, tree->ula) < 0) {
+			tree = tree->fdb_avl_left;
+		} else {
+			tree = tree->fdb_avl_right;
+		}
+	}
+}
+
+/*
+ * Rebalance a tree.
+ * After inserting or deleting a node of a tree we have a sequence of subtrees
+ * nodes[0]..nodes[k-1] such that
+ * nodes[0] is the root and nodes[i+1] = nodes[i]->{fdb_avl_left|fdb_avl_right}.
+ */
+static void 
+br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
+{
+	if (!fdb_inited)
+		fdb_init();
+	for ( ; count > 0 ; count--) {
+		struct fdb ** nodeplace = *--nodeplaces_ptr;
+		struct fdb * node = *nodeplace;
+		struct fdb * nodeleft = node->fdb_avl_left;
+		struct fdb * noderight = node->fdb_avl_right;
+		int heightleft = heightof(nodeleft);
+		int heightright = heightof(noderight);
+		if (heightright + 1 < heightleft) {
+			/*                                                      */
+			/*                            *                         */
+			/*                          /   \                       */
+			/*                       n+2      n                     */
+			/*                                                      */
+			struct fdb * nodeleftleft = nodeleft->fdb_avl_left;
+			struct fdb * nodeleftright = nodeleft->fdb_avl_right;
+			int heightleftright = heightof(nodeleftright);
+			if (heightof(nodeleftleft) >= heightleftright) {
+				/*                                                        */
+				/*                *                    n+2|n+3            */
+				/*              /   \                  /    \             */
+				/*           n+2      n      -->      /   n+1|n+2         */
+				/*           / \                      |    /    \         */
+				/*         n+1 n|n+1                 n+1  n|n+1  n        */
+				/*                                                        */
+				node->fdb_avl_left = nodeleftright; 
+				nodeleft->fdb_avl_right = node;
+				nodeleft->fdb_avl_height = 1 + (node->fdb_avl_height = 1 + heightleftright);
+				*nodeplace = nodeleft;
+			} else {
+				/*                                                        */
+				/*                *                     n+2               */
+				/*              /   \                 /     \             */
+				/*           n+2      n      -->    n+1     n+1           */
+				/*           / \                    / \     / \           */
+				/*          n  n+1                 n   L   R   n          */
+				/*             / \                                        */
+				/*            L   R                                       */
+				/*                                                        */
+				nodeleft->fdb_avl_right = nodeleftright->fdb_avl_left;
+				node->fdb_avl_left = nodeleftright->fdb_avl_right;
+				nodeleftright->fdb_avl_left = nodeleft;
+				nodeleftright->fdb_avl_right = node;
+				nodeleft->fdb_avl_height = node->fdb_avl_height = heightleftright;
+				nodeleftright->fdb_avl_height = heightleft;
+				*nodeplace = nodeleftright;
+			}
+		} else if (heightleft + 1 < heightright) {
+			/* similar to the above, just interchange 'left' <--> 'right' */
+			struct fdb * noderightright = noderight->fdb_avl_right;
+			struct fdb * noderightleft = noderight->fdb_avl_left;
+			int heightrightleft = heightof(noderightleft);
+			if (heightof(noderightright) >= heightrightleft) {
+				node->fdb_avl_right = noderightleft; 
+				noderight->fdb_avl_left = node;
+				noderight->fdb_avl_height = 1 + (node->fdb_avl_height = 1 + heightrightleft);
+				*nodeplace = noderight;
+			} else {
+				noderight->fdb_avl_left = noderightleft->fdb_avl_right;
+				node->fdb_avl_right = noderightleft->fdb_avl_left;
+				noderightleft->fdb_avl_right = noderight;
+				noderightleft->fdb_avl_left = node;
+				noderight->fdb_avl_height = node->fdb_avl_height = heightrightleft;
+				noderightleft->fdb_avl_height = heightright;
+				*nodeplace = noderightleft;
+			}
+		} else {
+			int height = (heightleft<heightright ? heightright : heightleft) + 1;
+			if (height == node->fdb_avl_height)
+				break;
+			node->fdb_avl_height = height;
+		}
+	}
+#ifdef DEBUG_AVL
+	printk_avl(&fdb_head);
+#endif /* DEBUG_AVL */
+}
+
+/* Insert a node into a tree. */
+int 
+br_avl_insert (struct fdb * new_node)
+{
+	struct fdb ** nodeplace = fhpp;
+	struct fdb ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct fdb *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	if (!fdb_inited)
+		fdb_init();
+	for (;;) {
+		struct fdb *node;
+		
+		node = *nodeplace;
+		if (node == avl_br_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */
+			node->flags = new_node->flags;
+			node->timer = new_node->timer;	
+			return(0);
+		}
+		if (addr_cmp(new_node->ula, node->ula) < 0) {
+			nodeplace = &node->fdb_avl_left;
+		} else {
+			nodeplace = &node->fdb_avl_right;
+		}
+	}
+#if (DEBUG_AVL)
+	printk("node 0x%x: adding ula %02x:%02x:%02x:%02x:%02x:%02x\n",
+		(unsigned int)new_node,
+		new_node->ula[0],
+		new_node->ula[1],
+		new_node->ula[2],
+		new_node->ula[3],
+		new_node->ula[4],
+		new_node->ula[5]);
+#endif /* (DEBUG_AVL) */
+	new_node->fdb_avl_left = avl_br_empty;
+	new_node->fdb_avl_right = avl_br_empty;
+	new_node->fdb_avl_height = 1;
+	*nodeplace = new_node;
+#if (0)	
+	br_avl_rebalance(stack_ptr,stack_count);
+#endif /* (0) */
+#ifdef DEBUG_AVL
+	printk_avl(&fdb_head);
+#endif /* DEBUG_AVL */
+	return(1);
+}
+
+/* Removes a node out of a tree. */
+int
+br_avl_remove (struct fdb * node_to_delete)
+{
+	struct fdb ** nodeplace = fhpp;
+	struct fdb ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct fdb *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	struct fdb ** nodeplace_to_delete;
+	if (!fdb_inited)
+		fdb_init();
+	for (;;) {
+		struct fdb * node = *nodeplace;
+		if (node == avl_br_empty) {
+			/* what? node_to_delete not found in tree? */
+			printk("avl_remove: node to delete not found in tree\n");
+			return(-1);
+		}
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (addr_cmp(node_to_delete->ula, node->ula) == 0)
+				break;
+		if (addr_cmp(node_to_delete->ula, node->ula) < 0)
+			nodeplace = &node->fdb_avl_left;
+		else
+			nodeplace = &node->fdb_avl_right;
+	}
+	nodeplace_to_delete = nodeplace;
+	/* Have to remove node_to_delete = *nodeplace_to_delete. */
+	if (node_to_delete->fdb_avl_left == avl_br_empty) {
+		*nodeplace_to_delete = node_to_delete->fdb_avl_right;
+		stack_ptr--; stack_count--;
+	} else {
+		struct fdb *** stack_ptr_to_delete = stack_ptr;
+		struct fdb ** nodeplace = &node_to_delete->fdb_avl_left;
+		struct fdb * node;
+		for (;;) {
+			node = *nodeplace;
+			if (node->fdb_avl_right == avl_br_empty)
+				break;
+			*stack_ptr++ = nodeplace; stack_count++;
+			nodeplace = &node->fdb_avl_right;
+		}
+		*nodeplace = node->fdb_avl_left;
+		/* node replaces node_to_delete */
+		node->fdb_avl_left = node_to_delete->fdb_avl_left;
+		node->fdb_avl_right = node_to_delete->fdb_avl_right;
+		node->fdb_avl_height = node_to_delete->fdb_avl_height;
+		*nodeplace_to_delete = node; /* replace node_to_delete */
+		*stack_ptr_to_delete = &node->fdb_avl_left; /* replace &node_to_delete->fdb_avl_left */
+	}
+	br_avl_rebalance(stack_ptr,stack_count);
+	return(0);
+}
+
+#ifdef DEBUG_AVL
+
+/* print a tree */
+static void printk_avl (struct fdb * tree)
+{
+	if (tree != avl_br_empty) {
+		printk("(");
+		printk("%02x:%02x:%02x:%02x:%02x:%02x",
+			tree->ula[0],
+			tree->ula[1],
+			tree->ula[2],
+			tree->ula[3],
+			tree->ula[4],
+			tree->ula[5]);
+		if (tree->fdb_avl_left != avl_br_empty) {
+			printk_avl(tree->fdb_avl_left);
+			printk("<");
+		}
+		if (tree->fdb_avl_right != avl_br_empty) {
+			printk(">");
+			printk_avl(tree->fdb_avl_right);
+		}
+		printk(")\n");
+	}
+}
+
+#if (0)
+static char *avl_check_point = "somewhere";
+
+/* check a tree's consistency and balancing */
+static void avl_checkheights (struct fdb * tree)
+{
+	int h, hl, hr;
+
+	if (tree == avl_br_empty)
+		return;
+	avl_checkheights(tree->fdb_avl_left);
+	avl_checkheights(tree->fdb_avl_right);
+	h = tree->fdb_avl_height;
+	hl = heightof(tree->fdb_avl_left);
+	hr = heightof(tree->fdb_avl_right);
+	if ((h == hl+1) && (hr <= hl) && (hl <= hr+1))
+		return;
+	if ((h == hr+1) && (hl <= hr) && (hr <= hl+1))
+		return;
+	printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point);
+}
+
+/* check that all values stored in a tree are < key */
+static void avl_checkleft (struct fdb * tree, fdb_avl_key_t key)
+{
+	if (tree == avl_br_empty)
+		return;
+	avl_checkleft(tree->fdb_avl_left,key);
+	avl_checkleft(tree->fdb_avl_right,key);
+	if (tree->fdb_avl_key < key)
+		return;
+	printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->fdb_avl_key,key);
+}
+
+/* check that all values stored in a tree are > key */
+static void avl_checkright (struct fdb * tree, fdb_avl_key_t key)
+{
+	if (tree == avl_br_empty)
+		return;
+	avl_checkright(tree->fdb_avl_left,key);
+	avl_checkright(tree->fdb_avl_right,key);
+	if (tree->fdb_avl_key > key)
+		return;
+	printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->fdb_avl_key,key);
+}
+
+/* check that all values are properly increasing */
+static void avl_checkorder (struct fdb * tree)
+{
+	if (tree == avl_br_empty)
+		return;
+	avl_checkorder(tree->fdb_avl_left);
+	avl_checkorder(tree->fdb_avl_right);
+	avl_checkleft(tree->fdb_avl_left,tree->fdb_avl_key);
+	avl_checkright(tree->fdb_avl_right,tree->fdb_avl_key);
+}
+
+#endif /* (0) */
+#endif /* DEBUG_AVL */
+
+int
+addr_cmp(unsigned char a1[], unsigned char a2[])
+{
+	int i;
+
+	for (i=0; i<6; i++) {
+		if (a1[i] > a2[i]) return(1);
+		if (a1[i] < a2[i]) return(-1);
+	}
+	return(0);
+}
+
diff -u --recursive --new-file v1.3.85/linux/net/core/Makefile linux/net/core/Makefile
--- v1.3.85/linux/net/core/Makefile	Tue Apr  2 13:32:24 1996
+++ linux/net/core/Makefile	Tue Apr  9 14:36:31 1996
@@ -16,7 +16,7 @@
 O_OBJS	+= dev.o dev_mcast.o
 
 ifdef CONFIG_FIREWALL
-O_OBJS += firewall.o
+OX_OBJS += firewall.o
 endif
 
 ifdef CONFIG_NET_ALIAS
diff -u --recursive --new-file v1.3.85/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.85/linux/net/core/dev.c	Mon Apr  8 19:01:47 1996
+++ linux/net/core/dev.c	Tue Apr  9 14:55:42 1996
@@ -75,6 +75,7 @@
 #include <net/slhc.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <net/br.h>
 #ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
 #endif
@@ -216,15 +217,15 @@
 
 extern __inline__ void dev_load(const char *name)
 {
-        if(!dev_get(name)) {
+	if(!dev_get(name)) {
 #ifdef CONFIG_NET_ALIAS
-	        const char *sptr;
+		const char *sptr;
  
-                for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
-                if (!(*sptr && *(sptr+1)))
+		for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
+		if (!(*sptr && *(sptr+1)))
 #endif
-                        request_module(name);
-        }
+		request_module(name);
+	}
 }
 
 #endif
@@ -391,6 +392,20 @@
 	if (net_alias_is(dev))
 	  	skb->dev = dev = net_alias_main_dev(dev);
 #endif
+
+	/*
+	 *	If we are bridging and this is directly generated output
+	 *	pass the frame via the bridge.
+	 */
+
+#ifdef CONFIG_BRIDGE
+	if(skb->pkt_bridged!=IS_BRIDGED && br_stats.flags & BR_UP)
+	{
+		if(br_tx_frame(skb))
+			return;
+	}
+#endif
+
 	list = dev->buffs + pri;
 
 	save_flags(flags);
@@ -502,11 +517,7 @@
 	 *	hardware interrupt returns.
 	 */
 
-#ifdef CONFIG_NET_RUNONIRQ	/* Dont enable yet, needs some driver mods */
-	net_bh();
-#else
 	mark_bh(NET_BH);
-#endif
 	return;
 }
 
@@ -584,12 +595,29 @@
   		backlog_size--;
 		sti();
 		
-	       /*
-		*	Bump the pointer to the next structure.
-		*
-		*	On entry to the protocol layer. skb->data and
-		*	skb->h.raw point to the MAC and encapsulated data
-		*/
+
+#ifdef CONFIG_BRIDGE
+
+		/*
+		 *	If we are bridging then pass the frame up to the
+		 *	bridging code. If it is bridged then move on
+		 */
+		 
+		if (br_stats.flags & BR_UP)
+		{
+			cli();
+			if(br_receive_frame(skb))
+				continue;
+			sti();
+		}
+#endif
+		
+		/*
+	 	 *	Bump the pointer to the next structure.
+		 * 
+		 *	On entry to the protocol layer. skb->data and
+		 *	skb->h.raw point to the MAC and encapsulated data
+		 */
 
 		skb->h.raw = skb->data;
 
@@ -604,6 +632,7 @@
 		 * 	list. There are two lists. The ptype_all list of taps (normally empty)
 		 *	and the main protocol list which is hashed perfectly for normal protocols.
 		 */
+		
 		pt_prev = NULL;
 		for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
 		{
@@ -775,8 +804,8 @@
 
 	for (dev = dev_base; dev != NULL; dev = dev->next) 
 	{
-        	if(!(dev->flags & IFF_UP))	/* Downed devices don't count */
-	        	continue;
+		if(!(dev->flags & IFF_UP))	/* Downed devices don't count */
+			continue;
 		/*
 		 *	Have we run out of space here ?
 		 */
@@ -1005,7 +1034,7 @@
 				 */				
 
 				dev_mc_upload(dev);
-	        	}
+			}
 			break;
 		
 		case SIOCGIFADDR:	/* Get interface address (and family) */
@@ -1142,10 +1171,10 @@
 			if(ifr.ifr_mtu<68)
 				return -EINVAL;
 
-                        if (dev->change_mtu)
+			if (dev->change_mtu)
 				ret = (*dev->change_mtu)(dev, ifr.ifr_mtu);
-                        else
-                        {
+			else
+			{
 				dev->mtu = ifr.ifr_mtu;
 				ret = 0;
 			}
@@ -1319,15 +1348,23 @@
 	skb_queue_head_init(&backlog);
 	
 	/*
-	 * This is VeryUgly(tm).
+	 *	The bridge has to be up before the devices
+	 */
+
+#ifdef CONFIG_BRIDGE	 
+	br_init();
+#endif	
+	
+	/*
+	 * This is Very Ugly(tm).
 	 *
-	 * Some devices want to be initialized eary..
+	 * Some devices want to be initialized early..
 	 */
 #if defined(CONFIG_LANCE)
 	lance_init();
 #endif
 #if defined(CONFIG_NI65)
-        ni65_init();
+	ni65_init();
 #endif
 #if defined(CONFIG_PI)
 	pi_init();
@@ -1336,10 +1373,10 @@
 	pt_init();
 #endif
 #if defined(CONFIG_DLCI)
-        dlci_setup();
+	dlci_setup();
 #endif
 #if defined(CONFIG_SDLA)
-        sdla_setup();
+	sdla_setup();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
diff -u --recursive --new-file v1.3.85/linux/net/core/firewall.c linux/net/core/firewall.c
--- v1.3.85/linux/net/core/firewall.c	Mon Apr  8 19:01:47 1996
+++ linux/net/core/firewall.c	Tue Apr  9 14:36:31 1996
@@ -5,7 +5,8 @@
  *	Authors:	Dave Bonn (for IP)
  *	much hacked by:	Alan Cox
  */
- 
+
+#include <linux/module.h> 
 #include <linux/skbuff.h>
 #include <linux/firewall.h>
 
@@ -145,9 +146,20 @@
 	return firewall_policy[pf];
 }
 
+static struct symbol_table firewall_syms = {
+#include <linux/symtab_begin.h>
+	X(register_firewall),
+	X(unregister_firewall),
+	X(call_in_firewall),
+	X(call_out_firewall),
+	X(call_fw_firewall),
+#include <linux/symtab_end.h>
+};
+
 void fwchain_init(void)
 {
 	int i;
 	for(i=0;i<NPROTO;i++)
 		firewall_policy[i]=FW_ACCEPT;
+	register_symtab(&firewall_syms);
 }
diff -u --recursive --new-file v1.3.85/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.85/linux/net/core/skbuff.c	Fri Apr  5 13:35:28 1996
+++ linux/net/core/skbuff.c	Tue Apr  9 14:36:31 1996
@@ -679,6 +679,7 @@
 	skb->free = 2;	/* Invalid so we pick up forgetful users */
 	skb->lock = 0;
 	skb->pkt_type = PACKET_HOST;	/* Default type */
+	skb->pkt_bridged = 0;		/* Not bridged */
 	skb->prev = skb->next = skb->link3 = NULL;
 	skb->list = NULL;
 	skb->sk = NULL;
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.85/linux/net/ipv4/af_inet.c	Tue Apr  2 13:32:24 1996
+++ linux/net/ipv4/af_inet.c	Tue Apr  9 14:36:31 1996
@@ -96,6 +96,9 @@
 #ifdef CONFIG_IP_ALIAS
 #include <net/ip_alias.h>
 #endif
+#ifdef CONFIG_BRIDGE
+#include <net/br.h>
+#endif
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
@@ -1276,6 +1279,14 @@
 		case SIOCGIFSLAVE:
 			return(dev_ioctl(cmd,(void *) arg));
 
+		case SIOCGIFBR:
+		case SIOCSIFBR:
+#ifdef CONFIG_BRIDGE		
+			return(br_ioctl(cmd,(void *) arg));
+#else
+			return -ENOPKG;
+#endif						
+			
 		default:
 			if ((cmd >= SIOCDEVPRIVATE) &&
 			   (cmd <= (SIOCDEVPRIVATE + 15)))
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.85/linux/net/ipv4/icmp.c	Sat Mar 16 13:52:11 1996
+++ linux/net/ipv4/icmp.c	Tue Apr  9 14:36:31 1996
@@ -495,7 +495,9 @@
 	/*
 	 *	We are a router. Routers should not respond to ICMP_REDIRECT messages.
 	 */
-	printk(KERN_INFO "icmp: ICMP redirect from %s on %s ignored.\n", in_ntoa(source), dev->name);
+	printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %s, "
+	       "orig gw = %s, \"new\" gw = %s, device = %s.\n", in_ntoa(ip),
+		in_ntoa(source), in_ntoa(icmph->un.gateway), dev->name);
 #else	
 	switch(icmph->code & 7) 
 	{
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.85/linux/net/ipv4/ip_fw.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipv4/ip_fw.c	Tue Apr  9 14:36:31 1996
@@ -899,6 +899,44 @@
 			return(ETIMEDOUT);
 	}
 
+	if ( cmd == IP_FW_MASQ_TIMEOUTS )
+	{
+#ifdef CONFIG_IP_MASQUERADE
+		struct ip_fw_masq *masq;
+
+		if ( len != sizeof(struct ip_fw_masq) )
+		{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+			printk("ip_fw_ctl (masq): length %d, expected %d\n",
+				len, sizeof(struct ip_fw_masq));
+
+#endif
+			return( EINVAL );
+		}
+
+		masq = (struct ip_fw_masq *) m;
+
+		if (masq->tcp_timeout)
+		{
+			ip_masq_expire->tcp_timeout = masq->tcp_timeout;
+		}
+
+		if (masq->tcp_fin_timeout)
+		{
+			ip_masq_expire->tcp_fin_timeout = masq->tcp_fin_timeout;
+		}
+
+		if (masq->udp_timeout)
+		{
+			ip_masq_expire->udp_timeout = masq->udp_timeout;
+		}
+
+		return 0;
+#else
+		return( EINVAL );
+#endif
+	}
+
 /*
  *	Here we really working hard-adding new elements
  *	to blocking/forwarding chains or deleting 'em
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.85/linux/net/ipv4/ip_input.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipv4/ip_input.c	Tue Apr  9 14:36:31 1996
@@ -447,6 +447,15 @@
 				return 0;
 			skb->dev = dev;
 			iph=skb->h.iph;
+#ifdef CONFIG_IP_MASQUERADE
+			if (ip_fw_demasquerade(&skb,dev))
+			{
+				struct iphdr *iph=skb->h.iph;
+				if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr))
+					kfree_skb(skb, FREE_WRITE);
+				return 0;
+			}
+#endif
 		}
 
 		/*
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v1.3.85/linux/net/ipv4/ip_masq.c	Tue Apr  2 13:32:24 1996
+++ linux/net/ipv4/ip_masq.c	Tue Apr  9 14:36:31 1996
@@ -72,6 +72,7 @@
 	X(ip_masq_new),
         X(ip_masq_set_expire),
         X(ip_masq_free_ports),
+	X(ip_masq_expire),
 #include <linux/symtab_end.h>
 };
 
@@ -83,6 +84,18 @@
 struct ip_masq *ip_masq_s_tab[IP_MASQ_TAB_SIZE];
 
 /*
+ * timeouts
+ */
+
+static struct ip_fw_masq ip_masq_dummy = {
+	MASQUERADE_EXPIRE_TCP,
+	MASQUERADE_EXPIRE_TCP_FIN,
+	MASQUERADE_EXPIRE_UDP
+};
+
+struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy;
+
+/*
  *	Returns hash value
  */
 
@@ -409,7 +422,7 @@
 
 	if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
 		return;
- 
+
 	/*
 	 *	Now hunt the list to see if we have an old entry
 	 */
@@ -472,7 +485,7 @@
  	 
  	if (iph->protocol==IPPROTO_UDP) 
  	{
-                timeout = MASQUERADE_EXPIRE_UDP;
+                timeout = ip_masq_expire->udp_timeout;
  		recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
  	}
  	else 
@@ -485,10 +498,10 @@
  		 */
  		if (ms->flags & IP_MASQ_F_SAW_FIN || th->fin)
                 {
-                        timeout = MASQUERADE_EXPIRE_TCP_FIN;
+                        timeout = ip_masq_expire->tcp_fin_timeout;
  			ms->flags |= IP_MASQ_F_SAW_FIN;
  		}
- 		else timeout = MASQUERADE_EXPIRE_TCP;
+ 		else timeout = ip_masq_expire->tcp_timeout;
  
 		skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0);
  		tcp_send_check(th,iph->saddr,iph->daddr,size,skb);
@@ -516,10 +529,22 @@
  	struct iphdr	*iph = skb->h.iph;
  	__u16	*portptr;
  	struct ip_masq	*ms;
+	unsigned short  frag;
  
  	if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
  		return 0;
  
+	/*
+   	 * Toss fragments, since we handle them in ip_rcv()
+	 */
+
+	frag = ntohs(iph->frag_off);
+
+	if ((frag & IP_MF) != 0 || (frag & IP_OFFSET) != 0)
+	{
+		return 0;
+	}
+
  	portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
  	if (ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
  	    ntohs(portptr[1]) > PORT_MASQ_END)
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_masq_app.c linux/net/ipv4/ip_masq_app.c
--- v1.3.85/linux/net/ipv4/ip_masq_app.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_masq_app.c	Tue Apr  9 14:36:31 1996
@@ -487,14 +487,26 @@
 
 static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
 {
-        int diff, o_offset;
+        int maxsize, diff, o_offset;
         struct sk_buff *n_skb;
 
+	maxsize = skb->truesize - sizeof(struct sk_buff);
+
         diff = n_len - o_len;
         o_offset = o_buf - (char*) skb->data;
 
-        if (diff != 0)  {
-                
+	if (maxsize <= n_len) {
+	    if (diff != 0) {
+		memcpy(skb->data + o_offset + n_len,o_buf + o_len,
+		       skb->len - (o_offset + o_len));
+	    }
+
+	    memcpy(skb->data + o_offset, n_buf, n_len);
+
+	    n_skb    = skb;
+	    skb->len = n_len;
+	    skb->end = skb->head+n_len;
+	} else {
                 /*
                  * 	Sizes differ, make a copy
                  */
@@ -526,14 +538,6 @@
                  */
                 
                 kfree_skb(skb, FREE_WRITE);
-                
-        } else {
-                
-                /*
-                 *	Same len, just copy
-                 */
-                n_skb = skb;
-                memcpy(n_skb->data + o_offset, n_buf, n_len);
         }
         return n_skb;
 }
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_masq_ftp.c linux/net/ipv4/ip_masq_ftp.c
--- v1.3.85/linux/net/ipv4/ip_masq_ftp.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_masq_ftp.c	Tue Apr  9 14:36:31 1996
@@ -112,7 +112,7 @@
 		if (n_ms==NULL)
 			return 0;
 
-                ip_masq_set_expire(n_ms, MASQUERADE_EXPIRE_TCP_FIN);
+                ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout);
                 
 		/*
 		 * Replace the old PORT with the new one
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_masq_irc.c linux/net/ipv4/ip_masq_irc.c
--- v1.3.85/linux/net/ipv4/ip_masq_irc.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_masq_irc.c	Tue Apr  9 14:36:32 1996
@@ -173,7 +173,7 @@
 		if (n_ms==NULL)
 			return 0;
 
-                ip_masq_set_expire(n_ms, MASQUERADE_EXPIRE_TCP_FIN);
+                ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout);
                 
 		/*
 		 * Replace the old "address port" with the new one
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v1.3.85/linux/net/ipv4/ip_options.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipv4/ip_options.c	Mon Apr  8 19:28:14 1996
@@ -210,7 +210,7 @@
 			continue;
 		}
 		optlen = optptr[1];
-		if (l<2 || optlen>l)
+		if (optlen<2 || optlen>l)
 		  return;
 		if (!(*optptr & 0x80))
 			memset(optptr, IPOPT_NOOP, optlen);
@@ -274,7 +274,7 @@
 			continue;
 		}
 		optlen = optptr[1];
-		if (l<2 || optlen>l || !optlen)
+		if (optlen<2 || optlen>l)
 		{
 			pp_ptr = optptr;
 			break;
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.85/linux/net/ipv4/ip_output.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipv4/ip_output.c	Tue Apr  9 14:36:32 1996
@@ -21,6 +21,8 @@
  *	Fixes:
  *		Alan Cox	:	Missing nonblock feature in ip_build_xmit.
  *		Mike Kilburn	:	htons() missing in ip_build_xmit.
+ *		Bradford Johnson:	Fix faulty handling of some frames when 
+ *					no route is found.
  */
 
 #include <asm/segment.h>
@@ -168,7 +170,7 @@
 #endif
 			skb->arp = 0;
 			skb->raddr = daddr;
-			return -dev->hard_header_len;
+			return dev->hard_header_len;
 		}
 		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
 		if (mac < 0)
@@ -265,7 +267,7 @@
 
 	skb->dev = *dev;
 	skb->saddr = saddr;
-
+	
 	/*
 	 *	Now build the IP header.
 	 */
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.85/linux/net/ipv4/ip_sockglue.c	Thu Feb  8 12:51:25 1996
+++ linux/net/ipv4/ip_sockglue.c	Tue Apr  9 14:36:32 1996
@@ -387,6 +387,7 @@
 		case IP_FW_POLICY_IN:
 		case IP_FW_POLICY_OUT:
 		case IP_FW_POLICY_FWD:
+		case IP_FW_MASQ_TIMEOUTS:
 			if(!suser())
 				return -EPERM;
 			if(optlen>sizeof(tmp_fw) || optlen<1)
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.85/linux/net/ipv4/tcp.c	Tue Apr  2 13:32:24 1996
+++ linux/net/ipv4/tcp.c	Wed Apr 10 15:58:35 1996
@@ -1205,18 +1205,13 @@
 	
 
 /*
- *	Send an ack if one is backlogged at this point. Ought to merge
- *	this with tcp_send_ack().
+ *	Send an ack if one is backlogged at this point. 
+ *
  *      This is called for delayed acks also.
  */
  
 void tcp_read_wakeup(struct sock *sk)
 {
-	int tmp;
-	struct device *dev = NULL;
-	struct tcphdr *t1;
-	struct sk_buff *buff;
-
 	if (!sk->ack_backlog) 
 		return;
 
@@ -1227,57 +1222,7 @@
 	if ((sk->state == TCP_CLOSE) || (sk->state == TCP_TIME_WAIT))
 		return; 
 
-	/*
-	 * FIXME: we need to put code here to prevent this routine from
-	 * being called.  Being called once in a while is ok, so only check
-	 * if this is the second time in a row.
- 	 */
-
-	/*
-	 * We need to grab some memory, and put together an ack,
-	 * and then put it into the queue to be sent.
-	 */
-
-	buff = sock_wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
-	if (buff == NULL) 
-	{
-		/* Try again real soon. */
-		tcp_reset_xmit_timer(sk, TIME_WRITE, HZ);
-		return;
- 	}
-
-	buff->sk = sk;
-	buff->localroute = sk->localroute;
-	buff->csum = 0;
-	
-	/*
-	 *	Put in the IP header and routing stuff. 
-	 */
-
-	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-			       IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
-	if (tmp < 0) 
-	{
-  		buff->free = 1;
-		sock_wfree(sk, buff);
-		return;
-	}
-
-	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
-
-	memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
-	t1->seq = htonl(sk->sent_seq);
-
-	sk->ack_backlog = 0;
-	sk->bytes_rcv = 0;
-
-	sk->window = tcp_select_window(sk);
-	t1->window = htons(sk->window);
-	t1->ack_seq = htonl(sk->acked_seq);
-	t1->doff = sizeof(*t1)/4;
-	tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), buff);
-	sk->prot->queue_xmit(sk, dev, buff, 1);
-	tcp_statistics.TcpOutSegs++;
+	tcp_send_ack(sk);
 }
 
 
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.85/linux/net/ipv4/tcp_input.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipv4/tcp_input.c	Wed Apr 10 16:06:15 1996
@@ -184,7 +184,7 @@
 	 *
 	 *	We also should be spotting triple bad sequences.
 	 */
-	tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
+	tcp_send_ack(sk);
 	return;
 }
 
@@ -733,7 +733,7 @@
 		 *	Was it a usable window open ?
 		 */
 		 
-  		if (skb_peek(&sk->write_queue) != NULL &&   /* should always be non-null */
+  		if (!skb_queue_empty(&sk->write_queue) &&   /* should always be true */
 		    ! before (sk->window_seq, sk->write_queue.next->end_seq)) 
 		{
 			sk->backoff = 0;
@@ -1204,8 +1204,7 @@
 	return skb->end_seq;
 }	
 
-static void tcp_queue(struct sk_buff * skb, struct sock * sk,
-	struct tcphdr *th, unsigned long saddr)
+static void tcp_queue(struct sk_buff * skb, struct sock * sk, struct tcphdr *th)
 {
 	u32 ack_seq;
 
@@ -1249,7 +1248,7 @@
 		 * side, so we only have to worry about the first two.
 		 */
 		if (!sk->delay_acks || th->fin) {
-			tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
+			tcp_send_ack(sk);
 		}
 		else
 		{
@@ -1299,7 +1298,7 @@
 		 *	(someone sent us dataless, boring frame)
 		 */
 		if (!th->ack)
-			tcp_send_ack(sk->sent_seq, sk->acked_seq,sk, th, saddr);
+			tcp_send_ack(sk);
 		kfree_skb(skb, FREE_READ);
 		return(0);
 	}
@@ -1361,7 +1360,7 @@
 
 #endif
 
-	tcp_queue(skb, sk, th, saddr);
+	tcp_queue(skb, sk, th);
 
 	/*
 	 *	If we've missed a packet, send an ack.
@@ -1370,7 +1369,7 @@
 	 
 	if (!skb->acked) 
 	{
-		tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
+		tcp_send_ack(sk);
 		sk->ack_backlog++;
 		tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, HZ/2));
 	}
@@ -1715,7 +1714,7 @@
 				sk->acked_seq = skb->seq+1;
 				sk->lastwin_seq = skb->seq+1;
 				sk->fin_seq = skb->seq;
-				tcp_send_ack(sk->sent_seq,sk->acked_seq,sk,th,sk->daddr);
+				tcp_send_ack(sk);
 				tcp_set_state(sk, TCP_ESTABLISHED);
 				tcp_options(sk,th);
 				sk->dummy_th.dest=th->source;
diff -u --recursive --new-file v1.3.85/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v1.3.85/linux/net/ipv4/tcp_output.c	Wed Mar 20 14:11:30 1996
+++ linux/net/ipv4/tcp_output.c	Wed Apr 10 15:37:06 1996
@@ -750,9 +750,7 @@
  *	This routine sends an ack and also updates the window. 
  */
  
-void tcp_send_ack(u32 sequence, u32 ack,
-	     struct sock *sk,
-	     struct tcphdr *th, u32 daddr)
+void tcp_send_ack(struct sock *sk)
 {
 	struct sk_buff *buff;
 	struct tcphdr *t1;
@@ -763,6 +761,26 @@
 		return;		/* We have been reset, we may not send again */
 		
 	/*
+	 *	If we have nothing queued for transmit and the transmit timer
+	 *	is on we are just doing an ACK timeout and need to switch
+	 *	to a keepalive.
+	 */
+	 
+	sk->ack_backlog = 0;
+	sk->bytes_rcv = 0;
+	sk->ack_timed = 0;
+
+	if (sk->send_head == NULL
+	    && skb_queue_empty(&sk->write_queue)
+	    && sk->ip_xmit_timeout == TIME_WRITE)
+	{
+		if(sk->keepopen)
+			tcp_reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
+		else
+			delete_timer(sk);
+	}
+
+	/*
 	 * We need to grab some memory, and put together an ack,
 	 * and then put it into the queue to be sent.
 	 */
@@ -797,7 +815,7 @@
 	 *	Put in the IP header and routing stuff. 
 	 */
 	 
-	tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
+	tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
 				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
 	if (tmp < 0) 
 	{
@@ -807,45 +825,20 @@
 	}
 	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
 
-	memcpy(t1, &sk->dummy_th, sizeof(*t1));
-
-	/*
-	 *	Swap the send and the receive. 
-	 */
-	 
-	t1->dest = th->source;
-	t1->source = th->dest;
-	t1->seq = ntohl(sequence);
-	sk->window = tcp_select_window(sk);
-	t1->window = ntohs(sk->window);
-	
-	/*
-	 *	If we have nothing queued for transmit and the transmit timer
-	 *	is on we are just doing an ACK timeout and need to switch
-	 *	to a keepalive.
-	 */
-	 
-	if (ack == sk->acked_seq) {	       	  
-		sk->ack_backlog = 0;
-		sk->bytes_rcv = 0;
-		sk->ack_timed = 0;
-
-		if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL
-		    && sk->ip_xmit_timeout == TIME_WRITE) 	
-		  if(sk->keepopen) 
-		    tcp_reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);
-		  else 
-		    delete_timer(sk);	         		
-	}
-
   	/*
   	 *	Fill in the packet and send it
   	 */
   	 
-  	t1->ack_seq = htonl(ack);
-  	tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), buff);
+	sk->window = tcp_select_window(sk);
+
+	memcpy(t1, &sk->dummy_th, sizeof(*t1));
+	t1->seq     = htonl(sk->sent_seq);
+  	t1->ack_seq = htonl(sk->acked_seq);
+	t1->window  = htons(sk->window);
+
+  	tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), buff);
   	if (sk->debug)
-  		 printk("\rtcp_ack: seq %x ack %x\n", sequence, ack);
+  		 printk("\rtcp_ack: seq %x ack %x\n", sk->sent_seq, sk->acked_seq);
   	sk->prot->queue_xmit(sk, dev, buff, 1);
   	tcp_statistics.TcpOutSegs++;
 }
diff -u --recursive --new-file v1.3.85/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.85/linux/net/ipx/af_ipx.c	Mon Apr  8 19:01:47 1996
+++ linux/net/ipx/af_ipx.c	Tue Apr  9 14:36:32 1996
@@ -2201,11 +2201,11 @@
 				return -EPERM;
 			return(ipxrtr_ioctl(cmd,(void *)arg));
 		case SIOCSIFADDR:
-		case SIOCGIFADDR:
 		case SIOCAIPXITFCRT:
 		case SIOCAIPXPRISLT:
 			if(!suser())
 				return -EPERM;
+		case SIOCGIFADDR:
 			return(ipxitf_ioctl(cmd,(void *)arg));
 		case SIOCIPXCFGDATA: 
 		{
@@ -2378,6 +2378,9 @@
 
 	unregister_snap_client(ipx_snap_id);
 	pSNAP_datalink = NULL;
+
+	unregister_8022tr_client(ipx_8022_type);
+	p8022tr_datalink = NULL;
 
 	unregister_8022_client(ipx_8022_type);
 	p8022_datalink = NULL;
diff -u --recursive --new-file v1.3.85/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.85/linux/net/netrom/af_netrom.c	Tue Apr  2 13:32:24 1996
+++ linux/net/netrom/af_netrom.c	Tue Apr  9 14:36:32 1996
@@ -432,25 +432,11 @@
 	sk->socket        = sock;
 	sk->type          = sock->type;
 	sk->protocol      = protocol;
-	sk->dead          = 0;
-	sk->next          = NULL;
-	sk->broadcast     = 0;
 	sk->allocation	  = GFP_KERNEL;
 	sk->rcvbuf        = SK_RMEM_MAX;
 	sk->sndbuf        = SK_WMEM_MAX;
-	sk->wmem_alloc    = 0;
-	sk->rmem_alloc    = 0;
-	sk->users         = 0;
-	sk->debug         = 0;
-	sk->destroy       = 0;
-	sk->prot          = NULL;	/* So we use default free mechanisms */
-	sk->err           = 0;
-	sk->localroute    = 0;
-	sk->send_head     = NULL;
 	sk->state         = TCP_CLOSE;
-	sk->shutdown      = 0;
 	sk->priority      = SOPRI_NORMAL;
-	sk->ack_backlog   = 0;
 	sk->mtu           = NETROM_MTU;	/* 236 */
 	sk->zapped        = 1;
 	sk->window	  = nr_default.window;
@@ -532,26 +518,13 @@
 
 	sk->type        = osk->type;
 	sk->socket      = osk->socket;
-	sk->dead        = 0;
-	sk->next        = NULL;
 	sk->priority    = osk->priority;
-	sk->broadcast   = 0;
 	sk->protocol    = osk->protocol;
 	sk->rcvbuf      = osk->rcvbuf;
 	sk->sndbuf      = osk->sndbuf;
-	sk->wmem_alloc  = 0;
-	sk->rmem_alloc  = 0;
-	sk->users       = 0;
-	sk->ack_backlog = 0;
-	sk->destroy     = 0;
-	sk->prot        = NULL;	/* So we use default free mechanisms */
-	sk->err         = 0;
-	sk->localroute  = 0;
-	sk->send_head   = NULL;
 	sk->debug       = osk->debug;
 	sk->state       = TCP_ESTABLISHED;
 	sk->window      = osk->window;
-	sk->shutdown    = 0;
 	sk->mtu         = osk->mtu;
 	sk->sleep       = osk->sleep;
 	sk->zapped      = osk->zapped;
diff -u --recursive --new-file v1.3.85/linux/net/netsyms.c linux/net/netsyms.c
--- v1.3.85/linux/net/netsyms.c	Mon Apr  8 19:01:47 1996
+++ linux/net/netsyms.c	Tue Apr  9 14:37:19 1996
@@ -11,7 +11,6 @@
 #include <linux/in.h>
 #include <linux/net.h>
 #include <linux/netdevice.h>
-#include <linux/firewall.h>
 #include <linux/trdevice.h>
 #include <linux/ioport.h>
 
@@ -81,12 +80,6 @@
 	X(destroy_EII_client),
 #endif
 
-#ifdef CONFIG_FIREWALL
-	/* Firewall registration */
-	X(register_firewall),
-	X(unregister_firewall),
-#endif
-
 #ifdef CONFIG_INET
 	/* Internet layer registration */
 	X(inet_add_protocol),
@@ -148,6 +141,7 @@
 	X(ether_setup),
 	X(eth_type_trans),
 	X(eth_copy_and_sum),
+	X(arp_query),
 	X(alloc_skb),
 	X(kfree_skb),
 	X(skb_clone),
@@ -169,11 +163,6 @@
 	X(tty_register_ldisc),
 	X(kill_fasync),
 	X(arp_query),
-#ifdef CONFIG_FIREWALL
-	X(call_in_firewall),
-	X(call_out_firewall),
-	X(call_fw_firewall),
-#endif
 #endif  /* CONFIG_INET */
 
 #include <linux/symtab_end.h>
diff -u --recursive --new-file v1.3.85/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.85/linux/net/unix/af_unix.c	Tue Apr  2 13:32:25 1996
+++ linux/net/unix/af_unix.c	Tue Apr  9 14:36:32 1996
@@ -309,28 +309,11 @@
 	sk->protinfo.af_unix.inode=NULL;
 	sk->protinfo.af_unix.locks=1;		/* Us */
 	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
-	sk->protinfo.af_unix.name=NULL;
-	sk->protinfo.af_unix.other=NULL;
-	sk->protocol=0;
-	sk->rmem_alloc=0;
-	sk->wmem_alloc=0;
-	sk->dead=0;
-	sk->next=NULL;
-	sk->broadcast=0;
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
 	sk->allocation=GFP_KERNEL;
-	sk->users=0;
-	sk->bsdism=0;
-	sk->debug=0;
-	sk->prot=NULL;
-	sk->err=0;
-	sk->localroute=0;
-	sk->send_head=NULL;
 	sk->state=TCP_CLOSE;
 	sk->priority=SOPRI_NORMAL;
-	sk->ack_backlog=0;
-	sk->shutdown=0;
 	sk->state_change=def_callback1;
 	sk->data_ready=def_callback2;
 	sk->write_space=def_callback3;
@@ -339,7 +322,6 @@
 	sk->socket=sock;
 	sock->data=(void *)sk;
 	sk->sleep=sock->wait;
-	sk->zapped=0;
 	unix_insert_socket(sk);
 	return 0;
 }
diff -u --recursive --new-file v1.3.85/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
--- v1.3.85/linux/scripts/ksymoops.cc	Tue Feb 20 12:38:52 1996
+++ linux/scripts/ksymoops.cc	Tue Apr  9 07:07:40 1996
@@ -95,7 +95,7 @@
   private:
     // Caution: Fixed Allocation!
     // This should suffice for awhile since 1.1.86 has only 2482 symbols.
-    KSym ksyms_0_[4096];
+    KSym ksyms_0_[8000];
     int cardinality_;
 
   public:
diff -u --recursive --new-file v1.3.85/linux/scripts/tkparse.c linux/scripts/tkparse.c
--- v1.3.85/linux/scripts/tkparse.c	Sun Mar 17 09:58:21 1996
+++ linux/scripts/tkparse.c	Wed Apr 10 08:41:08 1996
@@ -135,7 +135,7 @@
 	pnt++;
 	while(*pnt && *pnt != '`') *pnt1++ = *pnt++;
 	*pnt1++ = '\0';
-	cpnt->variable = strdup(varname);
+	cpnt->variable.str = strdup(varname);
 	if( *pnt == '`' ) pnt++;
 	if( *pnt == '"' ) pnt++;
 	continue;
@@ -147,7 +147,7 @@
 	pnt++;
 	while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
 	*pnt1++ = '\0';
-	cpnt->variable = strdup(varname);
+	cpnt->variable.str = strdup(varname);
 	if( *pnt == '"' ) pnt++;
 	continue;
       }
@@ -156,7 +156,7 @@
     pnt1 = varname;
     while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
     *pnt1++ = '\0';
-    cpnt->variable = strdup(varname);
+    cpnt->variable.str = strdup(varname);
     if( *pnt == '"' ) pnt++;
     continue;
   }
