]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.42 1.3.42
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:22 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:22 +0000 (15:10 -0500)
119 files changed:
CREDITS
Documentation/Configure.help
Documentation/SMP.txt
Documentation/networking/net-modules.txt
Makefile
arch/alpha/kernel/irq.c
arch/i386/Makefile
arch/i386/boot/Makefile
arch/i386/boot/bootsect.S
arch/i386/boot/compressed/Makefile
arch/i386/boot/setup.S
arch/i386/defconfig
arch/i386/kernel/Makefile
arch/i386/kernel/bios32.c
arch/i386/kernel/setup.c
drivers/block/floppy.c
drivers/block/ll_rw_blk.c
drivers/cdrom/mcd.c
drivers/cdrom/mcdx.c
drivers/char/cyclades.c
drivers/net/3c503.c
drivers/net/8390.c
drivers/net/8390.h
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/ac3200.c
drivers/net/e2100.c
drivers/net/hp-plus.c
drivers/net/hp.c
drivers/net/ne.c
drivers/net/net_init.c
drivers/net/smc-ultra.c
drivers/net/tunnel.c
drivers/net/wd.c
drivers/pci/pci.c
drivers/scsi/53c7,8xx.c
drivers/scsi/BusLogic.c [new file with mode: 0644]
drivers/scsi/BusLogic.h [new file with mode: 0644]
drivers/scsi/ChangeLog
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/README.BusLogic [new file with mode: 0644]
drivers/scsi/buslogic.c [deleted file]
drivers/scsi/buslogic.h [deleted file]
drivers/scsi/hosts.c
drivers/scsi/scsi_syms.c
drivers/scsi/seagate.c
drivers/scsi/seagate.h
fs/Config.in
fs/binfmt_elf.c
fs/buffer.c
fs/dcache.c
fs/exec.c
fs/ext/inode.c
fs/fcntl.c
fs/file_table.c
fs/filesystems.c
fs/inode.c
fs/nfs/Makefile
fs/nfs/nfsroot.c [new file with mode: 0644]
fs/nfs/proc.c
fs/super.c
include/asm-i386/ioctl.h
include/asm-i386/smp.h
include/linux/bios32.h
include/linux/cyclades.h
include/linux/etherdevice.h
include/linux/fs.h
include/linux/if_arp.h
include/linux/if_ppp.h
include/linux/ioport.h
include/linux/ip_fw.h
include/linux/major.h
include/linux/mm.h
include/linux/netdevice.h
include/linux/nfs_fs.h
include/linux/nfs_mount.h
include/linux/pci.h
include/linux/proc_fs.h
include/linux/sockios.h
include/linux/tcp.h
include/net/arp.h
include/net/ip.h
include/net/netlink.h
include/net/route.h
include/net/sock.h
init/main.c
kernel/exit.c
kernel/fork.c
kernel/ksyms.c
kernel/signal.c
mm/mmap.c
net/Changes
net/ax25/af_ax25.c
net/core/dev.c
net/ethernet/eth.c
net/ipv4/Config.in
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/devinet.c
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/ip_forward.c
net/ipv4/ip_fw.c
net/ipv4/ip_options.c
net/ipv4/ip_output.c
net/ipv4/ip_sockglue.c
net/ipv4/rarp.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/timer.c
net/ipv4/udp.c
net/ipx/af_ipx.c
net/netlink.c
net/netrom/nr_dev.c
net/netrom/nr_route.c
net/socket.c

diff --git a/CREDITS b/CREDITS
index 216eea069a7719bbb271f5bd1a9b4dfce84ee388..b3fa273ec86eb25ab190c95d77684100788cd4a1 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -336,11 +336,11 @@ S: Laval, Quebec
 S: Canada H7Y 1V9
 
 N: David Gentzel
-E: gentzel@nova.enet.dec.com
-D: BusLogic driver and original UltraStor driver
+E: gentzel@telerama.lm.com
+D: Original BusLogic driver and original UltraStor driver
 S: Whitfield Software Services
-S: 631 Idlewood Avenue
-S: Carnegie, Pennsylvania 15106-1126
+S: 600 North Bell Avenue, Suite 160
+S: Carnegie, Pennsylvania 15106-4304
 S: USA
 
 N: Philip Gladstone
@@ -1077,7 +1077,8 @@ S: Germany
 
 N: Leonard N. Zubkoff
 E: lnz@dandelion.com
-D: XFree86 and BusLogic driver additions
+D: BusLogic SCSI driver
+D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
 S: USA
index 10682ed64506062d3a4bc71146fc1871867b75d9..f872fbba2533b01fb0bed3e53290eb735b175f84 100644 (file)
@@ -748,14 +748,11 @@ CONFIG_SCSI_AIC7XXX
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
-  This is support for the BusLogic family of SCSI host adaptors.  If
-  it doesn't work out of the box, you may have to change some settings
-  in drivers/scsi/buslogic.h. Please read the SCSI-HOWTO, available
-  via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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.
+  This is support for BusLogic MultiMaster SCSI Host Adaptors.  Consult
+  the documentation in drivers/scsi/README.BusLogic for more information.
+  BusLogic FlashPoint SCSI Host Adapters are not supported by this driver.
+  If this driver does not work correctly without modification, please
+  consult the author.  This driver is not currently available as a module.
  
 EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support
 CONFIG_SCSI_EATA_DMA
index 6b537da8bd28e5b08578527f6399fabfe3c7202d..1bb9a49e00912af5a4a1b47b90244af4834dfb4c 100644 (file)
@@ -3,9 +3,7 @@ SMP support for Linux with up to 32 processors using the Intel MP
 specification. 
 
 WARNING:
-       This is experimental. Back up your disks first. Build only
-with gcc2.5.8.
-
+       This is experimental. Back up your disks first.
 
 To fix:
 
@@ -27,4 +25,4 @@ o     Distribute irq's (locking present just needs the 82489 to be asked
        nicely).
 o      486 startup code.
 o      How to handle mixed FPU/non FPU processors.
-o      Support 4Mb page mode again
+o      Support 4Mb page mode again     [TESTING]
index 4961083587027154a33813326fd85ea6c312a608..5f0e5930321bb8ceee1078a3981cf7afbc171db6 100644 (file)
@@ -38,14 +38,54 @@ Wed 2-Aug-95  <matti.aarnio@utu.fi>
        --------------------------
 
 
+       8390 based Network Modules              (Paul Gortmaker, Nov 12, 1995)
+       --------------------------
+
+(Includes: smc-ultra, ne, wd, 3c503, hp, hp-plus, e2100 and ac3200)
+
+The 8390 series of network drivers now support multiple card systems without 
+reloading the same module multiple times (memory efficient!) This is done by 
+specifying multiple comma separated values, such as:
+
+       insmod 3c503.o io=0x280,0x300,0x330,0x350  xcvr=0,1,0,1
+
+The above would have the one module controlling four 3c503 cards, with card 2
+and 4 using external transcievers. The "insmod" manual describes the usage
+of comma separated value lists.
+
+It is *STRONGLY RECOMMENDED* that you supply "io=" instead of autoprobing.
+If an "io=" argument is not supplied, then the ISA drivers will complain
+about autoprobing being not recommended, and begrudgingly autoprobe for
+a *SINGLE CARD ONLY* -- if you want to use multiple cards you *have* to 
+supply an "io=0xNNN,0xQQQ,..." argument.
+
+The ne module is an exception to the above. A NE2000 is essentially an
+8390 chip, some bus glue and some RAM. Because of this, the ne probe is
+more invasive than the rest, and so at boot we make sure the ne probe is 
+done last of all the 8390 cards (so that it won't trip over other 8390 based
+cards) With modules we can't ensure that all other non-ne 8390 cards have
+already been found. Because of this, the ne module REQUIRES an "io=0xNNN" 
+argument passed in via insmod. It will refuse to autoprobe.
+
+It is also worth noting that auto-IRQ probably isn't as reliable during 
+the flurry of interrupt activity on a running machine. Cards such as the 
+ne2000 that can't get the IRQ setting from an EEPROM or configuration
+register are probably best supplied with an "irq=M" argument as well.
+
+
+----------------------------------------------------------------------
+Card/Module List - Configurable Parameters and Default Values
+----------------------------------------------------------------------
+
 3c501.c:
        io  = 0x280     IO base address
        irq = 5         IRQ
        (Probes ports:  0x280, 0x300)
 
 3c503.c:
-       io = 0x300
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ software selected by driver using autoIRQ)
+       xcvr = 0        (Use xcvr=1 to select external transceiver.)
        (Probes ports: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2A0, 0x2E0)
 
 3c505.c:
@@ -69,8 +109,8 @@ Wed 2-Aug-95  <matti.aarnio@utu.fi>
        (No public options, several other modules need this one)
 
 ac3200.c:
-       io = 0
-       irq = 0
+       io = 0          (Checks 0x1000 to 0x8fff in 0x1000 intervals)
+       irq = 0         (Read from config register)
        (EISA probing..)
 
 apricot.c:
@@ -101,14 +141,10 @@ atp.c: *Not modularized*
        (Probes ports: 0x378, 0x278, 0x3BC;
         fixed IRQs: 5 and 7                    )
 
-
-auto_irq.c: *Static kernel component*
-
-
 de4x5.c:
        io = 0x000b
        irq = 10
-       is_not_dec = 0  -- For SMC card using DEC 21140 set this to 1
+       is_not_dec = 0  -- For non-DEC card using DEC 21040/21041/21140 chip, set this to 1
        (EISA, and PCI probing)
 
 de600.c:
@@ -130,8 +166,10 @@ dummy.c:
        No options
 
 e2100.c:
-       io = 0x300
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ software selected by driver)
+       mem = 0         (Override default shared memory start of 0xd0000)
+       xcvr = 0        (Use xcvr=1 to select external transceiver.)
        (Probes ports: 0x300, 0x280, 0x380, 0x220)
 
 eepro.c:
@@ -158,13 +196,13 @@ ewrk3.c:
                0x300,        0x340, 0x360, 0x380, 0x3A0, 0x3C0)
 
 hp-plus.c:
-       io = 0x200
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ read from configuration register)
        (Probes ports: 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340)
 
 hp.c:
-       io = 0x300
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ software selected by driver using autoIRQ)
        (Probes ports: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240)
 
 hp100.c:
@@ -184,8 +222,8 @@ lance.c: *Not modularized*
 loopback.c: *Static kernel component*
 
 ne.c:
-       io = 0x300
-       irq = 0
+       io = 0          (Explicitly *requires* an "io=0xNNN" value)
+       irq = 0         (Tries to determine configured IRQ via autoIRQ)
        (Probes ports: 0x300, 0x280, 0x320, 0x340, 0x360)
 
 net_init.c: *Static kernel component*
@@ -225,8 +263,8 @@ slip.c:
 
 
 smc-ultra.c:
-       io = 0x200
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ val. read from EEPROM)
        (Probes ports:  0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380)
 
 tulip.c: *Partial modularization*
@@ -240,11 +278,12 @@ wavelan.c:
        irq = 0         (Not honoured, if changed..)
 
 wd.c:
-       io = 0x300
-       irq = 0
+       io = 0          (It will complain if you don't supply an "io=0xNNN")
+       irq = 0         (IRQ val. read from EEPROM, ancient cards use autoIRQ)
        mem = 0         (Force shared-memory on address 0xC8000, or whatever..)
-       (Probes ports:  0x300, 0x280, 0x380, 0x240,
-        uses AUTOIRQ)
+       mem_end = 0     (Force non-std. mem. size via supplying mem_end val.)
+                       (eg. for 32k WD8003EBT, use mem=0xd0000 mem_end=0xd8000)
+       (Probes ports:  0x300, 0x280, 0x380, 0x240)
 
 znet.c: *Not modularized*
        (Only one device on  Zenith Z-Note (notebook?) systems,
index 381b7e207778a5ce84941cdf9087a43e1bf23d71..3413150b37d5d7b219d0856ff3bebca8ebb4c08a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 41
+SUBLEVEL = 42
 
 ARCH = i386
 
@@ -9,8 +9,7 @@ ARCH = i386
 # because it makes re-config very ugly and too many fundamental files depend
 # on "CONFIG_SMP"
 #
-# NOTE! SMP is experimental, and gcc-2.5.8 is recommended. See the file
-# Documentation/SMP.
+# NOTE! SMP is experimental. See the file Documentation/SMP.txt
 #
 # SMP = 1
 
@@ -61,6 +60,15 @@ endif
 
 ROOT_DEV = CURRENT
 
+#
+# NFS_ROOT_NAME specifies the default name of the directory to mount
+# as root via NFS, if the kernel does not get the "root=" option from
+# the boot loader. The "%s" will be replaced by the IP-number of the
+# local system.
+#
+
+NFS_ROOT = -DNFS_ROOT="\"/tftpboot/%s\""
+
 #
 # INSTALL_PATH specifies where to place the updated kernel and system map
 # images.  Uncomment if you want to place them anywhere other than root.
@@ -126,11 +134,22 @@ endif
 
 include arch/$(ARCH)/Makefile
 
+ifdef SMP
+
+.S.s:
+       $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -E -o $*.s $<
+.S.o:
+       $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c -o $*.o $<
+
+else
+
 .S.s:
        $(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $<
 .S.o:
        $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
+endif
+
 Version: dummy
        @rm -f include/linux/compile.h
 
@@ -200,7 +219,7 @@ init/version.o: init/version.c include/linux/compile.h
        $(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
 
 init/main.o: init/main.c
-       $(CC) $(CFLAGS) $(PROFILING) -c -o $*.o $<
+       $(CC) $(CFLAGS) $(PROFILING) $(NFS_ROOT) -c -o $*.o $<
 
 fs: dummy
        $(MAKE) linuxsubdirs SUBDIRS=fs
index 6c797791a710d51e272baf2afd39d118c18529ae..e8f8302287d975e81bcda656b60a8804d42dde9f 100644 (file)
@@ -352,8 +352,39 @@ static inline void device_interrupt(int irq, int ack, struct pt_regs * regs)
 static inline void isa_device_interrupt(unsigned long vector,
                                        struct pt_regs * regs)
 {
-       unsigned long pic;
+#if defined(CONFIG_ALPHA_APECS)
+#      define IACK_SC  APECS_IACK_SC
+#elif defined(CONFIG_ALPHA_LCA)
+#      define IACK_SC  LCA_IACK_SC
+#endif
        int j;
+
+       /*
+        * Generate a PCI interrupt acknowledge cycle.  The PIC will
+        * respond with the interrupt vector of the highest priority
+        * interrupt that is pending.  The PALcode sets up the
+        * interrupts vectors such that irq level L generates vector
+        * L.
+        */
+       j = *(volatile int *) IACK_SC;
+       j &= 0xff;
+       if (j == 7) {
+           if (!(inb(0x20) & 0x80)) {
+               /* it's only a passive release... */
+               return;
+           }
+       }
+       device_interrupt(j, j, regs);
+#if 0
+       unsigned long pic;
+
+       /*
+        * It seems to me that the probability of two or more *device*
+        * interrupts occuring at almost exactly the same time is
+        * pretty low.  So why pay the price of checking for
+        * additional interrupts here if the common case can be
+        * handled so much easier?
+        */
        /* 
         *  The first read of gives you *all* interrupting lines.
         *  Therefore, read the mask register and and out those lines
@@ -369,6 +400,7 @@ static inline void isa_device_interrupt(unsigned long vector,
                pic &= pic - 1;
                device_interrupt(j, j, regs);
        }
+#endif
 }
 
 static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
index 0b6d7f378ad0ba304a771f455bfd3b7ce04e5d5a..b90dc0bea4a32a53781ae1f5a6dc1ebc30c45b00 100644 (file)
@@ -56,6 +56,10 @@ CFLAGS := $(CFLAGS) -m386
 endif
 endif
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 HEAD := arch/i386/kernel/head.o
 
 SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm arch/i386/lib
index c0a77a028eef9325fb2e25c377f468d4348aed6d..2af754e371ae983ff3397c0612118ba84cb22f92 100644 (file)
@@ -15,6 +15,10 @@ ifdef CONFIG_KERNEL_ELF
 CFLAGS := $(CFLAGS) -D__BFD__
 endif
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
 ifdef CONFIG_KERNEL_ELF
        $(OBJDUMP) $(OBJDUMP_FLAGS) -o $(ZIMAGE_OFFSET) compressed/vmlinux > compressed/vmlinux.out
index f6a0d315881e47b901637cf8a45a18ecf7ed82ef..6ed9a66074524087bb0650a0872f466462a8b20f 100644 (file)
@@ -47,7 +47,7 @@ SWAP_DEV = 0
 #define RAMDISK 0
 #endif 
 #ifndef CONFIG_ROOT_RDONLY
-#define CONFIG_ROOT_RDONLY 0
+#define CONFIG_ROOT_RDONLY 1
 #endif
 
 ! ld86 requires an entry symbol. This may as well be the usual one.
index 326f5443cce4014d9ce5fd3a4dba149c98188df6..91407065c974664a63218edcb17518c835f5f4e1 100644 (file)
@@ -11,6 +11,10 @@ OBJECTS = $(HEAD) inflate.o unzip.o misc.o
 
 CFLAGS = -O2 -DSTDC_HEADERS
 
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
 ifdef CONFIG_KERNEL_ELF
 TARGET=--target elf32-i386
 INPUT_DATA=input_data
@@ -22,9 +26,18 @@ all: vmlinux
 vmlinux: piggy.o $(OBJECTS)
        $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o
 
+ifdef SMP
+
+head.o: head.S $(TOPDIR)/include/linux/tasks.h
+       $(CC) -D__SMP__ -traditional -c head.S
+
+else
+
 head.o: head.S $(TOPDIR)/include/linux/tasks.h
        $(CC) -traditional -c head.S
 
+endif
+
 ifdef CONFIG_KERNEL_ELF
 
 # You cannot compress a file and have the kernel uncompress it, it must
index d12951a79e50c245e4ba6e719ab0a066a0f71067..c1a327c9a43a2b09d31ccab643c0af269ca5a717 100644 (file)
@@ -1,31 +1,32 @@
 !
 !      setup.S         Copyright (C) 1991, 1992 Linus Torvalds
 !
-! setup.s is responsible for getting the system data from the BIOS,
-! and putting them into the appropriate places in system memory.
-! both setup.s and system has been loaded by the bootblock.
-!
-! This code asks the bios for memory/disk/other parameters, and
-! puts them in a "safe" place: 0x90000-0x901FF, ie where the
-! boot-block used to be. It is then up to the protected mode
+! This code performs all initialization procedures that should be done
+! before entering the protected mode. It's responsible for getting of all
+! system data offered by BIOS and for detection / selection of video
+! mode. All information is put in a "safe" place: 0x90000-0x901FF, i. e.
+! where the boot-block used to be.  It is then up to the protected mode
 ! system to read them from there before the area is overwritten
 ! for buffer-blocks.
 !
 ! Move PS/2 aux init code to psaux.c
 ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
 !
-! some changes and additional features by Christoph Niemann,
+! Some changes and additional features by Christoph Niemann,
 ! March 1993/June 1994 (Christoph.Niemann@linux.org)
 !
+! Completely new video-mode handling code, VESA mode detection, support
+! for new Cirrus Logic cards and some additional changes
+! by Martin Mares <mj@k332.feld.cvut.cz>, October 1995.
+!
 
-! NOTE! These had better be the same as in bootsect.s!
+! NOTE! These had better be the same as in bootsect.S!
 #define __ASSEMBLY__
 #include <linux/config.h>
 #include <asm/segment.h>
 
-#ifndef SVGA_MODE
-#define SVGA_MODE ASK_VGA
-#endif
+! Uncomment this if you want the BIOS mode numbers to be listed
+!#define SHOW_BIOS_MODES
 
 ! Signature words to ensure LILO loaded us right
 #define SIG1   0xAA55
@@ -46,6 +47,7 @@ begbss:
 
 entry start
 start:
+
 ! Bootlin depends on this being done early
        mov     ax,#0x01500
        mov     dl,#0x81
@@ -72,12 +74,14 @@ fin:        ret
 ! Part of above routine, this one just prints ascii al
 
 prnt1: push    ax
+       push    bx
        push    cx
        xor     bh,bh
        mov     cx,#0x01
        mov     ah,#0x0e
        int     0x10
        pop     cx
+       pop     bx
        pop     ax
        ret
 
@@ -145,43 +149,10 @@ good_sig:
        xor     bx,bx           ! clear bx
        int     0x16
 
-! check for EGA/VGA and some config parameters
+! Check for video adapter and its parameters
+! Video mode selection is also handled here
 
-       mov     ah,#0x12
-       mov     bl,#0x10
-       int     0x10
-       mov     [8],ax
-       mov     [10],bx
-       mov     [12],cx
-       mov     ax,#0x5019
-       cmp     bl,#0x10
-       je      novga
-       mov     ax,#0x1a00      ! Added check for EGA/VGA discrimination
-       int     0x10
-       mov     bx,ax
-       mov     ax,#0x5019
-       movb    [15],#0         ! by default, no VGA
-       cmp     bl,#0x1a        ! 1a means VGA, anything else EGA or lower
-       jne     novga
-       movb    [15],#1         ! we've detected a VGA
-       call    chsvga
-novga: mov     [14],al
-       mov     ah,#0x03        ! read cursor pos
-       xor     bh,bh           ! clear bh
-       int     0x10            ! save it in known place, con_init fetches
-       mov     [0],dx          ! it from 0x90000.
-       
-! Get video-card data:
-       
-       mov     ah,#0x0f
-       int     0x10
-       mov     [4],bx          ! bh = display page
-       mov     [6],ax          ! al = video mode, ah = window width
-       xor     ax,ax
-       mov     es,ax           ! Access low memory
-       seg es
-       mov     ax,[0x485]      ! POINTS - Height of character matrix
-       mov     [16],ax
+       call    video
 
 ! Get hd0 data
 
@@ -334,17 +305,16 @@ end_move:
 ! Well, now's the time to actually move into protected mode. To make
 ! things as simple as possible, we do no register set-up or anything,
 ! we let the gnu-compiled 32-bit programs do that. We just jump to
-! absolute address 0x00000, in 32-bit protected mode.
+! absolute address 0x10000, in 32-bit protected mode.
 !
 ! Note that the short jump isn't strictly needed, although there are
 ! reasons why it might be a good idea. It won't hurt in any case.
 !
-       xor     ax,ax
-       inc     ax              ! protected mode (PE) bit
+       mov     ax,#1           ! protected mode (PE) bit
        lmsw    ax              ! This is it!
        jmp     flush_instr
 flush_instr:
-       mov     bx,#0           ! Flag to indicate a boot
+       xor     bx,bx           ! Flag to indicate a boot
        jmpi    0x1000,KERNEL_CS        ! jmp offset 1000 of segment 0x10 (cs)
 
 ! This routine checks that the keyboard command queue is empty
@@ -364,61 +334,6 @@ no_output:
        test    al,#2           ! is input buffer full?
        jnz     empty_8042      ! yes - loop
        ret
-!
-! Read a key and return the (US-)ascii code in al, scan code in ah
-!
-getkey:
-       xor     ah,ah
-       int     0x16
-       ret
-
-!
-! Read a key with a timeout of 30 seconds. The cmos clock is used to get
-! the time.
-!
-getkt:
-       call    gettime
-       add     al,#30          ! wait 30 seconds
-       cmp     al,#60
-       jl      lminute
-       sub     al,#60
-lminute:
-       mov     cl,al
-again: mov     ah,#0x01
-       int     0x16
-       jnz     getkey          ! key pressed, so get it
-       call    gettime
-       cmp     al,cl
-       jne     again
-       mov     al,#0x20        ! timeout, return default char `space'
-       ret
-
-!
-! Flush the keyboard buffer
-!
-flush: mov     ah,#0x01
-       int     0x16
-       jz      empty
-       xor     ah,ah
-       int     0x16
-       jmp     flush
-empty: ret
-
-!
-! Read the cmos clock. Return the seconds in al
-!
-gettime:
-       push    cx
-       mov     ah,#0x02
-       int     0x1a
-       mov     al,dh                   ! dh contains the seconds
-       and     al,#0x0f
-       mov     ah,dh
-       mov     cl,#0x04
-       shr     ah,cl
-       aad
-       pop     cx
-       ret
 
 !
 ! Delay is needed after doing i/o
@@ -427,43 +342,199 @@ delay:
        .word   0x00eb                  ! jmp $+2
        ret
 
-! Routine trying to recognize type of SVGA-board present (if any)
-! and if it recognize one gives the choices of resolution it offers.
-! If one is found the resolution chosen is given by al,ah (rows,cols).
+!
+! Video card / mode detection. We do some hardware testing and build
+! a video mode list (placed directly after our code and data). Then we
+! choose the right mode given in the configuration or ask the user if
+! we are requested to do so. After all, all video parameters are stored
+! for later perusal by the kernel.
+!
 
-chsvga:        cld
+video: movb    [15],#0         ! Default is no VGA
+       mov     ax,[0x01fa]
        push    ds
+       push    ds
+       pop     fs              ! In this routine: FS=orig. DS
        push    cs
-       mov     ax,[0x01fa]
-       pop     ds
+       pop     ds              ! ES=DS=CS
+       push    cs
+       pop     es
        mov     modesave,ax
-       mov     ax,#0xc000
-       mov     es,ax
-       mov     ax,modesave
-       cmp     ax,#NORMAL_VGA
-       je      defvga
-       cmp     ax,#EXTENDED_VGA
-       je      vga50
+       lea     di,modelist     ! ES:DI points to current item in our mode list
+       mov     eax,#0x50190000 ! Store current mode: 80x25
+       cld
+       stosd
+
+       mov     ah,#0x12        ! Check EGA/VGA
+       mov     bl,#0x10
+       int     0x10
+       seg     fs
+       mov     [10],bx         ! Used for identification of VGA in the kernel
+       cmp     bl,#0x10        ! Not EGA nor VGA -> 80x25 only
+       je      selmd1
+
+       mov     eax,#0x5032FFFF ! EGA or VGA: 80x50 supported
+       stosd
+
+       mov     ax,#0x1a00      ! Added check for EGA/VGA discrimination
+       int     0x10
+       cmp     al,#0x1a        ! 1a means VGA, anything else EGA
+       jne     selmd1
+       seg     fs
+       movb    [15],#1         ! We've detected a VGA
+
+       mov     eax,#0x501cFFFE ! VGA: 80x28 supported
+       stosd
+
+       lea     si,vgatable     ! Test all known SVGA adapters
+dosvga:        lodsw
+       mov     bp,ax           ! Default mode table
+       or      ax,ax
+       jz      didsv
+       lodsw                   ! Pointer to test routine
+       push    si
+       push    di
+       push    es
+       mov     bx,#0xc000
+       mov     es,bx
+       call    ax              ! Call test routine
+       pop     es
+       pop     di
+       pop     si
+       or      bp,bp
+       jz      dosvga
+       mov     si,bp           ! Found, copy the modes
+       mov     ah,#0
+cpsvga:        lodsb
+       or      al,al
+       jz      didsv
+       stosw
+       movsw
+       jmp     cpsvga
+
+selmd1:        jmp     selmd
+
+didsv: mov     ax,#0x4f00      ! Fetch VESA information to ES:DI+0x400
+       add     di,#0x400
+       int     0x10
+       sub     di,#0x400
+       cmp     al,#0x4f
+       jne     selmd
+       lgs     bx,(di+0x40e)
+       cmp     (di+0x400),#0x4556
+       jne     selmd
+       cmp     (di+0x402),#0x4153
+       jne     selmd
+
+vesa1: seg     gs
+       mov     cx,(bx)
+       add     bx,#2
+       cmp     cx,#0xffff
+       je      selmd
+       mov     ax,#0x4f01
+       add     di,#0xc00
+       int     0x10
+       sub     di,#0xc00
+       cmp     al,#0x4f
+       jne     selmd
+       testb   (di+0xc00),#0x10        ! Is it a text mode?
+       jne     vesa1
+       testb   (di+0xc00),#0x08        ! Has it colors?
+       je      vesa1
+       mov     dh,(di+0xc12)   ! DX=dimensions, CX=mode
+       mov     dl,(di+0xc14)
+
+       lea     si,modelist     ! Check if it's already on the list
+vesa2: lodsw
+       lodsw
+       cmp     ax,dx
+       je      vesa1
+       cmp     si,di
+       jc      vesa2
+
+       mov     ax,cx           ! New mode, store it
+       stosw
+       mov     ax,dx
+       stosw
+       jmp     vesa1
+
+!
+! Video mode table built. Determine the mode we should use and set it.
+!
+selmd: mov     ax,modesave
+       cmp     ax,#NORMAL_VGA  ! Current mode (80x25)
+       je      defmd1
+       cmp     ax,#EXTENDED_VGA ! 80x50 mode
+       je      try50
        cmp     ax,#ASK_VGA
-       jne     svga
-       lea     si,msg1
+       jne     usemd
+banner:        lea     si,keymsg
        call    prtstr
        call    flush
 nokey: call    getkt
-       cmp     al,#0x0d                ! enter ?
-       je      svga                    ! yes - svga selection
-       cmp     al,#0x20                ! space ?
-       je      defvga                  ! no - repeat
+       cmp     al,#0x0d        ! ENTER ?
+       je      listm           ! yes - manual mode selection
+       cmp     al,#0x20        ! SPACE ?
+       je      defmd1          ! no - repeat
        call    beep
        jmp     nokey
-defvga:        mov     ax,#0x5019
-       pop     ds
-       ret
-/* extended vga mode: 80x50 */
-vga50:
-       mov     ax,#0x1112
+
+defmd1:        br      defmd
+
+listm: call    listmodes       ! List all available modes
+keymd: call    getkey          ! Get key representing mode ID
+       xor     ah,ah
+       sub     al,#0x30
+       jc      keymd
+       cmp     al,#10
+       jc      usemd
+       sub     al,#39
+       cmp     al,#10
+       jc      keymd
+       cmp     al,#26
+       jnc     keymd
+       jmp     usemd
+
+try50: mov     ax,#1           ! 80x50 is mode #1
+usemd: shl     ax,#2           ! We're requested to set mode in AX
+       lea     si,modelist
+       add     si,ax
+       cmp     si,di
+       jc      mdok
+       cmp     modesave,#ASK_VGA
+       je      keymd
+       lea     si,undefd
+       call    prtstr
+       jmp     banner
+
+mdok:  lodsw                   ! AX=mode number
+       cmp     ah,#0xff
+       jz      mdspec
+       or      ax,ax
+       jz      mdsetd
+       or      ah,ah
+       jz      mdset
+       mov     bx,ax
+       mov     ax,#0x4f02
+mdset: int     0x10
+mdsetd:        lodsb                   ! AL=number of lines
+       jmp     getpar
+
+mdspec:        inc     ax              ! Special modes
+       jz      m80x50
+
+m80x28: mov    ax,#0x1111      ! Setting 80x28 (VGA with EGA font)
+       xor     bl,bl
+       int     0x10            ! use 9x14 fontset (28 lines on VGA)
+       mov     ah,#0x01
+       mov     cx,#0x0b0c
+       int     0x10            ! turn on cursor (scan lines 11 to 12)
+       mov     al,#28
+       jmp     getpar
+
+m80x50: mov    ax,#0x1112      ! Setting 80x50 (EGA/VGA)
        xor     bl,bl
-       int     0x10            ! use 8x8 font set (50 lines on VGA)
+       int     0x10            ! use 8x8 font set
        mov     ax,#0x1200
        mov     bl,#0x20
        int     0x10            ! use alternate print screen
@@ -473,28 +544,65 @@ vga50:
        mov     ah,#0x01
        mov     cx,#0x0607
        int     0x10            ! turn on cursor (scan lines 6 to 7)
-       pop     ds
-       mov     ax,#0x5032      ! return 80x50
-       ret
-/* extended vga mode: 80x28 */
-vga28:
-       pop     ax              ! clean the stack
-       mov     ax,#0x1111
-       xor     bl,bl
-       int     0x10            ! use 9x14 fontset (28 lines on VGA)
-       mov     ah, #0x01
-       mov     cx,#0x0b0c
-       int     0x10            ! turn on cursor (scan lines 11 to 12)
-       pop     ds
-       mov     ax,#0x501c      ! return 80x28
-       ret
-/* svga modes */
+       mov     al,#50
+       jmp     getpar
+
+defmd: mov     al,#25          ! Default is 25 lines
+
+!
+! Correct video mode set. Determine the remaining parameters.
+!
+
+getpar:        pop     ds              ! Restore original DS
+       mov     [14],al         ! Number of lines
+
+       mov     ah,#0x03        ! read cursor pos
+       xor     bh,bh           ! clear bh
+       int     0x10            ! save it in known place, con_init fetches
+       mov     [0],dx          ! it from 0x90000.
+       
+       mov     ah,#0x0f
+       int     0x10
+       mov     [4],bx          ! bh = display page
+       mov     [6],ax          ! al = video mode, ah = window width
+       xor     ax,ax
+       mov     es,ax           ! Access low memory
+       seg es
+       mov     ax,[0x485]      ! POINTS - Height of character matrix
+       mov     [16],ax
+
+       ret                     ! Well done...
+
+!
+! Table of all known SVGA cards. For each card, we store a pointer to
+! a table of video modes supported by the card and a pointer to a routine
+! used for testing of presence of the card.
 !
-!      test for presence of an S3 VGA chip. The algorithm was taken
-!      from the SuperProbe package of XFree86 1.2.1
-!      report bugs to Christoph.Niemann@linux.org
+
+vgatable:
+       .word   s3_md, s3_test
+       .word   ati_md, ati_test
+       .word   ahead_md, ahead_test
+       .word   chips_md, chips_test
+       .word   cirrus2_md, cirrus2_test
+       .word   cirrus1_md, cirrus1_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
+
 !
-svga:   cld
+! Test routines and mode tables:
+!
+
+! S3 - The test algorithm was taken from the SuperProbe package
+! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
+
+s3_test:
        mov     cx,#0x0f35      ! we store some constants in cl/ch
        mov     dx,#0x03d4
        movb    al,#0x38
@@ -559,29 +667,64 @@ s3_1:     mov     ax,#0x4838      ! allow writing to special regs by putting
        repne
        scasb
        je      no_s31
-       lea     si,dsc_S3       ! table of descriptions of video modes for BIOS
-       lea     di,mo_S3        ! table of sizes of video modes for my BIOS
        movb    ah,bh
        movb    al,#0x38
-       call    outidx          ! restore old value of CRT register 0x38
-       br      selmod          ! go ask for video mode
+       jmp     s3rest
 no_s3: movb    al,#0x35        ! restore CRT register 0x35
        movb    ah,bl
        call    outidx
-no_s31:        movb    ah,bh
-       movb    al,#0x38
-       call    outidx          ! restore old value of CRT register 0x38
+no_s31:        xor     bp,bp           ! Detection failed
+s3rest:        movb    ah,bh
+       movb    al,#0x38        ! restore old value of CRT register 0x38
+outidx:        out     dx,al           ! Write to indexed VGA register
+       push    ax              ! AL=index, AH=data, DX=index reg port
+       mov     al,ah
+       inc     dx
+       out     dx,al
+       dec     dx
+       pop     ax
+       ret
 
-       lea     si,idati                ! Check ATI 'clues'
+tstidx:        out     dx,ax           ! OUT DX,AX and inidx
+inidx: out     dx,al           ! Read from indexed VGA register
+       inc     dx              ! AL=index, DX=index reg port -> AL=data
+       in      al,dx
+       dec     dx
+       ret
+
+idS3:  .byte   0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
+       .byte   0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
+
+s3_md: .byte   0x54, 0x2b, 0x84
+       .byte   0x55, 0x19, 0x84
+       .byte   0
+
+! ATI cards.
+
+ati_test:
+       lea     si,idati
        mov     di,#0x31
        mov     cx,#0x09
        repe
        cmpsb
-       jne     noati
-       lea     si,dscati
-       lea     di,moati
-       br      selmod
-noati: mov     ax,#0x200f              ! Check Ahead 'clues'
+       je      atiok
+       xor     bp,bp
+atiok: ret
+
+idati: .ascii  "761295520"
+
+ati_md:        .byte   0x23, 0x19, 0x84
+       .byte   0x33, 0x2c, 0x84
+       .byte   0x22, 0x1e, 0x64
+       .byte   0x21, 0x19, 0x64
+       .byte   0x58, 0x21, 0x50
+       .byte   0x5b, 0x1e, 0x50
+       .byte   0
+
+! AHEAD
+
+ahead_test:
+       mov     ax,#0x200f
        mov     dx,#0x3ce
        out     dx,ax
        inc     dx
@@ -589,11 +732,23 @@ noati:    mov     ax,#0x200f              ! Check Ahead 'clues'
        cmp     al,#0x20
        je      isahed
        cmp     al,#0x21
-       jne     noahed
-isahed:        lea     si,dscahead
-       lea     di,moahead
-       br      selmod
-noahed:        mov     dx,#0x3c3               ! Check Chips & Tech. 'clues'
+       je      isahed
+       xor     bp,bp
+isahed:        ret
+
+ahead_md:
+       .byte   0x22, 0x2c, 0x84
+       .byte   0x23, 0x19, 0x84
+       .byte   0x24, 0x1c, 0x84
+       .byte   0x2f, 0x32, 0xa0
+       .byte   0x32, 0x22, 0x50
+       .byte   0x34, 0x42, 0x50
+       .byte   0
+
+! Chips & Tech.
+
+chips_test:
+       mov     dx,#0x3c3
        in      al,dx
        or      al,#0x10
        out     dx,al
@@ -604,12 +759,20 @@ noahed:   mov     dx,#0x3c3               ! Check Chips & Tech. 'clues'
        in      al,dx
        and     al,#0xef
        out     dx,al
-       cmp     bl,[idcandt]
-       jne     nocant
-       lea     si,dsccandt
-       lea     di,mocandt
-       br      selmod
-nocant:        mov     dx,#0x3d4               ! Check Cirrus 'clues'
+       cmp     bl,#0xa5
+       je      cantok
+       xor     bp,bp
+cantok:        ret
+
+chips_md:
+       .byte   0x60, 0x19, 0x84
+       .byte   0x61, 0x32, 0x84
+       .byte   0
+
+! Cirrus Logic 5X0
+
+cirrus1_test:
+       mov     dx,#0x3d4
        mov     al,#0x0c
        out     dx,al
        inc     dx
@@ -642,19 +805,76 @@ nocant:   mov     dx,#0x3d4               ! Check Cirrus 'clues'
        out     dx,al
        in      al,dx
        cmp     al,#0x01
-       jne     nocirr
-       call    rst3d4  
-       lea     si,dsccirrus
-       lea     di,mocirrus
-       br      selmod
-rst3d4:        mov     dx,#0x3d4
+       je      iscirr
+nocirr:        xor     bp,bp
+iscirr: mov    dx,#0x3d4
        mov     al,bl
        xor     ah,ah
        shl     ax,#8
        add     ax,#0x0c
        out     dx,ax
-       ret     
-nocirr:        call    rst3d4                  ! Check Everex 'clues'
+       ret
+
+cirrus1_md:
+       .byte   0x1f, 0x19, 0x84
+       .byte   0x20, 0x2c, 0x84
+       .byte   0x22, 0x1e, 0x84
+       .byte   0x31, 0x25, 0x64
+       .byte   0
+
+! Cirrus Logic 54XX
+
+cirrus2_test:
+       mov     dx,#0x3c4
+       mov     al,#6
+       call    inidx
+       mov     bl,al                   ! BL=backup
+       mov     al,#6
+       xor     ah,ah
+       call    tstidx
+       cmp     al,#0x0f
+       jne     c2fail
+       mov     ax,#0x1206
+       call    tstidx
+       cmp     al,#0x12
+       jne     c2fail
+       mov     al,#0x1e
+       call    inidx
+       mov     bh,al
+       and     bh,#0xc0
+       mov     ah,bh
+       mov     al,#0x1e
+       call    tstidx
+       xor     al,bh
+       and     al,#0x3f
+       jne     c2xx
+       mov     al,#0x1e
+       mov     ah,bh
+       or      ah,#0x3f
+       call    tstidx
+       xor     al,bh
+       xor     al,#0x3f
+       and     al,#0x3f
+c2xx:  pushf
+       mov     al,#0x1e
+       mov     ah,bh
+       out     dx,ax
+       popf
+       je      c2done
+c2fail:        xor     bp,bp
+c2done:        mov     al,#6
+       mov     ah,bl
+       out     dx,ax
+       ret
+
+cirrus2_md:
+       .byte   0x14, 0x19, 0x84
+       .byte   0x54, 0x2b, 0x84
+       .byte   0
+
+! Everex / Trident
+
+everex_test:
        mov     ax,#0x7000
        xor     bx,bx
        int     0x10
@@ -662,15 +882,32 @@ nocirr:   call    rst3d4                  ! Check Everex 'clues'
        jne     noevrx
        shr     dx,#4
        cmp     dx,#0x678
-       je      istrid
+       je      evtrid
        cmp     dx,#0x236
-       je      istrid
-       lea     si,dsceverex
-       lea     di,moeverex
-       br      selmod
-istrid:        lea     cx,ev2tri
-       jmp     cx
-noevrx:        lea     si,idgenoa              ! Check Genoa 'clues'
+       jne     evrxok
+evtrid:        lea     bp,trident_md
+evrxok:        ret
+
+noevrx:        xor     bp,bp
+       ret
+
+everex_md:
+       .byte   0x03, 0x22, 0x50
+       .byte   0x04, 0x3c, 0x50
+       .byte   0x07, 0x2b, 0x64
+       .byte   0x08, 0x4b, 0x64
+       .byte   0x0a, 0x19, 0x84
+       .byte   0x0b, 0x2c, 0x84
+       .byte   0x16, 0x1e, 0x50
+       .byte   0x18, 0x1b, 0x64
+       .byte   0x21, 0x40, 0xa0
+       .byte   0x40, 0x1e, 0x84
+       .byte   0
+
+! Genoa.
+
+genoa_test:
+       lea     si,idgenoa              ! Check Genoa 'clues'
        xor     ax,ax
        seg es
        mov     al,[0x37]
@@ -686,32 +923,72 @@ l1:       inc     si
        seg es
        cmp     al,(di)
 l2:    loope   l1
-       cmp     cx,#0x00
-       jne     nogen
-       lea     si,dscgenoa
-       lea     di,mogenoa
-       br      selmod
-nogen: cld
+       or      cx,cx
+       je      isgen
+       xor     bp,bp
+isgen: ret
+
+idgenoa: .byte 0x77, 0x00, 0x99, 0x66
+
+genoa_md:
+       .byte   0x58, 0x20, 0x50
+       .byte   0x5a, 0x2a, 0x64
+       .byte   0x60, 0x19, 0x84
+       .byte   0x61, 0x1d, 0x84
+       .byte   0x62, 0x20, 0x84
+       .byte   0x63, 0x2c, 0x84
+       .byte   0x64, 0x3c, 0x84
+       .byte   0x6b, 0x4f, 0x64
+       .byte   0x72, 0x3c, 0x50
+       .byte   0x74, 0x42, 0x50
+       .byte   0x78, 0x4b, 0x64
+       .byte   0
+
+! OAK
+
+oak_test:
        lea     si,idoakvga
        mov     di,#0x08
        mov     cx,#0x08
        repe
        cmpsb
-       jne     nooak
-       lea     si,dscoakvga
-       lea     di,mooakvga
-       br      selmod
-nooak: cld
-       lea     si,idparadise           ! Check Paradise 'clues'
+       je      isoak
+       xor     bp,bp
+isoak: ret
+
+idoakvga: .ascii  "OAK VGA "
+
+oak_md: .byte  0x4e, 0x3c, 0x50
+       .byte   0x4f, 0x3c, 0x84
+       .byte   0x50, 0x19, 0x84
+       .byte   0x51, 0x2b, 0x84
+       .byte   0
+
+! WD Paradise.
+
+paradise_test:
+       lea     si,idparadise
        mov     di,#0x7d
        mov     cx,#0x04
        repe
        cmpsb
-       jne     nopara
-       lea     si,dscparadise
-       lea     di,moparadise
-       br      selmod
-nopara:        mov     dx,#0x3c4               ! Check Trident 'clues'
+       je      ispara
+       xor     bp,bp
+ispara:        ret
+
+idparadise:    .ascii  "VGA="
+
+paradise_md:
+       .byte   0x41, 0x22, 0x50
+       .byte   0x47, 0x1c, 0x84
+       .byte   0x55, 0x19, 0x84
+       .byte   0x54, 0x2c, 0x84
+       .byte   0
+
+! Trident.
+
+trident_test:
+       mov     dx,#0x3c4
        mov     al,#0x0e
        out     dx,al
        inc     dx
@@ -730,11 +1007,24 @@ setb2:   or      al,#0x02        !
 clrb2: out     dx,al
        and     ah,#0x0f
        cmp     ah,#0x02
-       jne     notrid
-ev2tri:        lea     si,dsctrident
-       lea     di,motrident
-       jmp     selmod
-notrid:        mov     dx,#0x3cd               ! Check Tseng 'clues'
+       je      istrid
+       xor     bp,bp
+istrid:        ret
+
+trident_md:
+       .byte   0x50, 0x1e, 0x50
+       .byte   0x51, 0x2b, 0x50
+       .byte   0x52, 0x3c, 0x50
+       .byte   0x57, 0x19, 0x84
+       .byte   0x58, 0x1e, 0x84
+       .byte   0x59, 0x2b, 0x84
+       .byte   0x5a, 0x3c, 0x84
+       .byte   0
+
+! Tseng.
+
+tseng_test:
+       mov     dx,#0x3cd
        in      al,dx                   ! Could things be this simple ! :-)
        mov     bl,al
        mov     al,#0x55
@@ -744,11 +1034,22 @@ notrid:  mov     dx,#0x3cd               ! Check Tseng 'clues'
        mov     al,bl
        out     dx,al
        cmp     ah,#0x55
-       jne     notsen
-       lea     si,dsctseng
-       lea     di,motseng
-       jmp     selmod
-notsen:        mov     dx,#0x3cc               ! Check Video7 'clues'
+       je      istsen
+       xor     bp,bp
+istsen:        ret
+
+tseng_md:
+       .byte   0x26, 0x3c, 0x50
+       .byte   0x2a, 0x28, 0x64
+       .byte   0x23, 0x19, 0x84
+       .byte   0x24, 0x1c, 0x84
+       .byte   0x22, 0x2c, 0x84
+       .byte   0
+
+! Video7.
+
+video7_test:
+       mov     dx,#0x3cc
        in      al,dx
        mov     dx,#0x3b4
        and     al,#0x01
@@ -777,115 +1078,78 @@ even7:  mov     al,#0x0c
        mov     al,#0x55
        xor     al,#0xea
        cmp     al,bh
-       jne     novid7
-       lea     si,dscvideo7
-       lea     di,movideo7
-       jmp     selmod
-novid7:        lea     si,dsunknown
-       lea     di,mounknown
-selmod:        xor     cx,cx
-       mov     cl,(di)
-       mov     ax,modesave
-       cmp     ax,#ASK_VGA
-       je      askmod
-       cmp     ax,#NORMAL_VGA
-       je      askmod
-       cmp     al,cl
-       jl      gotmode
-       push    si
-       lea     si,msg4
+       je      isvid7
+       xor     bp,bp
+isvid7:        ret
+
+video7_md:
+       .byte   0x40, 0x2b, 0x50
+       .byte   0x43, 0x3c, 0x50
+       .byte   0x44, 0x3c, 0x64
+       .byte   0x41, 0x19, 0x84
+       .byte   0x42, 0x2c, 0x84
+       .byte   0x45, 0x1c, 0x84
+       .byte   0
+
+!
+! Displaying of the mode list.
+!
+
+listmodes:
+       lea     si,listhdr
        call    prtstr
-       pop     si
-askmod:        push    si
-       lea     si,msg2
+       lea     bx,modelist
+       mov     cl,#0x30
+listm1:        mov     modenr,cl
+       lea     si,modestring
        call    prtstr
-       pop     si
-       push    si
-       push    cx
-tbl:   pop     bx
-       push    bx
-       mov     al,bl
-       sub     al,cl
-       call    modepr
-       lodsw
-       xchg    al,ah
+       mov     al,(bx+3)
        call    dprnt
-       xchg    ah,al
-       push    ax
        mov     al,#0x78
        call    prnt1
-       pop     ax
+       mov     al,(bx+2)
        call    dprnt
-       push    si
-       lea     si,crlf         ! print CR+LF
-       call    prtstr
-       pop     si
-       loop    tbl
-       pop     cx
-       lea     si,msg3
+#ifdef SHOW_BIOS_MODES
+       mov     al,#0x20
+       call    prnt1
+       mov     al,#0x28
+       call    prnt1
+       mov     al,(bx+1)
+       call    prthex
+       mov     al,(bx)
+       call    prthex
+       mov     al,#0x29
+       call    prnt1
+#endif
+       lea     si,crlf
        call    prtstr
-       pop     si
-       add     cl,#0x30
-       jmp     nonum
-nonumb:        call    beep
-nonum: call    getkey
-       cmp     al,#0x30        ! ascii `0'
-       jb      nonumb
-       cmp     al,#0x3a        ! ascii `9'
-       jbe     number
-       cmp     al,#0x61        ! ascii `a'
-       jb      nonumb
-       cmp     al,#0x7a        ! ascii `z'
-       ja      nonumb
-       sub     al,#0x27
-       cmp     al,cl
-       jae     nonumb
-       sub     al,#0x30
-       jmp     gotmode
-number: cmp    al,cl
-       jae     nonumb
-       sub     al,#0x30
-gotmode:       xor     ah,ah
-       or      al,al
-       beq     vga50
-       push    ax
-       dec     ax
-       beq     vga28
-       add     di,ax
-       mov     al,(di)
-       int     0x10
-       pop     ax
-       shl     ax,#1
-       add     si,ax
-       lodsw
-       pop     ds
-       ret
-
-! Routine to write al into a VGA-register that is
-! accessed via an index register
-!
-! dx contains the address of the index register
-! al contains the index
-! ah contains the value to write to the data register (dx + 1)
-!
-! no registers are changed
-
-outidx:        out     dx,al
-       push    ax
-       mov     al,ah
-       inc     dx
-       out     dx,al
-       dec     dx
+       add     bx,#4
+       inc     cl
+       cmp     cl,#0x3a
+       jnz     listm2
+       mov     cl,#0x61
+listm2:        cmp     bx,di
+       jc      listm1
+       lea     si,prompt
+       br      prtstr
+
+! Routine to print a hexadecimal byte (AL) on screen.
+
+#ifdef SHOW_BIOS_MODES
+prthex:        push    ax
+       shr     al,#4
+       call    prth1
        pop     ax
-       ret
-inidx: out     dx,al
-       inc     dx
-       in      al,dx
-       dec     dx
-       ret
+prth1: and     al,#15
+       cmp     al,#10
+       jc      prth2
+       add     al,#7
+prth2: add     al,#0x30
+       br      prnt1
+#endif
 
 ! Routine to print a decimal value on screen, the value to be
-! printed is put in al (i.e 0-255). 
+! printed is put in AL (i.e 0-255). 
 
 dprnt: push    ax
        push    cx
@@ -906,25 +1170,65 @@ skip10:  mov     al,ah
        ret
 
 !
-! Routine to print the mode number key on screen. Mode numbers
-! 0-9 print the ascii values `0' to '9', 10-35 are represented by
-! the letters `a' to `z'. This routine prints some spaces around the
-! mode no.
+! Read a key and return the (US-)ascii code in al, scan code in ah
 !
+getkey:
+       xor     ah,ah
+       int     0x16
+       ret
 
-modepr:        push    ax
-       cmp     al,#0x0a
-       jb      digit           ! Here is no check for number > 35
-       add     al,#0x27
-digit: add     al,#0x30
-       mov     modenr, al
-       push    si
-       lea     si, modestring
-       call    prtstr
-       pop     si
-       pop     ax
+!
+! Read a key with a timeout of 30 seconds. The cmos clock is used to get
+! the time.
+!
+getkt:
+       call    gettime
+       add     al,#30          ! wait 30 seconds
+       cmp     al,#60
+       jl      lminute
+       sub     al,#60
+lminute:
+       mov     cl,al
+again: mov     ah,#0x01
+       int     0x16
+       jnz     getkey          ! key pressed, so get it
+       call    gettime
+       cmp     al,cl
+       jne     again
+       mov     al,#0x20        ! timeout, return default char `space'
        ret
 
+!
+! Flush the keyboard buffer
+!
+flush: mov     ah,#0x01
+       int     0x16
+       jz      empty
+       xor     ah,ah
+       int     0x16
+       jmp     flush
+empty: ret
+
+!
+! Read the cmos clock. Return the seconds in al
+!
+gettime:
+       push    cx
+       mov     ah,#0x02
+       int     0x1a
+       mov     al,dh                   ! dh contains the seconds
+       and     al,#0x0f
+       mov     ah,dh
+       mov     cl,#0x04
+       shr     ah,cl
+       aad
+       pop     cx
+       ret
+
+!
+! Descriptor table for our protected mode transition.
+!
+
 gdt:
        .word   0,0,0,0         ! dummy
 
@@ -948,69 +1252,37 @@ gdt_48:
        .word   0x800           ! gdt limit=2048, 256 GDT entries
        .word   512+gdt,0x9     ! gdt base = 0X9xxxx
 
-msg1:          .ascii  "Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
-               db      0x0d, 0x0a, 0x0a, 0x00
-msg2:          .ascii  "Mode:  COLSxROWS:"
+!
+! Assorted messages.
+!
+
+keymsg:                .ascii  "Press <RETURN> to see video modes available, <SPACE> to continue or wait 30 secs"
+               db      0x0d, 0x0a, 0x00
+listhdr:       .ascii  "Mode:  COLSxROWS:"
                db      0x0d, 0x0a, 0x0a, 0x00
-msg3:          db      0x0d, 0x0a
+prompt:                db      0x0d, 0x0a
                .ascii  "Choose mode by pressing the corresponding number or letter."
 crlf:          db      0x0d, 0x0a, 0x00
-msg4:          .ascii  "You passed an undefined mode number to setup. Please choose a new mode."
+undefd:                .ascii  "You passed an undefined mode number to setup. Please choose a new mode."
                db      0x0d, 0x0a, 0x0a, 0x07, 0x00
 modestring:    .ascii  "   "
 modenr:                db      0x00    ! mode number
                .ascii  ":    "
                db      0x00
-               
-idati:         .ascii  "761295520"
-idcandt:       .byte   0xa5
-idgenoa:       .byte   0x77, 0x00, 0x99, 0x66
-idparadise:    .ascii  "VGA="
-idoakvga:      .ascii  "OAK VGA "
-idS3:          .byte   0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
-               .byte   0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
-
-! Manufacturer:          Numofmodes+2: Mode:
-! Number of modes is the number of chip-specific svga modes plus the extended
-! modes available on any vga (currently 2)
-
-moati:         .byte   0x06,   0x23, 0x33, 0x22, 0x21
-moahead:       .byte   0x07,   0x22, 0x23, 0x24, 0x2f, 0x34
-mocandt:       .byte   0x04,   0x60, 0x61
-mocirrus:      .byte   0x06,   0x1f, 0x20, 0x22, 0x31
-moeverex:      .byte   0x0c,   0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
-mogenoa:       .byte   0x0c,   0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
-moparadise:    .byte   0x04,   0x55, 0x54
-motrident:     .byte   0x09,   0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
-motseng:       .byte   0x07,   0x26, 0x2a, 0x23, 0x24, 0x22
-movideo7:      .byte   0x08,   0x40, 0x43, 0x44, 0x41, 0x42, 0x45
-mooakvga:      .byte   0x08,   0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
-mo_S3:         .byte   0x04,   0x54, 0x55
-mounknown:     .byte   0x02
-
-!                      msb = Cols lsb = Rows:
-! The first two modes are standard vga modes available on any vga.
-! mode 0 is 80x50 and mode 1 is 80x28
-
-dscati:                .word   0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419
-dscahead:      .word   0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
-dsccandt:      .word   0x5032, 0x501c, 0x8419, 0x8432
-dsccirrus:     .word   0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
-dsceverex:     .word   0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
-dscgenoa:      .word   0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
-dscparadise:   .word   0x5032, 0x501c, 0x8419, 0x842c
-dsctrident:    .word   0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
-dsctseng:      .word   0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
-dscvideo7:     .word   0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
-dscoakvga:     .word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
-dsc_S3:                .word   0x5032, 0x501c, 0x842b, 0x8419
-dsunknown:     .word   0x5032, 0x501c
-modesave:      .word   SVGA_MODE
+
+modesave:      .word   0       ! Requsted mode ID.
 
 ! This must be last
 setup_sig1:    .word   SIG1
 setup_sig2:    .word   SIG2
 
+! After our code and data, we'll store the mode list.
+! Mode record: .word   modenr
+!              .byte   lines
+!              .byte   columns
+! Mode numbers used: 0=current, >=0x100=VESA, -1=80x50, -2=80x28
+modelist:
+
 .text
 endtext:
 .data
index 60b7eb29060687090259da2fce9053b6815a171c..c3e4cff756709d691ca3fef230045f4d0d98ca12 100644 (file)
@@ -49,7 +49,7 @@ CONFIG_INET=y
 #
 # CONFIG_INET_PCTCP is not set
 # CONFIG_INET_RARP is not set
-CONFIG_INET_SNARL=y
+# CONFIG_NO_PATH_MTU_DISCOVERY is not set
 # CONFIG_TCP_NAGLE_OFF is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
@@ -102,6 +102,7 @@ CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
+# CONFIG_ROOT_NFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
index 521ac5994b4cb541ea472e331d79eb8a73b073c2..1186c23064d76a5d0c00d9b78bb89da00989f0cb 100644 (file)
@@ -14,8 +14,15 @@ LD86    =ld86 -0
 
 #.S.s:
 #      $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+
+ifdef SMP
+
+.S.o:
+       $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $< -o $*.o
+else
 .S.o:
        $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+endif
 
 all: kernel.o head.o
 
@@ -24,12 +31,19 @@ O_OBJS   := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \
             ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o
 
 ifdef SMP
+
 O_OBJS += smp.o
-endif
+
+head.o: head.S $(TOPDIR)/include/linux/tasks.h
+       $(CC) -D__ASSEMBLY__ -D__SMP__ -traditional -c $*.S -o $*.o
+
+else
 
 head.o: head.S $(TOPDIR)/include/linux/tasks.h
        $(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o
 
+endif
+
 hexify:
        $(HOSTCC) hexify.c -o hexify
 
@@ -48,10 +62,10 @@ trampoline32.o: trampoline32.s
        $(AS386) -o $@ $<
 
 trampoline.s: trampoline.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
-       $(CPP) -traditional $< -o $@
+       $(CPP) -D__SMP__ -traditional $< -o $@
 
 trampoline32.s: trampoline32.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
-       $(CPP) -traditional $< -o $@
+       $(CPP) -D__SMP__ -traditional $< -o $@
 
 clean:
        rm -f trampoline hexify
index 85d40575063b3e3b54d980eed55b7d01189ea22c..e10fdab3054984026efb3d1429e1cb0a84d4a971 100644 (file)
@@ -383,6 +383,12 @@ const char *pcibios_strerror (int error)
                case PCIBIOS_BAD_REGISTER_NUMBER:
                        return "BAD_REGISTER_NUMBER";
 
+                case PCIBIOS_SET_FAILED:          
+                       return "SET_FAILED";
+
+                case PCIBIOS_BUFFER_TOO_SMALL:    
+                       return "BUFFER_TOO_SMALL";
+
                default:
                        sprintf (buf, "UNKNOWN RETURN 0x%x", error);
                        return buf;
index 2ed851457113a39ed2484713f83c4cae484a57d7..9c8a19c21c7c2132409a07699070612fb1804b71 100644 (file)
@@ -103,8 +103,8 @@ void setup_arch(char **cmdline_p,
        if (memory_end > 16*1024*1024)
                memory_end = 16*1024*1024;
 #endif
-       if (MOUNT_ROOT_RDONLY)
-               root_mountflags |= MS_RDONLY;
+       if (!MOUNT_ROOT_RDONLY)
+               root_mountflags &= ~MS_RDONLY;
        memory_start = (unsigned long) &_end;
        init_task.mm->start_code = TASK_SIZE;
        init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
index 86d855d9e9902d1cd83f445827e563277b0a0933..000a55a73545a184ed8bb843d40458c2cf5a97a0 100644 (file)
@@ -2440,14 +2440,21 @@ static int make_raw_rw_request(void)
                raw_cmd->flags |= FD_RAW_READ;
                COMMAND = FM_MODE(floppy,FD_READ);
        } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
+               unsigned long dma_limit;
                int direct, indirect;
 
                indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
                        sector_t;
 
-               max_size = minimum(buffer_chain_size(),
-                                  (MAX_DMA_ADDRESS - 
-                                   ((unsigned long) CURRENT->buffer))>>9);
+               /*
+                * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+                * on a 64 bit machine!
+                */
+               max_size = buffer_chain_size();
+               dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
+               if ((unsigned long) max_size > dma_limit) {
+                       max_size = dma_limit;
+               }
                /* 64 kb boundaries */
                if (CROSS_64KB(CURRENT->buffer, max_size << 9))
                        max_size = (K_64 - ((long) CURRENT->buffer) % K_64)>>9;
@@ -3033,8 +3040,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
                        return -EPERM;
                LOCK_FDC(drive,1);
                for (cnt = 0; cnt < N_DRIVE; cnt++){
-                       if (TYPE(drive_state[cnt].fd_device) ==
-                           type &&
+                       if (TYPE(drive_state[cnt].fd_device) == type &&
                            drive_state[cnt].fd_ref)
                                set_bit(drive, &fake_change);
                }
@@ -3047,7 +3053,9 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
                for (cnt = 0; cnt < N_DRIVE; cnt++){
                        if (TYPE(drive_state[cnt].fd_device) == type &&
                            drive_state[cnt].fd_ref)
-                               check_disk_change(drive_state[cnt].fd_device);
+                               check_disk_change(
+                                       MKDEV(FLOPPY_MAJOR,
+                                             drive_state[cnt].fd_device));
                }
        } else {
                LOCK_FDC(drive,1);
@@ -3125,7 +3133,7 @@ static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
                                DPRINT1("warning: obsolete ioctl 0x%x\n",ocmd);
                                DPRINT("please recompile your program\n");
                                /* these ioctls only existed
-                                * in two (development)
+                                * in six (development)
                                 * kernels anyways. That's why we
                                 * complain about these, and not about
                                 * the much older 0x00xx ioctl's
@@ -3206,12 +3214,12 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                return -EPERM;
 
        /* verify writability of result, and fail early */
-       if (_IOC_DIR(cmd) & _IOC_WRITE)
+       if (_IOC_DIR(cmd) & _IOC_READ)
                ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
                
        /* copyin */
        CLEARSTRUCT(&inparam);
-       if (_IOC_DIR(cmd) & _IOC_READ)
+       if (_IOC_DIR(cmd) & _IOC_WRITE)
                ECALL(fd_copyin((void *)param, &inparam, size))
 
        switch (cmd) {
@@ -3304,7 +3312,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                        return -EINVAL;
        }
 
-       if (_IOC_DIR(cmd) & _IOC_WRITE)
+       if (_IOC_DIR(cmd) & _IOC_READ)
                return fd_copyout((void *)param, outparam, size);
        else
                return 0;
@@ -3479,11 +3487,10 @@ static int floppy_open(struct inode * inode, struct file * filp)
        }
 
        UDRS->fd_device = MINOR(inode->i_rdev);
-
-       if (old_dev && old_dev != inode->i_rdev) {
+       if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
                if (buffer_drive == drive)
                        buffer_track = -1;
-               invalidate_buffers(old_dev);
+               invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
        }
 
        /* Allow ioctls if we have write-permissions even if read-only open */
@@ -3831,6 +3838,7 @@ int floppy_init(void)
                CLEARSTRUCT(UDRS);
                CLEARSTRUCT(UDRWE);
                UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+               UDRS->fd_device = -1;
                floppy_track_buffer = NULL;
                max_buffer_sectors = 0;
        }
index d5554b7f9399124e1cddc41b64e2888542db8796..6b5a4813e5a108fd380e21f26531384fb4420ca0 100644 (file)
@@ -104,7 +104,7 @@ int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
  * force the transfer to start only after we have put all the requests
  * on the list.
  */
-static void plug_device(struct blk_dev_struct * dev, struct request * plug)
+static inline void plug_device(struct blk_dev_struct * dev, struct request * plug)
 {
        unsigned long flags;
 
@@ -121,7 +121,7 @@ static void plug_device(struct blk_dev_struct * dev, struct request * plug)
 /*
  * remove the plug and let it rip..
  */
-static void unplug_device(struct blk_dev_struct * dev)
+static inline void unplug_device(struct blk_dev_struct * dev)
 {
        struct request * req;
        unsigned long flags;
index d89753a1f8b16987c00d5cfccbf8f4241dc4c204..8207cf810f3a7a56888a1b58cb1e88626479e2e2 100644 (file)
 
 */
 
-#include <linux/config.h>
 
-#ifdef MODULE
-# include <linux/module.h>
-#endif
-
-#include <linux/version.h>
+#include <linux/module.h>
 
 #ifdef MODULE
-# ifndef CONFIG_MODVERSIONS
-    char kernel_version[]= UTS_RELEASE;
-# endif
 #define mcd_init init_module
-#else
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
 #endif
 
 #include <linux/errno.h>
index 592abf3a94cf6038daea3057f84e8ec3295dab6d..915649805343bf6a322c189cd5a625d048e320f1 100644 (file)
@@ -37,23 +37,7 @@ static const char *mcdx_c_version
                = "mcdx.c,v 1.17 1995/11/06 01:07:57 heiko Exp";
 #endif
 
-#include <linux/config.h>
-
-#ifdef MODULE
 #include <linux/module.h>
-#endif
-
-#include <linux/version.h>
-
-#ifdef MODULE
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[] = UTS_RELEASE;
-#endif
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#define MOD_IN_USE 1
-#endif MODULE
 
 #include <linux/errno.h>
 #include <linux/signal.h>
index a49344ffe2df42cf366d89fcd52a877b4179a58b..6c253b3a2e155c6207c4d02164f7976b1e9d3435 100644 (file)
@@ -1,5 +1,5 @@
 static char rcsid[] =
-"$Revision: 1.36.3.2 $$Date: 1995/09/08 22:07:14 $";
+"$Revision: 1.36.3.4 $$Date: 1995/11/13 20:45:10 $";
 /*
  *  linux/drivers/char/cyclades.c
  *
@@ -24,6 +24,19 @@ static char rcsid[] =
  *   int cy_open(struct tty_struct *tty, struct file *filp);
  *
  * $Log: cyclades.c,v $
+ * Revision 1.36.3.4  1995/11/13  20:45:10  bentson
+ * Changes by Corey Minyard <minyard@wf-rch.cirr.com> distributed
+ * in 1.3.41 kernel to remove a possible race condition, extend
+ * some error messages, and let the driver run as a loadable module
+ * Change by Alan Wendt <alan@ez0.ezlink.com> to remove a
+ * possible race condition.
+ * Change by Marcio Saito <marcio@cyclades.com> to fix PCI addressing.
+ *
+ * Revision 1.36.3.3  1995/11/13  19:44:48  bentson
+ * Changes by Linus Torvalds in 1.3.33 kernel distribution
+ * required due to reordering of driver initialization.
+ * Drivers are now initialized *after* memory management.
+ *
  * Revision 1.36.3.2  1995/09/08  22:07:14  bentson
  * remove printk from ISR; fix typo
  *
@@ -452,12 +465,13 @@ SP(char *data){
         console_print(data);
     restore_flags(flags);
 }
-char scrn[2];
 void
 CP(char data){
   unsigned long flags;
+  char scrn[2];
     save_flags(flags); cli();
         scrn[0] = data;
+        scrn[1] = '\0';
         console_print(scrn);
     restore_flags(flags);
 }/* CP */
@@ -1296,8 +1310,10 @@ shutdown(struct cyclades_port * info)
      */
     save_flags(flags); cli();
        if (info->xmit_buf){
-           free_page((unsigned long) info->xmit_buf);
-           info->xmit_buf = 0;
+            unsigned long temp;
+            temp = info->xmit_buf;
+            info->xmit_buf = 0;
+           free_page((unsigned long) temp);
        }
 
        base_addr[CyCAR<<index] = (u_char)channel;
@@ -2693,7 +2709,7 @@ cy_init_card(unsigned char *true_base_addr,int index)
            and this must be a Cyclom-16Y, not a Cyclom-32Ye.
         */
         if (chip_number == 4
-        && *(true_base_addr + cy_chip_offset[0] + CyGFRCR) == 0){
+        && *(true_base_addr + (cy_chip_offset[0]<<index) + (CyGFRCR<<index)) == 0){
            return chip_number;
         }
 
index 210a98a98e2ccd28b3635fe9438bf32a7008453f..a98e2b8e5d0bd96246613c23bf3c913092167d69 100644 (file)
@@ -25,6 +25,7 @@
     Changelog:
 
     Paul Gortmaker     : add support for the 2nd 8kB of RAM on 16 bit cards.
+    Paul Gortmaker     : multiple card support for module users.
 
 */
 
@@ -182,16 +183,24 @@ el2_probe1(struct device *dev, int ioaddr)
        return ENODEV;
     }
 
-    if (dev == NULL)
-       dev = init_etherdev(0, sizeof(struct ei_device));
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+       printk("3c503.c: Passed a NULL device.\n");
+       dev = init_etherdev(0, 0);
+    }
 
     if (ei_debug  &&  version_printed++ == 0)
        printk(version);
 
     dev->base_addr = ioaddr;
-    ethdev_init(dev);
 
-    printk("%s: 3c503 at i/o base %#3x, node address", dev->name, ioaddr);
+    /* Allocate dev->priv and fill in 8390 specific dev fields. */
+    if (ethdev_init(dev)) {
+       printk ("3c503: unable to allocate memory for dev->priv.\n");
+       return -ENOMEM;
+     }
+
+    printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
 
     /* Retrieve and print the ethernet address. */
     for (i = 0; i < 6; i++)
@@ -220,6 +229,7 @@ el2_probe1(struct device *dev, int ioaddr)
 #else
     ei_status.interface_num = dev->mem_end & 0xf;
 #endif
+    printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
 
     if ((membase_reg & 0xf0) == 0) {
        dev->mem_start = 0;
@@ -241,7 +251,7 @@ el2_probe1(struct device *dev, int ioaddr)
                writel(test_val, mem_base + i);
                if (readl(mem_base) != 0xba5eba5e
                    || readl(mem_base + i) != test_val) {
-                   printk(" memory failure or memory address conflict.\n");
+                   printk("3c503.c: memory failure or memory address conflict.\n");
                    dev->mem_start = 0;
                    ei_status.name = "3c503-PIO";
                    break;
@@ -292,7 +302,7 @@ el2_probe1(struct device *dev, int ioaddr)
     if (dev->irq == 2)
        dev->irq = 9;
     else if (dev->irq > 5 && dev->irq != 9) {
-       printk("\n3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+       printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
               dev->irq);
        dev->irq = 0;
     }
@@ -304,7 +314,7 @@ el2_probe1(struct device *dev, int ioaddr)
     dev->stop = &el2_close;
 
     if (dev->mem_start)
-       printk("\n%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+       printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
                dev->name, ei_status.name, (wordlength+1)<<3,
                dev->mem_start, dev->mem_end-1);
 
@@ -344,8 +354,11 @@ el2_open(struct device *dev)
            return -EAGAIN;
        }
     }
+
     el2_init_card(dev);
-    return ei_open(dev);
+    ei_open(dev);
+    MOD_INC_USE_COUNT;
+    return 0;
 }
 
 static int
@@ -356,9 +369,8 @@ el2_close(struct device *dev)
     irq2dev_map[dev->irq] = NULL;
     outb(EGACFR_IRQOFF, E33G_GACFR);   /* disable interrupts. */
 
-    NS8390_init(dev, 0);
-    dev->start = 0;
-
+    ei_close(dev);
+    MOD_DEC_USE_COUNT;
     return 0;
 }
 
@@ -526,56 +538,69 @@ el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_off
     }
     outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
 }
-#ifdef MODULE
-static struct device el2_drv =
-{"3c503", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_probe };
-       
-static struct device el2pio_drv =
-{"3c503pio", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, el2_pio_probe };
 
-static int io = 0x300;
-static int irq = 0;
-
-static int no_pio = 1;
-int init_module(void)
+\f
+#ifdef MODULE
+#define MAX_EL2_CARDS  4       /* Max number of EL2 cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, };
+static struct device dev_el2[MAX_EL2_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_EL2_CARDS] = { 0, };
+static int irq[MAX_EL2_CARDS]  = { 0, };
+static int xcvr[MAX_EL2_CARDS] = { 0, };       /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-       int rc1, rc2;
-       el2_drv.base_addr = io;
-       el2_drv.irq       = irq;
-       el2pio_drv.base_addr = io;
-       el2pio_drv.irq       = irq;
-
-       if (io == 0)
-           printk("3c503: You should not use auto-probing with insmod!\n");
-
-       rc2 = 0;
-       no_pio = 1;
-       rc1 = register_netdev(&el2_drv);
-       if (rc1 != 0) {
-           rc2 = register_netdev(&el2pio_drv);
-           no_pio = 0;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+               struct device *dev = &dev_el2[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->mem_end = xcvr[this_dev];  /* low 4bits = xcvr sel. */
+               dev->init = el2_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only autoprobe 1st one */
+                       printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
 
-       if (rc1 != 0 && rc2 != 0)
-               return -EIO;
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       int ioaddr;
-
-       if (no_pio) {
-               ioaddr = el2_drv.base_addr;
-               unregister_netdev(&el2_drv);
-       } else {
-               ioaddr = el2pio_drv.base_addr;
-               unregister_netdev(&el2pio_drv);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+               struct device *dev = &dev_el2[this_dev];
+               if (dev->priv != NULL) {
+                       /* NB: el2_close() handles free_irq + irq2dev map */
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       release_region(dev->base_addr, EL2_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
        }
-
-       /* If we don't do this, we can't re-insmod it later. */
-       release_region(ioaddr, EL2_IO_EXTENT);
 }
 #endif /* MODULE */
 \f
index f21462bc9b4b4a907a678c6a1a1ece52f976d74b..dcd1ff814d05bc63cabe3a64c4b652925dc288c0 100644 (file)
@@ -55,7 +55,6 @@ static const char *version =
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/skbuff.h>
 
 #include "8390.h"
 
@@ -118,10 +117,11 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
 int ei_open(struct device *dev)
 {
     struct ei_device *ei_local = (struct ei_device *) dev->priv;
-    
-    if ( ! ei_local) {
-               printk("%s: Opening a non-existent physical device\n", dev->name);
-               return ENXIO;
+
+    /* This can't happen unless somebody forgot to call ethdev_init(). */
+    if (ei_local == NULL) {
+       printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
+       return -ENXIO;
     }
     
     irq2dev_map[dev->irq] = dev;
@@ -131,6 +131,14 @@ int ei_open(struct device *dev)
     return 0;
 }
 
+/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+int ei_close(struct device *dev)
+{
+    NS8390_init(dev, 0);
+    dev->start = 0;
+    return 0;
+}
+
 static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
 {
     int e8390_base = dev->base_addr;
@@ -603,10 +611,6 @@ int ethdev_init(struct device *dev)
                ei_local->pingpong = ei_pingpong;
     }
     
-    /* The open call may be overridden by the card-specific code. */
-    if (dev->open == NULL)
-               dev->open = &ei_open;
-    /* We should have a dev->stop entry also. */
     dev->hard_start_xmit = &ei_start_xmit;
     dev->get_stats     = get_stats;
 #ifdef HAVE_MULTICAST
index fce3b00d70d8c0bd31431d60e143fec75fd10ad1..17b8cdb588a685cb9b66779e31cdd168e7e2eae6 100644 (file)
@@ -32,12 +32,13 @@ extern int ethif_init(struct device *dev);
 extern int ethdev_init(struct device *dev);
 extern void NS8390_init(struct device *dev, int startp);
 extern int ei_open(struct device *dev);
+extern int ei_close(struct device *dev);
 extern void ei_interrupt(int irq, struct pt_regs *regs);
 
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c */
 extern struct device *irq2dev_map[16];
-extern void autoirq_setup(int waittime);
+extern int autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 #endif
 
index 1a78940fede8626c3af713329d6be0960ac8d134..3752df5c858ba9b1acd35a578f772b84d37f9cab 100644 (file)
@@ -10,11 +10,7 @@ tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
   comment 'CCP compressors for PPP are only built as modules.'
 fi
-if [ "$CONFIG_AX25" = "y" ]; then
-  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
-else
-  bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
-fi
+bool '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
 bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA
index 6cb6a82d6c29e334cb80824a97fa1ac7f7560a3e..3bf3176da77c577c1f01c76c0d7a22e85254c023 100644 (file)
@@ -139,7 +139,7 @@ else
   endif
 endif
 
-ifneq ($(CONFIG_PPP),n)
+ifdef CONFIG_PPP
   M_OBJS += bsd_comp.o
 endif
 
index dac48c2f990c3d3b66029fc506157b60901178f7..65bd3e1cbf33bb11d9b97f96399a8f35268338ac 100644 (file)
@@ -97,15 +97,21 @@ ethif_probe(struct device *dev)
 #if defined(CONFIG_EL2) || defined(EL2)        /* 3c503 */
        && el2_probe(dev)
 #endif
-#if defined(CONFIG_NE2000) || defined(NE2000)
-       && ne_probe(dev)
-#endif
 #if defined(CONFIG_HPLAN) || defined(HPLAN)
        && hp_probe(dev)
 #endif
 #if defined(CONFIG_HPLAN_PLUS)
        && hp_plus_probe(dev)
 #endif
+#ifdef CONFIG_AC3200           /* Ansel Communications EISA 3200. */
+       && ac3200_probe(dev)
+#endif
+#ifdef CONFIG_E2100            /* Cabletron E21xx series. */
+       && e2100_probe(dev)
+#endif
+#if defined(CONFIG_NE2000) || defined(NE2000)
+       && ne_probe(dev)
+#endif
 #ifdef CONFIG_AT1500
        && at1500_probe(dev)
 #endif
@@ -148,12 +154,6 @@ ethif_probe(struct device *dev)
 #ifdef CONFIG_ELPLUS           /* 3c505 */
        && elplus_probe(dev)
 #endif
-#ifdef CONFIG_AC3200           /* Ansel Communications EISA 3200. */
-       && ac3200_probe(dev)
-#endif
-#ifdef CONFIG_E2100            /* Cabletron E21xx series. */
-       && e2100_probe(dev)
-#endif
 #ifdef CONFIG_DE600            /* D-Link DE-600 adapter */
        && de600_probe(dev)
 #endif
index 86eb9a6dd4b35663c4fd0046885884217c00aa6d..bbb5332f33f0561b33f2e32e1db41d8c3c891ced 100644 (file)
@@ -97,13 +97,16 @@ int ac3200_probe(struct device *dev)
        else if (ioaddr > 0)            /* Don't probe at all. */
                return ENXIO;
 
-       /* If you have a pre-pl15 machine you should delete this line. */
+       /* If you have a pre 0.99pl15 machine you should delete this line. */
        if ( ! EISA_bus)
                return ENXIO;
 
-       for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000)
+       for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+               if (check_region(ioaddr, AC_IO_EXTENT))
+                       continue;
                if (ac_probe1(ioaddr, dev) == 0)
                        return 0;
+       }
 
        return ENODEV;
 }
@@ -138,6 +141,13 @@ static int ac_probe1(int ioaddr, struct device *dev)
                        return ENODEV;
                }
 
+
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("ac3200.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
+
        for(i = 0; i < ETHER_ADDR_LEN; i++)
                dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
 
@@ -159,6 +169,13 @@ static int ac_probe1(int ioaddr, struct device *dev)
                return EAGAIN;
        }
 
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) {
+               printk (" unable to allocate memory for dev->priv.\n");
+               free_irq(dev->irq);
+               return -ENOMEM;
+       }
+
        request_region(ioaddr, AC_IO_EXTENT, "ac3200");
 
        dev->base_addr = ioaddr;
@@ -180,8 +197,6 @@ static int ac_probe1(int ioaddr, struct device *dev)
        dev->mem_end = dev->rmem_end = dev->mem_start
                + (AC_STOP_PG - AC_START_PG)*256;
 
-       ethdev_init(dev);
-
        ei_status.name = "AC3200";
        ei_status.tx_start_page = AC_START_PG;
        ei_status.rx_start_page = AC_START_PG + TX_PAGES;
@@ -208,7 +223,6 @@ static int ac_probe1(int ioaddr, struct device *dev)
 
 static int ac_open(struct device *dev)
 {
-       int rc;
 #ifdef notyet
        /* Someday we may enable the IRQ and shared memory here. */
        int ioaddr = dev->base_addr;
@@ -217,8 +231,7 @@ static int ac_open(struct device *dev)
                return -EAGAIN;
 #endif
 
-       rc = ei_open(dev);
-       if (rc != 0) return rc;
+       ei_open(dev);
 
        MOD_INC_USE_COUNT;
 
@@ -293,7 +306,7 @@ static int ac_close_card(struct device *dev)
        irq2dev_map[dev->irq] = 0;
 #endif
 
-       NS8390_init(dev, 0);
+       ei_close(dev);
 
        MOD_DEC_USE_COUNT;
 
@@ -301,35 +314,64 @@ static int ac_close_card(struct device *dev)
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ac3200 = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, ac3200_probe };
-
-static int io = 0;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_AC32_CARDS 4       /* Max number of AC32 cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, };
+static struct device dev_ac32[MAX_AC32_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_AC32_CARDS] = { 0, };
+static int irq[MAX_AC32_CARDS]  = { 0, };
+static int mem[MAX_AC32_CARDS] = { 0, };
+
+int
+init_module(void)
 {
-       dev_ac3200.base_addr = io;
-       dev_ac3200.irq       = irq;
-       if (register_netdev(&dev_ac3200) != 0) {
-               printk("ac3200: register_netdev() returned non-zero.\n");
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+               struct device *dev = &dev_ac32[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->mem_start = mem[this_dev];         /* Currently ignored by driver */
+               dev->init = ac3200_probe;
+               /* Default is to only install one card. */
+               if (io[this_dev] == 0 && this_dev != 0) break;
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       unregister_netdev(&dev_ac3200);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       free_irq(dev_ac3200.irq);
-       release_region(dev_ac3200.base_addr, AC_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+               struct device *dev = &dev_ac32[this_dev];
+               if (dev->priv != NULL) {
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       /* Someday free_irq + irq2dev may be in ac_close_card() */
+                       free_irq(dev->irq);
+                       irq2dev_map[dev->irq] = NULL;
+                       release_region(dev->base_addr, AC_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index 756a72bfe3362fa017bcbef06ea8c49ef287aa4f..ac10e9041d8f014700c2c08be6b3dfcb16f8a4af 100644 (file)
@@ -139,6 +139,7 @@ int e21_probe1(struct device *dev, int ioaddr)
 {
        int i, status;
        unsigned char *station_addr = dev->dev_addr;
+       static unsigned version_printed = 0;
 
        /* First check the station address for the Ctron prefix. */
        if (inb(ioaddr + E21_SAPROM + 0) != 0x00
@@ -160,6 +161,15 @@ int e21_probe1(struct device *dev, int ioaddr)
        inb(ioaddr + E21_MEDIA);                /* Point to media selection. */
        outb(0, ioaddr + E21_ASIC);     /* and disable the secondary interface. */
 
+       if (ei_debug  &&  version_printed++ == 0)
+               printk(version);
+
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("e2100.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
+
        printk("%s: E21** at %#3x,", dev->name, ioaddr);
        for (i = 0; i < 6; i++)
                printk(" %02X", station_addr[i]);
@@ -178,14 +188,18 @@ int e21_probe1(struct device *dev, int ioaddr)
        } else if (dev->irq == 2)       /* Fixup luser bogosity: IRQ2 is really IRQ9 */
                dev->irq = 9;
 
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) {
+               printk (" unable to get memory for dev->priv.\n");
+               return -ENOMEM;
+       }
+
        /* Grab the region so we can find a different board if IRQ select fails. */
        request_region(ioaddr, E21_IO_EXTENT, "e2100");
 
        /* The 8390 is at the base address. */
        dev->base_addr = ioaddr;
 
-       ethdev_init(dev);
-
        ei_status.name = "E2100";
        ei_status.word16 = 1;
        ei_status.tx_start_page = E21_TX_START_PG;
@@ -223,9 +237,6 @@ int e21_probe1(struct device *dev, int ioaddr)
        printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
                   dev->if_port ? "secondary" : "primary", dev->mem_start);
 
-       if (ei_debug > 0)
-               printk(version);
-
        ei_status.reset_8390 = &e21_reset_8390;
        ei_status.block_input = &e21_block_input;
        ei_status.block_output = &e21_block_output;
@@ -241,7 +252,6 @@ static int
 e21_open(struct device *dev)
 {
        short ioaddr = dev->base_addr;
-       int rc;
 
        if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) {
                return EBUSY;
@@ -257,8 +267,7 @@ e21_open(struct device *dev)
        inb(ioaddr + E21_MEM_BASE);
        outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
 
-       rc = ei_open(dev);
-       if (rc != 0) return rc;
+       ei_open(dev);
        MOD_INC_USE_COUNT;
        return 0;
 }
@@ -338,8 +347,8 @@ e21_close(struct device *dev)
        if (ei_debug > 1)
                printk("%s: Shutting down ethercard.\n", dev->name);
 
-    free_irq(dev->irq);
-    dev->irq = ei_status.saved_irq;
+       free_irq(dev->irq);
+       dev->irq = ei_status.saved_irq;
 
        /* Shut off the interrupt line and secondary interface. */
        inb(ioaddr + E21_IRQ_LOW);
@@ -347,9 +356,9 @@ e21_close(struct device *dev)
        inb(ioaddr + E21_IRQ_HIGH);                     /* High IRQ bit, and if_port. */
        outb(0, ioaddr + E21_ASIC);
 
-    irq2dev_map[dev->irq] = NULL;
+       irq2dev_map[dev->irq] = NULL;
 
-       NS8390_init(dev, 0);
+       ei_close(dev);
 
        /* Double-check that the memory has been turned off, because really
           really bad things happen if it isn't. */
@@ -365,37 +374,70 @@ struct netdev_entry e21_drv =
 {"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list};
 #endif
 
+\f
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_e2100 = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, e2100_probe };
-
-static int io = 0x300;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_E21_CARDS  4       /* Max number of E21 cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, };
+static struct device dev_e21[MAX_E21_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_E21_CARDS] = { 0, };
+static int irq[MAX_E21_CARDS]  = { 0, };
+static int mem[MAX_E21_CARDS] = { 0, };
+static int xcvr[MAX_E21_CARDS] = { 0, };               /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-       if (io == 0)
-               printk("e2100: You should not use auto-probing with insmod!\n");
-       dev_e2100.base_addr = io;
-       dev_e2100.irq       = irq;
-       if (register_netdev(&dev_e2100) != 0) {
-               printk("e2100: register_netdev() returned non-zero.\n");
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+               struct device *dev = &dev_e21[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->mem_start = mem[this_dev];
+               dev->mem_end = xcvr[this_dev];  /* low 4bits = xcvr sel. */
+               dev->init = e2100_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only autoprobe 1st one */
+                       printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       unregister_netdev(&dev_e2100);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       release_region(dev_e2100.base_addr, E21_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+               struct device *dev = &dev_e21[this_dev];
+               if (dev->priv != NULL) {
+                       /* NB: e21_close() handles free_irq + irq2dev map */
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       release_region(dev->base_addr, E21_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index 57e39498047c8a17e4bf19802421d868cf180e64..aed7ee01e7db2106b83f93ad76db3f8de88d93a4 100644 (file)
@@ -150,14 +150,21 @@ int hpp_probe1(struct device *dev, int ioaddr)
        unsigned char checksum = 0;
        const char *name = "HP-PC-LAN+";
        int mem_start;
+       static unsigned version_printed = 0;
 
        /* Check for the HP+ signature, 50 48 0x 53. */
        if (inw(ioaddr + HP_ID) != 0x4850
                || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
                return ENODEV;
 
-    if (dev == NULL)
-               dev = init_etherdev(0, sizeof(struct ei_device));
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("hp-plus.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
+
+       if (ei_debug  &&  version_printed++ == 0)
+               printk(version);
 
        printk("%s: %s at %#3x,", dev->name, name, ioaddr);
 
@@ -181,6 +188,12 @@ int hpp_probe1(struct device *dev, int ioaddr)
                printk(" ID %4.4x", inw(ioaddr + 12));
        }
 
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) {
+               printk ("hp-plus.c: unable to allocate memory for dev->priv.\n");
+               return -ENOMEM;
+        }
+
        /* Grab the region so we can find another board if something fails. */
        request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
 
@@ -200,18 +213,14 @@ int hpp_probe1(struct device *dev, int ioaddr)
                }
        }
 
-       printk( "%s%s", KERN_INFO, version);
-
        /* Set the wrap registers for string I/O reads.   */
        outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 
        /* Set the base address to point to the NIC, not the "real" base! */
        dev->base_addr = ioaddr + NIC_OFFSET;
 
-       ethdev_init(dev);
-
-    dev->open = &hpp_open;
-    dev->stop = &hpp_close;
+       dev->open = &hpp_open;
+       dev->stop = &hpp_close;
 
        ei_status.name = name;
        ei_status.word16 = 0;           /* Agggghhhhh! Debug time: 2 days! */
@@ -267,7 +276,9 @@ hpp_open(struct device *dev)
        /* Select the operational page. */
        outw(Perf_Page, ioaddr + HP_PAGING);
 
-    return ei_open(dev);
+       ei_open(dev);
+       MOD_INC_USE_COUNT;
+       return 0;
 }
 
 static int
@@ -276,13 +287,14 @@ hpp_close(struct device *dev)
        int ioaddr = dev->base_addr - NIC_OFFSET;
        int option_reg = inw(ioaddr + HPP_OPTION);
 
-    free_irq(dev->irq);
-    irq2dev_map[dev->irq] = NULL;
-    NS8390_init(dev, 0);
+       free_irq(dev->irq);
+       irq2dev_map[dev->irq] = NULL;
+       ei_close(dev);
        outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
                 ioaddr + HPP_OPTION);
 
-    return 0;
+       MOD_DEC_USE_COUNT;
+       return 0;
 }
 
 static void
@@ -396,39 +408,67 @@ hpp_mem_block_output(struct device *dev, int count,
        return;
 }
 
+\f
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_hp = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, hp_plus_probe };
-
-static int io = 0x200;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_HPP_CARDS  4       /* Max number of HPP cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, };
+static struct device dev_hpp[MAX_HPP_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_HPP_CARDS] = { 0, };
+static int irq[MAX_HPP_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-       if (io == 0)
-               printk("HP-plus: You should not use auto-probing with insmod!\n");
-       dev_hp.base_addr = io;
-       dev_hp.irq       = irq;
-       if (register_netdev(&dev_hp) != 0) {
-               printk("HP-plus: register_netdev() returned non-zero.\n");
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+               struct device *dev = &dev_hpp[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->init = hp_plus_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only autoprobe 1st one */
+                       printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       int ioaddr = dev_hp.base_addr - NIC_OFFSET;
-
-       unregister_netdev(&dev_hp);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       release_region(ioaddr, HP_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+               struct device *dev = &dev_hpp[this_dev];
+               if (dev->priv != NULL) {
+                       /* NB: hpp_close() handles free_irq + irq2dev map */
+                       int ioaddr = dev->base_addr - NIC_OFFSET;
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       release_region(ioaddr, HP_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index a3631a803768fb69873f4f405aabac8e11502471..d0443a7dd3740c09f56ddf6577b53f45f1b6b1ab 100644 (file)
@@ -57,6 +57,8 @@ static unsigned int hppclan_portlist[] =
 int hp_probe(struct device *dev);
 int hp_probe1(struct device *dev, int ioaddr);
 
+static int hp_open(struct device *dev);
+static int hp_close(struct device *dev);
 static void hp_reset_8390(struct device *dev);
 static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                                        int ring_page);
@@ -106,6 +108,7 @@ int hp_probe1(struct device *dev, int ioaddr)
 {
        int i, board_id, wordmode;
        const char *name;
+       static unsigned version_printed = 0;
 
        /* Check for the HP physical address, 08 00 09 xx xx xx. */
        /* This really isn't good enough: we may pick up HP LANCE boards
@@ -126,9 +129,15 @@ int hp_probe1(struct device *dev, int ioaddr)
                wordmode = 0;
        }
 
-       if (dev == NULL)
-               dev = init_etherdev(0, sizeof(struct ei_device));
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("hp.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
 
+       if (ei_debug  &&  version_printed++ == 0)
+               printk(version);
        printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
 
        for(i = 0; i < ETHER_ADDR_LEN; i++)
@@ -167,16 +176,20 @@ int hp_probe1(struct device *dev, int ioaddr)
                }
        }
 
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) {
+               printk (" unable to get memory for dev->priv.\n");
+               free_irq(dev->irq);
+               return -ENOMEM;
+       }
+
        /* Grab the region so we can find another board if something fails. */
        request_region(ioaddr, HP_IO_EXTENT,"hp");
 
-       if (ei_debug > 1)
-               printk(version);
-
        /* Set the base address to point to the NIC, not the "real" base! */
        dev->base_addr = ioaddr + NIC_OFFSET;
-
-       ethdev_init(dev);
+       dev->open = &hp_open;
+       dev->stop = &hp_close;
 
        ei_status.name = name;
        ei_status.word16 = wordmode;
@@ -193,6 +206,22 @@ int hp_probe1(struct device *dev, int ioaddr)
        return 0;
 }
 
+static int
+hp_open(struct device *dev)
+{
+       ei_open(dev);
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int
+hp_close(struct device *dev)
+{
+       ei_close(dev);
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
 static void
 hp_reset_8390(struct device *dev)
 {
@@ -348,39 +377,66 @@ hp_init_card(struct device *dev)
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_hp = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, hp_probe };
-
-static int io = 300;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_HP_CARDS   4       /* Max number of HP cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, };
+static struct device dev_hp[MAX_HP_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_HP_CARDS] = { 0, };
+static int irq[MAX_HP_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-       if (io == 0)
-               printk("hp: You should not use auto-probing with insmod!\n");
-       dev_hp.base_addr = io;
-       dev_hp.irq       = irq;
-       if (register_netdev(&dev_hp) != 0) {
-               printk("hp: register_netdev() returned non-zero.\n");
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+               struct device *dev = &dev_hp[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->init = hp_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only autoprobe 1st one */
+                       printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       int ioaddr = dev_hp.base_addr - NIC_OFFSET;
-
-       unregister_netdev(&dev_hp);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       free_irq(dev_hp.irq);
-       release_region(ioaddr, HP_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+               struct device *dev = &dev_hp[this_dev];
+               if (dev->priv != NULL) {
+                       int ioaddr = dev->base_addr - NIC_OFFSET;
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       free_irq(dev->irq);
+                       irq2dev_map[dev->irq] = NULL;
+                       release_region(ioaddr, HP_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index 7f565aa312c62f71e02c41d02c0536c59824217f..ae22a6a4843dd3a02a6cd73ca5d3ae28a3ab1c1d 100644 (file)
@@ -21,6 +21,7 @@
     Paul Gortmaker     : use ENISR_RDC to monitor Tx PIO uploads, made
                          sanity checks and bad clone support optional.
     Paul Gortmaker     : new reset code, reset card after probe at boot.
+    Paul Gortmaker     : multiple card support for module users.
 
 */
 
@@ -53,6 +54,9 @@ static const char *version =
 /* Do we implement the read before write bugfix ? */
 /* #define NE_RW_BUGFIX */
 
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE   0x40 */
+
 /* ---- No user-serviceable parts below ---- */
 
 /* A zero-terminated list of I/O addresses to be probed. */
@@ -89,6 +93,9 @@ bad_clone_list[] = {
 int ne_probe(struct device *dev);
 static int ne_probe1(struct device *dev, int ioaddr);
 
+static int ne_open(struct device *dev);
+static int ne_close(struct device *dev);
+
 static void ne_reset_8390(struct device *dev);
 static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
                          int ring_page);
@@ -155,6 +162,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
     int start_page, stop_page;
     int neX000, ctron;
     int reg0 = inb_p(ioaddr);
+    static unsigned version_printed = 0;
 
     if (reg0 == 0xFF)
        return ENODEV;
@@ -173,6 +181,9 @@ static int ne_probe1(struct device *dev, int ioaddr)
        }
     }
 
+    if (ei_debug  &&  version_printed++ == 0)
+       printk(version);
+
     printk("NE*000 ethercard probe at %#3x:", ioaddr);
 
     /* Reset card. Who knows what dain-bramaged state it was left in. */
@@ -236,11 +247,6 @@ static int ne_probe1(struct device *dev, int ioaddr)
        stop_page = NE1SM_STOP_PG;
     }
 
-    for(i = 0; i < ETHER_ADDR_LEN; i++) {
-       dev->dev_addr[i] = SA_prom[i];
-       printk(" %2.2x", SA_prom[i]);
-    }
-
     neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
     ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
 
@@ -248,7 +254,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
     if (neX000) {
        name = (wordlength == 2) ? "NE2000" : "NE1000";
     } else if (ctron) {
-       name = "Cabletron";
+       name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
        start_page = 0x01;
        stop_page = (wordlength == 2) ? 0x40 : 0x20;
     } else {
@@ -279,9 +285,11 @@ static int ne_probe1(struct device *dev, int ioaddr)
 
     }
 
-
-    if (dev == NULL)
-       dev = init_etherdev(0, sizeof(struct ei_device));
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+       printk("ne.c: Passed a NULL device.\n");
+       dev = init_etherdev(0, 0);
+    }
 
     if (dev->irq < 2) {
        autoirq_setup(0);
@@ -297,11 +305,16 @@ static int ne_probe1(struct device *dev, int ioaddr)
        /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
           or don't know which one to set. */
        dev->irq = 9;
+
+    if (! dev->irq) {
+       printk(" failed to detect IRQ line.\n");
+       return EAGAIN;
+    }
     
     /* Snarf the interrupt now.  There's no point in waiting since we cannot
        share and the board will usually be enabled. */
     {
-       int irqval = request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");
+       int irqval = request_irq(dev->irq, ei_interrupt, 0, name);
        if (irqval) {
            printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
            return EAGAIN;
@@ -310,18 +323,23 @@ static int ne_probe1(struct device *dev, int ioaddr)
 
     dev->base_addr = ioaddr;
 
-    request_region(ioaddr, NE_IO_EXTENT, wordlength==2 ? "ne2000":"ne1000");
+    /* Allocate dev->priv and fill in 8390 specific dev fields. */
+    if (ethdev_init(dev)) {
+       printk (" unable to get memory for dev->priv.\n");
+       free_irq(dev->irq);
+       return -ENOMEM;
+    }
+    request_region(ioaddr, NE_IO_EXTENT, name);
 
-    for(i = 0; i < ETHER_ADDR_LEN; i++)
+    for(i = 0; i < ETHER_ADDR_LEN; i++) {
+       printk(" %2.2x", SA_prom[i]);
        dev->dev_addr[i] = SA_prom[i];
+    }
 
-    ethdev_init(dev);
     printk("\n%s: %s found at %#x, using IRQ %d.\n",
           dev->name, name, ioaddr, dev->irq);
 
-    if (ei_debug > 0)
-       printk(version);
-
     ei_status.name = name;
     ei_status.tx_start_page = start_page;
     ei_status.stop_page = stop_page;
@@ -337,10 +355,30 @@ static int ne_probe1(struct device *dev, int ioaddr)
     ei_status.block_input = &ne_block_input;
     ei_status.block_output = &ne_block_output;
     ei_status.get_8390_hdr = &ne_get_8390_hdr;
+    dev->open = &ne_open;
+    dev->stop = &ne_close;
     NS8390_init(dev, 0);
     return 0;
 }
 
+static int
+ne_open(struct device *dev)
+{
+    ei_open(dev);
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int
+ne_close(struct device *dev)
+{
+    if (ei_debug > 1)
+       printk("%s: Shutting down ethercard.\n", dev->name);
+    ei_close(dev);
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
 /* Hard reset the card.  This used to pause for the same period that a
    8390 reset command required, but that shouldn't be necessary. */
 static void
@@ -568,36 +606,71 @@ ne_block_output(struct device *dev, int count,
     return;
 }
 
+\f
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ne2000 = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, ne_probe };
+#define MAX_NE_CARDS   4       /* Max number of NE cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, };
+static struct device dev_ne[MAX_NE_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_NE_CARDS] = { 0, };
+static int irq[MAX_NE_CARDS]  = { 0, };
 
-static int io = 0x300;
-static int irq = 0;
+/* This is set up so that no autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
 
-int init_module(void)
+int
+init_module(void)
 {
-       if (io == 0)
-               printk("ne: You should not use auto-probing with insmod!\n");
-       dev_ne2000.base_addr = io;
-       dev_ne2000.irq       = irq;
-       if (register_netdev(&dev_ne2000) != 0)
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+               struct device *dev = &dev_ne[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->init = ne_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only complain once */
+                       printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+                       return -EPERM;
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
+       }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       unregister_netdev(&dev_ne2000);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       free_irq(dev_ne2000.irq);
-       release_region(dev_ne2000.base_addr, NE_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+               struct device *dev = &dev_ne[this_dev];
+               if (dev->priv != NULL) {
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       free_irq(dev->irq);
+                       irq2dev_map[dev->irq] = NULL;
+                       release_region(dev->base_addr, NE_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index c9eea60cb93ecb277bf9e00e84a581eac9f566e8..921f20348f7e85fe3e6b2b99c6c24e8243a72c18 100644 (file)
@@ -132,8 +132,9 @@ init_etherdev(struct device *dev, int sizeof_priv)
 }
 
 
-static int eth_mac_addr(struct device *dev, struct sockaddr *addr)
+static int eth_mac_addr(struct device *dev, void *p)
 {
+       struct sockaddr *addr=p;
        if(dev->start)
                return -EBUSY;
        memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
@@ -164,7 +165,8 @@ void ether_setup(struct device *dev)
        dev->hard_header        = eth_header;
        dev->rebuild_header = eth_rebuild_header;
        dev->set_mac_address = eth_mac_addr;
-       dev->header_cache = eth_header_cache;
+       dev->header_cache_bind = eth_header_cache_bind;
+       dev->header_cache_update = eth_header_cache_update;
 
        dev->type               = ARPHRD_ETHER;
        dev->hard_header_len = ETH_HLEN;
index 36a301ca6feb406bf1ef30dc023b65d060cd2c41..20537baf4c84ac2ed815dc7e39b0e50f0d0a8d5c 100644 (file)
        This driver does not support the programmed-I/O data transfer mode of
        the EtherEZ.  That support (if available) is smc-ez.c.  Nor does it
        use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+       Changelog:
+
+       Paul Gortmaker  : multiple card support for module users.
 */
 
 static const char *version =
@@ -117,6 +121,7 @@ int ultra_probe1(struct device *dev, int ioaddr)
        int checksum = 0;
        const char *model_name;
        unsigned char eeprom_irq = 0;
+       static unsigned version_printed = 0;
        /* Values from various config regs. */
        unsigned char num_pages, irqreg, addr;
        unsigned char idreg = inb(ioaddr + 7);
@@ -135,10 +140,14 @@ int ultra_probe1(struct device *dev, int ioaddr)
        if ((checksum & 0xff) != 0xFF)
                return ENODEV;
 
-       if (dev == NULL)
-               dev = init_etherdev(0, sizeof(struct ei_device));
-       if (dev == NULL) /* Still.. */
-               return ENOMEM; /* Out of memory ?? */
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("smc-ultra.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
+
+       if (ei_debug  &&  version_printed++ == 0)
+               printk(version);
 
        model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
 
@@ -175,7 +184,12 @@ int ultra_probe1(struct device *dev, int ioaddr)
                eeprom_irq = 1;
        }
 
-
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) {
+               printk (", no memory for dev->priv.\n");
+                return -ENOMEM;
+        }
        /* OK, we are certain this is going to work.  Setup the device. */
        request_region(ioaddr, ULTRA_IO_EXTENT, model_name);
 
@@ -190,8 +204,6 @@ int ultra_probe1(struct device *dev, int ioaddr)
                num_pages = num_pages_tbl[(addr >> 4) & 3];
        }
 
-       ethdev_init(dev);
-
        ei_status.name = model_name;
        ei_status.word16 = 1;
        ei_status.tx_start_page = START_PG;
@@ -204,8 +216,6 @@ int ultra_probe1(struct device *dev, int ioaddr)
 
        printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
                   dev->irq, dev->mem_start, dev->mem_end-1);
-       if (ei_debug > 0)
-               printk(version);
 
        ei_status.reset_8390 = &ultra_reset_8390;
        ei_status.block_input = &ultra_block_input;
@@ -222,7 +232,6 @@ static int
 ultra_open(struct device *dev)
 {
        int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
-       int rc;
 
        if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name))
                return -EAGAIN;
@@ -230,8 +239,7 @@ ultra_open(struct device *dev)
        outb(ULTRA_MEMENB, ioaddr);     /* Enable memory, 16 bit mode. */
        outb(0x80, ioaddr + 5);
        outb(0x01, ioaddr + 6);         /* Enable interrupts and memory. */
-       rc = ei_open(dev);
-       if (rc != 0) return rc;
+       ei_open(dev);
        MOD_INC_USE_COUNT;
        return 0;
 }
@@ -330,39 +338,67 @@ ultra_close_card(struct device *dev)
        return 0;
 }
 
+\f
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_ultra = {
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, ultra_probe };
-
-static int io = 0x200;
-static int irq = 0;
-
-int init_module(void)
+#define MAX_ULTRA_CARDS        4       /* Max number of Ultra cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA_CARDS] = {
+       {
+               NULL,           /* assign a chunk of namelist[] below */
+               0, 0, 0, 0,
+               0, 0,
+               0, 0, 0, NULL, NULL
+       },
+};
+
+static int io[MAX_ULTRA_CARDS] = { 0, };
+static int irq[MAX_ULTRA_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
 {
-       if (io == 0)
-               printk("smc-ultra: You should not use auto-probing with insmod!\n");
-       dev_ultra.base_addr = io;
-       dev_ultra.irq       = irq;
-       if (register_netdev(&dev_ultra) != 0) {
-               printk("smc-ultra: register_netdev() returned non-zero.\n");
-               return -EIO;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+               struct device *dev = &dev_ultra[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->init = ultra_probe;
+               if (io[this_dev] == 0)  {
+                       if (this_dev != 0) break; /* only autoprobe 1st one */
+                       printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+               }
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
+               }
+               found++;
        }
+
        return 0;
 }
 
 void
 cleanup_module(void)
 {
-       int ioaddr = dev_ultra.base_addr - ULTRA_NIC_OFFSET;
-
-       unregister_netdev(&dev_ultra);
-
-       /* If we don't do this, we can't re-insmod it later. */
-       release_region(ioaddr, ULTRA_IO_EXTENT);
+       int this_dev;
+
+       for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+               struct device *dev = &dev_ultra[this_dev];
+               if (dev->priv != NULL) {
+                       /* NB: ultra_close_card() does free_irq + irq2dev */
+                       int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       release_region(ioaddr, ULTRA_IO_EXTENT);
+                       unregister_netdev(dev);
+               }
+       }
 }
 #endif /* MODULE */
 \f
index dcd54df4840ccba576c53a29699bde913416665d..bac60259a35521be964418719fd433744d82f55d 100644 (file)
@@ -88,7 +88,7 @@ int tunnel_init(struct device *dev)
        dev->addr_len=0;
        dev->hard_header_len=0;
        dev->hard_header=NULL;
-       dev->header_cache=NULL;
+       dev->header_cache_bind=NULL;
        dev->rebuild_header=NULL;
        /* End of stomp 8) */
        return 0;
@@ -271,7 +271,6 @@ tunnel_get_stats(struct device *dev)
 }
 
 #ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
 
 static int tunnel_probe(struct device *dev)
 {
index eacf953521a339fa40c8ff5758f7e5183ec9654b..84a6129bca1d9ff666c3597ece6be5fae0c979eb 100644 (file)
@@ -18,7 +18,9 @@
 
        Changelog:
 
-       Paul Gortmaker  : multiple card support for module users
+       Paul Gortmaker  : multiple card support for module users, support
+                         for non-standard memory sizes.
+                        
 
 */
 
@@ -121,8 +123,11 @@ int wd_probe1(struct device *dev, int ioaddr)
                || (checksum & 0xff) != 0xFF)
                return ENODEV;
 
-       if (dev == NULL)
-               dev = init_etherdev(0, sizeof(struct ei_device));
+       /* We should have a "dev" from Space.c or the static module table. */
+       if (dev == NULL) {
+               printk("wd.c: Passed a NULL device.\n");
+               dev = init_etherdev(0, 0);
+       }
 
        if (ei_debug  &&  version_printed++ == 0)
                printk(version);
@@ -246,20 +251,32 @@ int wd_probe1(struct device *dev, int ioaddr)
                return EAGAIN;
        }
 
+       /* Allocate dev->priv and fill in 8390 specific dev fields. */
+       if (ethdev_init(dev)) { 
+               printk (" unable to get memory for dev->priv.\n");
+               free_irq(dev->irq);
+               return -ENOMEM;
+       }
+
        /* OK, were are certain this is going to work.  Setup the device. */
        request_region(ioaddr, WD_IO_EXTENT,"wd");
-       ethdev_init(dev);
 
        ei_status.name = model_name;
        ei_status.word16 = word16;
        ei_status.tx_start_page = WD_START_PG;
        ei_status.rx_start_page = WD_START_PG + TX_PAGES;
-       ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
 
        /* Don't map in the shared memory until the board is actually opened. */
        dev->rmem_start = dev->mem_start + TX_PAGES*256;
-       dev->mem_end = dev->rmem_end
-               = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+
+       /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+       if (dev->mem_end != 0) {
+               ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+       } else {
+               ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+               dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+       }
+       dev->rmem_end = dev->mem_end;
 
        printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
                   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
@@ -286,7 +303,6 @@ static int
 wd_open(struct device *dev)
 {
   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
-  int rc;
 
   /* Map in the shared memory. Always set register 0 last to remain
         compatible with very old boards. */
@@ -297,8 +313,7 @@ wd_open(struct device *dev)
          outb(ei_status.reg5, ioaddr+WD_CMDREG5);
   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
 
-  rc = ei_open(dev);
-  if (rc != 0) return rc;
+  ei_open(dev);
   MOD_INC_USE_COUNT;
   return 0;
 }
@@ -392,11 +407,11 @@ wd_close_card(struct device *dev)
 
        if (ei_debug > 1)
                printk("%s: Shutting down ethercard.\n", dev->name);
-       NS8390_init(dev, 0);
-       dev->start = 0;
+       ei_close(dev);
 
        /* Change from 16-bit to 8-bit shared memory so reboot works. */
-       outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+       if (ei_status.word16)
+               outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
 
        /* And disable the shared memory. */
        outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
@@ -406,12 +421,12 @@ wd_close_card(struct device *dev)
        return 0;
 }
 
-
+\f
 #ifdef MODULE
-#define MAX_WD_MODS    4       /* Max number of wd modules allowed */
-#define NAMELEN                9       /* # of chars for storing dev->name */
-static char namelist[NAMELEN * MAX_WD_MODS] = { 0, };
-static struct device dev_wd80x3[MAX_WD_MODS] = {
+#define MAX_WD_CARDS   4       /* Max number of wd cards per module */
+#define NAMELEN                8       /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, };
+static struct device dev_wd[MAX_WD_CARDS] = {
        {
                NULL,           /* assign a chunk of namelist[] below */
                0, 0, 0, 0,
@@ -420,31 +435,36 @@ static struct device dev_wd80x3[MAX_WD_MODS] = {
        },
 };
 
-static int io[MAX_WD_MODS] = { 0, };
-static int irq[MAX_WD_MODS]  = { 0, };
-static int mem[MAX_WD_MODS] = { 0, };
+static int io[MAX_WD_CARDS] = { 0, };
+static int irq[MAX_WD_CARDS]  = { 0, };
+static int mem[MAX_WD_CARDS] = { 0, };
+static int mem_end[MAX_WD_CARDS] = { 0, };     /* for non std. mem size */
 
 /* This is set up so that only a single autoprobe takes place per call.
 ISA device autoprobes on a running machine are not recommended. */
 int
 init_module(void)
 {
-       int this_dev;
-
-       for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
-               dev_wd80x3[this_dev].name = namelist+(NAMELEN*this_dev);
-               dev_wd80x3[this_dev].irq = irq[this_dev];
-               dev_wd80x3[this_dev].base_addr = io[this_dev];
-               dev_wd80x3[this_dev].mem_start = mem[this_dev];
-               dev_wd80x3[this_dev].init = wd_probe;
+       int this_dev, found = 0;
+
+       for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+               struct device *dev = &dev_wd[this_dev];
+               dev->name = namelist+(NAMELEN*this_dev);
+               dev->irq = irq[this_dev];
+               dev->base_addr = io[this_dev];
+               dev->mem_start = mem[this_dev];
+               dev->mem_end = mem_end[this_dev];
+               dev->init = wd_probe;
                if (io[this_dev] == 0)  {
                        if (this_dev != 0) break; /* only autoprobe 1st one */
                        printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
                }
-               if (register_netdev(&dev_wd80x3[this_dev]) != 0) {
-                       printk(KERN_WARNING "modules: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
-                       return -EIO;
+               if (register_netdev(dev) != 0) {
+                       printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+                       if (found != 0) return 0;       /* Got at least one. */
+                       return -ENXIO;
                }
+               found++;
        }
 
        return 0;
@@ -455,12 +475,16 @@ cleanup_module(void)
 {
        int this_dev;
 
-       for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
-               if (dev_wd80x3[this_dev].priv != NULL) {
-                       int ioaddr = dev_wd80x3[this_dev].base_addr - WD_NIC_OFFSET;
-                       unregister_netdev(&dev_wd80x3[this_dev]);
-                       free_irq(dev_wd80x3[this_dev].irq);
+       for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+               struct device *dev = &dev_wd[this_dev];
+               if (dev->priv != NULL) {
+                       int ioaddr = dev->base_addr - WD_NIC_OFFSET;
+                       kfree(dev->priv);
+                       dev->priv = NULL;
+                       free_irq(dev->irq);
+                       irq2dev_map[dev->irq] = NULL;
                        release_region(ioaddr, WD_IO_EXTENT);
+                       unregister_netdev(dev);
                }
        }
 }
index cab5ddec6315d15d29802bb5ee715f4bdf3ed2b4..8b586894d1f3951ac0fbf59a46460f9da26ba0e6 100644 (file)
@@ -289,22 +289,6 @@ struct pci_dev_info *pci_lookup_dev(unsigned int vendor, unsigned int dev)
        }
 }
 
-
-const char *pci_strbioserr (int error)
-{
-       switch (error) {
-             case PCIBIOS_SUCCESSFUL:          return "SUCCESSFUL";
-             case PCIBIOS_FUNC_NOT_SUPPORTED:  return "FUNC_NOT_SUPPORTED";
-             case PCIBIOS_BAD_VENDOR_ID:       return "SUCCESSFUL";
-             case PCIBIOS_DEVICE_NOT_FOUND:    return "DEVICE_NOT_FOUND";
-             case PCIBIOS_BAD_REGISTER_NUMBER: return "BAD_REGISTER_NUMBER";
-             case PCIBIOS_SET_FAILED:          return "SET_FAILED";
-             case PCIBIOS_BUFFER_TOO_SMALL:    return "BUFFER_TOO_SMALL";
-             default:                          return "Unknown error status";
-       }
-}
-
-
 const char *pci_strclass (unsigned int class)
 {
        switch (class >> 8) {
index 4874477c1e43502dbcd018b8bea87f1553aa8453..3756492585642c94368cf94f8df7fa12c8cc66df 100644 (file)
@@ -894,7 +894,7 @@ static int ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
            &irq))) {
        printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
                "        perhaps you specified an incorrect PCI bus, device, or function.\n"
-               , pci_strbioserr(error));
+               , pcibios_strerror(error));
        return -1;
     }
 
diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
new file mode 100644 (file)
index 0000000..dfa10eb
--- /dev/null
@@ -0,0 +1,2577 @@
+/*
+
+  Linux Driver for BusLogic SCSI Host Adapters
+
+  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation, provided that none of the source code or runtime
+  copyright notices are removed or modified.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that all modifications to this software be
+  sent directly to him for evaluation and testing.
+
+  Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable,
+  and to David B. Gentzel, for writing the original Linux BusLogic driver.
+
+*/
+
+
+#define BusLogic_DriverVersion         "1.3.0"
+#define BusLogic_DriverDate            "13 November 1995"
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "BusLogic.h"
+
+
+/*
+  BusLogic_CommandLineEntryCount is a count of the number of "BusLogic="
+  entries provided on the Linux Kernel Command Line.
+*/
+
+static int
+  BusLogic_CommandLineEntryCount =     0;
+
+
+/*
+  BusLogic_CommandLineEntries is an array of Command Line Entry structures
+  representing the "BusLogic=" entries provided on the Linux Kernel Command
+  Line.
+*/
+
+static BusLogic_CommandLineEntry_T
+  BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters];
+
+
+/*
+  BusLogic_TracingOptions is a bit mask of Tracing Options to be applied
+  across all Host Adapters.
+*/
+
+static int
+  BusLogic_TracingOptions =            0;
+
+
+/*
+  BusLogic_RegisteredHostAdapters is a linked list of all the registered
+  BusLogic Host Adapters.
+*/
+
+static BusLogic_HostAdapter_T
+  *BusLogic_RegisteredHostAdapters =   NULL;
+
+
+/*
+  BusLogic_Standard_IO_Addresses is the list of standard I/O Addresses at which
+  BusLogic Host Adapters may potentially be found.
+*/
+
+static unsigned short
+  BusLogic_IO_StandardAddresses[] =
+    { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 };
+
+
+/*
+  BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for
+  potential BusLogic Host Adapters.  It is initialized by interrogating the
+  PCI Configuration Space on PCI machines as well as from the list of
+  standard BusLogic I/O Addresses.
+*/
+
+static unsigned short
+  BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] =   { 0 };
+
+
+/*
+  BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using
+  a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared
+  interrupts.  Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by
+  BusLogic Host Adapters.
+*/
+
+static short
+  BusLogic_IRQ_UsageCount[7] =         { 0 };
+
+
+/*
+  BusLogic_CommandFailureReason holds a string identifying the reason why a
+  call to BusLogic_Command failed.  It is only valid when BusLogic_Command
+  returns a failure code.
+*/
+
+static char
+  *BusLogic_CommandFailureReason;
+
+
+/*
+  BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
+*/
+
+static struct proc_dir_entry
+  BusLogic_ProcDirectoryEntry =
+    { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+
+/*
+  BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+  Name, Copyright Notice, and Contact Address.
+*/
+
+static void BusLogic_AnnounceDriver(void)
+{
+  static boolean DriverAnnouncementPrinted = false;
+  if (DriverAnnouncementPrinted) return;
+  printk("scsi: ***** BusLogic SCSI Driver Version "
+        BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n");
+  printk("scsi: Copyright 1995 by Leonard N. Zubkoff "
+        "<lnz@dandelion.com>\n");
+  DriverAnnouncementPrinted = true;
+}
+
+
+/*
+  BusLogic_DriverInfo returns the Board Name to identify this SCSI Driver
+  and Host Adapter.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Host->hostdata;
+  return HostAdapter->BoardName;
+}
+
+
+/*
+  BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses
+  to be probed for potential BusLogic SCSI Host Adapters by interrogating the
+  PCI Configuration Space on PCI machines as well as from the list of standard
+  BusLogic I/O Addresses.
+*/
+
+static void BusLogic_InitializeAddressProbeList(void)
+{
+  int DestinationIndex = 0, SourceIndex = 0;
+  /*
+    If BusLogic_Setup has been called, do not override the Kernel Command
+    Line specifications.
+  */
+  if (BusLogic_IO_AddressProbeList[0] != 0) return;
+#ifdef CONFIG_PCI
+  /*
+    Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters.
+  */
+  if (pcibios_present())
+    {
+      unsigned short Index = 0, VendorID;
+      unsigned char Bus, DeviceAndFunction;
+      unsigned int BaseAddress0;
+      while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++,
+                               &Bus, &DeviceAndFunction) == 0)
+       if (pcibios_read_config_word(Bus, DeviceAndFunction,
+                                    PCI_VENDOR_ID, &VendorID) == 0 &&
+           VendorID == PCI_VENDOR_ID_BUSLOGIC &&
+           pcibios_read_config_dword(Bus, DeviceAndFunction,
+                                     PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+           (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) ==
+             PCI_BASE_ADDRESS_SPACE_IO)
+         {
+           BusLogic_IO_AddressProbeList[DestinationIndex++] =
+             BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+         }
+    }
+#endif
+  /*
+    Append the list of standard BusLogic I/O Addresses.
+  */
+  while (DestinationIndex < BusLogic_IO_MaxProbeAddresses &&
+        BusLogic_IO_StandardAddresses[SourceIndex] > 0)
+    BusLogic_IO_AddressProbeList[DestinationIndex++] =
+      BusLogic_IO_StandardAddresses[SourceIndex++];
+  BusLogic_IO_AddressProbeList[DestinationIndex] = 0;
+}
+
+
+/*
+  BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered
+  BusLogic Host Adapters.
+*/
+
+static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  HostAdapter->Next = NULL;
+  if (BusLogic_RegisteredHostAdapters != NULL)
+    {
+      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      BusLogic_HostAdapter_T *NextHostAdapter;
+      while ((NextHostAdapter = LastHostAdapter->Next) != NULL)
+       LastHostAdapter = NextHostAdapter;
+      LastHostAdapter->Next = HostAdapter;
+    }
+  else BusLogic_RegisteredHostAdapters = HostAdapter;
+}
+
+
+/*
+  BusLogic_UnregisterHostAdapter removes Host Adapter from the list of
+  registered BusLogic Host Adapters.
+*/
+
+static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  if (BusLogic_RegisteredHostAdapters != HostAdapter)
+    {
+      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter)
+       LastHostAdapter = LastHostAdapter->Next;
+      if (LastHostAdapter != NULL)
+       LastHostAdapter->Next = HostAdapter->Next;
+    }
+  else BusLogic_RegisteredHostAdapters = HostAdapter->Next;
+  HostAdapter->Next = NULL;
+}
+
+
+/*
+  BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs)
+  for Host Adapter.
+*/
+
+static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+  int i;
+  for (i = 0; i < BusLogic_InitialCCBs; i++)
+    {
+      BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+       scsi_init_malloc(sizeof(BusLogic_CCB_T), GFP_ATOMIC | GFP_DMA);
+      if (CCB == NULL)
+       {
+         printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n",
+                HostAdapter->HostNumber, i);
+         return false;
+       }
+      memset(CCB, 0, sizeof(BusLogic_CCB_T));
+      CCB->HostAdapter = HostAdapter;
+      CCB->Status = BusLogic_CCB_Free;
+      CCB->Next = HostAdapter->Free_CCBs;
+      CCB->NextAll = HostAdapter->All_CCBs;
+      HostAdapter->Free_CCBs = CCB;
+      HostAdapter->All_CCBs = CCB;
+    }
+  return true;
+}
+
+
+/*
+  BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB;
+  HostAdapter->All_CCBs = NULL;
+  HostAdapter->Free_CCBs = NULL;
+  while ((CCB = NextCCB) != NULL)
+    {
+      NextCCB = CCB->NextAll;
+      scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T));
+    }
+}
+
+
+/*
+  BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list,
+  allocating more memory from the Kernel if necessary.
+*/
+
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
+{
+  static unsigned int SerialNumber = 0;
+  BusLogic_CCB_T *CCB;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB = HostAdapter->Free_CCBs;
+  if (CCB != NULL)
+    {
+      CCB->SerialNumber = SerialNumber++;
+      HostAdapter->Free_CCBs = CCB->Next;
+      BusLogic_UnlockHostAdapter(HostAdapter);
+      return CCB;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  CCB = (BusLogic_CCB_T *) scsi_init_malloc(sizeof(BusLogic_CCB_T),
+                                           GFP_ATOMIC | GFP_DMA);
+  if (CCB == NULL)
+    {
+      printk("scsi%d: Failed to allocate an additional CCB\n",
+            HostAdapter->HostNumber);
+      return NULL;
+    }
+  printk("scsi%d: Allocated an additional CCB\n", HostAdapter->HostNumber);
+  memset(CCB, 0, sizeof(BusLogic_CCB_T));
+  CCB->HostAdapter = HostAdapter;
+  CCB->Status = BusLogic_CCB_Free;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB->SerialNumber = SerialNumber++;
+  CCB->NextAll = HostAdapter->All_CCBs;
+  HostAdapter->All_CCBs = CCB;
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return CCB;
+}
+
+
+/*
+  BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+  free list.
+*/
+
+static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
+{
+  BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+  BusLogic_LockHostAdapter(HostAdapter);
+  CCB->Command = NULL;
+  CCB->Status = BusLogic_CCB_Free;
+  CCB->SerialNumber = 0;
+  CCB->Next = HostAdapter->Free_CCBs;
+  HostAdapter->Free_CCBs = CCB;
+  BusLogic_UnlockHostAdapter(HostAdapter);
+}
+
+
+/*
+  BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+  providing ParameterLength bytes of ParameterData and receiving at most
+  ReplyLength bytes of ReplyData; any excess reply data is received but
+  discarded.
+
+  On success, this function returns the number of reply bytes read from
+  the Host Adapter (including any discarded data); on failure, it returns
+  -1 if the command was invalid, or -2 if a timeout occurred.
+
+  This function is only called during board detection and initialization, so
+  performance and latency are not critical, and exclusive access to the Host
+  Adapter hardware is assumed.  Once the board and driver are initialized, the
+  only Host Adapter command that is issued is the single byte Start Mailbox
+  Scan command, which does not require waiting for the Host Adapter Ready bit
+  to be set in the Status Register.
+*/
+
+static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
+                           BusLogic_OperationCode_T OperationCode,
+                           void *ParameterData,
+                           int ParameterLength,
+                           void *ReplyData,
+                           int ReplyLength)
+{
+  unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+  unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+  unsigned char StatusRegister = 0, InterruptRegister;
+  long TimeoutCounter;
+  int ReplyBytes = 0;
+  /*
+    Clear out the Reply Data if provided.
+  */
+  if (ReplyLength > 0)
+    memset(ReplyData, 0, ReplyLength);
+  /*
+    Select an appropriate timeout value.
+  */
+  switch (OperationCode)
+    {
+    case BusLogic_InquireInstalledDevicesID0to7:
+    case BusLogic_InquireInstalledDevicesID8to15:
+      /* Approximately 60 seconds. */
+      TimeoutCounter = loops_per_sec << 2;
+      break;
+    default:
+      /* Approximately 1 second. */
+      TimeoutCounter = loops_per_sec >> 4;
+      break;
+    }
+  /*
+    Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+    Register Busy bit to be reset in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if ((StatusRegister & BusLogic_HostAdapterReady) &&
+         !(StatusRegister & BusLogic_CommandParameterRegisterBusy))
+       break;
+    }
+  BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+  if (TimeoutCounter < 0) return -2;
+  /*
+    Write the OperationCode to the Command/Parameter Register.
+  */
+  BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+  /*
+    Write any additional Parameter Bytes.
+  */
+  HostAdapter->HostAdapterCommandCompleted = false;
+  while (--ParameterLength >= 0)
+    {
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_CommandComplete) break;
+      if (HostAdapter->HostAdapterCommandCompleted) break;
+      while (--TimeoutCounter >= 0)
+       {
+         StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+         if (!(StatusRegister & BusLogic_CommandParameterRegisterBusy)) break;
+       }
+      BusLogic_CommandFailureReason =
+       "Timeout waiting for Parameter Acceptance";
+      if (TimeoutCounter < 0) return -2;
+      BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+    }
+  BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+  if (ParameterLength >= 0) return -1;
+  /*
+    The Modify I/O Address command does not cause a Command Complete Interrupt.
+  */
+  if (OperationCode == BusLogic_ModifyIOAddress)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+      return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : 0);
+    }
+  /*
+    Receive any Reply Bytes, waiting for either the Command Complete bit to
+    be set in the Interrupt Register, or for the Interrupt Handler to set the
+    HostAdapterCommandCompleted bit in the Host Adapter structure.
+  */
+  HostAdapter->HostAdapterCommandCompleted = false;
+  while (--TimeoutCounter >= 0)
+    {
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_CommandComplete) break;
+      if (HostAdapter->HostAdapterCommandCompleted) break;
+      if (StatusRegister & BusLogic_DataInRegisterReady)
+       if (++ReplyBytes <= ReplyLength)
+         *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+       else BusLogic_ReadDataInRegister(HostAdapter);
+  }
+  BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+  if (TimeoutCounter < 0) return -2;
+  /*
+    Clear any pending Command Complete Interrupt, unless this is a
+    Test Command Complete Interrupt command.
+  */
+  if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+    BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+  if (BusLogic_TracingOptions & BusLogic_TraceConfiguration)
+    if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+      {
+       int i;
+       printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+              OperationCode, StatusRegister, ReplyLength, ReplyBytes);
+       for (i = 0; i < ReplyBytes; i++)
+         printk(" %02X", ((unsigned char *) ReplyData)[i]);
+       printk("\n");
+      }
+  /*
+    Return count of Reply Bytes, or -1 if the command was invalid.
+  */
+  BusLogic_CommandFailureReason = "Command Invalid";
+  return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : ReplyBytes);
+}
+
+
+/*
+  BusLogic_Failure prints a standardized error message for tests that are
+  executed before the SCSI Host is registered, and then returns false.
+*/
+
+static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
+                               char *ErrorMessage)
+{
+  BusLogic_AnnounceDriver();
+  printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n",
+        HostAdapter->IO_Address);
+  printk("%s FAILED - DETACHING\n", ErrorMessage);
+  if (BusLogic_CommandFailureReason != NULL)
+    printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason);
+  return false;
+}
+
+
+/*
+  BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+*/
+
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  boolean TraceProbe = (BusLogic_TracingOptions & BusLogic_TraceProbe);
+  unsigned char StatusRegister, GeometryRegister;
+  /*
+    Read the Status Register to test if there is an I/O port that responds.  A
+    nonexistent I/O port will return 0xFF, in which case there is definitely no
+    BusLogic Host Adapter at this base I/O Address.
+  */
+  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+  if (TraceProbe)
+    printk("BusLogic_Probe(0x%X): Status 0x%02X\n",
+          HostAdapter->IO_Address, StatusRegister);
+  if (StatusRegister == 0xFF) return false;
+  /*
+    Read the undocumented BusLogic Geometry Register to test if there is an I/O
+    port that responds.  Adaptec Host Adapters do not implement the Geometry
+    Register, so this test helps serve to avoid incorrectly recognizing an
+    Adaptec 1542A or 1542B as a BusLogic.  Unfortunately, the Adaptec 1542C
+    series does respond to the Geometry Register I/O port, but it will be
+    rejected later when the Inquire Extended Setup Information command is
+    issued in BusLogic_CheckHostAdapter.  The AMI FastDisk Host Adapter is a
+    BusLogic clone that implements the same interface as earlier BusLogic
+    boards, including the undocumented commands, and is therefore supported by
+    this driver.  However, the AMI FastDisk always returns 0x00 upon reading
+    the Geometry Register, so the extended translation option should always be
+    left disabled on the AMI FastDisk.
+  */
+  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+  if (TraceProbe)
+    printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n",
+          HostAdapter->IO_Address, GeometryRegister);
+  if (GeometryRegister == 0xFF) return false;
+  /*
+    Indicate the Host Adapter Probe completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter,
+  and waits for Host Adapter Diagnostics to complete.
+*/
+
+static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
+                                            *HostAdapter)
+{
+  boolean TraceHardReset = (BusLogic_TracingOptions & BusLogic_TraceHardReset);
+  long TimeoutCounter = loops_per_sec >> 2;
+  unsigned char StatusRegister = 0;
+  /*
+    Issue a Hard Reset Command to the Host Adapter.  The Host Adapter should
+    respond by setting Diagnostic Active in the Status Register.
+  */
+  BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset);
+  /*
+    Wait until Diagnostic Active is set in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if ((StatusRegister & BusLogic_DiagnosticActive)) break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n",
+          HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    Wait until Diagnostic Active is reset in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (!(StatusRegister & BusLogic_DiagnosticActive)) break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n",
+          HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
+    or Data In Register Ready bits is set in the Status Register.
+  */
+  while (--TimeoutCounter >= 0)
+    {
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister & (BusLogic_DiagnosticFailure |
+                           BusLogic_HostAdapterReady |
+                           BusLogic_DataInRegisterReady))
+       break;
+    }
+  if (TraceHardReset)
+    printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n",
+          HostAdapter->IO_Address, StatusRegister);
+  if (TimeoutCounter < 0) return false;
+  /*
+    If Diagnostic Failure is set or Host Adapter Ready is reset, then an
+    error occurred during the Host Adapter diagnostics.  If Data In Register
+    Ready is set, then there is an Error Code available.
+  */
+  if ((StatusRegister & BusLogic_DiagnosticFailure) ||
+      !(StatusRegister & BusLogic_HostAdapterReady))
+    {
+      BusLogic_CommandFailureReason = NULL;
+      BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
+      printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister);
+      if (StatusRegister & BusLogic_DataInRegisterReady)
+       {
+         unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
+         printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode);
+       }
+      return false;
+    }
+  /*
+    Indicate the Host Adapter Hard Reset completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+  Host Adapter.
+*/
+
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  unsigned long ProcessorFlags;
+  int Result;
+  /*
+    Issue the Inquire Setup Information command.  Only genuine BusLogic Host
+    Adapters and true clones support this command.  Adaptec 1542C series Host
+    Adapters that respond to the Geometry Register I/O port will fail this
+    command.  Interrupts must be disabled around the call to BusLogic_Command
+    since a Command Complete interrupt could occur if the IRQ Channel was
+    previously enabled for another BusLogic Host Adapter sharing the same IRQ
+    Channel.
+  */
+  save_flags(ProcessorFlags);
+  cli();
+  RequestedReplyLength = sizeof(ExtendedSetupInformation);
+  Result = BusLogic_Command(HostAdapter,
+                           BusLogic_InquireExtendedSetupInformation,
+                           &RequestedReplyLength, sizeof(RequestedReplyLength),
+                           &ExtendedSetupInformation,
+                           sizeof(ExtendedSetupInformation));
+  restore_flags(ProcessorFlags);
+  if (BusLogic_TracingOptions & BusLogic_TraceProbe)
+    printk("BusLogic_Check(0x%X): Result %d\n",
+          HostAdapter->IO_Address, Result);
+  return (Result == sizeof(ExtendedSetupInformation));
+}
+
+
+/*
+  BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+  from Host Adapter.
+*/
+
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+                                                    *HostAdapter)
+{
+  BusLogic_BoardID_T BoardID;
+  BusLogic_Configuration_T Configuration;
+  BusLogic_SetupInformation_T SetupInformation;
+  BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+  BusLogic_ModelAndRevision_T ModelAndRevision;
+  BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
+  BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  unsigned char GeometryRegister, *TargetPointer, Character;
+  unsigned short AllTargetsMask, DisconnectPermitted;
+  unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault;
+  boolean CommonErrorRecovery;
+  int TargetID, i;
+  /*
+    Issue the Inquire Board ID command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0,
+                      &BoardID, sizeof(BoardID)) != sizeof(BoardID))
+    return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+  /*
+    Issue the Inquire Configuration command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0,
+                      &Configuration, sizeof(Configuration))
+      != sizeof(Configuration))
+    return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+  /*
+    Issue the Inquire Setup Information command.
+  */
+  RequestedReplyLength = sizeof(SetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+                      &RequestedReplyLength, sizeof(RequestedReplyLength),
+                      &SetupInformation, sizeof(SetupInformation))
+      != sizeof(SetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+  /*
+    Issue the Inquire Extended Setup Information command.
+  */
+  RequestedReplyLength = sizeof(ExtendedSetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation,
+                      &RequestedReplyLength, sizeof(RequestedReplyLength),
+                      &ExtendedSetupInformation,
+                      sizeof(ExtendedSetupInformation))
+      != sizeof(ExtendedSetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+  /*
+    Issue the Inquire Board Model and Revision command.
+  */
+  RequestedReplyLength = sizeof(ModelAndRevision);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelAndRevision,
+                      &RequestedReplyLength, sizeof(RequestedReplyLength),
+                      &ModelAndRevision, sizeof(ModelAndRevision))
+      != sizeof(ModelAndRevision))
+    return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL AND REVISION");
+  /*
+    Issue the Inquire Firmware Version 3rd Digit command.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+                      NULL, 0, &FirmwareVersion3rdDigit,
+                      sizeof(FirmwareVersion3rdDigit))
+      != sizeof(FirmwareVersion3rdDigit))
+    return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+  /*
+    Issue the Inquire Firmware Version Letter command.
+  */
+  FirmwareVersionLetter = '\0';
+  if (BoardID.FirmwareVersion1stDigit >= '3')
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+                        NULL, 0, &FirmwareVersionLetter,
+                        sizeof(FirmwareVersionLetter))
+       != sizeof(FirmwareVersionLetter))
+      return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+  /*
+    BusLogic Host Adapters can be identified by their model number and
+    the major version number of their firmware as follows:
+
+    4.xx       BusLogic "C" Series Host Adapters:
+                 946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+    3.xx       BusLogic "S" Series Host Adapters:
+                 747S/747D/757S/757D/445S/545S/542D
+                 542B/742A (revision H)
+    2.xx       BusLogic "A" Series Host Adapters:
+                 542B/742A (revision G and below)
+    0.xx       AMI FastDisk VLB BusLogic Clone Host Adapter
+  */
+  /*
+    Save the Model Name and Board Name in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->ModelName;
+  for (i = 0; i < sizeof(ModelAndRevision.Model); i++)
+    {
+      Character = ModelAndRevision.Model[i];
+      if (Character == ' ' || Character == '\0') break;
+      *TargetPointer++ = Character;
+    }
+  *TargetPointer++ = '\0';
+  strcpy(HostAdapter->BoardName, "BusLogic ");
+  strcat(HostAdapter->BoardName, HostAdapter->ModelName);
+  strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
+  /*
+    Save the Firmware Version in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->FirmwareVersion;
+  *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+  *TargetPointer++ = '.';
+  *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+  if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+    *TargetPointer++ = FirmwareVersion3rdDigit;
+  if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+    *TargetPointer++ = FirmwareVersionLetter;
+  *TargetPointer++ = '\0';
+  /*
+    Determine the IRQ Channel and save it in the Host Adapter structure.
+  */
+  if (Configuration.IRQ_Channel9)
+    HostAdapter->IRQ_Channel = 9;
+  else if (Configuration.IRQ_Channel10)
+    HostAdapter->IRQ_Channel = 10;
+  else if (Configuration.IRQ_Channel11)
+    HostAdapter->IRQ_Channel = 11;
+  else if (Configuration.IRQ_Channel12)
+    HostAdapter->IRQ_Channel = 12;
+  else if (Configuration.IRQ_Channel14)
+    HostAdapter->IRQ_Channel = 14;
+  else if (Configuration.IRQ_Channel15)
+    HostAdapter->IRQ_Channel = 15;
+  /*
+    Determine the DMA Channel and save it in the Host Adapter structure.
+  */
+  if (Configuration.DMA_Channel5)
+    HostAdapter->DMA_Channel = 5;
+  else if (Configuration.DMA_Channel6)
+    HostAdapter->DMA_Channel = 6;
+  else if (Configuration.DMA_Channel7)
+    HostAdapter->DMA_Channel = 7;
+  /*
+    Save the Host Adapter SCSI ID in the Host Adapter structure.
+  */
+  HostAdapter->SCSI_ID = Configuration.HostAdapterID;
+  /*
+    Save the Synchronous Initiation flag and SCSI Parity Checking flag
+    in the Host Adapter structure.
+  */
+  HostAdapter->SynchronousInitiation =
+    SetupInformation.SynchronousInitiationEnabled;
+  HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled;
+  /*
+    Determine the Bus Type and save it in the Host Adapter structure,
+    overriding the DMA Channel if it is inappropriate for the bus type.
+  */
+  if (ExtendedSetupInformation.BusType == 'A')
+    HostAdapter->BusType = BusLogic_ISA_Bus;
+  else
+    switch (HostAdapter->ModelName[0])
+      {
+      case '4':
+       HostAdapter->BusType = BusLogic_VESA_Bus;
+       HostAdapter->DMA_Channel = 0;
+       break;
+      case '5':
+       HostAdapter->BusType = BusLogic_ISA_Bus;
+       break;
+      case '6':
+       HostAdapter->BusType = BusLogic_MCA_Bus;
+       HostAdapter->DMA_Channel = 0;
+       break;
+      case '7':
+       HostAdapter->BusType = BusLogic_EISA_Bus;
+       HostAdapter->DMA_Channel = 0;
+       break;
+      case '9':
+       HostAdapter->BusType = BusLogic_PCI_Bus;
+       HostAdapter->DMA_Channel = 0;
+       break;
+      }
+  /*
+    Determine whether Extended Translation is enabled and save it in
+    the Host Adapter structure.
+  */
+  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+  if (GeometryRegister & BusLogic_ExtendedTranslationEnabled)
+    HostAdapter->ExtendedTranslation = true;
+  /*
+    Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter
+    structure.  The Disconnect Permitted information is only valid on "C"
+    Series boards, but Disconnect/Reconnect is always permitted on "S" and
+    "A" Series boards.
+  */
+  if (HostAdapter->FirmwareVersion[0] >= '4')
+    HostAdapter->DisconnectPermitted =
+      (SetupInformation.DisconnectPermittedID8to15 << 8)
+      | SetupInformation.DisconnectPermittedID0to7;
+  else HostAdapter->DisconnectPermitted = 0xFF;
+  /*
+    Save the Scatter Gather Limits, Level Triggered Interrupts flag,
+    Wide SCSI flag, and Differential SCSI flag in the Host Adapter structure.
+  */
+  HostAdapter->HostAdapterScatterGatherLimit =
+    ExtendedSetupInformation.ScatterGatherLimit;
+  HostAdapter->DriverScatterGatherLimit =
+    HostAdapter->HostAdapterScatterGatherLimit;
+  if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
+    HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+  if (ExtendedSetupInformation.Misc.LevelTriggeredInterrupts)
+    HostAdapter->LevelTriggeredInterrupts = true;
+  if (ExtendedSetupInformation.HostWideSCSI)
+    {
+      HostAdapter->HostWideSCSI = true;
+      HostAdapter->MaxTargetIDs = 16;
+      HostAdapter->MaxLogicalUnits = 64;
+    }
+  else
+    {
+      HostAdapter->HostWideSCSI = false;
+      HostAdapter->MaxTargetIDs = 8;
+      HostAdapter->MaxLogicalUnits = 8;
+    }
+  HostAdapter->HostDifferentialSCSI =
+    ExtendedSetupInformation.HostDifferentialSCSI;
+  /*
+    Determine the Host Adapter BIOS Address if the BIOS is enabled and
+    save it in the Host Adapter structure.  The BIOS is disabled if the
+    BIOS_Address is 0.
+  */
+  HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+  /*
+    Select an appropriate value for Concurrency (Commands per Logical Unit)
+    either from a Command Line Entry, or based on whether this is an ISA
+    or non-ISA Host Adapter.
+  */
+  if (HostAdapter->CommandLineEntry != NULL &&
+      HostAdapter->CommandLineEntry->Concurrency > 0)
+    HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency;
+  else if (HostAdapter->BusType == BusLogic_ISA_Bus)
+    HostAdapter->Concurrency = BusLogic_Concurrency_ISA;
+  else HostAdapter->Concurrency = BusLogic_Concurrency;
+  /*
+    Select an appropriate value for Bus Settle Time either from a Command
+    Line Entry, or from BusLogic_DefaultBusSettleTime.
+  */
+  if (HostAdapter->CommandLineEntry != NULL &&
+      HostAdapter->CommandLineEntry->BusSettleTime > 0)
+    HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime;
+  else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+  /*
+    Select appropriate values for the Error Recovery Option array either from
+    a Command Line Entry, or using BusLogic_ErrorRecoveryDefault.
+  */
+  if (HostAdapter->CommandLineEntry != NULL)
+    memcpy(HostAdapter->ErrorRecoveryOption,
+          HostAdapter->CommandLineEntry->ErrorRecoveryOption,
+          sizeof(HostAdapter->ErrorRecoveryOption));
+  else memset(HostAdapter->ErrorRecoveryOption,
+             BusLogic_ErrorRecoveryDefault,
+             sizeof(HostAdapter->ErrorRecoveryOption));
+  /*
+    Tagged Queuing support is available and operates properly only on "C"
+    Series boards with firmware version 4.22 and above and on "S" Series
+    boards with firmware version 3.35 and above.  Tagged Queuing is disabled
+    by default when the Concurrency value is 1 since queuing multiple commands
+    is not possible.
+  */
+  TaggedQueuingPermittedDefault = 0;
+  if (HostAdapter->Concurrency > 1)
+    switch (HostAdapter->FirmwareVersion[0])
+      {
+      case '4':
+       if (HostAdapter->FirmwareVersion[2] > '2' ||
+           (HostAdapter->FirmwareVersion[2] == '2' &&
+            HostAdapter->FirmwareVersion[3] >= '2'))
+         TaggedQueuingPermittedDefault = 0xFFFF;
+       break;
+      case '3':
+       if (HostAdapter->FirmwareVersion[2] > '3' ||
+           (HostAdapter->FirmwareVersion[2] == '3' &&
+            HostAdapter->FirmwareVersion[3] >= '5'))
+         TaggedQueuingPermittedDefault = 0xFFFF;
+       break;
+      }
+  /*
+    Combine the default Tagged Queuing permission based on the Host Adapter
+    firmware version and Concurrency with any Command Line Entry Tagged
+    Queuing specification.
+  */
+  if (HostAdapter->CommandLineEntry != NULL)
+    HostAdapter->TaggedQueuingPermitted =
+      (HostAdapter->CommandLineEntry->TaggedQueuingPermitted &
+       HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) |
+      (TaggedQueuingPermittedDefault &
+       ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask);
+  else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault;
+  /*
+    Announce the Host Adapter Configuration.
+  */
+  printk("scsi%d: Configuring BusLogic Model %s %s%s%s SCSI Host Adapter\n",
+        HostAdapter->HostNumber, HostAdapter->ModelName,
+        BusLogic_BusNames[HostAdapter->BusType],
+        (HostAdapter->HostWideSCSI ? " Wide" : ""),
+        (HostAdapter->HostDifferentialSCSI ? " Differential" : ""));
+  printk("scsi%d:   Firmware Version: %s, I/O Address: 0x%X, "
+        "IRQ Channel: %d/%s\n",
+        HostAdapter->HostNumber, HostAdapter->FirmwareVersion,
+        HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
+        (HostAdapter->LevelTriggeredInterrupts ? "Level" : "Edge"));
+  printk("scsi%d:   DMA Channel: ", HostAdapter->HostNumber);
+  if (HostAdapter->DMA_Channel > 0)
+    printk("%d, ", HostAdapter->DMA_Channel);
+  else printk("None, ");
+  if (HostAdapter->BIOS_Address > 0)
+    printk("BIOS Address: 0x%lX, ", HostAdapter->BIOS_Address);
+  else printk("BIOS Address: None, ");
+  printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID);
+  printk("scsi%d:   Scatter/Gather Limit: %d segments, "
+        "Synchronous Initiation: %s\n", HostAdapter->HostNumber,
+        HostAdapter->HostAdapterScatterGatherLimit,
+        (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"));
+  printk("scsi%d:   SCSI Parity Checking: %s, "
+        "Extended Disk Translation: %s\n", HostAdapter->HostNumber,
+        (HostAdapter->ParityChecking ? "Enabled" : "Disabled"),
+        (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled"));
+  AllTargetsMask = (1 << HostAdapter->MaxTargetIDs) - 1;
+  DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
+  printk("scsi%d:   Disconnect/Reconnect: ", HostAdapter->HostNumber);
+  if (DisconnectPermitted == 0)
+    printk("Disabled");
+  else if (DisconnectPermitted == AllTargetsMask)
+    printk("Enabled");
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+  printk(", Tagged Queuing: ");
+  TaggedQueuingPermitted =
+    HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
+  if (TaggedQueuingPermitted == 0)
+    printk("Disabled");
+  else if (TaggedQueuingPermitted == AllTargetsMask)
+    printk("Enabled");
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+  printk("\n");
+  CommonErrorRecovery = true;
+  for (TargetID = 1; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+    if (HostAdapter->ErrorRecoveryOption[TargetID] !=
+       HostAdapter->ErrorRecoveryOption[0])
+      {
+       CommonErrorRecovery = false;
+       break;
+      }
+  printk("scsi%d:   Error Recovery: ", HostAdapter->HostNumber);
+  if (CommonErrorRecovery)
+    printk("%s", BusLogic_ErrorRecoveryOptions[
+                  HostAdapter->ErrorRecoveryOption[0]]);
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      printk("%s", BusLogic_ErrorRecoveryOptions2[
+                    HostAdapter->ErrorRecoveryOption[TargetID]]);
+  printk(", Mailboxes: %d, Initial CCBs: %d\n",
+        BusLogic_MailboxCount, BusLogic_InitialCCBs);
+  printk("scsi%d:   Driver Scatter/Gather Limit: %d segments, "
+        "Concurrency: %d\n", HostAdapter->HostNumber,
+        HostAdapter->DriverScatterGatherLimit, HostAdapter->Concurrency);
+  /*
+    Indicate reading the Host Adapter Configuration completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_AcquireResources acquires the system resources necessary to use Host
+  Adapter, and initializes the fields in the SCSI Host structure.  The base,
+  io_port, n_io_ports, irq, and dma_channel fields in the SCSI Host structure
+  are intentionally left uninitialized, as this driver handles acquisition and
+  release of these resources explicitly, as well as ensuring exclusive access
+  to the Host Adapter hardware and data structures through explicit locking.
+*/
+
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter,
+                                        SCSI_Host_T *Host)
+{
+  /*
+    Acquire exclusive or shared access to the IRQ Channel.  A usage count is
+    maintained so that PCI, EISA, or MCA shared Interrupts can be supported.
+  */
+  if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]++ == 0)
+    {
+      if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+                     SA_INTERRUPT, HostAdapter->InterruptLabel) < 0)
+       {
+         BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--;
+         printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+                HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+         return false;
+       }
+    }
+  else
+    {
+      BusLogic_HostAdapter_T *FirstHostAdapter =
+       BusLogic_RegisteredHostAdapters;
+      while (FirstHostAdapter != NULL)
+       {
+         if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel)
+           {
+             if (strlen(FirstHostAdapter->InterruptLabel) + 8
+                 < sizeof(FirstHostAdapter->InterruptLabel))
+               {
+                 strcat(FirstHostAdapter->InterruptLabel, " + ");
+                 strcat(FirstHostAdapter->InterruptLabel,
+                        HostAdapter->ModelName);
+               }
+             break;
+           }
+         FirstHostAdapter = FirstHostAdapter->Next;
+       }
+    }
+  HostAdapter->IRQ_ChannelAcquired = true;
+  /*
+    Acquire exclusive access to the DMA Channel.
+  */
+  if (HostAdapter->DMA_Channel > 0)
+    {
+      if (request_dma(HostAdapter->DMA_Channel, HostAdapter->BoardName) < 0)
+       {
+         printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
+                HostAdapter->HostNumber, HostAdapter->DMA_Channel);
+         return false;
+       }
+      set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
+      enable_dma(HostAdapter->DMA_Channel);
+      HostAdapter->DMA_ChannelAcquired = true;
+    }
+  /*
+    Initialize necessary fields in the SCSI Host structure.
+  */
+  Host->max_id = HostAdapter->MaxTargetIDs;
+  Host->max_lun = HostAdapter->MaxLogicalUnits;
+  Host->max_channel = 0;
+  Host->this_id = HostAdapter->SCSI_ID;
+  Host->can_queue = BusLogic_MailboxCount;
+  Host->cmd_per_lun = HostAdapter->Concurrency;
+  Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+  Host->unchecked_isa_dma = (HostAdapter->BusType == BusLogic_ISA_Bus);
+  /*
+    BusLogic 445S Host Adapters prior to board revision D have a hardware bug
+    whereby when the BIOS is enabled, transfers to/from the same address range
+    the BIOS occupies modulo 16MB are handled incorrectly.  Since 16KB out of
+    each 16MB after the first is such a small amount of memory, this memory
+    can be marked as reserved without a significant loss of performance; this
+    is a much cheaper solution than requiring that ISA bounce buffers be used.
+  */
+  if (HostAdapter->BIOS_Address > 0 &&
+      strcmp(HostAdapter->ModelName, "445S") == 0)
+    {
+      Host->forbidden_addr = HostAdapter->BIOS_Address;
+      Host->forbidden_size = 16*1024;
+    }
+  /*
+    Indicate the System Resource Acquisition completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_ReleaseResources releases any system resources previously acquired
+  by BusLogic_AcquireResources.
+*/
+
+static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
+{
+  /*
+    Release exclusive or shared access to the IRQ Channel.
+  */
+  if (HostAdapter->IRQ_ChannelAcquired)
+    if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9] == 0)
+      free_irq(HostAdapter->IRQ_Channel);
+  /*
+    Release exclusive access to the DMA Channel.
+  */
+  if (HostAdapter->DMA_ChannelAcquired)
+    free_dma(HostAdapter->DMA_Channel);
+}
+
+
+/*
+  BusLogic_TestInterrupts tests for proper functioning of the Host Adapter
+  Interrupt Register and that interrupts generated by the Host Adapter are
+  getting through to the Interrupt Handler.  A large proportion of initial
+  problems with installing PCI Host Adapters are due to configuration problems
+  where either the Host Adapter or Motherboard is configured incorrectly, and
+  interrupts do not get through as a result.
+*/
+
+static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
+{
+  unsigned int InitialInterruptCount, FinalInterruptCount;
+  int TestCount = 5, i;
+  InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+  /*
+    Issue the Test Command Complete Interrupt commands.
+  */
+  for (i = 0; i < TestCount; i++)
+    BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt,
+                    NULL, 0, NULL, 0);
+  /*
+    Verify that BusLogic_InterruptHandler was called at least TestCount times.
+    Shared IRQ Channels could cause more than TestCount interrupts to occur,
+    but there should never be fewer than TestCount.
+  */
+  FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+  if (FinalInterruptCount < InitialInterruptCount + TestCount)
+    {
+      printk("scsi%d: HOST ADAPTER INTERRUPT TEST FAILED - DETACHING\n",
+            HostAdapter->HostNumber);
+      printk("scsi%d:  Interrupts are not getting through "
+            "from the Host Adapter to the\n", HostAdapter->HostNumber);
+      printk("scsi%d:  BusLogic Driver Interrupt Handler.  "
+            "The most likely cause is that\n", HostAdapter->HostNumber);
+      printk("scsi%d:  either the Host Adapter or Motherboard "
+            "is configured incorrectly.\n", HostAdapter->HostNumber);
+      printk("scsi%d:  Please check the Host Adapter configuration "
+            "with AutoSCSI or by\n", HostAdapter->HostNumber);
+      printk("scsi%d:  examining any dip switch and jumper settings "
+            "on the Host Adapter, and\n", HostAdapter->HostNumber);
+      printk("scsi%d:  verify that no other device is attempting to "
+            "use the same IRQ Channel.\n", HostAdapter->HostNumber);
+      printk("scsi%d:  For PCI Host Adapters, it may also be necessary "
+            "to investigate and\n", HostAdapter->HostNumber);
+      printk("scsi%d:  manually set the PCI interrupt assignments "
+            "and edge/level interrupt\n", HostAdapter->HostNumber);
+      printk("scsi%d:  type selection in the BIOS Setup Program or "
+            "with Motherboard jumpers.\n", HostAdapter->HostNumber);
+      return false;
+    }
+  /*
+    Indicate the Host Adapter Interrupt Test completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_InitializeHostAdapter initializes Host Adapter.  This is the only
+  function called during SCSI Host Adapter detection which modifies the state
+  of the Host Adapter from its initial power on or hard reset state.
+*/
+
+static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
+                                             *HostAdapter)
+{
+  BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest;
+  BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest;
+  BusLogic_WideModeCCBRequest_T WideModeCCBRequest;
+  BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+  /*
+    Initialize Read/Write Operation Count and Command Successful Flag
+    for each Target.
+  */
+  memset(HostAdapter->ReadWriteOperationCount, 0,
+        sizeof(HostAdapter->ReadWriteOperationCount));
+  memset(HostAdapter->CommandSuccessfulFlag, false,
+        sizeof(HostAdapter->CommandSuccessfulFlag));
+  /*
+    Initialize the Outgoing and Incoming Mailbox structures.
+  */
+  memset(HostAdapter->OutgoingMailboxes, 0,
+        sizeof(HostAdapter->OutgoingMailboxes));
+  memset(HostAdapter->IncomingMailboxes, 0,
+        sizeof(HostAdapter->IncomingMailboxes));
+  /*
+    Initialize the pointers to the First, Last, and Next Mailboxes.
+  */
+  HostAdapter->FirstOutgoingMailbox = &HostAdapter->OutgoingMailboxes[0];
+  HostAdapter->LastOutgoingMailbox =
+    &HostAdapter->OutgoingMailboxes[BusLogic_MailboxCount-1];
+  HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+  HostAdapter->FirstIncomingMailbox = &HostAdapter->IncomingMailboxes[0];
+  HostAdapter->LastIncomingMailbox =
+    &HostAdapter->IncomingMailboxes[BusLogic_MailboxCount-1];
+  HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+  /*
+    Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+  */
+  ExtendedMailboxRequest.MailboxCount = BusLogic_MailboxCount;
+  ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->OutgoingMailboxes;
+  if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
+                      &ExtendedMailboxRequest,
+                      sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+    {
+      printk("scsi%d: MAILBOX INITIALIZATION FAILED - DETACHING\n",
+            HostAdapter->HostNumber);
+      return false;
+    }
+  /*
+    Enable Strict Round Robin Mode if supported by the Host Adapter.  In Strict
+    Round Robin Mode, the Host Adapter only looks at the next Outgoing Mailbox
+    for each new command, rather than scanning through all the Outgoing
+    Mailboxes to find any that have new commands in them.  BusLogic indicates
+    that Strict Round Robin Mode is significantly more efficient.
+  */
+  RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+  BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+                  &RoundRobinModeRequest,
+                  sizeof(RoundRobinModeRequest), NULL, 0);
+  /*
+    For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to
+    allow more than 8 Logical Units per Target to be supported.
+  */
+  if (HostAdapter->HostWideSCSI)
+    {
+      WideModeCCBRequest = BusLogic_WideModeCCB;
+      if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB,
+                          &WideModeCCBRequest,
+                          sizeof(WideModeCCBRequest), NULL, 0) < 0)
+       {
+         printk("scsi%d: ENABLE WIDE MODE CCB FAILED - DETACHING\n",
+                HostAdapter->HostNumber);
+         return false;
+       }
+    }
+  /*
+    For PCI Host Adapters being accessed through the PCI compliant I/O
+    Address, disable the ISA compatible I/O Address to avoid detecting the
+    same Host Adapter at both I/O Addresses.
+  */
+  if (HostAdapter->BusType == BusLogic_PCI_Bus)
+    {
+      int Index;
+      for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++)
+       if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index])
+         break;
+      if (BusLogic_IO_StandardAddresses[Index] == 0)
+       {
+         ModifyIOAddressRequest = BusLogic_ModifyIO_Disable;
+         if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+                              &ModifyIOAddressRequest,
+                              sizeof(ModifyIOAddressRequest), NULL, 0) < 0)
+           {
+             printk("scsi%d: MODIFY I/O ADDRESS FAILED - DETACHING\n",
+                    HostAdapter->HostNumber);
+             return false;
+           }
+       }
+    }
+  /*
+    Announce Successful Initialization.
+  */
+  printk("scsi%d: *** %s Initialized Successfully ***\n",
+        HostAdapter->HostNumber, HostAdapter->BoardName);
+  /*
+    Indicate the Host Adapter Initialization completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_InquireTargetDevices inquires about the Target Devices accessible
+  through Host Adapter and reports on the results.
+*/
+
+static boolean BusLogic_InquireTargetDevices(BusLogic_HostAdapter_T
+                                            *HostAdapter)
+{
+  BusLogic_InstalledDevices8_T InstalledDevicesID0to7;
+  BusLogic_InstalledDevices8_T InstalledDevicesID8to15;
+  BusLogic_SetupInformation_T SetupInformation;
+  BusLogic_SynchronousPeriod_T SynchronousPeriod;
+  BusLogic_RequestedReplyLength_T RequestedReplyLength;
+  int TargetDevicesFound = 0, TargetID;
+  /*
+    Wait a few seconds between the Host Adapter Hard Reset which initiates
+    a SCSI Bus Reset and issuing any SCSI commands.  Some SCSI devices get
+    confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+  */
+  BusLogic_Delay(HostAdapter->BusSettleTime);
+  /*
+    Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI
+    Host Adapters the Inquire Installed Devices ID 8 to 15 command.  This is
+    necessary to force Synchronous Transfer Negotiation so that the Inquire
+    Setup Information and Inquire Synchronous Period commands will return
+    valid data.
+  */
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7,
+                      NULL, 0, &InstalledDevicesID0to7,
+                      sizeof(InstalledDevicesID0to7))
+      != sizeof(InstalledDevicesID0to7))
+    return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+  if (HostAdapter->HostWideSCSI)
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID8to15,
+                        NULL, 0, &InstalledDevicesID8to15,
+                        sizeof(InstalledDevicesID8to15))
+       != sizeof(InstalledDevicesID8to15))
+      return BusLogic_Failure(HostAdapter,
+                             "INQUIRE INSTALLED DEVICES ID 8 TO 15");
+  /*
+    Issue the Inquire Setup Information command.
+  */
+  RequestedReplyLength = sizeof(SetupInformation);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+                      &RequestedReplyLength, sizeof(RequestedReplyLength),
+                      &SetupInformation, sizeof(SetupInformation))
+      != sizeof(SetupInformation))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+  /*
+    Issue the Inquire Synchronous Period command.
+  */
+  RequestedReplyLength = sizeof(SynchronousPeriod);
+  if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+                      &RequestedReplyLength, 1,
+                      &SynchronousPeriod, sizeof(SynchronousPeriod))
+      != sizeof(SynchronousPeriod))
+    return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+  /*
+    Save the Installed Devices, Synchronous Values, and Synchronous Period
+    information in the Host Adapter structure.
+  */
+  memcpy(HostAdapter->InstalledDevices, InstalledDevicesID0to7,
+        sizeof(BusLogic_InstalledDevices8_T));
+  memcpy(HostAdapter->SynchronousValues,
+        SetupInformation.SynchronousValuesID0to7,
+        sizeof(BusLogic_SynchronousValues8_T));
+  if (HostAdapter->HostWideSCSI)
+    {
+      memcpy(&HostAdapter->InstalledDevices[8], InstalledDevicesID8to15,
+            sizeof(BusLogic_InstalledDevices8_T));
+      memcpy(&HostAdapter->SynchronousValues[8],
+            SetupInformation.SynchronousValuesID8to15,
+            sizeof(BusLogic_SynchronousValues8_T));
+    }
+  memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod,
+        sizeof(BusLogic_SynchronousPeriod_T));
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+    if (HostAdapter->InstalledDevices[TargetID] != 0)
+      {
+       int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID];
+       if (SynchronousPeriod > 10)
+         {
+           int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+           int RoundedSynchronousTransferRate =
+             (SynchronousTransferRate + 5000) / 10000;
+           printk("scsi%d:   Target %d: Synchronous at "
+                  "%d.%02d mega-transfers/sec, offset %d\n",
+                  HostAdapter->HostNumber, TargetID,
+                  RoundedSynchronousTransferRate / 100,
+                  RoundedSynchronousTransferRate % 100,
+                  HostAdapter->SynchronousValues[TargetID].Offset);
+         }
+       else if (SynchronousPeriod > 0)
+         {
+           int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+           int RoundedSynchronousTransferRate =
+             (SynchronousTransferRate + 50000) / 100000;
+           printk("scsi%d:   Target %d: Synchronous at "
+                  "%d.%01d mega-transfers/sec, offset %d\n",
+                  HostAdapter->HostNumber, TargetID,
+                  RoundedSynchronousTransferRate / 10,
+                  RoundedSynchronousTransferRate % 10,
+                  HostAdapter->SynchronousValues[TargetID].Offset);
+         }
+       else printk("scsi%d:   Target %d: Asynchronous\n",
+                   HostAdapter->HostNumber, TargetID);
+       TargetDevicesFound++;
+      }
+  if (TargetDevicesFound == 0)
+    printk("scsi%d:   No Target Devices Found\n", HostAdapter->HostNumber);
+  /*
+    Indicate the Target Device Inquiry completed successfully.
+  */
+  return true;
+}
+
+
+/*
+  BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+  I/O Addresses where they may be located, initializing, registering, and
+  reporting the configuration of each BusLogic Host Adapter it finds.  It
+  returns the number of BusLogic Host Adapters successfully initialized and
+  registered.
+*/
+
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
+{
+  int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0;
+  int AddressProbeIndex = 0;
+  BusLogic_InitializeAddressProbeList();
+  while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0)
+    {
+      BusLogic_HostAdapter_T HostAdapterPrototype;
+      BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
+      SCSI_Host_T *Host;
+      memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
+      HostAdapter->IO_Address =
+       BusLogic_IO_AddressProbeList[AddressProbeIndex++];
+      /*
+       Initialize the Command Line Entry field if an explicit I/O Address
+       was specified.
+      */
+      if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+         BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address ==
+         HostAdapter->IO_Address)
+       HostAdapter->CommandLineEntry =
+         &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+      /*
+       Check whether the I/O Address range is already in use.
+      */
+      if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0)
+       continue;
+      /*
+       Probe the Host Adapter.  If unsuccessful, abort further initialization.
+      */
+      if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue;
+      /*
+       Hard Reset the Host Adapter.  If unsuccessful, abort further
+       initialization.
+      */
+      if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue;
+      /*
+       Check the Host Adapter.  If unsuccessful, abort further initialization.
+      */
+      if (!BusLogic_CheckHostAdapter(HostAdapter)) continue;
+      /*
+       Initialize the Command Line Entry field if an explicit I/O Address
+       was not specified.
+      */
+      if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+         BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0)
+       HostAdapter->CommandLineEntry =
+         &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+      /*
+       Announce the Driver Version and Date, Author's Name, Copyright Notice,
+       and Contact Address.
+      */
+      BusLogic_AnnounceDriver();
+      /*
+       Register usage of the I/O Address range.  From this point onward, any
+       failure will be assumed to be due to a problem with the Host Adapter,
+       rather than due to having mistakenly identified this port as belonging
+       to a BusLogic Host Adapter.  The I/O Address range will not be
+       released, thereby preventing it from being incorrectly identified as
+       any other type of Host Adapter.
+      */
+      request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+                    "BusLogic");
+      /*
+       Register the SCSI Host structure.
+      */
+      HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry;
+      Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T));
+      HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata;
+      memcpy(HostAdapter, &HostAdapterPrototype,
+            sizeof(BusLogic_HostAdapter_T));
+      HostAdapter->SCSI_Host = Host;
+      HostAdapter->HostNumber = Host->host_no;
+      /*
+       Add Host Adapter to the end of the list of registered BusLogic
+       Host Adapters.  In order for Command Complete Interrupts to be
+       properly dismissed by BusLogic_InterruptHandler, the Host Adapter
+       must be registered.  This must be done before the IRQ Channel is
+       acquired, and in a shared IRQ Channel environment, must be done
+       before any Command Complete Interrupts occur, since the IRQ Channel
+       may have already been acquired by a previous BusLogic Host Adapter.
+      */
+      BusLogic_RegisterHostAdapter(HostAdapter);
+      /*
+       Read the Host Adapter Configuration, Acquire the System Resources
+       necessary to use Host Adapter and initialize the fields in the SCSI
+       Host structure, then Test Interrupts, Create the CCBs, and finally
+       Initialize the Host Adapter.
+      */
+      if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+         BusLogic_AcquireResources(HostAdapter, Host) &&
+         BusLogic_TestInterrupts(HostAdapter) &&
+         BusLogic_CreateCCBs(HostAdapter) &&
+         BusLogic_InitializeHostAdapter(HostAdapter) &&
+         BusLogic_InquireTargetDevices(HostAdapter))
+       {
+         /*
+           Initialization has been completed successfully.  Release and
+           re-register usage of the I/O Address range so that the Model
+           Name of the Host Adapter will appear.
+         */
+         release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+         request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+                        HostAdapter->BoardName);
+         BusLogicHostAdapterCount++;
+       }
+      else
+       {
+         /*
+           An error occurred during Host Adapter Configuration Querying,
+           Resource Acquisition, Interrupt Testing, CCB Creation, or Host
+           Adapter Initialization, so remove Host Adapter from the list of
+           registered BusLogic Host Adapters, destroy the CCBs, Release
+           the System Resources, and Unregister the SCSI Host.
+         */
+         BusLogic_DestroyCCBs(HostAdapter);
+         BusLogic_ReleaseResources(HostAdapter);
+         BusLogic_UnregisterHostAdapter(HostAdapter);
+         scsi_unregister(Host);
+       }
+    }
+  return BusLogicHostAdapterCount;
+}
+
+
+/*
+  BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+  support a specific Host Adapter, including the I/O Address range, and
+  unregisters the BusLogic Host Adapter.
+*/
+
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Host->hostdata;
+  /*
+    Destroy the CCBs and release any system resources acquired to use
+    Host Adapter.
+  */
+  BusLogic_DestroyCCBs(HostAdapter);
+  BusLogic_ReleaseResources(HostAdapter);
+  /*
+    Release usage of the I/O Address range.
+  */
+  release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+  /*
+    Remove Host Adapter from the list of registered BusLogic Host Adapters.
+  */
+  BusLogic_UnregisterHostAdapter(HostAdapter);
+  return 0;
+}
+
+
+/*
+  BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+  the Host Adapter Status and Target Device Status.
+*/
+
+static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
+                                       HostAdapterStatus,
+                                     BusLogic_TargetDeviceStatus_T
+                                       TargetDeviceStatus)
+{
+  int HostStatus;
+  switch (HostAdapterStatus)
+    {
+    case BusLogic_CommandCompletedNormally:
+    case BusLogic_LinkedCommandCompleted:
+    case BusLogic_LinkedCommandCompletedWithFlag:
+      HostStatus = DID_OK;
+      break;
+    case BusLogic_SCSISelectionTimeout:
+      HostStatus = DID_TIME_OUT;
+      break;
+    case BusLogic_InvalidOutgoingMailboxActionCode:
+    case BusLogic_InvalidCommandOperationCode:
+    case BusLogic_InvalidCommandParameter:
+      printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n",
+            HostAdapterStatus);
+    case BusLogic_DataOverUnderRun:
+    case BusLogic_UnexpectedBusFree:
+    case BusLogic_LinkedCCBhasInvalidLUN:
+    case BusLogic_AutoRequestSenseFailed:
+    case BusLogic_TaggedQueuingMessageRejected:
+    case BusLogic_UnsupportedMessageReceived:
+    case BusLogic_HostAdapterHardwareFailed:
+    case BusLogic_TargetDeviceReconnectedImproperly:
+    case BusLogic_AbortQueueGenerated:
+    case BusLogic_HostAdapterSoftwareError:
+    case BusLogic_HostAdapterHardwareTimeoutError:
+    case BusLogic_SCSIParityErrorDetected:
+      HostStatus = DID_ERROR;
+      break;
+    case BusLogic_InvalidBusPhaseRequested:
+    case BusLogic_TargetFailedResponseToATN:
+    case BusLogic_HostAdapterAssertedRST:
+    case BusLogic_OtherDeviceAssertedRST:
+    case BusLogic_HostAdapterAssertedBusDeviceReset:
+      HostStatus = DID_RESET;
+      break;
+    default:
+      printk("BusLogic: unknown Host Adapter Status 0x%02X\n",
+            HostAdapterStatus);
+      HostStatus = DID_ERROR;
+      break;
+    }
+  return (HostStatus << 16) | TargetDeviceStatus;
+}
+
+
+/*
+  BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+  Adapters.  To simplify handling shared IRQ Channels, all installed BusLogic
+  Host Adapters are scanned whenever any one of them signals a hardware
+  interrupt.
+*/
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
+                                     Registers_T *InterruptRegisters)
+{
+  BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL;
+  BusLogic_HostAdapter_T *HostAdapter;
+  int HostAdapterResetPendingCount = 0;
+  /*
+    Iterate over the installed BusLogic Host Adapters accepting any Incoming
+    Mailbox entries and saving the completed CCBs for processing.  This
+    interrupt handler is installed with SA_INTERRUPT, so interrupts are
+    disabled when the interrupt handler is entered.
+  */
+  for (HostAdapter = BusLogic_RegisteredHostAdapters;
+       HostAdapter != NULL;
+       HostAdapter = HostAdapter->Next)
+    {
+      unsigned char InterruptRegister;
+      /*
+       Acquire exclusive access to Host Adapter.
+      */
+      BusLogic_LockHostAdapterID(HostAdapter);
+      /*
+       Read the Host Adapter Interrupt Register.
+      */
+      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      if (InterruptRegister & BusLogic_InterruptValid)
+       {
+         /*
+           Acknowledge the interrupt and reset the Host Adapter
+           Interrupt Register.
+         */
+         BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+         /*
+           Process valid SCSI Reset State and Incoming Mailbox Loaded
+           interrupts.  Command Complete interrupts are noted, and
+           Outgoing Mailbox Available interrupts are ignored, as they
+           are never enabled.
+         */
+         if (InterruptRegister & BusLogic_SCSIResetState)
+           {
+             HostAdapter->HostAdapterResetPending = true;
+             HostAdapterResetPendingCount++;
+           }
+         else if (InterruptRegister & BusLogic_IncomingMailboxLoaded)
+           {
+             /*
+               Scan through the Incoming Mailboxes in Strict Round Robin
+               fashion, saving any completed CCBs for further processing.
+               It is essential that for each CCB and SCSI Command issued,
+               completion processing is performed exactly once.  Therefore,
+               only Incoming Mailbox entries with completion code Command
+               Completed Without Error, Command Completed With Error, or
+               Command Aborted At Host Request are saved for completion
+               processing.  When an Incoming Mailbox entry has a completion
+               code of Aborted Command Not Found, the CCB had already
+               completed or been aborted before the current Abort request
+               was processed, and so completion processing has already
+               occurred and no further action should be taken.
+             */
+             BusLogic_IncomingMailbox_T *NextIncomingMailbox =
+               HostAdapter->NextIncomingMailbox;
+             while (NextIncomingMailbox->CompletionCode !=
+                    BusLogic_IncomingMailboxFree)
+               {
+                 BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
+                 BusLogic_CompletionCode_T MailboxCompletionCode =
+                   NextIncomingMailbox->CompletionCode;
+                 if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
+                   {
+                     /*
+                       Mark this CCB as completed and add it to the end
+                       of the list of completed CCBs.
+                     */
+                     CCB->Status = BusLogic_CCB_Completed;
+                     CCB->MailboxCompletionCode = MailboxCompletionCode;
+                     CCB->Next = NULL;
+                     if (FirstCompletedCCB == NULL)
+                       {
+                         FirstCompletedCCB = CCB;
+                         LastCompletedCCB = CCB;
+                       }
+                     else
+                       {
+                         LastCompletedCCB->Next = CCB;
+                         LastCompletedCCB = CCB;
+                       }
+                   }
+                 else printk("scsi%d: Aborted CCB #%d Not Found\n",
+                             HostAdapter->HostNumber, CCB->SerialNumber);
+                 NextIncomingMailbox->CompletionCode =
+                   BusLogic_IncomingMailboxFree;
+                 if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+                   NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+               }
+             HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+           }
+         else if (InterruptRegister & BusLogic_CommandComplete)
+           HostAdapter->HostAdapterCommandCompleted = true;
+       }
+      /*
+       Release exclusive access to Host Adapter.
+      */
+      BusLogic_UnlockHostAdapterID(HostAdapter);
+    }
+  /*
+    Enable interrupts while the completed CCBs are processed.
+  */
+  sti();
+  /*
+    Iterate over the Host Adapters performing any pending Host Adapter Resets.
+  */
+  if (HostAdapterResetPendingCount > 0)
+    for (HostAdapter = BusLogic_RegisteredHostAdapters;
+        HostAdapter != NULL;
+        HostAdapter = HostAdapter->Next)
+      if (HostAdapter->HostAdapterResetPending)
+       {
+         BusLogic_ResetHostAdapter(HostAdapter, NULL);
+         HostAdapter->HostAdapterResetPending = false;
+         scsi_mark_host_bus_reset(HostAdapter->SCSI_Host);
+       }
+  /*
+    Iterate over the completed CCBs setting the SCSI Command Result Codes,
+    deallocating the CCBs, and calling the Completion Routines.
+  */
+  while (FirstCompletedCCB != NULL)
+    {
+      BusLogic_CCB_T *CCB = FirstCompletedCCB;
+      SCSI_Command_T *Command = CCB->Command;
+      FirstCompletedCCB = FirstCompletedCCB->Next;
+      HostAdapter = CCB->HostAdapter;
+      /*
+       Bus Device Reset CCBs have the Command field non-NULL only when a Bus
+       Device Reset was requested for a command that was not currently active
+       in the Host Adapter, and hence would not have its Completion Routine
+       called otherwise.
+      */
+      if (CCB->Opcode == BusLogic_SCSIBusDeviceReset)
+       {
+         printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n",
+                HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+         if (Command != NULL) Command->result = DID_RESET << 16;
+       }
+      else
+       /*
+         Translate the Mailbox Completion Code, Host Adapter Status, and
+         Target Device Status into a SCSI Subsystem Result Code.
+       */
+       switch (CCB->MailboxCompletionCode)
+         {
+         case BusLogic_IncomingMailboxFree:
+         case BusLogic_AbortedCommandNotFound:
+           printk("scsi%d: CCB #%d Impossible State\n",
+                  HostAdapter->HostNumber, CCB->SerialNumber);
+           break;
+         case BusLogic_CommandCompletedWithoutError:
+           HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
+           Command->result = DID_OK << 16;
+           break;
+         case BusLogic_CommandAbortedAtHostRequest:
+           printk("scsi%d: CCB #%d Aborted\n",
+                  HostAdapter->HostNumber, CCB->SerialNumber);
+           Command->result = DID_ABORT << 16;
+           break;
+         case BusLogic_CommandCompletedWithError:
+           Command->result =
+             BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
+                                        CCB->TargetDeviceStatus);
+           if (BusLogic_TracingOptions & BusLogic_TraceErrors)
+             if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+               {
+                 int i;
+                 printk("scsi%d: CCB #%d Target %d: Result %X "
+                        "Host Adapter Status %02X Target Status %02X\n",
+                        HostAdapter->HostNumber, CCB->SerialNumber,
+                        CCB->TargetID, Command->result,
+                        CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
+                 printk("scsi%d: CDB   ", HostAdapter->HostNumber);
+                 for (i = 0; i < CCB->CDB_Length; i++)
+                   printk(" %02X", CCB->CDB[i]);
+                 printk("\n");
+                 printk("scsi%d: Sense ", HostAdapter->HostNumber);
+                 for (i = 0; i < CCB->SenseDataLength; i++)
+                   printk(" %02X", (*CCB->SenseDataPointer)[i]);
+                 printk("\n");
+               }
+           break;
+         }
+      /*
+       Place CCB back on the Host Adapter's free list.
+      */
+      BusLogic_DeallocateCCB(CCB);
+      /*
+       Call the SCSI Command Completion Routine if appropriate.
+      */
+      if (Command != NULL) Command->scsi_done(Command);
+    }
+}
+
+
+/*
+  BusLogic_WriteOutgoingMailboxEntry writes an Outgoing Mailbox entry
+  for Host Adapter with Action Code and CCB.
+*/
+
+static boolean BusLogic_WriteOutgoingMailboxEntry(BusLogic_HostAdapter_T
+                                                   *HostAdapter,
+                                                 BusLogic_ActionCode_T
+                                                   ActionCode,
+                                                 BusLogic_CCB_T *CCB)
+{
+  BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+  boolean Result = false;
+  BusLogic_LockHostAdapter(HostAdapter);
+  NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+  if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
+    {
+      NextOutgoingMailbox->ActionCode = ActionCode;
+      NextOutgoingMailbox->CCB = CCB;
+      CCB->Status = BusLogic_CCB_Active;
+      BusLogic_StartMailboxScan(HostAdapter);
+      if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+       NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+      HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+      Result = true;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return Result;
+}
+
+
+/*
+  BusLogic_QueueCommand creates a CCB for Command and places it into an
+  Outgoing Mailbox for execution by the associated Host Adapter.
+*/
+
+int BusLogic_QueueCommand(SCSI_Command_T *Command,
+                         void (*CompletionRoutine)(SCSI_Command_T *))
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char *CDB = Command->cmnd;
+  unsigned char CDB_Length = Command->cmd_len;
+  unsigned char TargetID = Command->target;
+  unsigned char LogicalUnit = Command->lun;
+  void *BufferPointer = Command->request_buffer;
+  int BufferLength = Command->request_bufflen;
+  int SegmentCount = Command->use_sg;
+  BusLogic_CCB_T *CCB;
+  long EnableTQ;
+  /*
+    SCSI REQUEST_SENSE commands will be executed automatically by the Host
+    Adapter for any errors, so they should not be executed explicitly unless
+    the Sense Data is zero indicating that no error occurred.
+  */
+  if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0)
+    {
+      Command->result = DID_OK << 16;
+      CompletionRoutine(Command);
+      return 0;
+    }
+  /*
+    Allocate a CCB from the Host Adapter's free list, aborting the command
+    with an error if there are none available and memory allocation fails.
+  */
+  CCB = BusLogic_AllocateCCB(HostAdapter);
+  if (CCB == NULL)
+    {
+      Command->result = DID_ERROR << 16;
+      CompletionRoutine(Command);
+      return 0;
+    }
+  /*
+    Initialize the fields in the BusLogic Command Control Block (CCB).
+  */
+  if (SegmentCount == 0)
+    {
+      CCB->Opcode = BusLogic_InitiatorCCB;
+      CCB->DataLength = BufferLength;
+      CCB->DataPointer = BufferPointer;
+    }
+  else
+    {
+      SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer;
+      int Segment;
+      CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
+      CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+      CCB->DataPointer = CCB->ScatterGatherList;
+      for (Segment = 0; Segment < SegmentCount; Segment++)
+       {
+         CCB->ScatterGatherList[Segment].SegmentByteCount =
+           ScatterList[Segment].length;
+         CCB->ScatterGatherList[Segment].SegmentDataPointer =
+           ScatterList[Segment].address;
+       }
+    }
+  switch (CDB[0])
+    {
+    case READ_6:
+    case READ_10:
+      CCB->DataDirection = BusLogic_DataInLengthChecked;
+      HostAdapter->ReadWriteOperationCount[TargetID]++;
+      break;
+    case WRITE_6:
+    case WRITE_10:
+      CCB->DataDirection = BusLogic_DataOutLengthChecked;
+      HostAdapter->ReadWriteOperationCount[TargetID]++;
+      break;
+    default:
+      CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+      break;
+    }
+  CCB->CDB_Length = CDB_Length;
+  CCB->SenseDataLength = sizeof(Command->sense_buffer);
+  CCB->TargetID = TargetID;
+  CCB->LogicalUnit = LogicalUnit;
+  /*
+    For Wide SCSI Host Adapters, Wide Mode CCBs are used to support more than
+    8 Logical Units per Target, and this requires setting the overloaded
+    TagEnable field to Logical Unit bit 5.
+  */
+  if (HostAdapter->HostWideSCSI)
+    {
+      CCB->TagEnable = LogicalUnit >> 5;
+      CCB->WideModeTagEnable = false;
+    }
+  else CCB->TagEnable = false;
+  /*
+    BusLogic recommends that after a Reset the first couple of commands that
+    are sent to a Target be sent in a non Tagged Queue fashion so that the Host
+    Adapter and Target can establish Synchronous Transfer before Queue Tag
+    messages can interfere with the Synchronous Negotiation message.
+  */
+  if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
+      Command->device->tagged_supported &&
+      (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 5) >= 0)
+    {
+      if (EnableTQ == 0)
+       printk("scsi%d: Tagged Queuing now active for Target %d\n",
+              HostAdapter->HostNumber, TargetID);
+      if (HostAdapter->HostWideSCSI)
+       {
+         CCB->WideModeTagEnable = true;
+         CCB->WideModeQueueTag = BusLogic_SimpleQueueTag;
+       }
+      else
+       {
+         CCB->TagEnable = true;
+         CCB->QueueTag = BusLogic_SimpleQueueTag;
+       }
+    }
+  memcpy(CCB->CDB, CDB, CDB_Length);
+  CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer;
+  CCB->Command = Command;
+  Command->scsi_done = CompletionRoutine;
+  /*
+    Place the CCB in an Outgoing Mailbox, aborting the command with an
+    error if there are none available.
+  */
+  if (!(BusLogic_WriteOutgoingMailboxEntry(
+         HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+    {
+      printk("scsi%d: cannot write Outgoing Mailbox Entry\n",
+            HostAdapter->HostNumber);
+      BusLogic_DeallocateCCB(CCB);
+      Command->result = DID_ERROR << 16;
+      CompletionRoutine(Command);
+    }
+  return 0;
+}
+
+
+/*
+  BusLogic_AbortCommand aborts Command if possible.
+*/
+
+int BusLogic_AbortCommand(SCSI_Command_T *Command)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char InterruptRegister;
+  BusLogic_CCB_T *CCB;
+  int Result;
+  /*
+    If the Host Adapter has posted an interrupt but the Interrupt Handler
+    has not been called for some reason (i.e. the interrupt was lost), try
+    calling the Interrupt Handler directly to process the commands that
+    have been completed.
+  */
+  InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+  if (InterruptRegister & BusLogic_InterruptValid)
+    {
+      unsigned long ProcessorFlags;
+      printk("scsi%d: Recovering Lost Interrupt for IRQ Channel %d\n",
+            HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+      save_flags(ProcessorFlags);
+      cli();
+      BusLogic_InterruptHandler(HostAdapter->IRQ_Channel, NULL);
+      restore_flags(ProcessorFlags);
+      return SCSI_ABORT_SNOOZE;
+    }
+  /*
+    Find the CCB to be aborted and determine how to proceed.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  Result = SCSI_ABORT_NOT_RUNNING;
+  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+    if (CCB->Command == Command)
+      {
+       if (CCB->Status == BusLogic_CCB_Active)
+         if ((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable &&
+              CCB->WideModeQueueTag != BusLogic_SimpleQueueTag) ||
+             (!HostAdapter->HostWideSCSI && CCB->TagEnable &&
+              CCB->QueueTag != BusLogic_SimpleQueueTag))
+           {
+             /*
+               CCBs using Tagged Queuing with other than Simple Queue Tag
+               should not be aborted.
+             */
+             Result = SCSI_ABORT_BUSY;
+           }
+         else
+           {
+             /*
+               Attempt to abort the CCB.
+             */
+             if (BusLogic_WriteOutgoingMailboxEntry(
+                   HostAdapter, BusLogic_MailboxAbortCommand, CCB))
+               {
+                 printk("scsi%d: Aborting CCB #%d\n",
+                        HostAdapter->HostNumber, CCB->SerialNumber);
+                 Result = SCSI_ABORT_PENDING;
+               }
+             else Result = SCSI_ABORT_BUSY;
+           }
+       break;
+      }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  return Result;
+}
+
+
+/*
+  BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+  currently executing SCSI commands as having been reset, as well as
+  the specified Command if non-NULL.
+*/
+
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
+                                    SCSI_Command_T *Command)
+{
+  BusLogic_CCB_T *CCB;
+  if (Command == NULL)
+    printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n",
+          HostAdapter->HostNumber, HostAdapter->BoardName);
+  else printk("scsi%d: Resetting %s due to Target %d\n",
+             HostAdapter->HostNumber, HostAdapter->BoardName, Command->target);
+  /*
+    Attempt to Reset and Reinitialize the Host Adapter.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  if (!(BusLogic_HardResetHostAdapter(HostAdapter) &&
+       BusLogic_InitializeHostAdapter(HostAdapter)))
+    {
+      printk("scsi%d: Resetting %s Failed\n",
+             HostAdapter->HostNumber, HostAdapter->BoardName);
+      BusLogic_UnlockHostAdapter(HostAdapter);
+      return SCSI_RESET_ERROR;
+    }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  /*
+    Wait a few seconds between the Host Adapter Hard Reset which initiates
+    a SCSI Bus Reset and issuing any SCSI commands.  Some SCSI devices get
+    confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+  */
+  BusLogic_Delay(HostAdapter->BusSettleTime);
+  /*
+    Mark all currently executing CCBs as having been reset.
+  */
+  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+    if (CCB->Status == BusLogic_CCB_Active)
+      {
+       SCSI_Command_T *ActiveCommand = CCB->Command;
+       if (ActiveCommand == Command) Command = NULL;
+       BusLogic_DeallocateCCB(CCB);
+       if (ActiveCommand != NULL)
+         {
+           ActiveCommand->result = DID_RESET << 16;
+           ActiveCommand->scsi_done(ActiveCommand);
+         }
+      }
+  if (Command != NULL)
+    {
+      Command->result = DID_RESET << 16;
+      Command->scsi_done(Command);
+    }
+  return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+}
+
+
+/*
+  BusLogic_BusDeviceReset sends a Bus Device Reset to the Target
+  associated with Command.
+*/
+
+static int BusLogic_BusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
+                                  SCSI_Command_T *Command)
+{
+  BusLogic_CCB_T *CCB = BusLogic_AllocateCCB(HostAdapter), *XCCB;
+  unsigned char TargetID = Command->target;
+  /*
+    If sending a Bus Device Reset is impossible, attempt a full Host
+    Adapter Hard Reset and SCSI Bus Reset.
+  */
+  if (CCB == NULL)
+    return BusLogic_ResetHostAdapter(HostAdapter, Command);
+  printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n",
+        HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
+  CCB->Opcode = BusLogic_SCSIBusDeviceReset;
+  CCB->TargetID = TargetID;
+  CCB->Command = Command;
+  /*
+    If there is a currently executing CCB in the Host Adapter for this Command,
+    then an Incoming Mailbox entry will be made with a completion code of
+    BusLogic_HostAdapterAssertedBusDeviceReset.  Otherwise, the CCB Command
+    field will be left pointing to the Command so that the interrupt for the
+    completion of the Bus Device Reset can call the Completion Routine for the
+    Command.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+    if (XCCB->Status == BusLogic_CCB_Active && XCCB->Command == Command)
+      {
+       CCB->Command = NULL;
+       break;
+      }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  /*
+    Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
+    If sending a Bus Device Reset is impossible, attempt a full Host
+    Adapter Hard Reset and SCSI Bus Reset.
+  */
+  if (!(BusLogic_WriteOutgoingMailboxEntry(
+         HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+    {
+      printk("scsi%d: cannot write Outgoing Mailbox Entry for "
+            "Bus Device Reset\n", HostAdapter->HostNumber);
+      BusLogic_DeallocateCCB(CCB);
+      return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    }
+  HostAdapter->ReadWriteOperationCount[TargetID] = 0;
+  return SCSI_RESET_PENDING;
+}
+
+
+/*
+  BusLogic_ResetCommand takes appropriate action to reset Command.
+*/
+
+int BusLogic_ResetCommand(SCSI_Command_T *Command)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned char TargetID = Command->target;
+  unsigned char ErrorRecoveryOption =
+    HostAdapter->ErrorRecoveryOption[TargetID];
+  if (ErrorRecoveryOption == BusLogic_ErrorRecoveryDefault)
+    if (Command->host->suggest_bus_reset)
+      ErrorRecoveryOption = BusLogic_ErrorRecoveryHardReset;
+    else ErrorRecoveryOption = BusLogic_ErrorRecoveryBusDeviceReset;
+  switch (ErrorRecoveryOption)
+    {
+    case BusLogic_ErrorRecoveryHardReset:
+      return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    case BusLogic_ErrorRecoveryBusDeviceReset:
+      if (HostAdapter->CommandSuccessfulFlag[TargetID])
+       {
+         HostAdapter->CommandSuccessfulFlag[TargetID] = false;
+         return BusLogic_BusDeviceReset(HostAdapter, Command);
+       }
+      else return BusLogic_ResetHostAdapter(HostAdapter, Command);
+    }
+  printk("scsi%d: Error Recovery Suppressed\n", HostAdapter->HostNumber);
+  return SCSI_RESET_PUNT;
+}
+
+
+/*
+  BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+  Parameters for Disk.  The default disk geometry is 64 heads, 32 sectors, and
+  the appropriate number of cylinders so as not to exceed drive capacity.  In
+  order for disks equal to or larger than 1 GB to be addressable by the BIOS
+  without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
+  may be enabled in AutoSCSI on "C" Series boards or by a dip switch setting
+  on older boards.  With Extended Translation enabled, drives between 1 GB
+  inclusive and 2 GB exclusive are given a disk geometry of 128 heads and 32
+  sectors, and drives between 2 GB inclusive and 8 GB exclusive are given a
+  disk geometry of 255 heads and 63 sectors.  On "C" Series boards the firmware
+  can be queried for the precise translation in effect for each drive
+  individually, but there is really no need to do so since we know the total
+  capacity of the drive and whether Extended Translation is enabled, hence we
+  can deduce the BIOS disk geometry that must be in effect.
+*/
+
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
+                               int *Parameters)
+{
+  BusLogic_HostAdapter_T *HostAdapter =
+    (BusLogic_HostAdapter_T *) Disk->device->host->hostdata;
+  BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters;
+  if (HostAdapter->ExtendedTranslation &&
+      Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */)
+    if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */)
+      {
+       DiskParameters->Heads = 255;
+       DiskParameters->Sectors = 63;
+      }
+    else
+      {
+       DiskParameters->Heads = 128;
+       DiskParameters->Sectors = 32;
+      }
+  else
+    {
+      DiskParameters->Heads = 64;
+      DiskParameters->Sectors = 32;
+    }
+  DiskParameters->Cylinders =
+    Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+  return 0;
+}
+
+
+/*
+  BusLogic_Setup handles processing of Kernel Command Line Arguments.
+
+  For the BusLogic driver, a kernel command line entry comprises the driver
+  identifier "BusLogic=" optionally followed by a comma-separated sequence of
+  integers and then optionally followed by a comma-separated sequence of
+  strings.  Each command line entry applies to one BusLogic Host Adapter.
+  Multiple command line entries may be used in systems which contain multiple
+  BusLogic Host Adapters.
+
+  The first integer specified is the I/O Address at which the Host Adapter is
+  located.  If unspecified, it defaults to 0 which means to apply this entry to
+  the first BusLogic Host Adapter found during the default probe sequence.  If
+  any I/O Address parameters are provided on the command line, then the default
+  probe sequence is omitted.
+
+  The second integer specified is the number of Concurrent Commands per Logical
+  Unit to allow for Target Devices on the Host Adapter.  If unspecified, it
+  defaults to 0 which means to use the value of BusLogic_Concurrency for
+  non-ISA Host Adapters, or BusLogic_Concurrency_ISA for ISA Host Adapters.
+
+  The third integer specified is the Bus Settle Time in seconds.  This is
+  the amount of time to wait between a Host Adapter Hard Reset which initiates
+  a SCSI Bus Reset and issuing any SCSI commands.  If unspecified, it defaults
+  to 0 which means to use the value of BusLogic_DefaultBusSettleTime.
+
+  The fourth integer specified is the Tracing Options.  If unspecified, it
+  defaults to 0 which means that no special tracing information is to be
+  printed.  Note that Tracing Options are applied across all Host Adapters.
+
+  The string options are used to provide control over Tagged Queuing and Error
+  Recovery. If both Tagged Queuing and Error Recovery strings are provided, the
+  Tagged Queuing specification string must come first.
+
+  The Tagged Queuing specification begins with "TQ:" and allows for explicitly
+  specifying whether Tagged Queuing is permitted on Target Devices that support
+  it.  The following specification options are available:
+
+  TQ:Default           Tagged Queuing will be permitted based on the firmware
+                       version of the BusLogic Host Adapter and based on
+                       whether the Concurrency value allows queuing multiple
+                       commands.
+
+  TQ:Enable            Tagged Queuing will be enabled for all Target Devices
+                       on this Host Adapter overriding any limitation that
+                       would otherwise be imposed based on the Host Adapter
+                       firmware version.
+
+  TQ:Disable           Tagged Queuing will be disabled for all Target Devices
+                       on this Host Adapter.
+
+  TQ:<Per-Target-Spec> Tagged Queuing will be controlled individually for each
+                       Target Device.  <Per-Target-Spec> is a sequence of "Y",
+                       "N", and "X" characters.  "Y" enabled Tagged Queuing,
+                       "N" disables Tagged Queuing, and "X" accepts the
+                       default based on the firmware version.  The first
+                       character refers to Target 0, the second to Target 1,
+                       and so on; if the sequence of "Y", "N", and "X"
+                       characters does not cover all the Target Devices,
+                       unspecified characters are assumed to be "X".
+
+  Note that explicitly requesting Tagged Queuing may lead to problems; this
+  facility is provided primarily to allow disabling Tagged Queuing on Target
+  Devices that do not implement it correctly.
+
+  The Error Recovery specification begins with "ER:" and allows for explicitly
+  specifying the Error Recovery action to be performed when ResetCommand is
+  called due to a SCSI Command failing to complete successfully.  The following
+  specification options are available:
+
+  ER:Default           Error Recovery will select between the Hard Reset and
+                       Bus Device Reset options based on the recommendation
+                       of the SCSI Subsystem.
+
+  ER:HardReset         Error Recovery will initiate a Host Adapter Hard Reset
+                       which also causes a SCSI Bus Reset.
+
+  ER:BusDeviceReset    Error Recovery will send a Bus Device Reset message to
+                       the individual Target Device causing the error.  If
+                       Error Recovery is again initiated for this Target
+                       Device and no SCSI Command to this Target Device has
+                       completed successfully since the Bus Device Reset
+                       message was sent, then a Hard Reset will be attempted.
+
+  ER:None              Error Recovery will be suppressed.  This option should
+                       only be selected if a SCSI Bus Reset or Bus Device
+                       Reset will cause the Target Device to fail completely
+                       and unrecoverably.
+
+  ER:<Per-Target-Spec> Error Recovery will be controlled individually for each
+                       Target Device.  <Per-Target-Spec> is a sequence of "D",
+                       "H", "B", and "N" characters.  "D" selects Default, "H"
+                       selects Hard Reset, "B" selects Bus Device Reset, and
+                       "N" selects None.  The first character refers to Target
+                       0, the second to Target 1, and so on; if the sequence
+                       of "D", "H", "B", and "N" characters does not cover all
+                       the Target Devices, unspecified characters are assumed
+                       to be "D".
+*/
+
+void BusLogic_Setup(char *Strings, int *Integers)
+{
+  BusLogic_CommandLineEntry_T *CommandLineEntry =
+    &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
+  static int ProbeListIndex = 0;
+  int IntegerCount = Integers[0], TargetID, i;
+  CommandLineEntry->IO_Address = 0;
+  CommandLineEntry->Concurrency = 0;
+  CommandLineEntry->BusSettleTime = 0;
+  CommandLineEntry->TaggedQueuingPermitted = 0;
+  CommandLineEntry->TaggedQueuingPermittedMask = 0;
+  memset(CommandLineEntry->ErrorRecoveryOption,
+        BusLogic_ErrorRecoveryDefault,
+        sizeof(CommandLineEntry->ErrorRecoveryOption));
+  if (IntegerCount > 4)
+    printk("BusLogic: Unexpected Command Line Integers ignored\n");
+  if (IntegerCount >= 1)
+    {
+      unsigned short IO_Address = Integers[1];
+      if (IO_Address > 0)
+       {
+         for (i = 0; ; i++)
+           if (BusLogic_IO_StandardAddresses[i] == 0)
+             {
+               printk("BusLogic: Invalid Command Line Entry "
+                      "(illegal I/O Address 0x%X)\n", IO_Address);
+               return;
+             }
+           else if (i < ProbeListIndex &&
+                    IO_Address == BusLogic_IO_AddressProbeList[i])
+             {
+               printk("BusLogic: Invalid Command Line Entry "
+                      "(duplicate I/O Address 0x%X)\n", IO_Address);
+               return;
+             }
+           else if (IO_Address == BusLogic_IO_StandardAddresses[i]) break;
+         BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address;
+         BusLogic_IO_AddressProbeList[ProbeListIndex] = 0;
+       }
+      CommandLineEntry->IO_Address = IO_Address;
+    }
+  if (IntegerCount >= 2)
+    {
+      unsigned short Concurrency = Integers[2];
+      if (Concurrency > BusLogic_MailboxCount)
+       {
+         printk("BusLogic: Invalid Command Line Entry "
+                "(illegal Concurrency %d)\n", Concurrency);
+         return;
+       }
+      CommandLineEntry->Concurrency = Concurrency;
+    }
+  if (IntegerCount >= 3)
+    CommandLineEntry->BusSettleTime = Integers[3];
+  if (IntegerCount >= 4)
+    BusLogic_TracingOptions |= Integers[4];
+  if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 ||
+       BusLogic_CommandLineEntryCount == ProbeListIndex))
+    {
+      printk("BusLogic: Invalid Command Line Entry "
+            "(all or no I/O Addresses must be specified)\n");
+      return;
+    }
+  if (Strings == NULL) return;
+  if (strncmp(Strings, "TQ:", 3) == 0)
+    {
+      Strings += 3;
+      if (strncmp(Strings, "Default", 7) == 0)
+       Strings += 7;
+      else if (strncmp(Strings, "Enable", 6) == 0)
+       {
+         Strings += 6;
+         CommandLineEntry->TaggedQueuingPermitted = 0xFFFF;
+         CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+       }
+      else if (strncmp(Strings, "Disable", 7) == 0)
+       {
+         Strings += 7;
+         CommandLineEntry->TaggedQueuingPermitted = 0x0000;
+         CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+       }
+      else
+       for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+         switch (*Strings++)
+           {
+           case 'Y':
+             CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID;
+             CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+             break;
+           case 'N':
+             CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+             break;
+           case 'X':
+             break;
+           default:
+             Strings--;
+             TargetID = BusLogic_MaxTargetIDs;
+             break;
+           }
+    }
+  if (*Strings == ',') Strings++;
+  if (strncmp(Strings, "ER:", 3) == 0)
+    {
+      Strings += 3;
+      if (strncmp(Strings, "Default", 7) == 0)
+       Strings += 7;
+      else if (strncmp(Strings, "HardReset", 9) == 0)
+       {
+         Strings += 9;
+         memset(CommandLineEntry->ErrorRecoveryOption,
+                BusLogic_ErrorRecoveryHardReset,
+                sizeof(CommandLineEntry->ErrorRecoveryOption));
+       }
+      else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
+       {
+         Strings += 14;
+         memset(CommandLineEntry->ErrorRecoveryOption,
+                BusLogic_ErrorRecoveryBusDeviceReset,
+                sizeof(CommandLineEntry->ErrorRecoveryOption));
+       }
+      else if (strncmp(Strings, "None", 4) == 0)
+       {
+         Strings += 4;
+         memset(CommandLineEntry->ErrorRecoveryOption,
+                BusLogic_ErrorRecoveryNone,
+                sizeof(CommandLineEntry->ErrorRecoveryOption));
+       }
+      else
+       for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+         switch (*Strings++)
+           {
+           case 'D':
+             CommandLineEntry->ErrorRecoveryOption[TargetID] =
+               BusLogic_ErrorRecoveryDefault;
+             break;
+           case 'H':
+             CommandLineEntry->ErrorRecoveryOption[TargetID] =
+               BusLogic_ErrorRecoveryHardReset;
+             break;
+           case 'B':
+             CommandLineEntry->ErrorRecoveryOption[TargetID] =
+               BusLogic_ErrorRecoveryBusDeviceReset;
+             break;
+           case 'N':
+             CommandLineEntry->ErrorRecoveryOption[TargetID] =
+               BusLogic_ErrorRecoveryNone;
+             break;
+           default:
+             Strings--;
+             TargetID = BusLogic_MaxTargetIDs;
+             break;
+           }
+    }
+  if (*Strings != '\0')
+    printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings);
+}
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
new file mode 100644 (file)
index 0000000..a513ce2
--- /dev/null
@@ -0,0 +1,967 @@
+/*
+
+  Linux Driver for BusLogic SCSI Host Adapters
+
+  Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  See BusLogic.c for licensing information.
+
+*/
+
+
+/*
+  Define types for some of the structures that interface with the rest
+  of the Linux Kernel and SCSI Subsystem.
+*/
+
+typedef struct pt_regs Registers_T;
+typedef Scsi_Host_Template SCSI_Host_Template_T;
+typedef struct Scsi_Host SCSI_Host_T;
+typedef struct scsi_disk SCSI_Disk_T;
+typedef struct scsi_cmnd SCSI_Command_T;
+typedef struct scatterlist SCSI_ScatterList_T;
+typedef kdev_t KernelDevice_T;
+
+
+/*
+  Define prototypes for the BusLogic Driver Interface Functions.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *);
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
+int BusLogic_QueueCommand(SCSI_Command_T *,
+                         void (*CompletionRoutine)(SCSI_Command_T *));
+int BusLogic_AbortCommand(SCSI_Command_T *);
+int BusLogic_ResetCommand(SCSI_Command_T *);
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+
+
+/*
+  Define the BusLogic SCSI Host Template structure.
+*/
+
+#define BUSLOGIC                                                        \
+  { NULL,                              /* Next                      */  \
+    NULL,                              /* Usage Count Pointer       */  \
+    NULL,                              /* /proc Directory Entry     */  \
+    NULL,                              /* /proc Info Function       */  \
+    "BusLogic",                                /* Driver Name               */  \
+    BusLogic_DetectHostAdapter,                /* Detect Host Adapter       */  \
+    BusLogic_ReleaseHostAdapter,       /* Release Host Adapter      */  \
+    BusLogic_DriverInfo,               /* Driver Info Function      */  \
+    NULL,                              /* Command Function          */  \
+    BusLogic_QueueCommand,             /* Queue Command Function    */  \
+    BusLogic_AbortCommand,             /* Abort Command Function    */  \
+    BusLogic_ResetCommand,             /* Reset Command Function    */  \
+    NULL,                              /* Slave Attach Function     */  \
+    BusLogic_BIOSDiskParameters,       /* Disk BIOS Parameters      */  \
+    0,                                 /* Can Queue                 */  \
+    0,                                 /* This ID                   */  \
+    0,                                 /* Scatter/Gather Table Size */  \
+    0,                                 /* SCSI Commands per LUN     */  \
+    0,                                 /* Present                   */  \
+    1,                                 /* Default Unchecked ISA DMA */  \
+    ENABLE_CLUSTERING }                        /* Enable Clustering         */
+
+
+/*
+  BusLogic_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef BusLogic_DriverVersion
+
+
+/*
+  Define the maximum number of BusLogic Host Adapters that are supported.
+*/
+
+#define BusLogic_MaxHostAdapters               10
+
+
+/*
+  Define the maximum number of I/O Addresses that may be probed.
+*/
+
+#define BusLogic_IO_MaxProbeAddresses          16
+
+
+/*
+  Define the maximum number of Target IDs supported by this driver.
+*/
+
+#define BusLogic_MaxTargetIDs                  16
+
+
+/*
+  Define the number of Incoming and Outgoing Mailboxes used by this driver.
+  Since BusLogic Host Adapters have room to buffer 32 commands internally,
+  there is significant virtue in setting BusLogic_MailboxCount to 32 or above.
+  The maximum possible value is 255, since the MailboxCount parameter to the
+  Initialize Extended Mailbox command is limited to a single byte.
+*/
+
+#define BusLogic_MailboxCount                  32
+
+
+/*
+  Define the number of Command Control Blocks (CCBs) to create during
+  initialization for each Host Adapter.  Additional CCBs will be allocated
+  if necessary as commands are queued.
+*/
+
+#define BusLogic_InitialCCBs                   32
+
+
+/*
+  Define the maximum number of Scatter/Gather Segments used by this driver.
+  For maximum performance, it is important that this limit be at least as
+  large as the maximum single request generated by the routine make_request.
+*/
+
+#define BusLogic_ScatterGatherLimit            128
+
+
+/*
+  Define the default number of Concurrent Commands per Logical Unit to allow
+  for Target Devices on non-ISA and ISA Host Adapters.
+*/
+
+#define BusLogic_Concurrency                   7
+#define BusLogic_Concurrency_ISA               1
+
+
+/*
+  Define the default amount of time in seconds to wait between a Host Adapter
+  Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands.
+  Some SCSI devices get confused if they receive SCSI commands too soon after
+  a SCSI Bus Reset.
+*/
+
+#define BusLogic_DefaultBusSettleTime          2
+
+
+/*
+  Define the possible Tracing Options.
+*/
+
+#define BusLogic_TraceProbe                    1
+#define BusLogic_TraceHardReset                        2
+#define BusLogic_TraceConfiguration            4
+#define BusLogic_TraceErrors                   8
+
+
+/*
+  Define the possible Error Recovery Options.
+*/
+
+#define BusLogic_ErrorRecoveryDefault          0
+#define BusLogic_ErrorRecoveryHardReset                1
+#define BusLogic_ErrorRecoveryBusDeviceReset   2
+#define BusLogic_ErrorRecoveryNone             3
+
+static char
+  *BusLogic_ErrorRecoveryOptions[] =
+    { "Default", "Hard Reset", "Bus Device Reset", "None" },
+  *BusLogic_ErrorRecoveryOptions2[] =
+    { "D", "H", "B", "N" };
+
+
+/*
+  Define a boolean data type.
+*/
+
+#define false 0
+#define true  1
+typedef unsigned char boolean;
+
+
+/*
+  Define the BusLogic SCSI Host Adapter I/O Register Offsets.
+*/
+
+#define BusLogic_IO_PortCount                  4       /* I/O Registers */
+#define BusLogic_ControlRegister               0       /* WO register */
+#define BusLogic_StatusRegister                        0       /* RO register */
+#define BusLogic_CommandParameterRegister      1       /* WO register */
+#define BusLogic_DataInRegister                        1       /* RO register */
+#define BusLogic_InterruptRegister             2       /* RO register */
+#define BusLogic_GeometryRegister              3       /* RO, undocumented */
+
+
+/*
+  Define the bits in the write-only Control Register.
+*/
+
+#define BusLogic_ReservedCR                    0x0F
+#define BusLogic_SCSIBusReset                  0x10
+#define BusLogic_InterruptReset                        0x20
+#define BusLogic_SoftReset                     0x40
+#define BusLogic_HardReset                     0x80
+
+
+/*
+  Define the bits in the read-only Status Register.
+*/
+
+#define BusLogic_CommandInvalid                        0x01
+#define BusLogic_ReservedSR                    0x02
+#define BusLogic_DataInRegisterReady           0x04
+#define BusLogic_CommandParameterRegisterBusy  0x08
+#define BusLogic_HostAdapterReady              0x10
+#define BusLogic_InitializationRequired                0x20
+#define BusLogic_DiagnosticFailure             0x40
+#define BusLogic_DiagnosticActive              0x80
+
+
+/*
+  Define the bits in the read-only Interrupt Register.
+*/
+
+#define BusLogic_IncomingMailboxLoaded         0x01
+#define BusLogic_OutgoingMailboxAvailable      0x02
+#define BusLogic_CommandComplete               0x04
+#define BusLogic_SCSIResetState                        0x08
+#define BusLogic_ReservedIR                    0x70
+#define BusLogic_InterruptValid                        0x80
+
+
+/*
+  Define the bits in the undocumented read-only Geometry Register.
+*/
+
+#define BusLogic_Drive0Geometry                        0x03
+#define BusLogic_Drive1Geometry                        0x0C
+#define BusLogic_ReservedGR                    0x70
+#define BusLogic_ExtendedTranslationEnabled    0x80
+
+
+/*
+  Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
+*/
+
+typedef enum
+{
+  BusLogic_TestCommandCompleteInterrupt =      0x00,   /* documented */
+  BusLogic_InitializeMailbox =                 0x01,   /* documented */
+  BusLogic_StartMailboxCommand =               0x02,   /* documented */
+  BusLogic_StartBIOSCommand =                  0x03,   /* documented */
+  BusLogic_InquireBoardID =                    0x04,   /* documented */
+  BusLogic_EnableOutgoingMailboxAvailableIRQ = 0x05,   /* documented */
+  BusLogic_SetSCSISelectionTimeout =           0x06,   /* documented */
+  BusLogic_SetPreemptTimeOnBus =               0x07,   /* documented */
+  BusLogic_SetTimeOffBus =                     0x08,   /* ISA Bus only */
+  BusLogic_SetBusTransferRate =                        0x09,   /* ISA Bus only */
+  BusLogic_InquireInstalledDevicesID0to7 =     0x0A,   /* documented */
+  BusLogic_InquireConfiguration =              0x0B,   /* documented */
+  BusLogic_SetTargetMode =                     0x0C,   /* now undocumented */
+  BusLogic_InquireSetupInformation =           0x0D,   /* documented */
+  BusLogic_WriteAdapterLocalRAM =              0x1A,   /* documented */
+  BusLogic_ReadAdapterLocalRAM =               0x1B,   /* documented */
+  BusLogic_WriteBusMasterChipFIFO =            0x1C,   /* documented */
+  BusLogic_ReadBusMasterChipFIFO =             0x1D,   /* documented */
+  BusLogic_EchoCommandData =                   0x1F,   /* documented */
+  BusLogic_HostAdapterDiagnostic =             0x20,   /* documented */
+  BusLogic_SetAdapterOptions =                 0x21,   /* documented */
+  BusLogic_InquireInstalledDevicesID8to15 =    0x23,   /* Wide only */
+  BusLogic_InitializeExtendedMailbox =         0x81,   /* documented */
+  BusLogic_InquireFirmwareVersion3rdDigit =    0x84,   /* undocumented */
+  BusLogic_InquireFirmwareVersionLetter =      0x85,   /* undocumented */
+  BusLogic_InquireBoardModelAndRevision =      0x8B,   /* undocumented */
+  BusLogic_InquireSynchronousPeriod =          0x8C,   /* undocumented */
+  BusLogic_InquireExtendedSetupInformation =   0x8D,   /* documented */
+  BusLogic_EnableStrictRoundRobinMode =                0x8F,   /* documented */
+  BusLogic_ModifyIOAddress =                   0x95,   /* PCI only */
+  BusLogic_EnableWideModeCCB =                 0x96    /* Wide only */
+}
+BusLogic_OperationCode_T;
+
+
+/*
+  Define the Inquire Board ID reply structure.
+*/
+
+typedef struct BusLogic_BoardID
+{
+  unsigned char BoardType;
+  unsigned char CustomFeatures;
+  unsigned char FirmwareVersion1stDigit;
+  unsigned char FirmwareVersion2ndDigit;
+}
+BusLogic_BoardID_T;
+
+
+/*
+  Define the Inquire Installed Devices ID 0 to 7 and Inquire Installed
+  Devices ID 8 to 15 reply type.  For each Target ID, a byte is returned
+  where bit 0 set indicates that Logical Unit 0 exists, bit 1 set indicates
+  that Logical Unit 1 exists, and so on.
+*/
+
+typedef unsigned char BusLogic_InstalledDevices8_T[8];
+
+typedef unsigned char BusLogic_InstalledDevices_T[BusLogic_MaxTargetIDs];
+
+
+/*
+  Define the Inquire Configuration reply structure.
+*/
+
+typedef struct BusLogic_Configuration
+{
+  unsigned char :5;                            /* Byte 0: DMA Channel */
+  boolean DMA_Channel5:1;
+  boolean DMA_Channel6:1;
+  boolean DMA_Channel7:1;
+  boolean IRQ_Channel9:1;                      /* Byte 1: IRQ Channel */
+  boolean IRQ_Channel10:1;
+  boolean IRQ_Channel11:1;
+  boolean IRQ_Channel12:1;
+  unsigned char :1;
+  boolean IRQ_Channel14:1;
+  boolean IRQ_Channel15:1;
+  unsigned char :1;
+  unsigned char HostAdapterID:4;               /* Byte 2: Host Adapter ID */
+  unsigned char :4;
+}
+BusLogic_Configuration_T;
+
+
+/*
+  Define the Inquire Setup Information reply structure.
+*/
+
+typedef struct BusLogic_SynchronousValue
+{
+  unsigned char Offset:4;
+  unsigned char TransferPeriod:3;
+  boolean Synchronous:1;
+}
+BusLogic_SynchronousValue_T;
+
+typedef BusLogic_SynchronousValue_T
+  BusLogic_SynchronousValues8_T[8];
+
+typedef BusLogic_SynchronousValue_T
+  BusLogic_SynchronousValues_T[BusLogic_MaxTargetIDs];
+
+typedef struct BusLogic_SetupInformation
+{
+  boolean SynchronousInitiationEnabled:1;      /* Byte 0 */
+  boolean ParityCheckEnabled:1;
+  unsigned char :6;
+  unsigned char BusTransferRate;               /* Byte 1 */
+  unsigned char PreemptTimeOnBus;              /* Byte 2 */
+  unsigned char TimeOffBus;                    /* Byte 3 */
+  unsigned char MailboxCount;                  /* Byte 4 */
+  unsigned char MailboxAddress[3];             /* Bytes 5-7 */
+  BusLogic_SynchronousValues8_T SynchronousValuesID0to7; /* Bytes 8-15 */
+  unsigned char DisconnectPermittedID0to7;     /* Byte 16 */
+  unsigned char Signature;                     /* Byte 17 */
+  unsigned char CharacterD;                    /* Byte 18 */
+  unsigned char BusLetter;                     /* Byte 19 */
+  unsigned char :8;                            /* Byte 20 */
+  unsigned char :8;                            /* Byte 21 */
+  BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */
+  unsigned char DisconnectPermittedID8to15;    /* Byte 30 */
+}
+BusLogic_SetupInformation_T;
+
+
+/*
+  Define the Initialize Extended Mailbox request structure.
+*/
+
+typedef struct BusLogic_ExtendedMailboxRequest
+{
+  unsigned char MailboxCount;
+  void *BaseMailboxAddress __attribute__ ((packed));
+}
+BusLogic_ExtendedMailboxRequest_T;
+
+
+/*
+  Define the Inquire Firmware Version 3rd Digit reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersion3rdDigit_T;
+
+
+/*
+  Define the Inquire Firmware Version Letter reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersionLetter_T;
+
+
+/*
+  Define the Inquire Board Model and Revision reply structure.
+*/
+
+typedef struct BusLogic_ModelAndRevision
+{
+  unsigned char Model[5];
+  unsigned char Revision;
+}
+BusLogic_ModelAndRevision_T;
+
+
+/*
+  Define the Inquire Synchronous Period reply type.  For each Target ID, a byte
+  is returned which represents the Synchronous Transfer Period in units of 10
+  nanoseconds.
+*/
+
+typedef unsigned char BusLogic_SynchronousPeriod_T[BusLogic_MaxTargetIDs];
+
+
+/*
+  Define the Inquire Extended Setup Information reply structure.
+*/
+
+typedef struct BusLogic_ExtendedSetupInformation
+{
+  unsigned char BusType;                               /* Byte 0 */
+  unsigned char BIOS_Address;                          /* Byte 1 */
+  unsigned short ScatterGatherLimit;                   /* Bytes 2-3 */
+  unsigned char MailboxCount;                          /* Byte 4 */
+  void *BaseMailboxAddress __attribute__ ((packed));   /* Bytes 5-8 */
+  struct { unsigned char :6;                           /* Byte 9 */
+          boolean LevelTriggeredInterrupts:1;
+          unsigned char :1; } Misc;
+  unsigned char FirmwareRevision[3];                   /* Bytes 10-12 */
+  boolean HostWideSCSI:1;                              /* Byte 13 Bit 0 */
+  boolean HostDifferentialSCSI:1;                      /* Byte 13 Bit 1 */
+  unsigned char :6;
+}
+BusLogic_ExtendedSetupInformation_T;
+
+
+/*
+  Define the Enable Strict Round Robin Mode request type.
+*/
+
+#define BusLogic_AggressiveRoundRobinMode      0x00
+#define BusLogic_StrictRoundRobinMode          0x01
+
+typedef unsigned char BusLogic_RoundRobinModeRequest_T;
+
+
+/*
+  Define the Modify I/O Address request type.  On PCI Host Adapters, the
+  Modify I/O Address command allows modification of the ISA compatible I/O
+  Address that the Host Adapter responds to; it does not affect the PCI
+  compliant I/O Address assigned at system initialization.
+*/
+
+#define BusLogic_ModifyIO_330                  0x00
+#define BusLogic_ModifyIO_334                  0x01
+#define BusLogic_ModifyIO_230                  0x02
+#define BusLogic_ModifyIO_234                  0x03
+#define BusLogic_ModifyIO_130                  0x04
+#define BusLogic_ModifyIO_134                  0x05
+#define BusLogic_ModifyIO_Disable              0x06
+#define BusLogic_ModifyIO_Disable2             0x07
+
+typedef unsigned char BusLogic_ModifyIOAddressRequest_T;
+
+
+/*
+  Define the Enable Wide Mode SCSI CCB request type.  Wide Mode CCBs are
+  necessary to support more than 8 Logical Units per Target.
+*/
+
+#define BusLogic_NormalModeCCB                 0x00
+#define BusLogic_WideModeCCB                   0x01
+
+typedef unsigned char BusLogic_WideModeCCBRequest_T;
+
+
+/*
+  Define the Requested Reply Length type used by the Inquire Setup Information,
+  Inquire Board Model and Revision, Inquire Synchronous Period, and Inquire
+  Extended Setup Information commands.
+*/
+
+typedef unsigned char BusLogic_RequestedReplyLength_T;
+
+
+/*
+  Define a Lock data structure.  Until a true symmetric multiprocessing kernel
+  is available, locking is implemented as saving the processor flags and
+  disabling interrupts, and unlocking restores the saved processor flags.
+*/
+
+typedef unsigned long BusLogic_Lock_T;
+
+
+/*
+  Define the Outgoing Mailbox Action Codes.
+*/
+
+typedef enum
+{
+  BusLogic_OutgoingMailboxFree =               0,
+  BusLogic_MailboxStartCommand =               1,
+  BusLogic_MailboxAbortCommand =               2
+}
+BusLogic_ActionCode_T;
+
+
+/*
+  Define the Incoming Mailbox Completion Codes.
+*/
+
+typedef enum
+{
+  BusLogic_IncomingMailboxFree =               0,
+  BusLogic_CommandCompletedWithoutError =      1,
+  BusLogic_CommandAbortedAtHostRequest =       2,
+  BusLogic_AbortedCommandNotFound =            3,
+  BusLogic_CommandCompletedWithError =         4
+}
+BusLogic_CompletionCode_T;
+
+
+/*
+  Define the Command Control Block (CCB) Opcodes.
+*/
+
+typedef enum
+{
+  BusLogic_InitiatorCCB =                      0x00,
+  BusLogic_TargetCCB =                         0x01,
+  BusLogic_InitiatorCCB_ScatterGather =                0x02,
+  BusLogic_InitiatorCCB_ResidualDataLength =   0x03,
+  BusLogic_InitiatorCCB_ScatterGatherResidual =        0x04,
+  BusLogic_SCSIBusDeviceReset =                        0x81
+}
+BusLogic_CCB_Opcode_T;
+
+
+/*
+  Define the CCB Data Direction Codes.
+*/
+
+typedef enum
+{
+  BusLogic_UncheckedDataTransfer =             0x00,
+  BusLogic_DataInLengthChecked =               0x01,
+  BusLogic_DataOutLengthChecked =              0x02,
+  BusLogic_NoDataTransfer =                    0x03
+}
+BusLogic_DataDirection_T;
+
+
+/*
+  Define the Host Adapter Status Codes.
+*/
+
+typedef enum
+{
+  BusLogic_CommandCompletedNormally =          0x00,
+  BusLogic_LinkedCommandCompleted =            0x0A,
+  BusLogic_LinkedCommandCompletedWithFlag =    0x0B,
+  BusLogic_SCSISelectionTimeout =              0x11,
+  BusLogic_DataOverUnderRun =                  0x12,
+  BusLogic_UnexpectedBusFree =                 0x13,
+  BusLogic_InvalidBusPhaseRequested =          0x14,
+  BusLogic_InvalidOutgoingMailboxActionCode =  0x15,
+  BusLogic_InvalidCommandOperationCode =       0x16,
+  BusLogic_LinkedCCBhasInvalidLUN =            0x17,
+  BusLogic_InvalidCommandParameter =           0x1A,
+  BusLogic_AutoRequestSenseFailed =            0x1B,
+  BusLogic_TaggedQueuingMessageRejected =      0x1C,
+  BusLogic_UnsupportedMessageReceived =                0x1D,
+  BusLogic_HostAdapterHardwareFailed =         0x20,
+  BusLogic_TargetFailedResponseToATN =         0x21,
+  BusLogic_HostAdapterAssertedRST =            0x22,
+  BusLogic_OtherDeviceAssertedRST =            0x23,
+  BusLogic_TargetDeviceReconnectedImproperly = 0x24,
+  BusLogic_HostAdapterAssertedBusDeviceReset = 0x25,
+  BusLogic_AbortQueueGenerated =               0x26,
+  BusLogic_HostAdapterSoftwareError =          0x27,
+  BusLogic_HostAdapterHardwareTimeoutError =   0x30,
+  BusLogic_SCSIParityErrorDetected =           0x34
+}
+BusLogic_HostAdapterStatus_T;
+
+
+/*
+  Define the SCSI Target Device Status Codes.
+*/
+
+typedef enum
+{
+  BusLogic_OperationGood =                     0x00,
+  BusLogic_CheckCondition =                    0x02,
+  BusLogic_DeviceBusy =                                0x08
+}
+BusLogic_TargetDeviceStatus_T;
+
+
+/*
+  Define the Queue Tag Codes.
+*/
+
+typedef enum
+{
+  BusLogic_SimpleQueueTag =                    0x00,
+  BusLogic_HeadOfQueueTag =                    0x01,
+  BusLogic_OrderedQueueTag =                   0x02,
+  BusLogic_ReservedQT =                                0x03
+}
+BusLogic_QueueTag_T;
+
+
+/*
+  Define the SCSI Command Descriptor Block (CDB).
+*/
+
+#define BusLogic_CDB_MaxLength                 12
+
+typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+
+
+/*
+  Define the SCSI Sense Data.
+*/
+
+#define BusLogic_SenseDataMaxLength            255
+
+typedef unsigned char SCSI_SenseData_T[BusLogic_SenseDataMaxLength];
+
+
+/*
+  Define the Scatter/Gather Segment structure required by the Host Adapter
+  Firmware Interface.
+*/
+
+typedef struct BusLogic_ScatterGatherSegment
+{
+  unsigned long SegmentByteCount;
+  void *SegmentDataPointer;
+}
+BusLogic_ScatterGatherSegment_T;
+
+
+/*
+  Define the 32 Bit Mode Command Control Block (CCB) structure.  The first 40
+  bytes are defined by the Host Adapter Firmware Interface.  The remaining
+  components are defined by the Linux BusLogic Driver.  Wide Mode CCBs differ
+  from standard 32 Bit Mode CCBs only in having the TagEnable and QueueTag
+  fields moved from byte 17 to byte 1, and the Logical Unit field in byte 17
+  expanded to 6 bits; unfortunately, using a union of structs containing
+  enumeration type bitfields to provide both definitions leads to packing
+  problems, so the following definition is used which requires setting
+  TagEnable to Logical Unit bit 5 in Wide Mode CCBs.
+*/
+
+typedef struct BusLogic_CCB
+{
+  /*
+    BusLogic Host Adapter Firmware Portion.
+  */
+  BusLogic_CCB_Opcode_T Opcode:8;                      /* Byte 0 */
+  unsigned char :3;                                    /* Byte 1 Bits 0-2 */
+  BusLogic_DataDirection_T DataDirection:2;            /* Byte 1 Bits 3-4 */
+  boolean WideModeTagEnable:1;                         /* Byte 1 Bit 5 */
+  BusLogic_QueueTag_T WideModeQueueTag:2;              /* Byte 1 Bits 6-7 */
+  unsigned char CDB_Length;                            /* Byte 2 */
+  unsigned char SenseDataLength;                       /* Byte 3 */
+  unsigned long DataLength;                            /* Bytes 4-7 */
+  void *DataPointer;                                   /* Bytes 8-11 */
+  unsigned char :8;                                    /* Byte 12 */
+  unsigned char :8;                                    /* Byte 13 */
+  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;    /* Byte 14 */
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;  /* Byte 15 */
+  unsigned char TargetID;                              /* Byte 16 */
+  unsigned char LogicalUnit:5;                         /* Byte 17 Bits 0-2 */
+  boolean TagEnable:1;                                 /* Byte 17 Bit 5 */
+  BusLogic_QueueTag_T QueueTag:2;                      /* Byte 17 Bits 6-7 */
+  SCSI_CDB_T CDB;                                      /* Bytes 18-29 */
+  unsigned char :8;                                    /* Byte 30 */
+  unsigned char :8;                                    /* Byte 31 */
+  unsigned long :32;                                   /* Bytes 32-35 */
+  SCSI_SenseData_T *SenseDataPointer;                  /* Bytes 36-39 */
+  /*
+    BusLogic Linux Driver Portion.
+  */
+  struct BusLogic_HostAdapter *HostAdapter;
+  SCSI_Command_T *Command;
+  enum { BusLogic_CCB_Free =       0,
+        BusLogic_CCB_Active =      1,
+        BusLogic_CCB_Completed =   2 } Status;
+  BusLogic_CompletionCode_T MailboxCompletionCode;
+  unsigned int SerialNumber;
+  struct BusLogic_CCB *Next;
+  struct BusLogic_CCB *NextAll;
+  BusLogic_ScatterGatherSegment_T
+    ScatterGatherList[BusLogic_ScatterGatherLimit];
+}
+BusLogic_CCB_T;
+
+
+/*
+  Define the 32 Bit Mode Outgoing Mailbox structure.
+*/
+
+typedef struct BusLogic_OutgoingMailbox
+{
+  BusLogic_CCB_T *CCB;
+  unsigned long :24;
+  BusLogic_ActionCode_T ActionCode:8;
+}
+BusLogic_OutgoingMailbox_T;
+
+
+/*
+  Define the 32 Bit Mode Incoming Mailbox structure.
+*/
+
+typedef struct BusLogic_IncomingMailbox
+{
+  BusLogic_CCB_T *CCB;
+  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;
+  unsigned char :8;
+  BusLogic_CompletionCode_T CompletionCode:8;
+}
+BusLogic_IncomingMailbox_T;
+
+
+/*
+  Define the possible Bus Types.
+*/
+
+typedef enum
+{
+  BusLogic_Unknown_Bus =                       0,
+  BusLogic_ISA_Bus =                           1,
+  BusLogic_MCA_Bus =                           2,
+  BusLogic_EISA_Bus =                          3,
+  BusLogic_VESA_Bus =                          4,
+  BusLogic_PCI_Bus =                           5
+}
+BusLogic_BusType_T;
+
+static char
+  *BusLogic_BusNames[] =
+    { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" };
+
+
+/*
+  Define the Linux BusLogic Driver Command Line Entry structure.
+*/
+
+typedef struct BusLogic_CommandLineEntry
+{
+  unsigned short IO_Address;
+  unsigned short Concurrency;
+  unsigned short BusSettleTime;
+  unsigned short TaggedQueuingPermitted;
+  unsigned short TaggedQueuingPermittedMask;
+  unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+}
+BusLogic_CommandLineEntry_T;
+
+
+/*
+  Define the Linux BusLogic Driver Host Adapter structure.
+*/
+
+typedef struct BusLogic_HostAdapter
+{
+  SCSI_Host_T *SCSI_Host;
+  unsigned char HostNumber;
+  unsigned char ModelName[6];
+  unsigned char FirmwareVersion[6];
+  unsigned char BoardName[15];
+  unsigned char InterruptLabel[63];
+  unsigned short IO_Address;
+  unsigned char IRQ_Channel;
+  unsigned char DMA_Channel;
+  unsigned char SCSI_ID;
+  BusLogic_BusType_T BusType:3;
+  boolean IRQ_ChannelAcquired:1;
+  boolean DMA_ChannelAcquired:1;
+  boolean SynchronousInitiation:1;
+  boolean ParityChecking:1;
+  boolean ExtendedTranslation:1;
+  boolean LevelTriggeredInterrupts:1;
+  boolean HostWideSCSI:1;
+  boolean HostDifferentialSCSI:1;
+  boolean HostAdapterResetPending:1;
+  volatile boolean HostAdapterCommandCompleted:1;
+  unsigned short HostAdapterScatterGatherLimit;
+  unsigned short DriverScatterGatherLimit;
+  unsigned short MaxTargetIDs;
+  unsigned short MaxLogicalUnits;
+  unsigned short Concurrency;
+  unsigned short BusSettleTime;
+  unsigned short DisconnectPermitted;
+  unsigned short TaggedQueuingPermitted;
+  unsigned long BIOS_Address;
+  BusLogic_InstalledDevices_T InstalledDevices;
+  BusLogic_SynchronousValues_T SynchronousValues;
+  BusLogic_SynchronousPeriod_T SynchronousPeriod;
+  BusLogic_Lock_T Lock;
+  struct BusLogic_HostAdapter *Next;
+  BusLogic_CommandLineEntry_T *CommandLineEntry;
+  BusLogic_CCB_T *All_CCBs;
+  BusLogic_CCB_T *Free_CCBs;
+  unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+  unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetIDs];
+  unsigned long ReadWriteOperationCount[BusLogic_MaxTargetIDs];
+  BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
+  BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
+  BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+  BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
+  BusLogic_IncomingMailbox_T *LastIncomingMailbox;
+  BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+  BusLogic_OutgoingMailbox_T OutgoingMailboxes[BusLogic_MailboxCount];
+  BusLogic_IncomingMailbox_T IncomingMailboxes[BusLogic_MailboxCount];
+}
+BusLogic_HostAdapter_T;
+
+
+/*
+  Define a symbolic structure for the BIOS Disk Parameters.
+*/
+
+typedef struct BIOS_DiskParameters
+{
+  int Heads;
+  int Sectors;
+  int Cylinders;
+}
+BIOS_DiskParameters_T;
+
+
+/*
+  BusLogic_LockHostAdapter acquires exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_LockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  save_flags(HostAdapter->Lock);
+  cli();
+}
+
+
+/*
+  BusLogic_UnlockHostAdapter releases exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+  restore_flags(HostAdapter->Lock);
+}
+
+
+/*
+  BusLogic_LockHostAdapterID acquires exclusive access to Host Adapter,
+  but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_LockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+  BusLogic_UnlockHostAdapterID releases exclusive access to Host Adapter,
+  but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+  Define functions to provide an abstraction for reading and writing the
+  Host Adapter I/O Registers.
+*/
+
+static inline
+void BusLogic_WriteControlRegister(BusLogic_HostAdapter_T *HostAdapter,
+                                  unsigned char Value)
+{
+  outb(Value, HostAdapter->IO_Address + BusLogic_ControlRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_StatusRegister);
+}
+
+static inline
+void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T *HostAdapter,
+                                           unsigned char Value)
+{
+  outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_DataInRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T
+                                            *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_InterruptRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+  return inb(HostAdapter->IO_Address + BusLogic_GeometryRegister);
+}
+
+
+/*
+  BusLogic_StartMailboxScan issues a Start Mailbox Scan command, which
+  notifies the Host Adapter that an entry has been made in an Outgoing
+  Mailbox.
+*/
+
+static inline
+void BusLogic_StartMailboxScan(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_WriteCommandParameterRegister(HostAdapter,
+                                        BusLogic_StartMailboxCommand);
+}
+
+
+/*
+  BusLogic_Delay waits for Seconds to elapse.  It must be called with
+  interrupts enabled so that jiffies is updated.
+*/
+
+static inline void BusLogic_Delay(int Seconds)
+{
+  unsigned long TimeoutJiffies = jiffies + Seconds * HZ;
+  while (jiffies < TimeoutJiffies) ;
+}
+
+
+/*
+  Define prototypes for the forward referenced BusLogic Driver
+  Internal Functions.
+*/
+
+static void BusLogic_InterruptHandler(int, Registers_T *);
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *,
+                                    SCSI_Command_T *);
+
+
+#endif /* BusLogic_DriverVersion */
index 97ea292162cde544d4378f214904205a146fe38c..20c185a8e8130834d93cf2c7517d34f5aaad6ae1 100644 (file)
@@ -1,3 +1,7 @@
+Fri Nov 10 15:29:49 1995  Leonard N. Zubkoff (lnz@dandelion.com)
+
+       * Released new BusLogic driver.
+
 Wed Aug  9 22:37:04 1995  Andries Brouwer  <aeb@cwi.nl>
 
        As a preparation for new device code, separated the various
index 68842058a73133a64e755059f28e5ae489b7fa0a..feb9306c8f5030a9a9657e3379eff6ad2190b2ce 100644 (file)
@@ -18,7 +18,7 @@ dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 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
+bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC
 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
index 9b03da7aeee38567be6d019f786a69fdb4f17881..166f0c2cf0dc7219acd48c5a02d9153357757611 100644 (file)
@@ -113,10 +113,10 @@ else
 endif
 
 ifeq ($(CONFIG_SCSI_BUSLOGIC),y)
-L_OBJS += buslogic.o
+L_OBJS += BusLogic.o
 else
   ifeq ($(CONFIG_SCSI_BUSLOGIC),m)
-  M_OBJS += buslogic.o
+  M_OBJS += BusLogic.o
   endif
 endif
 
diff --git a/drivers/scsi/README.BusLogic b/drivers/scsi/README.BusLogic
new file mode 100644 (file)
index 0000000..a4a2aa6
--- /dev/null
@@ -0,0 +1,262 @@
+                    BusLogic SCSI Driver for Linux 1.3.41
+                      Version 1.3.0 ~ 13 November 1995
+
+                              Leonard N. Zubkoff
+                              Dandelion Digital
+                              lnz@dandelion.com
+
+          Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+                                INTRODUCTION
+
+BusLogic, Inc. designs and manufactures a variety of high performance SCSI host
+adapters which share a common programming interface across a diverse collection
+of bus architectures by virtue of their MultiMaster ASIC technology.  This
+driver supports all present BusLogic MultiMaster Host Adapters, and should
+support any future MultiMaster designs with little or no modification.  Host
+adapters based on the new FlashPoint technology are not supported by this
+driver.
+
+My primary goals in writing this completely new BusLogic driver for Linux are
+to achieve the full performance that BusLogic SCSI Host Adapters and modern
+SCSI peripherals are capable of, and to provide a highly robust driver that can
+be depended upon for high performance mission critical applications.  All of
+the major performance and error recovery features can be configured from the
+Linux kernel command line, allowing individual installations to tune driver
+performance and error recovery to their particular needs.
+
+The most recent versions of this driver will always be available by anonymous
+FTP from ftp.dandelion.com.  While directory listings are not permitted, the
+introductory banner displayed on anonymous FTP login will provide a list of the
+driver versions and any other files available for retrieval.
+
+Bug reports should be sent via electronic mail to "lnz@dandelion.com".  Please
+include with the bug report the complete configuration messages reported by the
+driver and SCSI subsystem at startup, along with any subsequent system messages
+relevant to SCSI operations, and a detailed description of your system's
+hardware configuration.
+
+I have recently had conversations with the Senior Product Marketing Manager at
+BusLogic regarding the needs of free software developers, and he has reaffirmed
+BusLogic's committment to providing the technical information and support we
+need to take full advantage of their products.  BusLogic has also been very
+accomodating in providing technical documentation, as well as access to their
+engineering staff for technical questions and advice.  In addition, they have
+loaned me ISA cards for configuration testing, and even allowed me use of their
+technical support lab to test EISA configurations, since I don't have an EISA
+system.  Their interest and support is greatly appreciated.
+
+BusLogic, Inc. is located at 4151 Burton Drive, Santa Clara, California, 95054,
+USA and can be reached by Voice at 408/492-9090 or by FAX at 408/492-1542.
+BusLogic maintains a World Wide Web site at http://www.buslogic.com, an
+anonymous FTP site at ftp.buslogic.com, and a BBS at 408/492-1984.  BusLogic
+Technical Support can be reached by electronic mail at techsup@buslogic.com, by
+Voice at 408/654-0760, or by FAX at 408/492-1542.  Contact information for
+offices in Europe and Japan is available on the Web site.
+
+
+                               DRIVER FEATURES
+
+o Configuration Reporting and Testing
+
+  During system initialization, the driver reports extensively on the host
+  adapter hardware configuration, including the synchronous transfer parameters
+  negotiated with each target device.  In addition, the driver tests the
+  hardware interrupt configuration to verify that interrupts are actually
+  delivered correctly to the interrupt handler.  This should catch a high
+  percentage of PCI motherboard configuration errors early, because when the
+  host adapter is probed successfully, most of the remaining problems appear to
+  be related to interrupts.  Most often, any remaining hardware problems are
+  related to the specific configuration of devices on the SCSI bus, and the
+  quality of cabling and termination used.  Finally, this BusLogic driver
+  should never incorrectly attempt to support an Adaptec 154x Host Adapter.
+
+o Performance Features
+
+  BusLogic SCSI Host Adapters directly implement SCSI-2 Tagged Queuing, and so
+  support has been included in the driver to utilize tagged queuing with any
+  target devices that report having the tagged queuing capability.  Tagged
+  queuing allows for multiple outstanding commands to be issued to each target
+  device or logical unit, and can improve I/O performance substantially.  In
+  addition, BusLogic's Strict Round Robin Mode is used to optimize host adapter
+  performance, and scatter/gather I/O can support as many segments as can be
+  effectively utilized by the Linux I/O subsystem.  Control over the use of
+  tagged queuing for each target device as well as selection of the maximum
+  number of concurrent commands per logical unit is available from the kernel
+  command line.  In addition, tagged queuing is automatically disabled whenever
+  the host adapter firmware version is known not to implement it correctly, or
+  whenever a concurrency value of 1 is selected.  In performance testing,
+  sustained disk writes of 7.3MB per second have been observed to a /dev/sd
+  device.
+
+o Robustness Features
+
+  The driver implements extensive error recovery procedures.  By default, when
+  the higher level parts of the SCSI subsystem request that a command be reset,
+  a selection is made between a full host adapter hard reset and SCSI bus reset
+  versus sending a bus device reset message to the individual device based on
+  the recommendation of the SCSI subsystem.  Error recovery options are
+  selectable from the kernel command line individually for each target device,
+  and also include forcing a full host adapter hard reset and SCSI bus reset,
+  sending a bus device reset to the specific target device associated with the
+  command being reset, as well as suppressing error recovery entirely to avoid
+  perturbing an improperly functioning device.  If the bus device reset error
+  recovery option is selected and sending a bus device reset does not restore
+  correct operation, the next command that is reset will force a full host
+  adapter hard reset and SCSI bus reset.  SCSI bus resets caused by other
+  devices and detected by the host adapter are also handled by issuing a hard
+  reset to the host adapter and full reinitialization.  These error recovery
+  options should improve overall system robustness by preventing individual
+  errant devices from causing the system as a whole to lock up or crash, and
+  thereby allowing a clean shutdown and restart after the offending component
+  is removed.
+
+o Extensive Testing
+
+  This driver has undergone extensive testing and improvement over a period of
+  several months, and is routinely being used on heavily loaded systems.  Over
+  300 people retrieved the driver during the beta test period.  In addition to
+  testing in normal system operation, error recovery tests have been performed
+  to verify proper system recovery in the case of simulated dropped interrupts,
+  external SCSI bus resets, and SCSI command errors due to bad CD-ROM media.
+
+o PCI Configuration Support
+
+  On PCI systems running kernels compiled with PCI BIOS support enabled, this
+  driver will interrogate the PCI configuration space and use the I/O port
+  addresses assigned by the system BIOS, rather than the ISA compatible I/O
+  port addresses.
+
+o Shared Interrupts Support
+
+  On systems that support shared interrupts, any number of BusLogic Host
+  Adapters may share the same interrupt request channel, and in fact it is more
+  efficient if they do so.  The driver scans all known BusLogic Host Adapters
+  whenever an interrupt is handled on an interrupt channel assigned to any
+  BusLogic Host Adapter.
+
+o Wide SCSI Support
+
+  All BusLogic MultiMaster SCSI Host Adapters share a common programming
+  interface, except for the inevitable improvements and extensions as new
+  models are released, so support for Wide SCSI data transfer has automatically
+  been available without explicit driver support.  When used with Linux 1.3.x,
+  this driver adds explicit support for up to 15 target devices and 64 logical
+  units per target device, to fully exploit the capabilities of the newest
+  BusLogic Wide SCSI Host Adapters.
+
+
+                           SUPPORTED HOST ADAPTERS
+
+The following list comprises the supported BusLogic SCSI Host Adapters as of
+the date of this document.  It is recommended that anyone purchasing a BusLogic
+Host Adapter not in the following table contact the author beforehand to verify
+that it is or will be supported.
+
+"C" Series Host Adapters:
+
+946C   PCI     Fast Single-ended SCSI-2
+956C   PCI     Fast/Wide Single-ended SCSI-2
+956CD  PCI     Fast/Wide Differential SCSI-2
+445C   VLB     Fast Single-ended SCSI-2
+747C   EISA    Fast Single-ended SCSI-2
+757C   EISA    Fast/Wide Single-ended SCSI-2
+757CD  EISA    Fast/Wide Differential SCSI-2
+545C   ISA     Fast Single-ended SCSI-2
+540CF  ISA     Fast Single-ended SCSI-2
+
+"S" Series Host Adapters:
+
+445S   VLB     Fast Single-ended SCSI-2
+747S   EISA    Fast Single-ended SCSI-2
+747D   EISA    Fast Differential SCSI-2
+757S   EISA    Fast/Wide Single-ended SCSI-2
+757D   EISA    Fast/Wide Differential SCSI-2
+545S   ISA     Fast Single-ended SCSI-2
+542D   ISA     Fast Differential SCSI-2
+742A   EISA    Single-ended SCSI-2 (742A revision H)
+542B   ISA     Single-ended SCSI-2 (542B revision H)
+
+"A" Series Host Adapters:
+
+742A   EISA    Single-ended SCSI-2 (742A revisions A - G)
+542B   ISA     Single-ended SCSI-2 (542B revisions A - G)
+
+The FlashPoint LT, also known as the 930 Ultra, implements a different host
+interface and is not supported by this driver.
+
+AMI FastDisk Host Adapters are true BusLogic clones and are supported by this
+driver.
+
+
+                            COMMAND LINE OPTIONS
+
+Many features of this driver are configurable by specification of appropriate
+kernel command line options.  A full description of the command line options
+may be found in the comments before BusLogic_Setup in the kernel source code
+file "BusLogic.c".  The following examples may be useful as a starting point:
+
+  "BusLogic=0x330"
+
+    This command line limits probing to the single I/O port at 0x330.
+
+  "BusLogic=0,1"
+
+    This command line selects default probing and a concurrency of 1 which also
+    disables tagged queuing.  It may be useful if problems arise during
+    installation on a system with a flakey SCSI configuration.  In cases of a
+    marginal SCSI configuration it may also be beneficial to disable fast
+    transfers and/or synchronous negotiation using AutoSCSI on "C" series
+    boards.  Disconnect/reconnect may also be disabled for fast devices such as
+    disk drives, but should not be disabled for tape drives or other devices
+    where a single command may take over a second to execute.
+
+  "BusLogic=0,0,10"
+
+    This command line selects default probing and concurrency but changes the
+    bus settle time to 10 seconds.  It may be useful with SCSI devices that
+    take an unusually long time to become ready to accept commands after a SCSI
+    bus reset.
+
+  "BusLogic=TQ:Disable"
+
+    This command line selects default probing and disables tagged queuing,
+    while keeping the default concurrency.
+
+  "BusLogic=0,15,TQ:N"
+
+    This command line selects a concurrency of 15 and disables tagged queuing
+    for target 0, while allowing tagged queuing for all other target devices.
+
+Note that limiting the concurrency to 1 or disabling tagged queuing can
+substantially impact performance.
+
+
+                                INSTALLATION
+
+This distribution was prepared for Linux kernel version 1.3.41.  Installation
+in later versions will probably be successful as well, though BusLogic.patch
+may not be required once this driver becomes part of the standard development
+kernel; installation in earlier versions should not be attempted as 1.3.41
+contains changes I made to the common code that are essential for correct error
+recovery.
+
+To install the BusLogic SCSI driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
+
+  cd /usr/src
+  tar -xvzf BusLogic-1.3.0.tar.gz
+  mv README.BusLogic BusLogic.[ch] linux/drivers/scsi
+  patch -p < BusLogic.patch
+  cd linux
+  make config
+  make depend
+  make zImage
+
+Then install "arch/i386/boot/zImage" as your standard kernel, run lilo if
+appropriate, and reboot.
+
+Be sure to answer "y" to the "BusLogic SCSI support" query during the "make
+config" step.  If your system was already configured for the old BusLogic
+driver, you may omit the "make config" step above.
diff --git a/drivers/scsi/buslogic.c b/drivers/scsi/buslogic.c
deleted file mode 100644 (file)
index 3602782..0000000
+++ /dev/null
@@ -1,1574 +0,0 @@
-/*
- *     buslogic.c      Copyright (C) 1993, 1994 David B. Gentzel
- *     Low-level scsi driver for BusLogic adapters
- *     by David B. Gentzel, Whitfield Software Services, Carnegie, PA
- *         (gentzel@nova.enet.dec.com)
- *     Thanks to BusLogic for providing the necessary documentation
- *
- *     The original version of this driver was derived from aha1542.[ch],
- *     which is Copyright (C) 1992 Tommy Thorn.  Much has been reworked, but
- *     most of basic structure and substantial chunks of code still remain.
- *
- *     Furthermore, many subsequent fixes and improvements to the aha1542
- *     driver have been folded back into this driver.  These changes to
- *     aha1542.[ch] are Copyright (C) 1993, 1994 Eric Youngdale.
- *
- *     Thanks to the following individuals who have made contributions (of
- *     (code, information, support, or testing) to this driver:
- *             Eric Youngdale          Leonard Zubkoff
- *             Tomas Hurka             Andrew Walker
- */
-
-/*
- * TODO:
- *     1. Clean up error handling & reporting.
- *     2. Find out why scatter/gather is limited to 16 requests per command.
- *     3. Test/improve/fix abort & reset functions.
- *     4. Look at command linking.
- *     5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA,
- *        and PCI).
- *     6. Avoid using the 445S workaround for board revs >= D.
- */
-
-/*
- * NOTES:
- *    BusLogic (formerly BusTek) manufactures an extensive family of
- *    intelligent, high performance SCSI-2 host adapters.  They all support
- *    command queueing and scatter/gather I/O.  Most importantly, they all
- *    support identical programming interfaces, so a single driver can be used
- *    for all boards.
- *
- *    Actually, they all support TWO identical programming interfaces!  They
- *    have an Adaptec 154x compatible interface (complete with 24 bit
- *    addresses) as well as a "native" 32 bit interface.  As such, the Linux
- *    aha1542 driver can be used to drive them, but with less than optimal
- *    performance (at least for the EISA, VESA, and MCA boards).
- *
- *    Here is the scoop on the various models:
- *     BT-542B - ISA first-party DMA with floppy support.
- *     BT-545S - 542B + FAST SCSI and active termination.
- *     BT-545D - 545S + differential termination.
- *     BT-640A - MCA bus-master with floppy support.
- *     BT-646S - 640A + FAST SCSI and active termination.
- *     BT-646D - 646S + differential termination.
- *     BT-742A - EISA bus-master with floppy support.
- *     BT-747S - 742A + FAST SCSI, active termination, and 2.88M floppy.
- *     BT-747D - 747S + differential termination.
- *     BT-757S - 747S + WIDE SCSI.
- *     BT-757D - 747D + WIDE SCSI.
- *     BT-445S - VESA bus-master FAST SCSI with active termination
- *               and floppy support.
- *     BT-445C - 445S + enhanced BIOS & firmware options.
- *     BT-946C - PCI bus-master FAST SCSI.
- *     BT-956C - PCI bus-master FAST/WIDE SCSI.
- *
- *    ??? I believe other boards besides the 445 now have a "C" model, but I
- *    have no facts on them.
- *
- *    This driver SHOULD support all of these boards.  It has only been tested
- *    with a 747S, 445S, 946C, and 956C; there is no PCI-specific support as
- *    yet.
- *
- *    Should you require further information on any of these boards, BusLogic
- *    can be reached at (408)492-9090.  Their BBS # is (408)492-1984 (maybe BBS
- *    stands for "Big Brother System"?).
- *
- *    Places flagged with a triple question-mark are things which are either
- *    unfinished, questionable, or wrong.
- */
-
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/head.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/config.h>
-#include <linux/proc_fs.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/dma.h>
-
-#include <linux/blk.h>
-#include "scsi.h"
-#include "hosts.h"
-#include "sd.h"
-#define BUSLOGIC_PRIVATE_H     /* Get the "private" stuff */
-#include "buslogic.h"
-
-#ifndef BUSLOGIC_DEBUG
-# define BUSLOGIC_DEBUG 0
-#endif
-
-#include<linux/stat.h>
-
-struct proc_dir_entry proc_scsi_buslogic = {
-    PROC_SCSI_BUSLOGIC, 8, "buslogic",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
-};
-
-/* ??? Until kmalloc actually implements GFP_DMA, we can't depend on it... */
-#undef GFP_DMA
-
-/* If different port addresses are needed (e.g. to install more than two
-   cards), you must define BUSLOGIC_PORT_OVERRIDE to be a comma-separated list
-   of the addresses which will be checked.  This can also be used to resolve a
-   conflict if the port-probing at a standard port causes problems with
-   another board. */
-/* #define BUSLOGIC_PORT_OVERRIDE 0x330, 0x334, 0x130, 0x134, 0x230, 0x234 */
-
-/* Define this to be either BIOS_TRANSLATION_DEFAULT or BIOS_TRANSLATION_BIG
-   if you wish to bypass the test for this, which uses an undocumented port.
-   The test is believed to fail on at least some AMI BusLogic clones. */
-/* #define BIOS_TRANSLATION_OVERRIDE BIOS_TRANSLATION_BIG */
-
-#define BUSLOGIC_VERSION "1.15"
-
-/* Not a random value - if this is too large, the system hangs for a long time
-   waiting for something to happen if a board is not installed. */
-/* ??? I don't really like this as it will wait longer on slow machines.
-   Perhaps we should base this on the loops_per_second "Bogomips" value? */
-#define WAITNEXTTIMEOUT 3000000
-
-/* This is for the scsi_malloc call in buslogic_queuecommand. */
-/* ??? I'd up this to 4096, but would we be in danger of using up the
-   scsi_malloc memory pool? */
-/* This could be a concern, I guess.  It may be possible to fix things so that
-   the table generated in sd.c is compatible with the low-level code, but
-   don't hold your breath.  -ERY */
-#define BUSLOGIC_SG_MALLOC 512
-
-/* Since the SG list is malloced, we have to limit the length. */
-#define BUSLOGIC_MAX_SG (BUSLOGIC_SG_MALLOC / sizeof (struct chain))
-
-/* Since the host adapters have room to buffer 32 commands internally, there
-   is some virtue in setting BUSLOGIC_MAILBOXES to 32.  The maximum value
-   appears to be 255, since the Count parameter to the Initialize Extended
-   Mailbox command is limited to one byte. */
-#define BUSLOGIC_MAILBOXES 32
-
-#define BUSLOGIC_CMDLUN 4      /* Arbitrary, but seems to work well. */
-
-/* BusLogic boards can be configured for quite a number of port addresses (six
-   to be exact), but I generally do not want the driver poking around at
-   random.  We allow two port addresses - this allows people to use a BusLogic
-   with a MIDI card, which frequently also uses 0x330.
-
-   This can also be overridden on the command line to the kernel, via LILO or
-   LOADLIN. */
-static unsigned short bases[7] = {
-#ifdef BUSLOGIC_PORT_OVERRIDE
-    BUSLOGIC_PORT_OVERRIDE,
-#else
-    0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234, */
-#endif
-    0
-};
-
-#define BIOS_TRANSLATION_DEFAULT 0     /* Default case */
-#define BIOS_TRANSLATION_BIG 1         /* Big disk (> 1G) case */
-
-struct hostdata {
-    unsigned int bus_type;
-    unsigned int bios_translation: 1;  /* BIOS mapping (for compatibility) */
-    int last_mbi_used;
-    int last_mbo_used;
-    char model[7];
-    char firmware_rev[6];
-    Scsi_Cmnd *sc[BUSLOGIC_MAILBOXES];
-    struct mailbox mb[2 * BUSLOGIC_MAILBOXES];
-    struct ccb ccbs[BUSLOGIC_MAILBOXES];
-};
-
-#define HOSTDATA(host) ((struct hostdata *)&(host)->hostdata)
-
-/* One for each IRQ level (9-15), although 13 will never be used. */
-static struct Scsi_Host *host[7] = { NULL, };
-
-static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt);
-static int restart(struct Scsi_Host *shpnt);
-
-#define INTR_RESET(base) outb(RINT, CONTROL(base))
-
-#define buslogic_printk buslogic_prefix(__PRETTY_FUNCTION__),printk
-
-#if defined(MODULE) && !defined(GFP_DMA)
-# define CHECK_DMA_ADDR(isa, addr, badstmt) \
-    do { if ((isa) && ((const void *)addr) > (const void *)ISA_DMA_THRESHOLD) badstmt; } while (0)
-#else
-# define CHECK_DMA_ADDR(isa, addr, badstmt)
-#endif
-
-#define CHECK(cond) if (cond) ; else goto fail
-
-#define WAIT(port, allof, noneof) \
-    CHECK(wait(port, allof, noneof, WAITNEXTTIMEOUT, FALSE))
-#define WAIT_WHILE(port, mask) WAIT(port, 0, mask)
-#define WAIT_UNTIL(port, mask) WAIT(port, mask, 0)
-#define WAIT_FAST(port, allof, noneof) \
-    CHECK(wait(port, allof, noneof, 100, TRUE))
-#define WAIT_WHILE_FAST(port, mask) WAIT_FAST(port, 0, mask)
-#define WAIT_UNTIL_FAST(port, mask) WAIT_FAST(port, mask, 0)
-
-/* If delay != 0, we use the udelay call to regulate the amount of time we
-   wait.
-
-   This is inline as it is always called with constant arguments and hence
-   will be very well optimized. */
-static __inline__ int wait(unsigned short port,
-                          unsigned char allof, unsigned char noneof,
-                          unsigned int timeout, int delay)
-{
-    int bits;
-
-    for (;;) {
-       bits = inb(port);
-       if ((bits & allof) == allof && (bits & noneof) == 0)
-           return TRUE;
-       if (delay)
-           udelay(1000);
-       if (--timeout == 0)
-           return FALSE;
-    }
-}
-
-static void buslogic_prefix(const char *func)
-{
-    printk("BusLogic SCSI: %s: ", func);
-}
-
-static void buslogic_stat(unsigned int base)
-{
-    int s = inb(STATUS(base)), i = inb(INTERRUPT(base));
-
-    buslogic_printk("status=%02X intrflags=%02X\n", s, i);
-}
-
-/* This is a bit complicated, but we need to make sure that an interrupt
-   routine does not send something out while we are in the middle of this.
-   Fortunately, it is only at boot time that multi-byte messages are ever
-   sent. */
-static int buslogic_out(unsigned int base, const unsigned char *cmdp,
-                       size_t len)
-{
-    unsigned long flags = 0;
-    
-    if (len == 1) {
-       for (;;) {
-           WAIT_WHILE(STATUS(base), CPRBSY);
-           save_flags(flags);
-           cli();
-           if (!(inb(STATUS(base)) & CPRBSY)) {
-               outb(*cmdp, COMMAND_PARAMETER(base));
-               restore_flags(flags);
-               return FALSE;
-           }
-           restore_flags(flags);
-       }
-    } else {
-       save_flags(flags);
-       cli();
-       while (len--) {
-           WAIT_WHILE(STATUS(base), CPRBSY);
-           outb(*cmdp++, COMMAND_PARAMETER(base));
-       }
-       restore_flags(flags);
-    }
-    return FALSE;
-  fail:
-    restore_flags(flags);
-    buslogic_printk("failed(%u): ", len + 1);
-    buslogic_stat(base);
-    return TRUE;
-}
-
-/* Only used at boot time, so we do not need to worry about latency as much
-   here.  This waits a very short period of time.  We use this if we are not
-   sure whether the board will respond to the command we just sent. */
-static int buslogic_in(unsigned int base, unsigned char *cmdp, size_t len)
-{
-    unsigned long flags;
-    
-    save_flags(flags);
-    cli();
-    while (len--) {
-       WAIT_UNTIL_FAST(STATUS(base), DIRRDY);
-       *cmdp++ = inb(DATA_IN(base));
-    }
-    restore_flags(flags);
-    return FALSE;
-  fail:
-    restore_flags(flags);
-#if (BUSLOGIC_DEBUG & BD_IO)
-    buslogic_printk("failed(%u): ", len + 1);
-    buslogic_stat(base);
-#endif
-    return TRUE;
-}
-
-static unsigned int makecode(unsigned int haerr, unsigned int scsierr)
-{
-    unsigned int hosterr;
-    const char *errstr = NULL;
-#if (BUSLOGIC_DEBUG & BD_ERRORS) && defined(CONFIG_SCSI_CONSTANTS)
-    static const char *const buslogic_status[] = {
-    /* 00 */   "Command completed normally",
-    /* 01-07 */        NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    /* 08-09 */        NULL, NULL,
-    /* 0A */   "Linked command completed normally",
-    /* 0B */   "Linked command completed normally, interrupt generated",
-    /* 0C-0F */        NULL, NULL, NULL, NULL,
-    /* 10 */   NULL,
-    /* 11 */   "Selection timed out",
-    /* 12 */   "Data overrun/underrun",
-    /* 13 */   "Unexpected bus free",
-    /* 14 */   "Target bus phase sequence failure",
-    /* 15 */   "First byte of outgoing MB was invalid",
-    /* 16 */   "Invalid CCB Operation Code",
-    /* 17 */   "Linked CCB does not have the same LUN",
-    /* 18 */   "Invalid Target Direction received from Host",
-    /* 19 */   "Duplicate CCB Received in Target Mode",
-    /* 1A */   "Invalid CCB or Segment List Parameter",
-    /* 1B */   "Auto request sense failed",
-    /* 1C */   "SCSI-2 tagged queueing message was rejected by the target",
-    /* 1D-1F */        NULL, NULL, NULL,
-    /* 20 */   "Host adapter hardware failure",
-    /* 21 */   "Target did not respond to SCSI ATN and the HA SCSI bus reset",
-    /* 22 */   "Host adapter asserted a SCSI bus reset",
-    /* 23 */   "Other SCSI devices asserted a SCSI bus reset",
-    };
-#endif
-
-    switch (haerr) {
-      case 0x00:       /* Normal completion. */
-      case 0x0A:       /* Linked command complete without error and linked
-                          normally. */
-      case 0x0B:       /* Linked command complete without error, interrupt
-                          generated. */
-       hosterr = DID_OK;
-       break;
-
-      case 0x11:       /* Selection time out: the initiator selection or
-                          target reselection was not complete within the SCSI
-                          time out period. */
-       hosterr = DID_TIME_OUT;
-       break;
-
-      case 0x14:       /* Target bus phase sequence failure - An invalid bus
-                          phase or bus phase sequence was requested by the
-                          target.  The host adapter will generate a SCSI
-                          Reset Condition, notifying the host with a RSTS
-                          interrupt. */
-      case 0x21:       /* The target did not respond to SCSI ATN and the host
-                          adapter consequently issued a SCSI bus reset to
-                          clear up the failure. */
-      case 0x22:       /* The host adapter asserted a SCSI bus reset. */
-       hosterr = DID_RESET;
-       break;
-
-      case 0x12:       /* Data overrun/underrun: the target attempted to
-                          transfer more data than was allocated by the Data
-                          Length field or the sum of the Scatter/Gather Data
-                          Length fields. */
-      case 0x13:       /* Unexpected bus free - The target dropped the SCSI
-                          BSY at an unexpected time. */
-      case 0x15:       /* MBO command was not 00, 01, or 02 - The first byte
-                          of the MB was invalid.  This usually indicates a
-                          software failure. */
-      case 0x16:       /* Invalid CCB Operation Code - The first byte of the
-                          CCB was invalid.  This usually indicates a software
-                          failure. */
-      case 0x17:       /* Linked CCB does not have the same LUN - A
-                          subsequent CCB of a set of linked CCB's does not
-                          specify the same logical unit number as the
-                          first. */
-      case 0x18:       /* Invalid Target Direction received from Host - The
-                          direction of a Target Mode CCB was invalid. */
-      case 0x19:       /* Duplicate CCB Received in Target Mode - More than
-                          once CCB was received to service data transfer
-                          between the same target LUN and initiator SCSI ID
-                          in the same direction. */
-      case 0x1A:       /* Invalid CCB or Segment List Parameter - A segment
-                          list with a zero length segment or invalid segment
-                          list boundaries was received.  A CCB parameter was
-                          invalid. */
-      case 0x1B:       /* Auto request sense failed. */
-      case 0x1C:       /* SCSI-2 tagged queueing message was rejected by the
-                          target. */
-      case 0x20:       /* The host adapter hardware failed. */
-      case 0x23:       /* Other SCSI devices asserted a SCSI bus reset. */
-       hosterr = DID_ERROR;    /* ??? Couldn't find any better. */
-       break;
-
-      default:
-#ifndef CONFIG_SCSI_CONSTANTS
-       errstr = "unknown hoststatus";
-#endif
-       hosterr = DID_ERROR;
-       break;
-    }
-#if (BUSLOGIC_DEBUG & BD_ERRORS)
-# ifdef CONFIG_SCSI_CONSTANTS
-    if (hosterr != DID_OK) {
-       if (haerr < ARRAY_SIZE(buslogic_status))
-           errstr = buslogic_status[haerr];
-       if (errstr == NULL)
-           errstr = "unknown hoststatus";
-    }
-# else
-    if (hosterr == DID_ERROR)
-       errstr = "";
-# endif
-#endif
-    if (errstr != NULL)
-       buslogic_printk("%s (%02X)\n", errstr, haerr);
-    return (hosterr << 16) | scsierr;
-}
-
-/* ??? this should really be "const struct Scsi_Host *" */
-const char *buslogic_info(struct Scsi_Host *shpnt)
-{
-    return "BusLogic SCSI driver " BUSLOGIC_VERSION;
-}
-
-/*
-  This is a major rewrite of the interrupt handler to support the newer
-  and faster PCI cards.  While the previous interrupt handler was supposed
-  to handle multiple incoming becoming available mailboxes during the same
-  interrupt, my testing showed that in practice only a single mailbox was
-  ever made available.  With the 946C and 956C, multiple incoming mailboxes
-  being ready for processing during a single interrupt occurs much more
-  frequently, and so care must be taken to avoid race conditions managing
-  the Host Adapter Interrupt Register, which can lead to lost interrupts.
-
-  Leonard N. Zubkoff, 23-Mar-95
-*/
-
-static void buslogic_interrupt(int irq, struct pt_regs * regs)
-{
-    int mbi, saved_mbo[BUSLOGIC_MAILBOXES];
-    int base, interrupt_flags, found, i;
-    struct Scsi_Host *shpnt;
-    Scsi_Cmnd *sctmp;
-    struct mailbox *mb;
-    struct ccb *ccb;
-
-    shpnt = host[irq - 9];
-    if (shpnt == NULL)
-      panic("buslogic_interrupt: NULL SCSI host entry");
-
-    mb = HOSTDATA(shpnt)->mb;
-    ccb = HOSTDATA(shpnt)->ccbs;
-    base = shpnt->io_port;
-
-    /*
-      This interrupt handler is now specified to use the SA_INTERRUPT
-      protocol, so interrupts are inhibited on entry until explicitly
-      allowed again.  Read the Host Adapter Interrupt Register, and
-      complain if there is no pending interrupt being signaled.
-    */
-
-    interrupt_flags = inb(INTERRUPT(base));
-
-    if (!(interrupt_flags & INTV))
-      buslogic_printk("interrupt received, but INTV not set\n");
-
-    /*
-      Reset the Host Adapter Interrupt Register.  It appears to be
-      important that this is only done once per interrupt to avoid
-      losing interrupts under heavy loads.
-    */
-
-    INTR_RESET(base);
-
-    if (interrupt_flags & RSTS)
-      {
-       restart(shpnt);
-       return;
-      }
-
-    /*
-      With interrupts still inhibited, scan through the incoming mailboxes
-      in strict round robin fashion saving the status information and
-      then freeing the mailbox.  A second pass over the completed commands
-      will be made separately to complete their processing.
-    */
-
-    mbi = HOSTDATA(shpnt)->last_mbi_used + 1;
-    if (mbi >= 2*BUSLOGIC_MAILBOXES)
-      mbi = BUSLOGIC_MAILBOXES;
-
-    found = 0;
-
-    while (mb[mbi].status != MBX_NOT_IN_USE && found < BUSLOGIC_MAILBOXES)
-      {
-       int mbo = (struct ccb *)mb[mbi].ccbptr - ccb;
-
-       sctmp = HOSTDATA(shpnt)->sc[mbo];
-
-       /*
-         If sctmp has become NULL, higher level code must have aborted
-         this operation and called the necessary completion routine.
-       */
-
-       if (sctmp != NULL && mb[mbi].status != MBX_COMPLETION_NOT_FOUND)
-         {
-           int result = 0;
-
-           saved_mbo[found++] = mbo;
-
-           if (mb[mbi].status != MBX_COMPLETION_OK)
-             result = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
-
-           sctmp->result = result;
-
-           mb[mbi].status = MBX_NOT_IN_USE;
-         }
-
-       HOSTDATA(shpnt)->last_mbi_used = mbi;
-
-       if (++mbi >= 2*BUSLOGIC_MAILBOXES)
-         mbi = BUSLOGIC_MAILBOXES;
-      }
-
-    /*
-      With interrupts no longer inhibited, iterate over the completed
-      commands freeing resources and calling the completion routines.
-      Since we exit upon completion of this loop, there is no need to
-      inhibit interrupts before exit, as this will be handled by the
-      fast interrupt assembly code we return to.
-    */
-
-    sti();
-
-    for (i = 0; i < found; i++)
-      {
-       int mbo = saved_mbo[i];
-       sctmp = HOSTDATA(shpnt)->sc[mbo];
-       if (sctmp == NULL) continue;
-       /*
-         First, free any storage allocated for a scatter/gather
-         data segment list.
-       */
-       if (sctmp->host_scribble)
-         scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
-       /*
-         Next, mark the SCSI Command as completed so it may be reused
-         for another command by buslogic_queuecommand.  This also signals
-         to buslogic_reset that the command is no longer active.
-       */
-       HOSTDATA(shpnt)->sc[mbo] = NULL;
-       /*
-         Finally, call the SCSI command completion handler.
-       */
-       sctmp->scsi_done(sctmp);
-      }
-}
-
-
-/* ??? Why does queuecommand return a value?  scsi.c never looks at it... */
-int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
-{
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    unsigned char direction;
-    unsigned char *cmd = (unsigned char *)scpnt->cmnd;
-    unsigned char target = scpnt->target;
-    unsigned char lun = scpnt->lun;
-    void *buff = scpnt->request_buffer;
-    int bufflen = scpnt->request_bufflen;
-    int mbo;
-    unsigned long flags;
-    struct Scsi_Host *shpnt = scpnt->host;
-    struct mailbox *mb = HOSTDATA(shpnt)->mb;
-    struct ccb *ccb;
-
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    if (target > 1) {
-       scpnt->result = DID_TIME_OUT << 16;
-       done(scpnt);
-       return 0;
-    }
-#endif
-
-    if (*cmd == REQUEST_SENSE) {
-#if (BUSLOGIC_DEBUG & (BD_COMMAND | BD_ERRORS))
-       if (bufflen != sizeof scpnt->sense_buffer) {
-           buslogic_printk("wrong buffer length supplied for request sense"
-                           " (%d).\n",
-                           bufflen);
-       }
-#endif
-       scpnt->result = 0;
-       done(scpnt);
-       return 0;
-    }
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    {
-       int i;
-
-       if (*cmd == READ_10 || *cmd == WRITE_10
-           || *cmd == READ_6 || *cmd == WRITE_6)
-           i = *(int *)(cmd + 2);
-       else
-           i = -1;
-       buslogic_printk("dev %d cmd %02X pos %d len %d ",
-                       target, *cmd, i, bufflen);
-       buslogic_stat(shpnt->io_port);
-       buslogic_printk("dumping scsi cmd:");
-       for (i = 0; i < scpnt->cmd_len; i++)
-           printk(" %02X", cmd[i]);
-       printk("\n");
-       if (*cmd == WRITE_10 || *cmd == WRITE_6)
-           return 0;   /* we are still testing, so *don't* write */
-    }
-#endif
-
-    /* Use the outgoing mailboxes in a round-robin fashion, because this
-       is how the host adapter will scan for them. */
-
-    save_flags(flags);
-    cli();
-
-    mbo = HOSTDATA(shpnt)->last_mbo_used + 1;
-    if (mbo >= BUSLOGIC_MAILBOXES)
-       mbo = 0;
-
-    do {
-       if (mb[mbo].status == MBX_NOT_IN_USE
-           && HOSTDATA(shpnt)->sc[mbo] == NULL)
-           break;
-       mbo++;
-       if (mbo >= BUSLOGIC_MAILBOXES)
-           mbo = 0;
-    } while (mbo != HOSTDATA(shpnt)->last_mbo_used);
-
-    if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(shpnt)->sc[mbo]) {
-       /* ??? Instead of failing, should we enable OMBR interrupts and sleep
-          until we get one? */
-       restore_flags(flags);
-       buslogic_printk("unable to find empty mailbox.\n");
-       goto fail;
-    }
-
-    HOSTDATA(shpnt)->sc[mbo] = scpnt;          /* This will effectively
-                                                  prevent someone else from
-                                                  screwing with this cdb. */
-
-    HOSTDATA(shpnt)->last_mbo_used = mbo;
-
-    restore_flags(flags);
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    buslogic_printk("sending command (%d %08X)...", mbo, done);
-#endif
-
-    ccb = &HOSTDATA(shpnt)->ccbs[mbo];
-
-    /* This gets trashed for some reason */
-    mb[mbo].ccbptr = ccb;
-
-    memset(ccb, 0, sizeof (struct ccb));
-
-    ccb->cdblen = scpnt->cmd_len;              /* SCSI Command Descriptor
-                                                  Block Length */
-
-    direction = 0;
-    if (*cmd == READ_10 || *cmd == READ_6)
-       direction = 8;
-    else if (*cmd == WRITE_10 || *cmd == WRITE_6)
-       direction = 16;
-
-    memcpy(ccb->cdb, cmd, ccb->cdblen);
-
-    if (scpnt->use_sg) {
-       struct scatterlist *sgpnt;
-       struct chain *cptr;
-       size_t i;
-
-       ccb->op = CCB_OP_INIT_SG;       /* SCSI Initiator Command
-                                          w/scatter-gather */
-       scpnt->host_scribble
-           = (unsigned char *)scsi_malloc(BUSLOGIC_SG_MALLOC);
-       if (scpnt->host_scribble == NULL) {
-           buslogic_printk("unable to allocate DMA memory.\n");
-           goto fail;
-       }
-       sgpnt = (struct scatterlist *)scpnt->request_buffer;
-       cptr = (struct chain *)scpnt->host_scribble;
-       if (scpnt->use_sg > shpnt->sg_tablesize) {
-           buslogic_printk("bad segment list, %d > %d.\n",
-                           scpnt->use_sg, shpnt->sg_tablesize);
-           goto fail;
-       }
-       for (i = 0; i < scpnt->use_sg; i++) {
-           CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, sgpnt[i].address,
-                          goto baddma);
-           cptr[i].dataptr = sgpnt[i].address;
-           cptr[i].datalen = sgpnt[i].length;
-       }
-       ccb->datalen = scpnt->use_sg * sizeof (struct chain);
-       ccb->dataptr = cptr;
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-       {
-           unsigned char *ptr;
-
-           buslogic_printk("cptr %08X:", cptr);
-           ptr = (unsigned char *)cptr;
-           for (i = 0; i < 18; i++)
-               printk(" %02X", ptr[i]);
-           printk("\n");
-       }
-#endif
-    } else {
-       ccb->op = CCB_OP_INIT;  /* SCSI Initiator Command */
-       scpnt->host_scribble = NULL;
-       CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, buff, goto baddma);
-       ccb->datalen = bufflen;
-       ccb->dataptr = buff;
-    }
-    ccb->id = target;
-    ccb->lun = lun;
-    ccb->dir = direction;
-    ccb->rsalen = sizeof scpnt->sense_buffer;
-    ccb->senseptr = scpnt->sense_buffer;
-    /* ccbcontrol, commlinkid, and linkptr are 0 due to above memset. */
-
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    {
-       size_t i;
-
-       buslogic_printk("sending...");
-       for (i = 0; i < sizeof(struct ccb) - 10; i++)
-           printk(" %02X", ((unsigned char *)ccb)[i]);
-       printk("\n");
-    }
-#endif
-
-    if (done) {
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-       buslogic_printk("now waiting for interrupt: ");
-       buslogic_stat(shpnt->io_port);
-#endif
-       scpnt->scsi_done = done;
-       mb[mbo].status = MBX_ACTION_START;
-       /* start scsi command */
-       buslogic_out(shpnt->io_port, buscmd, sizeof buscmd);
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-       buslogic_stat(shpnt->io_port);
-#endif
-    } else
-       buslogic_printk("done can't be NULL.\n");
-
-    while (0) {
-#if defined(MODULE) && !defined(GFP_DMA)
-      baddma:
-       buslogic_printk("address > 16MB used for ISA HA.\n");
-#endif
-      fail:
-       scpnt->result = DID_ERROR << 16;
-       done(scpnt);
-    }
-
-    return 0;
-}
-
-#if 0
-static void internal_done(Scsi_Cmnd *scpnt)
-{
-    scpnt->SCp.Status++;
-}
-
-int buslogic_command(Scsi_Cmnd *scpnt)
-{
-#if (BUSLOGIC_DEBUG & BD_COMMAND)
-    buslogic_printk("calling buslogic_queuecommand.\n");
-#endif
-
-    buslogic_queuecommand(scpnt, internal_done);
-
-    scpnt->SCp.Status = 0;
-    while (!scpnt->SCp.Status)
-       barrier();
-    return scpnt->result;
-}
-#endif
-
-/* Initialize mailboxes. */
-static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt)
-{
-    size_t i;
-    int ok = FALSE;            /* Innocent until proven guilty... */
-    struct mailbox *mb = HOSTDATA(shpnt)->mb;
-    struct ccb *ccb = HOSTDATA(shpnt)->ccbs;
-    struct {
-       unsigned char cmd, count;
-       void *base PACKED;
-    } cmd = { CMD_INITEXTMB, BUSLOGIC_MAILBOXES, mb };
-
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++) {
-       mb[i].status = mb[BUSLOGIC_MAILBOXES + i].status = MBX_NOT_IN_USE;
-       mb[i].ccbptr = &ccb[i];
-    }
-    INTR_RESET(base);  /* reset interrupts, so they don't block */
-
-    if (buslogic_out(base, (unsigned char *)&cmd, sizeof cmd))
-       goto fail;
-    WAIT_UNTIL(INTERRUPT(base), CMDC);
-
-    ok = TRUE;
-
-    while (0) {
-      fail:
-       buslogic_printk("failed setting up mailboxes.\n");
-    }
-
-    INTR_RESET(base);
-
-    return !ok;
-}
-
-static int getconfig(unsigned int base, unsigned char *irq,
-                    unsigned char *dma, unsigned char *id,
-                    char *bus_type, unsigned short *max_sg,
-                    const unsigned char **bios)
-{
-    unsigned char inquiry_cmd[2];
-    unsigned char inquiry_result[4];
-    int i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    i = inb(STATUS(base));
-    if (i & DIRRDY)
-       i = inb(DATA_IN(base));
-    inquiry_cmd[0] = CMD_RETCONF;
-    buslogic_out(base, inquiry_cmd, 1);
-    if (buslogic_in(base, inquiry_result, 3))
-       goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    INTR_RESET(base);
-    /* Defer using the DMA value until we know the bus type. */
-    *dma = inquiry_result[0];
-    switch (inquiry_result[1]) {
-      case 0x01:
-       *irq = 9;
-       break;
-      case 0x02:
-       *irq = 10;
-       break;
-      case 0x04:
-       *irq = 11;
-       break;
-      case 0x08:
-       *irq = 12;
-       break;
-      case 0x20:
-       *irq = 14;
-       break;
-      case 0x40:
-       *irq = 15;
-       break;
-      default:
-       buslogic_printk("unable to determine BusLogic IRQ level, "
-                       " disabling board.\n");
-       goto fail;
-    }
-    *id = inquiry_result[2] & 0x7;
-
-    /* I expected Adaptec boards to fail on this, but it doesn't happen... */
-    inquiry_cmd[0] = CMD_INQEXTSETUP;
-    inquiry_cmd[1] = 4;
-    if (buslogic_out(base, inquiry_cmd, 2))
-       goto fail;
-    if (buslogic_in(base, inquiry_result, inquiry_cmd[1]))
-       goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    if (inb(STATUS(base)) & CMDINV)
-       goto fail;
-    INTR_RESET(base);
-
-    *bus_type = inquiry_result[0];
-    CHECK(*bus_type == 'A' || *bus_type == 'E' || *bus_type == 'M');
-
-    *bios = (const unsigned char *)((unsigned int)inquiry_result[1] << 12);
-
-    *max_sg = (inquiry_result[3] << 8) | inquiry_result[2];
-
-    /* We only need a DMA channel for ISA boards.  Some other types of boards
-       (such as the 747S) have an option to report a DMA channel even though
-       none is used (for compatibility with Adaptec drivers which require a
-       DMA channel).  We ignore this. */
-    if (*bus_type == 'A')
-       switch (*dma) {
-         case 0:       /* This indicates that no DMA channel is used. */
-           *dma = 0;
-           break;
-         case 0x20:
-           *dma = 5;
-           break;
-         case 0x40:
-           *dma = 6;
-           break;
-         case 0x80:
-           *dma = 7;
-           break;
-         default:
-           buslogic_printk("unable to determine BusLogic DMA channel,"
-                           " disabling board.\n");
-           goto fail;
-       }
-    else
-       *dma = 0;
-
-    while (0) {
-      fail:
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-       buslogic_printk("query board settings\n");
-#endif
-       return TRUE;
-    }
-
-    return FALSE;
-}
-
-/* Query the board.  This acts both as part of the detection sequence and as a
-   means to get necessary configuration information. */
-static int buslogic_query(unsigned int base, unsigned char *trans,
-                         unsigned char *irq, unsigned char *dma,
-                         unsigned char *id, char *bus_type,
-                         unsigned short *max_sg, const unsigned char **bios,
-                         char *model, char *firmware_rev)
-{
-    unsigned char inquiry_cmd[2];
-    unsigned char inquiry_result[6];
-    unsigned char geo;
-    unsigned int i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    /* Quick and dirty test for presence of the card. */
-    if (inb(STATUS(base)) == 0xFF)
-       goto fail;
-
-    /* Check the GEOMETRY port early for quick bailout on Adaptec boards. */
-    geo = inb(GEOMETRY(base));
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("geometry bits: %02X\n", geo);
-#endif
-    /* Here is where we tell the men from the boys (i.e. Adaptec's don't
-       support the GEOMETRY port, the men do :-) */
-    if (geo == 0xFF)
-       goto fail;
-
-    /* In case some other card was probing here, reset interrupts. */
-    INTR_RESET(base);
-
-    /* Reset the adapter.  I ought to make a hard reset, but it's not really
-       necessary. */
-    outb(RSOFT | RINT/* | RSBUS*/, CONTROL(base));
-
-    /* Wait a little bit for things to settle down. */
-    i = jiffies + 2;
-    while (i > jiffies);
-
-    /* Expect INREQ and HARDY, any of the others are bad. */
-    WAIT(STATUS(base), INREQ | HARDY, DACT | DFAIL | CMDINV | DIRRDY | CPRBSY);
-
-    /* Shouldn't have generated any interrupts during reset. */
-    if (inb(INTERRUPT(base)) & INTRMASK)
-       goto fail;
-
-    /* Getting the BusLogic firmware revision level is a bit tricky.  We get
-       the first two digits (d.d) from CMD_INQUIRY and then use two undocumented
-       commands to get the remaining digit and letter (d.ddl as in 3.31C). */
-
-    inquiry_cmd[0] = CMD_INQUIRY;
-    buslogic_out(base, inquiry_cmd, 1);
-    if (buslogic_in(base, inquiry_result, 4))
-       goto fail;
-    /* Reading port should reset DIRRDY. */
-    if (inb(STATUS(base)) & DIRRDY)
-       goto fail;
-    WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-    INTR_RESET(base);
-    firmware_rev[0] = inquiry_result[2];
-    firmware_rev[1] = '.';
-    firmware_rev[2] = inquiry_result[3];
-    firmware_rev[3] = '\0';
-#if 0
-    buslogic_printk("inquiry bytes: %02X(%c) %02X(%c)\n",
-                   inquiry_result[0], inquiry_result[0],
-                   inquiry_result[1], inquiry_result[1]);
-#endif
-
-    if (getconfig(base, irq, dma, id, bus_type, max_sg, bios))
-       goto fail;
-
-    /* Set up defaults */
-#ifdef BIOS_TRANSLATION_OVERRIDE
-    *trans = BIOS_TRANSLATION_OVERRIDE;
-#else
-    *trans = BIOS_TRANSLATION_DEFAULT;
-#endif
-    model[0] = '\0';
-    model[6] = 0;
-
-    /* ??? Begin undocumented command use.
-       These may not be supported by clones. */
-
-    do {
-       /* ??? It appears as though AMI BusLogic clones don't implement this
-          feature.  As an experiment, if we read a 00 we ignore the GEO_GT_1GB
-          bit and skip all further undocumented commands. */
-       if (geo == 0x00)
-           break;
-#ifndef BIOS_TRANSLATION_OVERRIDE
-       *trans = ((geo & GEO_GT_1GB)
-                 ? BIOS_TRANSLATION_BIG : BIOS_TRANSLATION_DEFAULT);
-#endif
-
-       inquiry_cmd[0] = CMD_VER_NO_LAST;
-       buslogic_out(base, inquiry_cmd, 1);
-       if (buslogic_in(base, inquiry_result, 1))
-           break;
-       WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-       INTR_RESET(base);
-       firmware_rev[3] = inquiry_result[0];
-       firmware_rev[4] = '\0';
-
-       inquiry_cmd[0] = CMD_VER_NO_LETTER;
-       buslogic_out(base, inquiry_cmd, 1);
-       if (buslogic_in(base, inquiry_result, 1))
-           break;
-       WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-       INTR_RESET(base);
-       firmware_rev[4] = inquiry_result[0];
-       firmware_rev[5] = '\0';
-
-       /* Use undocumented command to get model number and revision. */
-
-       inquiry_cmd[0] = CMD_RET_MODEL_NO;
-       inquiry_cmd[1] = 6;
-       buslogic_out(base, inquiry_cmd, 2);
-       if (buslogic_in(base, inquiry_result, inquiry_cmd[1]))
-           break;
-       WAIT_UNTIL_FAST(INTERRUPT(base), CMDC);
-       INTR_RESET(base);
-       memcpy(model, inquiry_result, 5);
-       model[5] = '\0';
-       model[6] = inquiry_result[5];
-    } while (0);
-
-    /* ??? End undocumented command use. */
-
-    /* bus_type from getconfig doesn't differentiate between EISA/VESA.  We
-       override using the model number here. */
-    switch (*bus_type) {
-      case 'E':
-       switch (model[0]) {
-         case '4':
-           *bus_type = 'V';
-           break;
-         case '9':
-           *bus_type = 'P';
-           break;
-         case '7':
-           break;
-         default:
-           *bus_type = 'X';
-           break;
-       }
-       break;
-      default:
-       break;
-    }
-
-    while (0) {
-      fail:
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-       buslogic_printk("query board settings\n");
-#endif
-       return TRUE;
-    }
-
-    return FALSE;
-}
-
-/* return non-zero on detection */
-int buslogic_detect(Scsi_Host_Template *tpnt)
-{
-    unsigned char dma;
-    unsigned char irq;
-    unsigned int base;
-    unsigned char id;
-    char bus_type;
-    unsigned short max_sg;
-    unsigned char bios_translation;
-    unsigned long flags;
-    const unsigned char *bios;
-    char *model;
-    char *firmware_rev;
-    struct Scsi_Host *shpnt;
-    size_t indx;
-    int unchecked_isa_dma;
-    int count = 0;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-    buslogic_printk("called\n");
-#endif
-
-    tpnt->proc_dir = &proc_scsi_buslogic; 
-    tpnt->can_queue = BUSLOGIC_MAILBOXES;
-    for (indx = 0; bases[indx] != 0; indx++)
-       if (!check_region(bases[indx], 4)) {
-           shpnt = scsi_register(tpnt, sizeof (struct hostdata));
-
-           base = bases[indx];
-
-           model = HOSTDATA(shpnt)->model;
-           firmware_rev = HOSTDATA(shpnt)->firmware_rev;
-           if (buslogic_query(base, &bios_translation, &irq, &dma, &id,
-                              &bus_type, &max_sg, &bios, model, firmware_rev))
-               goto unregister;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-           buslogic_stat(base);
-#endif
-
-           /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */
-           unchecked_isa_dma = (bus_type == 'A');
-#ifndef CONFIG_NO_BUGGY_BUSLOGIC
-           /* There is a hardware bug in the BT-445S prior to revision D.
-              When the BIOS is enabled and you have more than 16MB of memory,
-              the card mishandles memory transfers over 16MB which (if viewed
-              as a 24-bit address) overlap with the BIOS address space.  For
-              example if you have the BIOS located at physical address
-              0xDC000 and a DMA transfer from the card to RAM starts at
-              physical address 0x10DC000 then the transfer is messed up.  To
-              be more precise every fourth byte of the transfer is messed up.
-              (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP
-              BusLogic driver.) */
-
-           if (bus_type == 'V'                             /* 445 */
-               && firmware_rev[0] <= '3'                   /* S */
-               && bios != NULL) {                          /* BIOS enabled */
-#if 1
-               /* Now that LNZ's forbidden_addr stuff is in the higher level
-                  scsi code, we can use this instead. */
-               /* Avoid addresses which "mirror" the BIOS for DMA. */
-               shpnt->forbidden_addr = (unsigned long)bios;
-               shpnt->forbidden_size = 16 * 1024;
-#else
-               /* Use double-buffering. */
-               unchecked_isa_dma = TRUE;
-#endif
-           }
-#endif
-
-           CHECK_DMA_ADDR(unchecked_isa_dma, shpnt, goto unregister);
-
-           if (setup_mailboxes(base, shpnt))
-               goto unregister;
-
-           /* Set the Bus on/off-times as not to ruin floppy performance.
-              CMD_BUSOFF_TIME is a noop for EISA boards (and possibly
-              others???). */
-           if (bus_type != 'E' && bus_type != 'P') {
-               /* The default ON/OFF times for BusLogic adapters is 7/4. */
-               static const unsigned char oncmd[] = { CMD_BUSON_TIME, 7 };
-               static const unsigned char offcmd[] = { CMD_BUSOFF_TIME, 5 };
-
-               INTR_RESET(base);
-               buslogic_out(base, oncmd, sizeof oncmd);
-               WAIT_UNTIL(INTERRUPT(base), CMDC);
-               INTR_RESET(base);
-               buslogic_out(base, offcmd, sizeof offcmd);
-               WAIT_UNTIL(INTERRUPT(base), CMDC);
-               while (0) {
-                 fail:
-                   buslogic_printk("setting bus on/off-time failed.\n");
-               }
-               INTR_RESET(base);
-           }
-
-           buslogic_printk("configuring %s HA at port 0x%03X, IRQ %u",
-                           (bus_type == 'A' ? "ISA"
-                            : (bus_type == 'E' ? "EISA"
-                               : (bus_type == 'M' ? "MCA"
-                                  : (bus_type == 'P' ? "PCI"
-                                     : (bus_type == 'V' ? "VESA"
-                                        : (bus_type == 'X' ? "EISA/VESA/PCI"
-                                           : "Unknown")))))),
-                           base, irq);
-           if (bios != NULL)
-               printk(", BIOS 0x%05X", (unsigned int)bios);
-           if (dma != 0)
-               printk(", DMA %u", dma);
-           printk(", ID %u\n", id);
-           buslogic_printk("Model Number: %s",
-                           (model[0] ? model : "Unknown"));
-           if (model[0])
-               printk(" (revision %d)", model[6]);
-           printk("\n");
-           buslogic_printk("firmware revision: %s\n", firmware_rev);
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-           buslogic_stat(base);
-#endif
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-           buslogic_printk("enable interrupt channel %d.\n", irq);
-#endif
-
-           save_flags(flags);
-           cli();
-           if (request_irq(irq, buslogic_interrupt,
-                           SA_INTERRUPT, "buslogic")) {
-               buslogic_printk("unable to allocate IRQ for "
-                               "BusLogic controller.\n");
-               restore_flags(flags);
-               goto unregister;
-           }
-
-           if (dma) {
-               if (request_dma(dma, "buslogic")) {
-                   buslogic_printk("unable to allocate DMA channel for "
-                                   "BusLogic controller.\n");
-                   free_irq(irq);
-                   restore_flags(flags);
-                   goto unregister;
-               }
-
-               /* The DMA-Controller.  We need to fool with this because we
-                  want to be able to use an ISA BusLogic without having to
-                  have the BIOS enabled. */
-               set_dma_mode(dma, DMA_MODE_CASCADE);
-               enable_dma(dma);
-           }
-
-           host[irq - 9] = shpnt;
-           shpnt->this_id = id;
-           shpnt->unchecked_isa_dma = unchecked_isa_dma;
-           /* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots
-              of memory gets sucked up for bounce buffers.  */
-           shpnt->cmd_per_lun = (unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN);
-           shpnt->sg_tablesize = max_sg;
-           if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG)
-               shpnt->sg_tablesize = BUSLOGIC_MAX_SG;
-           /* ??? shpnt->base should really be "const unsigned char *"... */
-           shpnt->base = (unsigned char *)bios;
-           shpnt->io_port = base;
-           shpnt->n_io_port = 4;       /* Number of bytes of I/O space used */
-           shpnt->dma_channel = dma;
-           shpnt->irq = irq;
-           HOSTDATA(shpnt)->bios_translation = bios_translation;
-           if (bios_translation == BIOS_TRANSLATION_BIG)
-               buslogic_printk("using extended bios translation.\n");
-           HOSTDATA(shpnt)->last_mbi_used = 2 * BUSLOGIC_MAILBOXES - 1;
-           HOSTDATA(shpnt)->last_mbo_used = BUSLOGIC_MAILBOXES - 1;
-           memset(HOSTDATA(shpnt)->sc, 0, sizeof HOSTDATA(shpnt)->sc);
-           restore_flags(flags);
-
-#if 0
-           {
-               unsigned char buf[8];
-               unsigned char cmd[]
-                   = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-               size_t i;
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-               buslogic_printk("*** READ CAPACITY ***\n");
-#endif
-               for (i = 0; i < sizeof buf; i++)
-                   buf[i] = 0x87;
-               for (i = 0; i < 2; i++)
-                   if (!buslogic_command(i, cmd, buf, sizeof buf)) {
-                       buslogic_printk("LU %u sector_size %d device_size %d\n",
-                                       i, *(int *)(buf + 4), *(int *)buf);
-                   }
-
-#if (BUSLOGIC_DEBUG & BD_DETECT)
-               buslogic_printk("*** NOW RUNNING MY OWN TEST ***\n");
-#endif
-               for (i = 0; i < 4; i++) {
-                   static buffer[512];
-
-                   cmd[0] = READ_10;
-                   cmd[1] = 0;
-                   xany2scsi(cmd + 2, i);
-                   cmd[6] = 0;
-                   cmd[7] = 0;
-                   cmd[8] = 1;
-                   cmd[9] = 0;
-                   buslogic_command(0, cmd, buffer, sizeof buffer);
-               }
-           }
-#endif
-
-           request_region(bases[indx], 4,"buslogic");
-           /* Register the IO ports that we use */
-           count++;
-           continue;
-         unregister:
-           scsi_unregister(shpnt);
-       }
-    return count;
-}
-
-static int restart(struct Scsi_Host *shpnt)
-{
-    unsigned int i;
-    unsigned int count = 0;
-#if 0
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-#endif
-
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-       if (HOSTDATA(shpnt)->sc[i]
-           && !HOSTDATA(shpnt)->sc[i]->device->soft_reset) {
-#if 0
-           HOSTDATA(shpnt)->mb[i].status
-               = MBX_ACTION_START;     /* Indicate ready to restart... */
-#endif
-           count++;
-       }
-
-    buslogic_printk("potential to restart %d stalled commands...\n", count);
-#if 0
-    /* start scsi command */
-    if (count)
-       buslogic_out(shpnt->host->io_port, buscmd, sizeof buscmd);
-#endif
-    return 0;
-}
-
-/* ??? The abort command for the aha1542 does not leave the device in a clean
-   state where it is available to be used again.  As it is not clear whether
-   the same problem exists with BusLogic boards, we will enable this and see
-   if it works. */
-int buslogic_abort(Scsi_Cmnd *scpnt)
-{
-#if 1
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    struct mailbox *mb;
-    int mbi, mbo, last_mbi;
-    unsigned long flags;
-    unsigned int i;
-
-    buslogic_printk("%X %X\n",
-                   inb(STATUS(scpnt->host->io_port)),
-                   inb(INTERRUPT(scpnt->host->io_port)));
-
-    save_flags(flags);
-    cli();
-    mb = HOSTDATA(scpnt->host)->mb;
-    last_mbi = HOSTDATA(scpnt->host)->last_mbi_used;
-    mbi = last_mbi + 1;
-    if (mbi >= 2 * BUSLOGIC_MAILBOXES)
-       mbi = BUSLOGIC_MAILBOXES;
-
-    do {
-       if (mb[mbi].status != MBX_NOT_IN_USE)
-           break;
-       last_mbi = mbi;
-       mbi++;
-       if (mbi >= 2 * BUSLOGIC_MAILBOXES)
-           mbi = BUSLOGIC_MAILBOXES;
-    } while (mbi != HOSTDATA(scpnt->host)->last_mbi_used);
-
-    if (mb[mbi].status != MBX_NOT_IN_USE) {
-       buslogic_printk("lost interrupt discovered on irq %d, "
-                       " - attempting to recover...\n",
-                       scpnt->host->irq);
-       HOSTDATA(scpnt->host)->last_mbi_used = last_mbi;
-       buslogic_interrupt(scpnt->host->irq, NULL);
-       restore_flags(flags);
-       return SCSI_ABORT_SUCCESS;
-    }
-    restore_flags(flags);
-
-    /* OK, no lost interrupt.  Try looking to see how many pending commands we
-       think we have. */
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-       if (HOSTDATA(scpnt->host)->sc[i]) {
-           if (HOSTDATA(scpnt->host)->sc[i] == scpnt) {
-               buslogic_printk("timed out command pending for %s.\n",
-                       kdevname(scpnt->request.rq_dev));
-               if (HOSTDATA(scpnt->host)->mb[i].status != MBX_NOT_IN_USE) {
-                   buslogic_printk("OGMB still full - restarting...\n");
-                   buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-               }
-           } else
-               buslogic_printk("other pending command: %s\n",
-                       kdevname(scpnt->request.rq_dev));
-       }
-#endif
-
-#if (BUSLOGIC_DEBUG & BD_ABORT)
-    buslogic_printk("called\n");
-#endif
-
-#if 1
-    /* This section of code should be used carefully - some devices cannot
-       abort a command, and this merely makes it worse. */
-    save_flags(flags);
-    cli();
-    for (mbo = 0; mbo < BUSLOGIC_MAILBOXES; mbo++)
-       if (scpnt == HOSTDATA(scpnt->host)->sc[mbo]) {
-           mb[mbo].status = MBX_ACTION_ABORT;
-           buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-           break;
-       }
-    restore_flags(flags);
-#endif
-
-    return SCSI_ABORT_SNOOZE;
-}
-
-/* We do not implement a reset function here, but the upper level code assumes
-   that it will get some kind of response for the command in scpnt.  We must
-   oblige, or the command will hang the SCSI system.  For a first go, we assume
-   that the BusLogic notifies us with all of the pending commands (it does
-   implement soft reset, after all). */
-int buslogic_reset(Scsi_Cmnd *scpnt)
-{
-    static const unsigned char buscmd[] = { CMD_START_SCSI };
-    unsigned int i;
-
-#if (BUSLOGIC_DEBUG & BD_RESET)
-    buslogic_printk("called\n");
-#endif
-#if 0
-    /* This does a scsi reset for all devices on the bus. */
-    outb(RSBUS, CONTROL(scpnt->host->io_port));
-#else
-    /* This does a selective reset of just the one device. */
-    /* First locate the ccb for this command. */
-    for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-       if (HOSTDATA(scpnt->host)->sc[i] == scpnt) {
-           HOSTDATA(scpnt->host)->ccbs[i].op = CCB_OP_BUS_RESET;
-
-           /* Now tell the BusLogic to flush all pending commands for this
-              target. */
-           buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
-
-           /* Here is the tricky part.  What to do next.  Do we get an
-              interrupt for the commands that we aborted with the specified
-              target, or do we generate this on our own?  Try it without first
-              and see what happens. */
-           buslogic_printk("sent BUS DEVICE RESET to target %d.\n",
-                           scpnt->target);
-
-           /* If the first does not work, then try the second.  I think the
-              first option is more likely to be correct.  Free the command
-              block for all commands running on this target... */
-#if 1
-           for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
-               if (HOSTDATA(scpnt->host)->sc[i]
-                   && HOSTDATA(scpnt->host)->sc[i]->target == scpnt->target) {
-                   Scsi_Cmnd *sctmp = HOSTDATA(scpnt->host)->sc[i];
-
-                   sctmp->result = DID_RESET << 16;
-                   if (sctmp->host_scribble)
-                       scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
-                   buslogic_printk("sending DID_RESET for target %d.\n",
-                                   scpnt->target);
-                   sctmp->scsi_done(scpnt);
-
-                   HOSTDATA(scpnt->host)->sc[i] = NULL;
-                   HOSTDATA(scpnt->host)->mb[i].status = MBX_NOT_IN_USE;
-               }
-           return SCSI_RESET_SUCCESS;
-#else
-           return SCSI_RESET_PENDING;
-#endif
-       }
-#endif
-    /* No active command at this time, so this means that each time we got some
-       kind of response the last time through.  Tell the mid-level code to
-       request sense information in order to decide what to do next. */
-    return SCSI_RESET_PUNT;
-}
-
-/* ??? This is probably not correct for series "C" boards.  I believe these
-   support separate mappings for each disk.  We would need to issue a
-   CMD_READ_FW_LOCAL_RAM command to check for the particular drive being
-   queried.  Note that series "C" boards can be differentiated by having
-   HOSTDATA(disk->device->host)->firmware_rev[0] >= '4'. */
-int buslogic_biosparam(Disk *disk, kdev_t dev, int *ip)
-{
-    unsigned int size = disk->capacity;
-
-    /* ip[0] == heads, ip[1] == sectors, ip[2] == cylinders */
-    if (HOSTDATA(disk->device->host)->bios_translation == BIOS_TRANSLATION_BIG
-       && size >= 0x200000) {          /* 1GB */
-       if (size >= 0x400000) {         /* 2GB */
-#if 0  /* ??? Used in earlier kernels, but disagrees with BusLogic info. */
-           if (mb >= 0x800000) {       /* 4GB */
-               ip[0] = 256;
-               ip[1] = 64;
-           } else {
-               ip[0] = 256;
-               ip[1] = 32;
-           }
-#else
-           ip[0] = 255;
-           ip[1] = 63;
-#endif
-       } else {
-           ip[0] = 128;
-           ip[1] = 32;
-       }
-    } else {
-       ip[0] = 64;
-       ip[1] = 32;
-    }
-    ip[2] = size / (ip[0] * ip[1]);
-/*    if (ip[2] > 1024)
-       ip[2] = 1024; */
-    return 0;
-}
-
-/* called from init/main.c */
-void buslogic_setup(char *str, int *ints)
-{
-    static const unsigned short valid_bases[]
-       = { 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 };
-    static size_t setup_idx = 0;
-    size_t i;
-
-    if (setup_idx >= ARRAY_SIZE(bases) - 1) {
-       buslogic_printk("called too many times.  Bad LILO params?\n");
-       return;
-    }
-    if (ints[0] != 1) {
-       buslogic_printk("malformed command line.\n");
-       buslogic_printk("usage: buslogic=<portbase>\n");
-       return;
-    }
-    for (i = 0; i < ARRAY_SIZE(valid_bases); i++)
-       if (valid_bases[i] == ints[1]) {
-           bases[setup_idx++] = ints[1];
-           bases[setup_idx] = 0;
-           return;
-       }
-    buslogic_printk("invalid base 0x%X specified.\n", ints[1]);
-}
-
-#ifdef MODULE
-/* Eventually this will go into an include file, but that's later... */
-Scsi_Host_Template driver_template = BUSLOGIC;
-
-# include "scsi_module.c"
-#endif
diff --git a/drivers/scsi/buslogic.h b/drivers/scsi/buslogic.h
deleted file mode 100644 (file)
index a47c490..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- *     buslogic.h      Copyright (C) 1993, 1994 David B. Gentzel
- *     See buslogic.c for more information.
- */
-
-#ifndef _BUSLOGIC_H
-
-int buslogic_detect(Scsi_Host_Template *);
-int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int buslogic_abort(Scsi_Cmnd *);
-const char *buslogic_info(struct Scsi_Host *);
-int buslogic_reset(Scsi_Cmnd *);
-int buslogic_biosparam(Disk *, kdev_t, int *);
-
-
-#define BUSLOGIC { NULL, NULL,                 \
-                  NULL,                        \
-                  NULL,                        \
-                  "BusLogic",                  \
-                  buslogic_detect,             \
-                  0,   /* no release func */   \
-                  buslogic_info,               \
-                  0,   /* no command func */   \
-                  buslogic_queuecommand,       \
-                  buslogic_abort,              \
-                  buslogic_reset,              \
-                  0,   /* slave_attach NYI */  \
-                  buslogic_biosparam,          \
-                  0,   /* set by driver */     \
-                  0,   /* set by driver */     \
-                  0,   /* set by driver */     \
-                  0,   /* set by driver */     \
-                  0,                           \
-                  0,   /* set by driver */     \
-                  ENABLE_CLUSTERING            \
-                }
-
-#ifdef BUSLOGIC_PRIVATE_H
-
-/* ??? These don't really belong here */
-#ifndef TRUE
-# define TRUE 1
-#endif
-#ifndef FALSE
-# define FALSE 0
-#endif
-
-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
-
-#define PACKED __attribute__((packed))
-
-#define BD_ABORT       0x0001
-#define BD_COMMAND     0x0002
-#define BD_DETECT      0x0004
-#define BD_ERRORS      0x0008
-#define BD_INTERRUPT   0x0010
-#define BD_IO          0x0020
-#define BD_RESET       0x0040
-#define BD_UNDOCUMENTED        0x0080
-
-/* I/O Port interface */
-/* READ */
-#define STATUS(base) (base)
-#define DACT 0x80              /* Diagnostic Active */
-#define DFAIL 0x40             /* Diagnostic Failure */
-#define INREQ 0x20             /* Initialization Required */
-#define HARDY 0x10             /* Host Adapter Ready */
-#define CPRBSY 0x08            /* Command/Parameter Register Busy */
-#define DIRRDY 0x04            /* Data In Register Ready */
-                               /* 0x02 is reserved */
-#define CMDINV 0x01            /* Command Invalid */
-
-#define DATA_IN(base) (STATUS(base) + 1)
-
-#define INTERRUPT(base) (STATUS(base) + 2)
-#define INTV 0x80              /* Interrupt Valid */
-                               /* 0x70 are reserved */
-#define RSTS 0x08              /* SCSI Reset State */
-#define CMDC 0x04              /* Command Complete */
-#define MBOR 0x02              /* Mailbox Out Ready */
-#define IMBL 0x01              /* Incoming Mailbox Loaded */
-#define INTRMASK 0x8F
-
-/* This undocumented port returns a bitmask indicating geometry translation. */
-#define GEOMETRY(base) (STATUS(base) + 3)
-#define GEO_GT_1GB 0x80                /* > 1GB under DOS geometry mapping */
-                               /* 0x70 are unknown */
-#define GEO_XLATION_S_D1 0x0C  /* Disk 1 geometry ("S" models only) */
-#define GEO_XLATION_S_D0 0x03  /* Disk 0 geometry ("S" models only) */
-
-
-/* WRITE */
-#define CONTROL(base) STATUS(base)
-#define RHARD 0x80             /* Hard Reset */
-#define RSOFT 0x40             /* Soft Reset */
-#define RINT 0x20              /* Interrupt Reset */
-#define RSBUS 0x10             /* SCSI Bus Reset */
-                               /* 0x0F are reserved */
-
-#define COMMAND_PARAMETER(base) (STATUS(base) + 1)
-#define CMD_TSTCMDCINT 0x00    /* Test CMDC Interrupt */
-#define CMD_INITMB 0x01                /* Initialize Mailbox */
-#define CMD_START_SCSI 0x02    /* Start Mailbox */
-#define CMD_START_BIOS 0x03    /* Start BIOS */
-#define CMD_INQUIRY 0x04       /* Inquire Board ID */
-#define CMD_ENBOMBRINT 0x05    /* Enable OMBR Interrupt */
-#define CMD_SETSELTIMOUT 0x06  /* Set SCSI Selection Time-Out */
-#define CMD_BUSON_TIME 0x07    /* Set Bus-On Time */
-#define CMD_BUSOFF_TIME 0x08   /* Set Bus-Off Time */
-#define CMD_BUSXFR_RATE 0x09   /* Set Bus Transfer Rate */
-#define CMD_INQ_DEVICES 0x0A   /* Inquire Installed Devices */
-#define CMD_RETCONF 0x0B       /* Return Configuration */
-#define CMD_TARGET_MODE 0x0C   /* Set Target Mode */
-#define CMD_INQ_SETUP_INFO 0x0D        /* Inquire Set-up Information */
-#define CMD_WRITE_LCL_RAM 0x1A /* Write Adapter Local RAM */
-#define CMD_READ_LCL_RAM 0x1B  /* Read Adapter Local RAM */
-#define CMD_WRITE_BM_FIFO 0x1C /* Write Bus Master Chip FIFO */
-#define CMD_READ_BM_FIFO 0x1D  /* Read Bus Master Chip FIFO */
-#define CMD_ECHO 0x1F          /* Echo Data Byte */
-#define CMD_HA_DIAG 0x20       /* Host Adapter Diagnostic */
-#define CMD_HA_OPTIONS 0x21    /* Host Adapter Options */
-#define CMD_INITEXTMB 0x81     /* Initialize Extended Mailbox */
-#define CMD_VER_NO_LAST 0x84   /* Version Number Last Byte (undocumented) */
-#define CMD_VER_NO_LETTER 0x85 /* Version Number One Letter (undocumented) */
-#define CMD_RET_MODEL_NO 0x8B  /* Return Model Number (undocumented) */
-#define CMD_INQEXTSETUP 0x8D   /* Inquire Extended Set-up Information */
-#define CMD_ROUND_ROBIN 0x8F   /* Enable strict vs. half-assed round-robin
-                                  mailbox filling (undocumented) */
-#define CMD_READ_FW_LCL_RAM 0x91/* Read Firmware Local RAM (undocumented) */
-#define CMD_WRITE_INQ_BUF 0x9A /* Write Inquiry Data Buffer
-                                  (Target Mode Only) */
-#define CMD_READ_INQ_BUF 0x9B  /* Read Inquiry Data Buffer
-                                  (Target Mode Only) */
-
-#define MBX_NOT_IN_USE 0x00
-#define MBX_ACTION_START 0x01
-#define MBX_ACTION_ABORT 0x02
-#define MBX_COMPLETION_OK 0x01
-#define MBX_COMPLETION_ABORTED 0x02
-#define MBX_COMPLETION_NOT_FOUND 0x03
-#define MBX_COMPLETION_ERROR 0x04
-
-/* Mailbox Definition */
-struct mailbox {
-    void *ccbptr;              /* lsb, ..., msb */
-    unsigned char btstat;
-    unsigned char sdstat;
-    unsigned char reserved;
-    unsigned char status;      /* Command/Status */
-};
-
-/* This is used with scatter-gather */
-struct chain {
-    unsigned long datalen;     /* Size of this part of chain */
-    void *dataptr;             /* Location of data */
-};
-
-#define MAX_CDB 12
-
-struct ccb {                   /* Command Control Block */
-    unsigned char op;          /* Command Control Block Operation Code */
-    unsigned char dir;
-    unsigned char cdblen;      /* SCSI Command Length */
-    unsigned char rsalen;      /* Request Sense Allocation Length/Disable */
-    unsigned long datalen;     /* Data Length (msb, ..., lsb) */
-    void *dataptr;             /* Data Pointer */
-    unsigned char reserved[2];
-    unsigned char hastat;      /* Host Adapter Status (HASTAT) */
-    unsigned char tarstat;     /* Target Device Status */
-    unsigned char id;
-    unsigned char lun;
-    unsigned char cdb[MAX_CDB];
-    unsigned char ccbcontrol;
-    unsigned char commlinkid;  /* Command Linking Identifier */
-    void *linkptr;             /* Link Pointer */
-    void *senseptr;
-};
-
-#define CCB_OP_INIT 0x00       /* Initiator CCB */
-#define CCB_OP_TARG 0x01       /* Target CCB */
-#define CCB_OP_INIT_SG 0x02    /* Initiator CCB with scatter-gather */
-#define CCB_OP_INIT_R 0x03     /* Initiator CCB with residual data length
-                                  returned */
-#define CCB_OP_INIT_SG_R 0x04  /* Initiator CCB with scatter-gather and
-                                  residual data length returned */
-#define CCB_OP_BUS_RESET 0x81  /* SCSI bus device reset */
-
-#endif
-
-#endif
index 2c726f57fef7d3cde4c3a06cd8c6574dba5a836d..ee6d8e9b71076a491fdd49609bed3f8e05f96d18 100644 (file)
@@ -58,7 +58,7 @@
 #endif
 
 #ifdef CONFIG_SCSI_BUSLOGIC
-#include "buslogic.h"
+#include "BusLogic.h"
 #endif
 
 #ifdef CONFIG_SCSI_EATA_DMA
@@ -167,7 +167,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_AHA152X
     AHA152X,
 #endif
-/* Buslogic must come before aha1542.c */
+/* BusLogic must come before aha1542.c */
 #ifdef CONFIG_SCSI_BUSLOGIC
     BUSLOGIC,
 #endif
index 600708bdde9b5bb8e876bfef9cf59409044d4923..db0a0b808d63f05ce5768eb6b7415cc4c69e8054 100644 (file)
@@ -53,6 +53,8 @@ struct symbol_table scsi_symbol_table = {
     X(scsi_ioctl),
     X(print_command),
     X(print_sense),
+    X(print_msg),
+    X(print_status),
     X(dma_free_sectors),
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
index 10337adbfc398e5380bcc2ff4dbf09e5aab53768..5363e538dfb102dc97dfcc481733658a92f82df3 100644 (file)
@@ -406,6 +406,33 @@ const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
        return buffer;
 }
 
+int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
+                               int length, int hostno, int inout)
+{
+       const char *info = seagate_st0x_info(NULL);
+       int len;
+       int pos;
+       int begin;
+
+       if (inout) return(-ENOSYS);
+
+       begin = 0;
+       strcpy(buffer,info);
+       strcat(buffer,"\n");
+
+       pos = len = strlen(buffer);
+
+       if (pos<offset) {
+               len = 0;
+               begin = pos;
+               }
+
+       *start = buffer + (offset - begin);
+       len -= (offset - begin);
+       if ( len > length ) len = length;
+       return(len);
+}
+
 /*
  * These are our saved pointers for the outstanding command that is 
  * waiting for a reconnect
index 7210cb3dc174cb11d32f03646f85a58e9b80d76b..8d9e1a42fcb17b6bf90de663a903fe63bbef39b2 100644 (file)
@@ -19,6 +19,7 @@ int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int seagate_st0x_abort(Scsi_Cmnd *);
 const char *seagate_st0x_info(struct Scsi_Host *);
 int seagate_st0x_reset(Scsi_Cmnd *); 
+int seagate_st0x_proc_info(char *,char **,off_t,int,int,int);
 
 #ifndef NULL
        #define NULL 0
@@ -27,7 +28,7 @@ int seagate_st0x_reset(Scsi_Cmnd *);
 #include <linux/kdev_t.h>
 int seagate_st0x_biosparam(Disk *, kdev_t, int*);
 
-#define SEAGATE_ST0X  {  NULL, NULL, NULL, NULL, \
+#define SEAGATE_ST0X  {  NULL, NULL, NULL, seagate_st0x_proc_info, \
                         NULL, seagate_st0x_detect,     \
                         NULL,                                          \
                         seagate_st0x_info, seagate_st0x_command,       \
index df6af9db03d69f71c0ae4a6e60b4aac1d9e9b244..bb61dcd6ad54efaefa573fb9fcc8e302eae72556 100644 (file)
@@ -15,6 +15,9 @@ fi
 bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
+  if [ "$CONFIG_NFS_FS" = "y" ]; then
+    bool 'Root file system on NFS' CONFIG_ROOT_NFS
+  fi
 fi
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
index 9990615ea1bcf2e034936f39287259e07f4e93bf..4722731c8e5796b9878afe78269bff36aec804ac 100644 (file)
@@ -345,8 +345,8 @@ static unsigned int load_aout_interp(struct exec * interp_ex,
 #define INTERPRETER_ELF 2
 
 
-static int
-load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static inline int
+do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 {
        struct elfhdr elf_ex;
        struct elfhdr interp_elf_ex;
@@ -370,8 +370,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        unsigned int elf_stack;
        char passed_fileno[6];
        
-       MOD_INC_USE_COUNT;
-
        ibcs2_interpreter = 0;
        status = 0;
        load_addr = 0;
@@ -379,7 +377,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        
        if (elf_ex.e_ident[0] != 0x7f ||
            strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-               MOD_DEC_USE_COUNT;
                return  -ENOEXEC;
        }
        
@@ -390,7 +387,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
           (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
           (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
            !bprm->inode->i_op->default_file_ops->mmap)){
-               MOD_DEC_USE_COUNT;
                return -ENOEXEC;
        }
        
@@ -399,7 +395,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * 
                                                 elf_ex.e_phnum, GFP_KERNEL);
        if (elf_phdata == NULL) {
-               MOD_DEC_USE_COUNT;
                return -ENOMEM;
        }
        
@@ -407,7 +402,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                           elf_ex.e_phentsize * elf_ex.e_phnum, 1);
        if (retval < 0) {
                kfree (elf_phdata);
-               MOD_DEC_USE_COUNT;
                return retval;
        }
        
@@ -420,7 +414,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 
        if (elf_exec_fileno < 0) {
                kfree (elf_phdata);
-               MOD_DEC_USE_COUNT;
                return elf_exec_fileno;
        }
        
@@ -438,7 +431,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                        {
                                kfree (elf_phdata);
                                kfree(elf_interpreter);
-                               MOD_DEC_USE_COUNT;
                                return -EINVAL;
                        }
 
@@ -451,7 +443,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                                                           GFP_KERNEL);
                        if (elf_interpreter == NULL) {
                                kfree (elf_phdata);
-                               MOD_DEC_USE_COUNT;
                                return -ENOMEM;
                        }
                        
@@ -484,7 +475,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                        if(retval < 0) {
                                kfree (elf_phdata);
                                kfree(elf_interpreter);
-                               MOD_DEC_USE_COUNT;
                                return retval;
                        }
                }
@@ -509,7 +499,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                  {
                    kfree(elf_interpreter);
                    kfree(elf_phdata);
-                   MOD_DEC_USE_COUNT;
                    return -ELIBBAD;
                  }
        }
@@ -534,7 +523,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                              kfree(elf_interpreter);
                        }
                        kfree (elf_phdata);
-                       MOD_DEC_USE_COUNT;
                        return -E2BIG;
                }
        }
@@ -587,7 +575,6 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                    printk("Unable to load interpreter\n");
                    kfree(elf_phdata);
                    send_sig(SIGSEGV, current, 0);
-                   MOD_DEC_USE_COUNT;
                    return 0;
                  }
                }
@@ -709,15 +696,25 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        start_thread(regs, elf_entry, bprm->p);
        if (current->flags & PF_PTRACED)
                send_sig(SIGTRAP, current, 0);
-       MOD_DEC_USE_COUNT;
        return 0;
 }
 
+static int
+load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+       int retval;
+
+       MOD_INC_USE_COUNT;
+       retval = do_load_elf_binary(bprm, regs);
+       MOD_DEC_USE_COUNT;
+       return retval;
+}
+
 /* This is really simpleminded and specialized - we are loading an
    a.out library that is given an ELF header. */
 
-static int
-load_elf_library(int fd){
+static inline int
+do_load_elf_library(int fd){
        struct file * file;
        struct elfhdr elf_ex;
        struct elf_phdr *elf_phdata  =  NULL;
@@ -729,47 +726,46 @@ load_elf_library(int fd){
        int error;
        int i,j, k;
 
-       MOD_INC_USE_COUNT;
        len = 0;
        file = current->files->fd[fd];
        inode = file->f_inode;
        elf_bss = 0;
        
-       set_fs(KERNEL_DS);
-       if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) {
-               SYS(close)(fd);
-               MOD_DEC_USE_COUNT;
+       if (!file || !file->f_op)
                return -EACCES;
-       }
+
+       /* seek to the beginning of the file */
+       if (file->f_op->lseek) {
+               if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
+                       return -ENOEXEC;
+       } else
+               file->f_pos = 0;
+
+       set_fs(KERNEL_DS);
+       error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex));
        set_fs(USER_DS);
-       
+       if (error != sizeof(elf_ex))
+               return -ENOEXEC;
+
        if (elf_ex.e_ident[0] != 0x7f ||
-           strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
-               MOD_DEC_USE_COUNT;
+           strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
                return -ENOEXEC;
-       }
-       
+
        /* First of all, some simple consistency checks */
        if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
           (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
-          (!inode->i_op || !inode->i_op->default_file_ops->mmap)){
-               MOD_DEC_USE_COUNT;
+          (!inode->i_op || !inode->i_op->default_file_ops->mmap))
                return -ENOEXEC;
-       }
        
        /* Now read in all of the header information */
        
-       if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) {
-               MOD_DEC_USE_COUNT;
+       if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE)
                return -ENOEXEC;
-       }
        
        elf_phdata =  (struct elf_phdr *) 
                kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
-       if (elf_phdata == NULL) {
-               MOD_DEC_USE_COUNT;
+       if (elf_phdata == NULL)
                return -ENOMEM;
-       }
        
        retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
                           sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
@@ -780,7 +776,6 @@ load_elf_library(int fd){
        
        if(j != 1)  {
                kfree(elf_phdata);
-               MOD_DEC_USE_COUNT;
                return -ENOEXEC;
        }
        
@@ -800,7 +795,6 @@ load_elf_library(int fd){
        SYS(close)(fd);
        if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
                kfree(elf_phdata);
-               MOD_DEC_USE_COUNT;
                return error;
        }
 
@@ -813,10 +807,19 @@ load_elf_library(int fd){
                  PROT_READ|PROT_WRITE|PROT_EXEC,
                  MAP_FIXED|MAP_PRIVATE, 0);
        kfree(elf_phdata);
-       MOD_DEC_USE_COUNT;
        return 0;
 }
 
+static int load_elf_library(int fd)
+{
+       int retval;
+
+       MOD_INC_USE_COUNT;
+       retval = do_load_elf_library(fd);
+       MOD_DEC_USE_COUNT;
+       return retval;
+}
+       
 /*
  * ELF core dumper
  *
index 0dde5edfc27cddc999384fab81d81cb253445ae9..05a60ed4c565b21904ed9503a5c2eeadc8bdb1ae 100644 (file)
@@ -428,7 +428,7 @@ static inline void insert_into_queues(struct buffer_head * bh)
                bh->b_next->b_prev = bh;
 }
 
-static struct buffer_head * find_buffer(kdev_t dev, int block, int size)
+static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
 {              
        struct buffer_head * tmp;
 
@@ -807,10 +807,8 @@ void refile_buffer(struct buffer_head * buf)
 /*
  * Release a buffer head
  */
-void brelse(struct buffer_head * buf)
+void __brelse(struct buffer_head * buf)
 {
-       if (!buf)
-               return;
        wait_on_buffer(buf);
 
        /* If dirty, mark the time this buffer should be written back */
@@ -828,10 +826,8 @@ void brelse(struct buffer_head * buf)
 /*
  * bforget() is like brelse(), except is throws the buffer away
  */
-void bforget(struct buffer_head * buf)
+void __bforget(struct buffer_head * buf)
 {
-       if (!buf)
-               return;
        wait_on_buffer(buf);
        if (buf->b_count != 1) {
                printk("Aieee... bforget(): count = %d\n", buf->b_count);
@@ -1044,7 +1040,7 @@ static void read_buffers(struct buffer_head * bh[], int nrbuf)
  * "address" points to the new page we can use to move things
  * around..
  */
-static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
+static inline unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
        unsigned long address)
 {
        while (nrbuf-- > 0)
index 3a232088a0dde56f653fae711ab5a152e03d9ee2..ec1629ea52466464516314f451883230ece9c42d 100644 (file)
@@ -130,7 +130,7 @@ static inline void add_hash(struct dir_cache_entry * de, struct hash_list * hash
 /*
  * Find a directory cache entry given all the necessary info.
  */
-static struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
+static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
 {
        struct dir_cache_entry * de = hash->next;
 
index 7c5919cccaf0c6f63c996ef76ca47207cd84def7..72fc3f45bbf47b8f76e4d90d2caf20689e6f7480 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -953,12 +953,22 @@ static int load_aout_library(int fd)
        file = current->files->fd[fd];
        inode = file->f_inode;
        
-       set_fs(KERNEL_DS);
-       if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
+       if (!file || !file->f_op)
                return -EACCES;
-       }
+
+       /* Seek into the file */
+       if (file->f_op->lseek) {
+               if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
+                       return -ENOEXEC;
+       } else
+               file->f_pos = 0;
+
+       set_fs(KERNEL_DS);
+       error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
        set_fs(USER_DS);
-       
+       if (error != sizeof(ex))
+               return -ENOEXEC;
+
        /* We come in here for the regular a.out style of shared libraries */
        if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
            N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
index aef0ed55a8eba520bc0ad06b93105f96afec4409..63e2123a7156854381ebe87a49f51c161959ee00 100644 (file)
@@ -171,7 +171,7 @@ void ext_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
 
 #define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)])
 
-static int block_bmap(struct buffer_head * bh, int nr)
+static inline int block_bmap(struct buffer_head * bh, int nr)
 {
        int tmp;
 
index 12f7cf4893da428fb86f3920066ed988d91d807d..166f79675c2cd55b9db60e70388c2d815c2ac465 100644 (file)
@@ -17,7 +17,7 @@ extern int fcntl_getlk(unsigned int, struct flock *);
 extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
 extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
 
-static int dupfd(unsigned int fd, unsigned int arg)
+static inline int dupfd(unsigned int fd, unsigned int arg)
 {
        if (fd >= NR_OPEN || !current->files->fd[fd])
                return -EBADF;
index 14ba6fdd9e69b261a4adcb80cca22e5ae4319fc2..a088e48ec73cb5ab6555e29d8771f0d8b35ed616 100644 (file)
@@ -19,7 +19,7 @@ int nr_files = 0;
 /*
  * Insert a new file structure at the head of the list of available ones.
  */
-static void insert_file_free(struct file *file)
+static inline void insert_file_free(struct file *file)
 {
        file->f_count = 0;
        file->f_next = first_file;
@@ -32,7 +32,7 @@ static void insert_file_free(struct file *file)
 /*
  * Remove a file structure from the list of available ones.
  */
-static void remove_file_free(struct file *file)
+static inline void remove_file_free(struct file *file)
 {
        if (first_file == file)
                first_file = first_file->f_next;
@@ -44,7 +44,7 @@ static void remove_file_free(struct file *file)
 /*
  * Insert a file structure at the end of the list of available ones.
  */
-static void put_last_free(struct file *file)
+static inline void put_last_free(struct file *file)
 {
        file->f_prev = first_file->f_prev;
        file->f_prev->f_next = file;
index b884e7e189c90c21f0570f04fd4583f2587105a7..3f273e59bbdeb5c9bfb6d265636d76950d263457 100644 (file)
 #include <linux/sysv_fs.h>
 #include <linux/hpfs_fs.h>
 #include <linux/smb_fs.h>
+#include <linux/major.h>
 
 extern void device_setup(void);
 
+#ifdef CONFIG_ROOT_NFS
+extern int nfs_root_init(char *nfsname);
+extern char nfs_root_name [];
+#endif
+
 /* This may be used only once, enforced by 'static int callable' */
 asmlinkage int sys_setup(void)
 {
@@ -100,6 +106,14 @@ asmlinkage int sys_setup(void)
                {hpfs_read_super, "hpfs", 1, NULL});
 #endif
 
+#ifdef CONFIG_ROOT_NFS
+       if (nfs_root_name [0]){
+               if (nfs_root_init(nfs_root_name) < 0) {
+                       printk(KERN_ERR "Root-NFS: Unable to mount NFS filesystem as /, using /dev/fd0 instead\n");
+                       ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+               }
+       }
+#endif
        mount_root();
        return 0;
 }
index 6992b68d207acb70b0c65900005e8f619d084414..14e9770b1f7564ba88ef5f1456535509f7181e27 100644 (file)
@@ -31,7 +31,7 @@ static inline struct inode_hash_entry * const hash(kdev_t dev, int i)
        return hash_table + hashfn(dev, i);
 }
 
-static void insert_inode_free(struct inode *inode)
+static inline void insert_inode_free(struct inode *inode)
 {
        inode->i_next = first_inode;
        inode->i_prev = first_inode->i_prev;
@@ -40,7 +40,7 @@ static void insert_inode_free(struct inode *inode)
        first_inode = inode;
 }
 
-static void remove_inode_free(struct inode *inode)
+static inline void remove_inode_free(struct inode *inode)
 {
        if (first_inode == inode)
                first_inode = first_inode->i_next;
@@ -63,7 +63,7 @@ void insert_inode_hash(struct inode *inode)
        h->inode = inode;
 }
 
-static void remove_inode_hash(struct inode *inode)
+static inline void remove_inode_hash(struct inode *inode)
 {
        struct inode_hash_entry *h;
        h = hash(inode->i_dev, inode->i_ino);
@@ -77,7 +77,7 @@ static void remove_inode_hash(struct inode *inode)
        inode->i_hash_prev = inode->i_hash_next = NULL;
 }
 
-static void put_last_free(struct inode *inode)
+static inline void put_last_free(struct inode *inode)
 {
        remove_inode_free(inode);
        inode->i_prev = first_inode->i_prev;
@@ -225,7 +225,7 @@ static void write_inode(struct inode * inode)
        unlock_inode(inode);
 }
 
-static void read_inode(struct inode * inode)
+static inline void read_inode(struct inode * inode)
 {
        lock_inode(inode);
        if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode)
index 61466a09dfb7c71fa65ed74001a596898c5f855e..8afce01fb8ecaedfff8d03880d56e8e5dc434551 100644 (file)
@@ -9,6 +9,11 @@
 
 O_TARGET := nfs.o
 O_OBJS   := proc.o sock.o rpcsock.o inode.o file.o dir.o symlink.o mmap.o
+
+ifdef CONFIG_ROOT_NFS
+O_OBJS += nfsroot.o
+endif
+
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
new file mode 100644 (file)
index 0000000..35b4674
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ *  linux/fs/nfs/nfsroot.c
+ *
+ *  Copyright (C) 1995  Gero Kuhlmann <gero@gkminix.han.de>
+ *
+ *  Allow an NFS filesystem to be mounted as root. The way this works
+ *  is to first determine the local IP address via RARP. Then handle
+ *  the RPC negotiation with the system which replied to the RARP. The
+ *  actual mounting is done later, when init() is running.
+ */
+
+
+/* Define this to allow debugging output */
+#define NFSROOT_DEBUG 1
+
+/* Define the timeout for waiting for a RARP reply */
+#define RARP_TIMEOUT   30      /* 30 seconds */
+#define RARP_RETRIES    5      /* 5 retries */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+#include <asm/param.h>
+#include <linux/utsname.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#ifdef CONFIG_AX25
+#include <net/ax25.h>  /* For AX25_P_IP */
+#endif
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <net/route.h>
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+/* #include <netinet/in.h> */
+
+#define IPPORT_RESERVED 1024
+
+/* Range of privileged ports */
+#define STARTPORT 600
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+
+
+struct open_dev
+{
+       struct device *dev;
+       unsigned short old_flags;
+       struct open_dev *next;
+};
+
+static struct open_dev *open_base = NULL;
+static struct device *root_dev = NULL;
+static struct sockaddr_in myaddr;      /* My IP address */
+static struct sockaddr_in server;      /* Server IP address */
+static struct nfs_mount_data nfs_data; /* NFS mount info */
+static char nfs_path[NFS_MAXPATHLEN];  /* Name of directory to mount */
+static int nfs_port;                   /* Port to connect to for NFS service */
+
+
+
+/***************************************************************************
+
+                       RARP Subroutines
+
+ ***************************************************************************/
+
+extern void arp_send(int type, int ptype, unsigned long target_ip, 
+                       struct device *dev, unsigned long src_ip, 
+                       unsigned char *dest_hw, unsigned char *src_hw,
+                       unsigned char *target_hw);
+
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
+                       struct packet_type *pt);
+
+
+static struct packet_type rarp_packet_type =
+{
+       0,      /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
+       NULL,   /* Listen to all devices */
+       root_rarp_recv,
+       NULL,
+       NULL
+};
+
+
+/*
+ *  For receiving rarp packets a packet type has to be registered. Also
+ *  initialize all devices for usage by RARP.
+ */
+static int root_rarp_open(void)
+{
+  struct open_dev *openp;
+  struct device *dev;
+  unsigned short old_flags;
+  int num;
+
+  /* Register the packet type */
+  rarp_packet_type.type=htons(ETH_P_RARP);
+  dev_add_pack(&rarp_packet_type);
+
+  /* Open all devices which allow RARP */
+  for (dev = dev_base, num = 0; dev != NULL; dev = dev->next) {
+       if (dev->type < ARPHRD_SLIP &&
+           dev->family == AF_INET &&
+           !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOARP))) {
+               /* First up the interface */
+               old_flags = dev->flags;
+               dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+               if (!(old_flags & IFF_UP) && dev_open(dev)) {
+                       dev->flags = old_flags;
+                       continue;
+               }
+               openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
+                                               GFP_ATOMIC);
+               if (openp == NULL)
+                       continue;
+               openp->dev = dev;
+               openp->old_flags = old_flags;
+               openp->next = open_base;
+               open_base = openp;
+               num++;
+       }
+  }
+  return num;
+}
+
+
+/*
+ *  Remove the packet type again when all rarp packets have been received
+ *  and restore the state of the device. However, keep the root device
+ *  open for the upcoming mount.
+ */
+static void root_rarp_close(void)
+{
+  struct open_dev *openp;
+  struct open_dev *nextp;
+
+  /* Deregister the packet type */
+  rarp_packet_type.type=htons(ETH_P_RARP);
+  dev_remove_pack(&rarp_packet_type);
+
+  /* Deactivate all previously opened devices except that one which is
+   * able to connect to a suitable server
+   */
+  openp = open_base;
+  while (openp != NULL) {
+       nextp = openp->next;
+       openp->next = NULL;
+       if (openp->dev != root_dev) {
+               if (!(openp->old_flags & IFF_UP))
+                       dev_close(openp->dev);
+               openp->dev->flags = openp->old_flags;
+       }
+       kfree_s(openp, sizeof(struct open_dev));
+       openp = nextp;
+  }
+}
+
+
+/*
+ * Receive RARP packets.
+ */
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+  struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+  unsigned char *rarp_ptr = (unsigned char *)(rarp+1);
+  unsigned long sip, tip;
+  unsigned char *sha, *tha;            /* s for "source", t for "target" */
+  
+  /* If this test doesn't pass, its not IP, or we should ignore it anyway */
+  if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
+       kfree_skb(skb, FREE_READ);
+       return 0;
+  }
+
+  /* If it's not a RARP reply, delete it. */
+  if (rarp->ar_op != htons(ARPOP_RREPLY)) {
+       kfree_skb(skb, FREE_READ);
+       return 0;
+  }
+
+  /* If it's not ethernet or AX25, delete it. */
+  if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) || 
+#ifdef CONFIG_AX25
+      (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+       rarp->ar_pln != 4) {
+       kfree_skb(skb, FREE_READ);
+       return 0;
+  }
+  
+  /* Extract variable width fields */
+  sha = rarp_ptr;
+  rarp_ptr += dev->addr_len;
+  memcpy(&sip, rarp_ptr, 4);
+  rarp_ptr += 4;
+  tha = rarp_ptr;
+  rarp_ptr += dev->addr_len;
+  memcpy(&tip, rarp_ptr, 4);
+
+  /* Discard packets which are not meant for us. */
+  if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
+       kfree_skb(skb, FREE_READ);
+       return 0;
+  }
+
+  /* The packet is what we were looking for. Setup the global variables. */
+  cli();
+  if (root_dev != NULL) {
+       sti();
+       kfree_skb(skb, FREE_READ);
+       return 0;
+  }
+  root_dev = dev;
+  sti();
+
+  myaddr.sin_family = dev->family;
+  myaddr.sin_addr.s_addr = tip;
+  server.sin_family = dev->family;
+  if (!server.sin_addr.s_addr)
+    server.sin_addr.s_addr = sip;
+
+  kfree_skb(skb, FREE_READ);
+  return 0;
+}
+
+
+/*
+ * Send RARP request packet over all devices which allow RARP.
+ */
+static void root_rarp_send(void)
+{
+  struct open_dev *openp;
+  struct device *dev;
+
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Sending RARP request...\n");
+#endif
+
+  for (openp = open_base; openp != NULL; openp = openp->next) {
+       dev = openp->dev;
+       arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+                                               dev->dev_addr, dev->dev_addr);
+  }
+}
+
+
+/*
+ * Determine client and server IP numbers and appropriate device by using
+ * the RARP protocol.
+ */
+static int do_rarp(void)
+{
+  int retries = 0;
+  unsigned long timeout;
+
+  /* Open all devices and setup RARP protocol */
+  if (!root_rarp_open()) {
+       printk(KERN_ERR "NFS: No network device found to send RARP request to\n");
+       return -1;
+  }
+
+  /* Send RARP request and wait, until we get an answer. This loop seems
+   * to be a terrible waste of cpu time, but actually there is no process
+   * running at all, so we don't need to use any scheduler functions.
+   */
+  for (retries = 0; retries < RARP_RETRIES && root_dev == NULL; retries++) {
+       root_rarp_send();
+       timeout = jiffies + (RARP_TIMEOUT * HZ);
+       while (jiffies < timeout && root_dev == NULL)
+               ;;
+  }
+
+  if (root_dev == NULL) {
+       printk(KERN_ERR "NFS: Timed out while waiting for RARP answer\n");
+       return -1;
+  }
+
+  root_rarp_close();
+
+  printk(KERN_NOTICE "NFS: ");
+  printk("Got RARP answer from %s, ", in_ntoa(server.sin_addr.s_addr));
+  printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
+
+  return 0;
+}
+
+
+
+
+/***************************************************************************
+
+                       Routines to setup NFS
+
+ ***************************************************************************/
+
+extern void ip_rt_add(short flags, unsigned long addr, unsigned long mask,
+                       unsigned long gw, struct device *dev,
+                       unsigned short mss, unsigned long window);
+
+
+/* The following integer options are recognized */
+static struct nfs_int_opts
+{
+       char *name;
+       int  *val;
+} root_int_opts[] = {
+       { "port",       &nfs_port },
+       { "rsize",      &nfs_data.rsize },
+       { "wsize",      &nfs_data.wsize },
+       { "timeo",      &nfs_data.timeo },
+       { "retrans",    &nfs_data.retrans },
+       { "acregmin",   &nfs_data.acregmin },
+       { "acregmax",   &nfs_data.acregmax },
+       { "acdirmin",   &nfs_data.acdirmin },
+       { "acdirmax",   &nfs_data.acdirmax },
+       { NULL,         NULL }};
+
+
+/* And now the flag options */
+static struct nfs_bool_opts
+{
+       char *name;
+       int  and_mask;
+       int  or_mask;
+} root_bool_opts[] = {
+       { "soft",       ~NFS_MOUNT_SOFT,        NFS_MOUNT_SOFT },
+       { "hard",       ~NFS_MOUNT_SOFT,        0 },
+       { "intr",       ~NFS_MOUNT_INTR,        NFS_MOUNT_INTR },
+       { "nointr",     ~NFS_MOUNT_INTR,        0 },
+       { "posix",      ~NFS_MOUNT_POSIX,       NFS_MOUNT_POSIX },
+       { "noposix",    ~NFS_MOUNT_POSIX,       0 },
+       { "cto",        ~NFS_MOUNT_NOCTO,       0 },
+       { "nocto",      ~NFS_MOUNT_NOCTO,       NFS_MOUNT_NOCTO },
+       { "ac",         ~NFS_MOUNT_NOAC,        0 },
+       { "noac",       ~NFS_MOUNT_NOAC,        NFS_MOUNT_NOAC },
+       { NULL,         0,                      0 }};
+
+
+unsigned long get_address (char **str)
+{
+   unsigned long l;
+   unsigned int val;
+   int i;
+   
+   l = 0;
+   for (i = 0; i < 4; i++) 
+     {
+       
+       l <<= 8;
+       if (**str != '\0')
+         {
+            
+            val = 0;
+            while (**str != '\0' && **str != '.' && **str != ':')
+              {
+                 
+                 val *= 10;
+                 val += **str - '0';
+                 (*str)++;
+              }
+            
+            l |= val;
+            if (**str != '\0') 
+              (*str)++;
+         }
+       
+     }
+   
+   return(htonl(l));
+}
+
+/*
+ * Prepare the NFS data structure and parse any options
+ */
+static int root_nfs_parse(char *name)
+{
+  char buf[NFS_MAXPATHLEN];
+  char *cp, *options, *val;
+
+  /* get the host ip number */
+  if (*name >= '0' && *name <= '9'){
+    server.sin_addr.s_addr = get_address (&name);
+  }
+  /* Setup the server hostname */
+  cp = in_ntoa(server.sin_addr.s_addr);
+  strncpy(nfs_data.hostname, cp, 255);
+  nfs_data.addr = server;
+
+  /* Set the name of the directory to mount */
+  cp = in_ntoa(myaddr.sin_addr.s_addr);
+  strncpy(buf, name, 255);
+  if ((options = strchr(buf, ',')))
+       *options++ = '\0';
+  if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+       printk(KERN_ERR "NFS: Pathname for remote directory too long\n");
+       return -1;
+  }
+  sprintf(nfs_path, buf, cp);
+
+  /* Set some default values */
+  nfs_port          = -1;
+  nfs_data.version  = NFS_MOUNT_VERSION;
+  nfs_data.flags    = 0;
+  nfs_data.rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
+  nfs_data.wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE;
+  nfs_data.timeo    = 7;
+  nfs_data.retrans  = 3;
+  nfs_data.acregmin = 3;
+  nfs_data.acregmax = 60;
+  nfs_data.acdirmin = 30;
+  nfs_data.acdirmax = 60;
+
+  /* Process any options */
+  if (options) {
+       cp = strtok(options, ",");
+       while (cp) {
+               if ((val = strchr(cp, '='))) {
+                       struct nfs_int_opts *opts = root_int_opts;
+                       *val++ = '\0';
+                       while (opts->name && strcmp(opts->name, cp))
+                               opts++;
+                       if (opts->name)
+                               *(opts->val) = (int) simple_strtoul(val, NULL, 10);
+               } else {
+                       struct nfs_bool_opts *opts = root_bool_opts;
+                       while (opts->name && strcmp(opts->name, cp))
+                               opts++;
+                       if (opts->name) {
+                               nfs_data.flags &= opts->and_mask;
+                               nfs_data.flags |= opts->or_mask;
+                       }
+               }
+               cp = strtok(NULL, ",");
+       }
+  }
+
+  return 0;
+}
+
+
+/*
+ * Tell the user what's going on.
+ */
+static void root_nfs_print(void)
+{
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Mounting %s on server %s as root\n",
+               nfs_path, nfs_data.hostname);
+  printk(KERN_NOTICE "NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+               nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
+  printk(KERN_NOTICE "NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+               nfs_data.acregmin, nfs_data.acregmax,
+               nfs_data.acdirmin, nfs_data.acdirmax);
+  printk(KERN_NOTICE "NFS:     port = %d, flags = %08x\n",
+               nfs_port, nfs_data.flags);
+#endif
+}
+
+
+/*
+ * Set the interface address and configure a route to the server.
+ */
+static void root_nfs_setup(void)
+{
+  struct rtentry server_route;
+  struct sockaddr_in *sin;
+
+  /* Setup the device correctly */
+  root_dev->family     = myaddr.sin_family;
+  root_dev->pa_addr    = myaddr.sin_addr.s_addr;
+  root_dev->pa_mask    = ip_get_mask(myaddr.sin_addr.s_addr);
+  root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
+  root_dev->pa_dstaddr = 0;
+
+  sin=(struct sockaddr_in *)&server_route.rt_dst;
+  *sin=server;
+  sin=(struct sockaddr_in *)&server_route.rt_genmask;
+  sin->sin_family=AF_INET;
+  sin->sin_addr.s_addr= root_dev->pa_mask;
+  server_route.rt_dev[0]=0;
+  server_route.rt_flags=RTF_HOST|RTF_UP;
+  
+  /* Now add a route to the server */
+  if(ip_rt_new(&server_route)==-1)
+       printk("Unable to add NFS server route.\n");
+}
+
+
+/*
+ * Get the necessary IP addresses and prepare for mounting the required
+ * NFS filesystem.
+ */
+int nfs_root_init(char *nfsname)
+{
+  /* Initialize network device and get local and server IP address */
+  if (do_rarp() < 0)
+       return -1;
+
+  /* Initialize the global variables necessary for NFS. The server
+   * directory is actually mounted after init() has been started.
+   */
+  if (root_nfs_parse(nfsname) < 0)
+       return -1;
+  root_nfs_print();
+  root_nfs_setup();
+  return 0;
+}
+
+
+
+
+/***************************************************************************
+
+               Routines to actually mount the root directory
+
+ ***************************************************************************/
+
+static struct file nfs_file;           /* File descriptor containing socket */
+static struct inode nfs_inode;         /* Inode containing socket */
+static int *rpc_packet = NULL;         /* RPC packet */
+
+extern asmlinkage int sys_socketcall(int call, unsigned long *args);
+extern struct socket *socki_lookup(struct inode *inode);
+
+
+/*
+ * Open a UDP socket.
+ */
+static int root_nfs_open(void)
+{
+  struct file *filp;
+  unsigned long opt[] = { AF_INET, SOCK_DGRAM, IPPROTO_UDP };
+
+  /* Open the socket */
+  if ((nfs_data.fd = sys_socketcall(SYS_SOCKET, opt)) < 0) {
+       printk(KERN_ERR "NFS: Cannot open UDP socket\n");
+       return -1;
+  }
+
+  /* Copy the file and inode data area so that we can remove the
+   * file lateron without killing the socket. After all this the
+   * closing routine just needs to remove the file pointer from
+   * the init-task descriptor.
+   */
+  filp = current->files->fd[nfs_data.fd];
+  memcpy(&nfs_file, filp, sizeof(struct file));
+  nfs_file.f_next = nfs_file.f_prev = NULL;
+  current->files->fd[nfs_data.fd] = &nfs_file;
+  filp->f_count = 0;           /* Free the file descriptor */
+
+  memcpy(&nfs_inode, nfs_file.f_inode, sizeof(struct inode));
+  nfs_inode.i_hash_next = nfs_inode.i_hash_prev = NULL;
+  nfs_inode.i_next = nfs_inode.i_prev = NULL;
+  clear_inode(nfs_file.f_inode);
+  nfs_file.f_inode = &nfs_inode;
+  nfs_inode.u.socket_i.inode = &nfs_inode;
+  nfs_file.private_data = NULL;
+
+  return 0;
+}
+
+
+/*
+ * Close the UDP file descriptor. The main part of preserving the socket
+ * has already been done after opening it. Now we have to remove the
+ * file descriptor from the init task.
+ */
+static void root_nfs_close(int close_all)
+{
+  /* Remove the file from the list of open files */
+  current->files->fd[nfs_data.fd] = NULL;
+  if (current->files->count > 0)
+       current->files->count--;
+
+  /* Clear memory use by the RPC packet */
+  if (rpc_packet != NULL)
+       kfree_s(rpc_packet, nfs_data.wsize + 1024);
+
+  /* In case of an error we also have to close the socket again (sigh) */
+  if (close_all) {
+       nfs_inode.u.socket_i.inode = NULL;      /* The inode is already cleared */
+       if (nfs_file.f_op->release)
+               nfs_file.f_op->release(&nfs_inode, &nfs_file);
+  }
+}
+
+
+/*
+ * Find a suitable listening port and bind to it
+ */
+static int root_nfs_bind(void)
+{
+  int res = -1;
+  short port = STARTPORT;
+  struct sockaddr_in *sin = &myaddr;
+  int i;
+
+  if (nfs_inode.u.socket_i.ops->bind) {
+       for (i = 0; i < NPORTS && res < 0; i++) {
+               sin->sin_port = htons(port++);
+               if (port > ENDPORT) {
+                       port = STARTPORT;
+               }
+               res = nfs_inode.u.socket_i.ops->bind(&nfs_inode.u.socket_i,
+                       (struct sockaddr *) sin, sizeof(struct sockaddr_in));
+       }
+  }
+  if (res < 0) {
+       printk(KERN_ERR "NFS: Cannot find a suitable listening port\n");
+       root_nfs_close(1);
+       return -1;
+  }
+
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Binding to listening port %d\n", port);
+#endif
+  return 0;
+}
+
+
+/*
+ * Send an RPC request and wait for the answer
+ */
+static int *root_nfs_call(int *end)
+{
+  struct file *filp;
+  struct socket *sock;
+  int dummylen;
+  static struct nfs_server s =
+       { &nfs_file, /* struct file * */
+         0,         /* struct rsock * */
+         {
+            0, "",
+         },         /* toaddr */
+          0,         /* lock */
+         NULL,      /* wait queue */
+         NFS_MOUNT_SOFT, /* flags */
+         0, 0,           /* rsize, wsize */
+         0,              /* timeo */
+         0,              /* retrans */
+         3*HZ, 60*HZ, 30*HZ, 60*HZ, "\0" };
+
+  filp = &nfs_file;
+  sock = &((filp->f_inode)->u.socket_i);
+    
+  /* extract the other end of the socket into server->toaddr */
+  sock->ops->getname(sock, &(s.toaddr), &dummylen, 1) ;
+  ((struct sockaddr_in *) &s.toaddr)->sin_port   = server.sin_port;
+  ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
+  ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
+  
+  s.rsock = rpc_makesock(filp);
+  s.flags = nfs_data.flags;
+  s.rsize = nfs_data.rsize;
+  s.wsize = nfs_data.wsize;
+  s.timeo = nfs_data.timeo * HZ / 10;
+  s.retrans = nfs_data.retrans;
+  strcpy(s.hostname, nfs_data.hostname);
+
+  /* First connect the UDP socket to a server port, then send the packet
+   * out, and finally check wether the answer is OK.
+   */
+  if (nfs_inode.u.socket_i.ops->connect &&
+      nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+               (struct sockaddr *) &server, sizeof(struct sockaddr_in),
+               nfs_file.f_flags) < 0)
+       return NULL;
+  if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
+       return NULL;
+  return rpc_verify(rpc_packet);
+}
+
+
+/*
+ * Create an RPC packet header
+ */
+static int *root_nfs_header(int proc, int program, int version)
+{
+  int groups[] = { 0, NOGROUP };
+
+  if (rpc_packet == NULL) {
+       if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
+               printk(KERN_ERR "NFS: Cannot allocate UDP buffer\n");
+               return NULL;
+       }
+  }
+  strcpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr));
+  return rpc_header(rpc_packet, proc, program, version, 0, 0, groups);
+}
+
+
+/*
+ * Query server portmapper for the port of a daemon program
+ */
+static int root_nfs_get_port(int program, int version)
+{
+  int *p;
+
+  /* Prepare header for portmap request */
+  server.sin_port = htons(NFS_PMAP_PORT);
+  p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
+  if (!p)
+       return -1;
+
+  /* Set arguments for portmapper */
+  *p++ = htonl(program);
+  *p++ = htonl(version);
+  *p++ = htonl(IPPROTO_UDP);
+  *p++ = 0;
+
+  /* Send request to server portmapper */
+  if ((p = root_nfs_call(p)) == NULL)
+       return -1;
+
+  return ntohl(*p);
+}
+
+
+/*
+ * Get portnumbers for mountd and nfsd from server
+ */
+static int root_nfs_ports(void)
+{
+  int port;
+
+  if (nfs_port < 0) {
+       if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
+               printk(KERN_ERR "NFS: Unable to get nfsd port number from server, using default\n");
+               port = NFS_NFS_PORT;
+       }
+       nfs_port = port;
+#ifdef NFSROOT_DEBUG
+       printk(KERN_NOTICE "NFS: Portmapper on server returned %d as nfsd port\n", port);
+#endif
+  }
+
+  if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
+       printk(KERN_ERR "NFS: Unable to get mountd port number from server, using default\n");
+       port = NFS_MOUNT_PORT;
+  }
+  server.sin_port = htons(port);
+#ifdef NFSROOT_DEBUG
+  printk(KERN_NOTICE "NFS: Portmapper on server returned %d as mountd port\n", port);
+#endif
+
+  return 0;
+}
+
+
+/*
+ * Get a file handle from the server for the directory which is to be mounted
+ */
+static int root_nfs_get_handle(void)
+{
+  int len, status, *p;
+
+  /* Prepare header for mountd request */
+  p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
+  if (!p) {
+       root_nfs_close(1);
+       return -1;
+  }
+
+  /* Set arguments for mountd */
+  len = strlen(nfs_path);
+  *p++ = htonl(len);
+  memcpy(p, nfs_path, len);
+  len = (len + 3) >> 2;
+  p[len] = 0;
+  p += len;
+
+  /* Send request to server portmapper */
+  if ((p = root_nfs_call(p)) == NULL) {
+       root_nfs_close(1);
+       return -1;
+  }
+
+  status = ntohl(*p++);
+  if (status == 0) {
+       nfs_data.root = *((struct nfs_fh *) p);
+  } else {
+       printk(KERN_ERR "NFS: Server returned error %d while mounting %s\n",
+                                                       status, nfs_path);
+       root_nfs_close(1);
+       return -1;
+  }
+
+  return 0;
+}
+
+
+/*
+ * Now actually mount the given directory
+ */
+static int root_nfs_do_mount(struct super_block *sb)
+{
+  /* First connect to the nfsd port on the server */
+  server.sin_port = htons(nfs_port);
+  nfs_data.addr = server;
+  if (nfs_inode.u.socket_i.ops->connect &&
+      nfs_inode.u.socket_i.ops->connect(&nfs_inode.u.socket_i,
+               (struct sockaddr *) &server, sizeof(struct sockaddr_in),
+               nfs_file.f_flags) < 0) {
+       root_nfs_close(1);
+       return -1;
+  }
+
+  /* Now (finally ;-)) read the super block for mounting */
+  if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
+       root_nfs_close(1);
+       return -1;
+  }
+
+  return 0;
+}
+
+
+/*
+ * Get the NFS port numbers and file handle, and then read the super-
+ * block for mounting.
+ */
+int nfs_root_mount(struct super_block *sb)
+{
+  if (root_nfs_open() < 0)
+       return -1;
+  if (root_nfs_bind() < 0)
+       return -1;
+  if (root_nfs_ports() < 0)
+       return -1;
+  if (root_nfs_get_handle() < 0)
+       return -1;
+  if (root_nfs_do_mount(sb) < 0)
+       return -1;
+  root_nfs_close(0);
+  return 0;
+}
index ce593760bd6a48b737630b5b85f398fca87cf30a..72f7b05f5f7100dea05138700c7742100eeab630 100644 (file)
@@ -818,7 +818,8 @@ retry:
  * Here are a few RPC-assist functions.
  */
 
-static int *nfs_rpc_header(int *p, int procedure, int ruid)
+int *rpc_header(int *p, int procedure, int program, int version,
+                                       int uid, int gid, int *groups)
 {
        int *p1, *p2;
        int i;
@@ -832,18 +833,18 @@ static int *nfs_rpc_header(int *p, int procedure, int ruid)
        *p++ = htonl(++xid);
        *p++ = htonl(RPC_CALL);
        *p++ = htonl(RPC_VERSION);
-       *p++ = htonl(NFS_PROGRAM);
-       *p++ = htonl(NFS_VERSION);
+       *p++ = htonl(program);
+       *p++ = htonl(version);
        *p++ = htonl(procedure);
        *p++ = htonl(RPC_AUTH_UNIX);
        p1 = p++;
        *p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
        p = xdr_encode_string(p, (char *) sys);
-       *p++ = htonl(ruid ? current->uid : current->fsuid);
-       *p++ = htonl(current->egid);
+       *p++ = htonl(uid);
+       *p++ = htonl(gid);
        p2 = p++;
-       for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++)
-               *p++ = htonl(current->groups[i]);
+       for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++)
+               *p++ = htonl(groups[i]);
        *p2 = htonl(i);
        *p1 = htonl((p - (p1 + 1)) << 2);
        *p++ = htonl(RPC_AUTH_NULL);
@@ -851,7 +852,16 @@ static int *nfs_rpc_header(int *p, int procedure, int ruid)
        return p;
 }
 
-static int *nfs_rpc_verify(int *p)
+
+static int *nfs_rpc_header(int *p, int procedure, int ruid)
+{
+       return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
+                       (ruid ? current->uid : current->fsuid),
+                       current->egid, current->groups);
+}
+
+
+int *rpc_verify(int *p)
 {
        unsigned int n;
 
@@ -882,7 +892,14 @@ static int *nfs_rpc_verify(int *p)
        }
        return p;
 }
-       
+
+
+static int *nfs_rpc_verify(int *p)
+{
+       return rpc_verify(p);
+}
+
+
 /*
  * We need to translate between nfs status return values and
  * the local errno values which may not be the same.
index e6ae81ec451db8e677a46757eb9eb583d9c66238..245a15cda3271a5cb361123e421495446015be6b 100644 (file)
@@ -6,6 +6,8 @@
 
 /*
  * super.c contains code to handle the super-block tables.
+ *
+ * GK 2/5/95  -  Changed to support mounting the root fs via NFS
  */
 #include <stdarg.h>
 
@@ -34,6 +36,10 @@ struct super_block super_blocks[NR_SUPER];
 
 static int do_remount_sb(struct super_block *sb, int flags, char * data);
 
+#ifdef CONFIG_ROOT_NFS
+extern int nfs_root_mount(struct super_block *sb);
+#endif
+
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
 
@@ -677,6 +683,35 @@ void mount_root(void)
        int retval;
 
        memset(super_blocks, 0, sizeof(super_blocks));
+#ifdef CONFIG_ROOT_NFS
+       if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
+               ROOT_DEV = 0;
+               if ((fs_type = get_fs_type("nfs"))) {
+                       sb = &super_blocks[0];
+                       sb->s_dev = get_unnamed_dev();
+                       sb->s_flags = root_mountflags & ~MS_RDONLY;
+                       if (nfs_root_mount(sb) >= 0) {
+                               inode = sb->s_mounted;
+                               inode->i_count += 3 ;
+                               sb->s_covered = inode;
+                               sb->s_rd_only = 0;
+                               sb->s_dirt = 0;
+                               sb->s_type = fs_type;
+                               current->fs->pwd = inode;
+                               current->fs->root = inode;
+                               ROOT_DEV = sb->s_dev;
+                               printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+                               return;
+                       }
+                       sb->s_dev = 0;
+               }
+               if (!ROOT_DEV) {
+                       printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
+                       ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+               }
+       }
+#endif
+
 #ifdef CONFIG_BLK_DEV_FD
        if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
                printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
index 22f4a20bb2253c3b318f314641fe5db9d820e61c..c75f20ade6b9311292efbf94a07860448f39402a 100644 (file)
@@ -43,8 +43,8 @@
  * Direction bits.
  */
 #define _IOC_NONE      0U
-#define _IOC_READ      1U
-#define _IOC_WRITE     2U
+#define _IOC_WRITE     1U
+#define _IOC_READ      2U
 
 #define _IOC(dir,type,nr,size) \
        (((dir)  << _IOC_DIRSHIFT) | \
index 32b599b752018cacfc806ccf6b7abf71cc9002d8..bd8a1f73c8adf1fc8e38db9af5180570b1067ba1 100644 (file)
@@ -239,7 +239,7 @@ extern __inline int smp_processor_id(void)
  *     processes are run.
  */
  
-#define PROC_CHANGE_PENALTY                  /* Schedule penalty */
+#define PROC_CHANGE_PENALTY    20              /* Schedule penalty */
 
 #endif
 #endif
index 263ded7866996f2f1f96e656d5e7cd49a489456d..f57398e533234b10418fadf91ab325694c146f54 100644 (file)
@@ -24,7 +24,7 @@
 
 /*
  * Error values that may be returned by the PCI bios.  Use
- * pci_strbioserr() to convert to a printable string.
+ * pcibios_strerror() to convert to a printable string.
  */
 #define PCIBIOS_SUCCESSFUL             0x00
 #define PCIBIOS_FUNC_NOT_SUPPORTED     0x81
index 2d12b8dc3c212de286dfca659e2ef8f8e985e91b..6b01e096d939906b52731e256b12c6dce151cab0 100644 (file)
@@ -5,8 +5,17 @@
  * Randolph Bentson <bentson@grieg.seaslug.org>.
  *
  * This file contains the general definitions for the cyclades.c driver
+ *$Log: cyclades.h,v $
+ * Revision 1.5  1995/11/13  21:13:31  bentson
+ * changes suggested by Michael Chastain <mec@duracef.shout.net>
+ * to support use of this file in non-kernel applications
+ *
+ *
  */
 
+#ifndef _LINUX_CYCLADES_H
+#define _LINUX_CYCLADES_H
+
 /* PCI vendor and device ID's */
 
 #ifndef PCI_VENDOR_ID_CYCLADES
 #define        PCI_DEVICE_ID_CYCLOMY   0x0100
 #endif
 
+struct cyclades_monitor {
+        unsigned long           int_count;
+        unsigned long           char_count;
+        unsigned long           char_max;
+        unsigned long           char_last;
+};
+
+#define CYCLADES_MAGIC  0x4359
+
+#define CYGETMON                0x435901
+#define CYGETTHRESH             0x435902
+#define CYSETTHRESH             0x435903
+#define CYGETDEFTHRESH          0x435904
+#define CYSETDEFTHRESH          0x435905
+#define CYGETTIMEOUT            0x435906
+#define CYSETTIMEOUT            0x435907
+#define CYGETDEFTIMEOUT         0x435908
+#define CYSETDEFTIMEOUT         0x435909
+
+#ifdef __KERNEL__
+
 /* Per card data structure */
 
 struct cyclades_card {
@@ -31,13 +61,6 @@ struct cyclades_chip {
   int filler;
 };
 
-struct cyclades_monitor {
-        unsigned long           int_count;
-        unsigned long           char_count;
-        unsigned long           char_max;
-        unsigned long           char_last;
-};
-
 /*
  * This is our internal structure for each serial port's state.
  * 
@@ -47,9 +70,6 @@ struct cyclades_monitor {
  * For definitions of the flags field, see tty.h
  */
 
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
 struct cyclades_port {
        int                     magic;
        int                     type;
@@ -87,18 +107,6 @@ struct cyclades_port {
         struct cyclades_monitor mon;
 };
 
-#define CYCLADES_MAGIC  0x4359
-
-#define CYGETMON                0x435901
-#define CYGETTHRESH             0x435902
-#define CYSETTHRESH             0x435903
-#define CYGETDEFTHRESH          0x435904
-#define CYSETDEFTHRESH          0x435905
-#define CYGETTIMEOUT            0x435906
-#define CYSETTIMEOUT            0x435907
-#define CYGETDEFTIMEOUT         0x435908
-#define CYSETDEFTIMEOUT         0x435909
-
 /*
  * Events are used to schedule things to happen at timer-interrupt
  * time, instead of at cy interrupt time.
@@ -281,3 +289,6 @@ struct cyclades_port {
 #define CyMAX_CHAR_FIFO        12
 
 /***************************************************************************/
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CYCLADES_H */
index c5f0d489d23b5351cf137a81e64fbed342117fb6..9f8b97ccfc66d2eb4dc63a97bebf5552fbf5f992 100644 (file)
@@ -34,7 +34,9 @@ extern int            eth_header(struct sk_buff *skb, struct device *dev,
 extern int             eth_rebuild_header(void *buff, struct device *dev,
                        unsigned long dst, struct sk_buff *skb);
 extern unsigned short  eth_type_trans(struct sk_buff *skb, struct device *dev);
-extern void            eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr);
+extern void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
+                                 unsigned short htype, __u32 daddr);
+extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
 extern void            eth_copy_and_sum(struct sk_buff *dest,
                                unsigned char *src, int length, int base);
 extern struct device   * init_etherdev(struct device *, int);
index 57af1a48f8a2cebd8c7b97532f455af48d9132dd..1536f6c645db242e5bbadaf42c021558be86bccd 100644 (file)
@@ -496,8 +496,18 @@ extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]);
 extern void ll_rw_page(int rw, kdev_t dev, unsigned long nr, char * buffer);
 extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer);
 extern int is_read_only(kdev_t dev);
-extern void brelse(struct buffer_head * buf);
-extern void bforget(struct buffer_head * buf);
+extern void __brelse(struct buffer_head *buf);
+extern inline void brelse(struct buffer_head *buf)
+{
+       if (buf)
+               __brelse(buf);
+}
+extern void __bforget(struct buffer_head *buf);
+extern inline void bforget(struct buffer_head *buf)
+{
+       if (buf)
+               __bforget(buf);
+}
 extern void set_blocksize(kdev_t dev, int size);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
 extern unsigned long bread_page(unsigned long addr,kdev_t dev,int b[],int size,int no_share);
index 0b7dd484214d3b80998355399763b7dd2496472d..34e4c78c8f7b7da2a82d5c74875e141def045f4f 100644 (file)
@@ -41,6 +41,8 @@
 #define ARPHRD_PPP     512
 #define ARPHRD_TUNNEL  768             /* IPIP tunnel                  */
 #define ARPHRD_TUNNEL6 769             /* IPIP6 tunnel                 */
+#define ARPHRD_FRAD    770             /* Frame Relay                  */
+#define ARPHRD_SKIP    771             /* SKIP vif                     */
 
 /* ARP protocol opcodes. */
 #define        ARPOP_REQUEST   1               /* ARP request                  */
@@ -55,6 +57,14 @@ struct arpreq {
   struct sockaddr      arp_ha;         /* hardware address             */
   int                  arp_flags;      /* flags                        */
   struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
+  char                 arp_dev[16];
+};
+
+struct arpreq_old {
+  struct sockaddr      arp_pa;         /* protocol address             */
+  struct sockaddr      arp_ha;         /* hardware address             */
+  int                  arp_flags;      /* flags                        */
+  struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */
 };
 
 /* ARP Flag values. */
index 2823b3f607cd3123d207ef79a461831995f420d1..922a2b98370a0f116d5c87448c6f687cc0404f41 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -50,6 +50,8 @@
 #define PPP_VERSION    "2.2.0"
 #define PPP_MAGIC      0x5002  /* Magic value for the ppp structure */
 #define PROTO_IPX      0x002b  /* protocol numbers */
+#define PROTO_DNA_RT    0x0027  /* DNA Routing */
+
 
 /*
  * Bit definitions for flags.
index 8a019adb6a16dcf423f3310c519737fdabf36edb..bbeb596e10778ad6148a6da8e8c47cf9a7d4bf1d 100644 (file)
@@ -26,7 +26,7 @@ extern int get_ioport_list(char *);
 
 #define HAVE_AUTOIRQ
 extern void *irq2dev_map[16];          /* Use only if you own the IRQ. */
-extern void autoirq_setup(int waittime);
+extern int autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 
 #endif /* _LINUX_PORTIO_H */
index c96d34f5898663e0c979f0ed7d35ba4da3fbbbf7..210d86867a2ce8feb8c23cda7a4b12b123437998 100644 (file)
 #define _IP_FW_H
 
 struct ip_fw 
+{
+       struct ip_fw  *fw_next;                 /* Next firewall on chain */
+       struct in_addr fw_src, fw_dst;          /* Source and destination IP addr */
+       struct in_addr fw_smsk, fw_dmsk;        /* Mask for src and dest IP addr */
+       struct in_addr fw_via;                  /* IP address of interface "via" */
+       unsigned short fw_flg;                  /* Flags word */
+       unsigned short fw_nsp, fw_ndp;          /* N'of src ports and # of dst ports */
+                                               /* in ports array (dst ports follow */
+                                               /* src ports; max of 10 ports in all; */
+                                               /* count of 0 means match all ports) */
+#define IP_FW_MAX_PORTS        10                      /* A reasonable maximum */
+       unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
+       unsigned long  fw_pcnt,fw_bcnt;         /* Packet and byte counters */
+       unsigned short fw_priority;             /* Revised packet priority */
+};
+
+struct ip_fw_old
 {
        struct ip_fw  *fw_next;                 /* Next firewall on chain */
        struct in_addr fw_src, fw_dst;          /* Source and destination IP addr */
index a5d9ccb9d8244dfaae0de3f7ff9a39eaf7bf11aa..bc68a82e13c11cca2602973f321e23423fc59e30 100644 (file)
@@ -50,6 +50,7 @@
  * 32 -                        philips/lms cm206 cdrom
  * 33 -                        ide2
  * 34 - z8530 driver           ide3
+ * 36 - netlink
  */
 
 #define UNNAMED_MAJOR  0
@@ -97,6 +98,7 @@
 #define CM206_CDROM_MAJOR 32
 #define IDE2_MAJOR     33
 #define IDE3_MAJOR     34
+#define NETLINK_MAJOR  36
 
 /*
  * Tests for SCSI devices.
index a9a9f0b708ce1cd116ddb9ff1fad720fe9163d4b..3c4e3c2f9f02c1fd9e7fd57880abfcefd66ef93f 100644 (file)
@@ -215,8 +215,6 @@ extern void rw_swap_page(int rw, unsigned long nr, char * buf);
 /* mmap.c */
 extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
        unsigned long prot, unsigned long flags, unsigned long off);
-extern struct vm_area_struct * find_vma (struct task_struct *, unsigned long);
-extern struct vm_area_struct * find_vma_intersection (struct task_struct *, unsigned long, unsigned long);
 extern void merge_segments(struct task_struct *, unsigned long, unsigned long);
 extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
 extern void remove_shared_vm_struct(struct vm_area_struct *);
@@ -244,6 +242,40 @@ extern unsigned long get_unmapped_area(unsigned long, unsigned long);
 
 #define GFP_LEVEL_MASK 0xf
 
+#define avl_empty      (struct vm_area_struct *) NULL
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
+{
+       struct vm_area_struct * result = NULL;
+       struct vm_area_struct * tree;
+
+       if (!task->mm)
+               return NULL;
+       for (tree = task->mm->mmap_avl ; ; ) {
+               if (tree == avl_empty)
+                       return result;
+               if (tree->vm_end > addr) {
+                       if (tree->vm_start <= addr)
+                               return tree;
+                       result = tree;
+                       tree = tree->vm_avl_left;
+               } else
+                       tree = tree->vm_avl_right;
+       }
+}
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+   NULL if none.  Assume start_addr < end_addr. */
+static inline struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+       struct vm_area_struct * vma;
+
+       vma = find_vma(task,start_addr);
+       if (!vma || end_addr <= vma->vm_start)
+               return NULL;
+       return vma;
+}
 
 /*
  * vm_ops not present page codes for shared memory.
index f0cbcf70dab2b7e9d99ee7149cc148f980e8fe07..8fbd37658c4754ef17a27d1dc457cf1879d471e7 100644 (file)
 #ifndef _LINUX_NETDEVICE_H
 #define _LINUX_NETDEVICE_H
 
+#include <linux/config.h>
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/skbuff.h>
 
-#include <linux/config.h>
-
 /* for future expansion when we will have different priorities. */
 #define DEV_NUMBUFFS   3
 #define MAX_ADDR_LEN   7
@@ -61,6 +60,19 @@ struct dev_mc_list
        unsigned short dmi_users;
 };
 
+struct hh_cache
+{
+       struct hh_cache *hh_next;
+       unsigned long   hh_refcnt;      /* number of users */
+       void            *hh_arp;        /* Opaque pointer, used by
+                                        * any address resolution module,
+                                        * not only ARP.
+                                        */
+       unsigned short  hh_type;        /* protocol identifier, f.e ETH_P_IP */
+       char            hh_uptodate;    /* hh_data is valid */
+       char            hh_data[16];    /* cached hardware header */
+};
+
 /*
  * The DEVICE structure.
  * Actually, this whole structure is a big mistake.  It mixes I/O
@@ -162,12 +174,14 @@ struct device
   void                   (*set_multicast_list)(struct device *dev,
                                         int num_addrs, void *addrs);
 #define HAVE_SET_MAC_ADDR               
-  int                    (*set_mac_address)(struct device *dev, struct sockaddr *addr);
+  int                    (*set_mac_address)(struct device *dev, void *addr);
 #define HAVE_PRIVATE_IOCTL
   int                    (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
 #define HAVE_SET_CONFIG
   int                    (*set_config)(struct device *dev, struct ifmap *map);
-  void                   (*header_cache)(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr);
+#define HAVE_HEADER_CACHE
+  void                   (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, unsigned short htype, __u32 daddr);
+  void                   (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
 };
 
 
@@ -201,6 +215,7 @@ extern struct device        *ip_dev_check(unsigned long daddr);
 extern unsigned long   ip_my_addr(void);
 extern unsigned long   ip_get_mask(unsigned long addr);
 extern struct device   *ip_dev_find(unsigned long addr);
+extern struct device    *dev_getbytype(unsigned short type);
 
 extern void            dev_add_pack(struct packet_type *pt);
 extern void            dev_remove_pack(struct packet_type *pt);
index 2ff70c025ac3baa188146c2c7f6edeac0a23527c..b30246c2ac1db4738aecedeb3c93b448ce788c12 100644 (file)
@@ -86,6 +86,9 @@ extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
                            int cookie, int count, struct nfs_entry *entry);
 extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
                            struct nfs_fsinfo *res);
+extern int *rpc_header(int *p, int procedure, int program, int version,
+                               int uid, int gid, int *groups);
+extern int *rpc_verify(int *p);
 
 /* linux/fs/nfs/sock.c */
 
index beb5f1dd845f4d9dea6568c07a6d45165dedfc2d..b122b43dc0b7d64cb2717b77d5d3ac0dd3b4a660 100644 (file)
  * but here they are anyway.
  */
 
-#define NFS_MOUNT_VERSION      1       /* current version */
+#define NFS_NFS_PROGRAM                100003  /* nfsd program number */
+#define NFS_NFS_VERSION                2       /* nfsd version */
+#define NFS_NFS_PORT           2049    /* portnumber on server for nfsd */
+
+#define NFS_MOUNT_PROGRAM      100005  /* mountd program number */
+#define NFS_MOUNT_VERSION      1       /* mountd version */
+#define NFS_MOUNT_PROC         1       /* mount process id */
+#define NFS_MOUNT_PORT         627     /* portnumber on server for mountd */
+
+#define NFS_PMAP_PROGRAM       100000  /* portmap program number */
+#define NFS_PMAP_VERSION       2       /* portmap version */
+#define NFS_PMAP_PROC          3       /* portmap getport id */
+#define NFS_PMAP_PORT          111     /* portnumber on server for portmap */
 
 struct nfs_mount_data {
        int version;                    /* 1 */
index 06df6d4a545d323784de2f04f79da7bde48f58ef..0c6c8063d35e2a08014da3fb28935721454c7ac7 100644 (file)
@@ -541,7 +541,6 @@ extern unsigned long pci_init (unsigned long mem_start, unsigned long mem_end);
 
 extern struct pci_dev_info *pci_lookup_dev (unsigned int vendor,
                                            unsigned int dev);
-extern const char *pci_strbioserr (int error);
 extern const char *pci_strclass (unsigned int class);
 extern const char *pci_strvendor (unsigned int vendor);
 extern const char *pci_strdev (unsigned int vendor, unsigned int device);
index bcc0b96c5c17210056dd5512c026f119a14352b5..9716ac786c1481e0c25a7452847ac47fbecee96a 100644 (file)
@@ -86,6 +86,7 @@ enum net_directory_inos {
        PROC_NET_NR_NEIGH,
        PROC_NET_NR,
        PROC_NET_SOCKSTAT,
+       PROC_NET_RTCACHE,
        PROC_NET_LAST
 };
 
index d067fe1734a6db073c5bb3ba45de4d9dde712f18..a93fa3068fd8663b3a2d24389aeaf7f521827992 100644 (file)
 /* end multicast support change */
 
 /* ARP cache control calls. */
-#define SIOCDARP       0x8950          /* delete ARP table entry       */
-#define SIOCGARP       0x8951          /* get ARP table entry          */
-#define SIOCSARP       0x8952          /* set ARP table entry          */
+#define OLD_SIOCDARP   0x8950          /* old delete ARP table entry   */
+#define OLD_SIOCGARP   0x8951          /* old get ARP table entry      */
+#define OLD_SIOCSARP   0x8952          /* old set ARP table entry      */
+#define SIOCDARP       0x8953          /* delete ARP table entry       */
+#define SIOCGARP       0x8954          /* get ARP table entry          */
+#define SIOCSARP       0x8955          /* set ARP table entry          */
 
 /* RARP cache control calls. */
 #define SIOCDRARP      0x8960          /* delete RARP table entry      */
index 521d20e923d5e84707949d3eadd257f22a10f5d3..5805203ea30ba4a098b67dc690eee88451d1e202 100644 (file)
 #ifndef _LINUX_TCP_H
 #define _LINUX_TCP_H
 
-
-#define HEADER_SIZE    128             /* maximum header size          */
-
-
 struct tcphdr {
        __u16   source;
        __u16   dest;
index 293e7c6a5d32852e6e0e71abd00f93babba379e5..db7a29c36f5b2fe7bfabe2cf9d10352e170f36f0 100644 (file)
@@ -3,19 +3,15 @@
 #define _ARP_H
 
 extern void    arp_init(void);
-extern void    arp_destroy(u32 paddr, int force);
-extern void    arp_device_down(struct device *dev);
 extern int     arp_rcv(struct sk_buff *skb, struct device *dev,
                        struct packet_type *pt);
-extern int     arp_query(unsigned char *haddr, u32 paddr, unsigned short type);
+extern int     arp_query(unsigned char *haddr, u32 paddr, struct device *dev);
 extern int     arp_find(unsigned char *haddr, u32 paddr,
                struct device *dev, u32 saddr, struct sk_buff *skb);
-extern int     arp_get_info(char *buffer, char **start, off_t origin, int length, int dummy);
 extern int     arp_ioctl(unsigned int cmd, void *arg);
 extern void     arp_send(int type, int ptype, u32 dest_ip, 
                         struct device *dev, u32 src_ip, 
-                        unsigned char *dest_hw, unsigned char *src_hw);
-extern int     arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev);
-
-extern unsigned long   arp_cache_stamp;
+                        unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
+extern int     arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr);
+extern int     arp_update_cache(struct hh_cache * hh);
 #endif /* _ARP_H */
index e23afaad310aba301c9bc42ee64282f405224b9f..88dbe05ad97d56d1ae03729811421c8846b0d688 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/socket.h>
 #include <linux/ip.h>
 #include <linux/netdevice.h>
+#include <net/route.h>
 
 #ifndef _SNMP_H
 #include <net/snmp.h>
@@ -84,13 +85,13 @@ struct ipq
 extern void            ip_print(const struct iphdr *ip);
 extern int             ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void            ip_route_check(__u32 daddr); 
-extern int             ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
+extern int             ip_send(struct rtable *rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
 extern int             ip_build_header(struct sk_buff *skb,
                                        __u32 saddr,
                                        __u32 daddr,
                                        struct device **dev, int type,
                                        struct options *opt, int len,
-                                       int tos,int ttl);
+                                       int tos,int ttl,struct rtable **rp);
 extern int             ip_rcv(struct sk_buff *skb, struct device *dev,
                               struct packet_type *pt);
 extern int             ip_options_echo(struct options * dopt, struct options * sopt,
index a785321a6017e055ea9c662fa78d7a5af9b60e1f..27db2d1a5f0dd46868079d4f091b988a160e39d4 100644 (file)
@@ -1,12 +1,12 @@
 #ifndef __NET_NETLINK_H
 #define __NET_NETLINK_H
 
-#include <linux/config.h>
-
 #define NET_MAJOR 18           /* Major 18 is reserved for networking          */
 #define MAX_LINKS 3            /* 18,0 for route updates, 18,1 for SKIP        */
 #define MAX_QBYTES 32768       /* Maximum bytes in the queue                   */
 
+#include <linux/config.h>
+
 extern int netlink_attach(int unit, int (*function)(struct sk_buff *skb));
 extern int netlink_donothing(struct sk_buff *skb);
 extern void netlink_detach(int unit);
index 0ca393b962dc32fdc3a0142a9e8c49a72cdc3f63..48001b0dc8d7b8efe4fdd33e5d8da7df8f77baaa 100644 (file)
  * Fixes:
  *             Alan Cox        :       Reformatted. Added ip_rt_local()
  *             Alan Cox        :       Support for TCP parameters.
+ *             Alexey Kuznetsov:       Major changes for new routing code.
+ *
+ *     FIXME:
+ *             Modules stuff is broken at the moment.
+ *             Make atomic ops more generic and hide them in asm/...
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 #ifndef _ROUTE_H
 #define _ROUTE_H
 
+/*
+ * 0 - no debugging messages
+ * 1 - rare events and bugs situations (default)
+ * 2 - trace mode.
+ */
+#define RT_CACHE_DEBUG         1
+
+#define RT_HASH_DIVISOR                256
+#define RT_CACHE_SIZE_MAX      256
+
+#define RTZ_HASH_DIVISOR       256
+
+#if RT_CACHE_DEBUG >= 2
+#define RTZ_HASHING_LIMIT 0
+#else
+#define RTZ_HASHING_LIMIT 16
+#endif
+
+/*
+ * Maximal time to live for unused entry.
+ */
+#define RT_CACHE_TIMEOUT               (HZ*300)
+
+/*
+ * Prevents LRU trashing, entries considered equivalent,
+ * if the difference between last use times is less then this number.
+ */
+#define RT_CACHE_BUBBLE_THRESHOULD     (HZ*5)
 
 #include <linux/route.h>
 
+#ifdef __KERNEL__
+#define RTF_LOCAL 0x8000
+#endif
+
+/*
+ * Semaphores.
+ */
+
+#ifdef __i386__
+#include <asm/bitops.h>
+
+extern __inline__ void ATOMIC_INCR(void * addr)
+{
+       __asm__ __volatile__(
+               "incl %0"
+               :"=m" (ADDR));
+}
+
+extern __inline__ void ATOMIC_DECR(void * addr)
+{
+       __asm__ __volatile__(
+               "decl %0"
+               :"=m" (ADDR));
+}
+
+/*
+ * It is DECR that is ATOMIC, not CHECK!
+ * If you want to do atomic checks, use cli()/sti(). --ANK
+ */
+
+extern __inline__ unsigned long ATOMIC_DECR_AND_CHECK(void * addr)
+{
+       unsigned long retval;
+       __asm__ __volatile__(
+               "decl %0\nmovl %0,%1"
+               : "=m" (ADDR), "=r"(retval));
+       return retval;
+}
+
+
+#else
+
+static __inline__ void ATOMIC_INCR(void * addr)
+{
+       (*(__volatile__ unsigned long*)addr)++;
+}
+
+static __inline__ void ATOMIC_DECR(void * addr)
+{
+       (*(__volatile__ unsigned long*)addr)--;
+}
+
+static __inline__ int ATOMIC_DECR_AND_CHECK (void * addr)
+{
+       ATOMIC_DECR(addr);
+       return *(volatile unsigned long*)addr;
+}
+
+#endif
+
+
 
-/* This is an entry in the IP routing table. */
 struct rtable 
 {
        struct rtable           *rt_next;
        __u32                   rt_dst;
-       __u32                   rt_mask;
+       __u32                   rt_src;
        __u32                   rt_gateway;
-       unsigned short          rt_flags;
-       short                   rt_metric;
-       unsigned int            rt_refcnt;
+       unsigned long           rt_refcnt;
        unsigned long           rt_use;
-       unsigned short          rt_mss;
-       unsigned short          rt_irtt;
        unsigned long           rt_window;
+       unsigned long           rt_lastuse;
+       struct hh_cache         *rt_hh;
        struct device           *rt_dev;
+       unsigned short          rt_flags;
+       unsigned short          rt_mtu;
+       unsigned short          rt_irtt;
+       unsigned char           rt_tos;
 };
 
-
 extern void            ip_rt_flush(struct device *dev);
-extern void            ip_rt_add(short flags, __u32 addr, __u32 mask,
-                              __u32 gw, struct device *dev, unsigned short mss, unsigned long window, unsigned short irtt, short metric);
-extern struct rtable   *ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr);
-extern struct rtable   *ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr);
+extern void            ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
+extern struct rtable   *ip_rt_slow_route(__u32 daddr, int local);
 extern int             rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
+extern int             rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int             ip_rt_ioctl(unsigned int cmd, void *arg);
+extern int             ip_rt_new(struct rtentry *rt);
+extern void            ip_rt_check_expire(void);
+extern void            ip_rt_advice(struct rtable **rp, int advice);
+
+extern void            ip_rt_run_bh(void);
+extern int             ip_rt_lock;
+extern unsigned                ip_rt_bh_mask;
+extern struct rtable   *ip_rt_hash_table[RT_HASH_DIVISOR];
+
+extern __inline__ void ip_rt_fast_lock(void)
+{
+       ATOMIC_INCR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_fast_unlock(void)
+{
+       ATOMIC_DECR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_unlock(void)
+{
+       if (!ATOMIC_DECR_AND_CHECK(&ip_rt_lock) && ip_rt_bh_mask)
+               ip_rt_run_bh();
+}
+
+extern __inline__ unsigned ip_rt_hash_code(__u32 addr)
+{
+       unsigned tmp = addr + (addr>>16);
+       return (tmp + (tmp>>8)) & 0xFF;
+}
+
+
+extern __inline__ void ip_rt_put(struct rtable * rt)
+#ifndef MODULE
+{
+       if (rt)
+               ATOMIC_DECR(&rt->rt_refcnt);
+}
+#else
+;
+#endif
+
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+#ifndef MODULE
+{
+       struct rtable * rth;
+
+       ip_rt_fast_lock();
+
+       for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
+       {
+               if (rth->rt_dst == daddr)
+               {
+                       rth->rt_lastuse = jiffies;
+                       ATOMIC_INCR(&rth->rt_use);
+                       ATOMIC_INCR(&rth->rt_refcnt);
+                       ip_rt_unlock();
+                       return rth;
+               }
+       }
+       return ip_rt_slow_route (daddr, local);
+}
+#else
+;
+#endif
+
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
+                                                      __u32 daddr, int local)
+{
+       struct rtable * rt = *rp;
+
+       if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+           || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
+       {
+               ip_rt_put(rt);
+               rt = ip_rt_route(daddr, local);
+               *rp = rt;
+       }
+       return rt;
+}      
 
-extern unsigned long   rt_stamp;
 
 #endif /* _ROUTE_H */
index 5964051d684b4b4cdb592a3834a491a92c7c7b7b..cb3d5f1e2634ae54d9a29ceb2ac88ee98decfa85 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/tcp.h>         /* struct tcphdr */
 #include <linux/config.h>
 
+#include <linux/netdevice.h>
 #include <linux/skbuff.h>      /* struct sk_buff */
 #include <net/protocol.h>              /* struct inet_protocol */
 #ifdef CONFIG_AX25
@@ -226,19 +227,6 @@ struct sock
        struct timer_list       ack_timer;              /* TCP delayed ack timer */
        int                     ip_xmit_timeout;        /* Why the timeout is running */
        struct rtable           *ip_route_cache;        /* Cached output route */
-       unsigned long           ip_route_stamp;         /* Route cache stamp */
-       unsigned long           ip_route_daddr;         /* Target address */
-       unsigned long           ip_route_saddr;         /* Source address */
-       int                     ip_route_local;         /* State of locality flag */
-       unsigned long           ip_hcache_stamp;        /* Header cache stamp */
-       unsigned long           *ip_hcache_ver;         /* Pointer to version of cache */
-       char                    ip_hcache_data[16];     /* Cached header */
-       int                     ip_hcache_state;        /* Have we a cached header */
-       unsigned char           ip_option_len;          /* Length of IP options */
-       unsigned char           ip_option_flen;         /* Second fragment option length */
-       unsigned char           ip_opt_next_strict;     /* Next hop is strict route */
-       unsigned long           ip_opt_next_hop;        /* Next hop if forced */
-       unsigned char           *ip_opt_ptr[2];         /* IP option pointers */
        unsigned char           ip_hdrincl;             /* Include headers ? */
 #ifdef CONFIG_IP_MULTICAST  
        int                     ip_mc_ttl;              /* Multicasting TTL */
@@ -286,7 +274,7 @@ struct proto
                                        __u32 daddr,
                                        struct device **dev, int type,
                                        struct options *opt, int len,
-                                       int tos, int ttl);
+                                       int tos, int ttl, struct rtable ** rp);
        int                     (*connect)(struct sock *sk,
                                        struct sockaddr_in *usin, int addr_len);
        struct sock *           (*accept) (struct sock *sk, int flags);
index 1b6c90be44381a86b3603fd69d0219de2ff5fa24..8ef1fb3a55005ab032bfd471b51eacf85dc9c27b 100644 (file)
@@ -2,6 +2,8 @@
  *  linux/init/main.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  GK 2/5/95  -  Changed to support mounting root fs via NFS
  */
 
 #define __KERNEL_SYSCALLS__
@@ -26,6 +28,7 @@
 #include <linux/ioport.h>
 #include <linux/hdreg.h>
 #include <linux/mm.h>
+#include <linux/major.h>
 
 #include <asm/bugs.h>
 
@@ -61,7 +64,7 @@ extern void generic_NCR5380_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
-extern void buslogic_setup(char *str, int *ints);
+extern void BusLogic_Setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
@@ -118,6 +121,11 @@ int rows, cols;
 int ramdisk_size;
 int root_mountflags = MS_RDONLY;
 
+#ifdef CONFIG_ROOT_NFS
+char nfs_root_name[256] = { 0, };
+extern int nfs_root_init(char *name);
+#endif
+
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 
@@ -204,7 +212,7 @@ struct {
        { "aic7xxx=", aic7xxx_setup},
 #endif
 #ifdef CONFIG_SCSI_BUSLOGIC
-       { "buslogic=", buslogic_setup},
+       { "BusLogic=", BusLogic_Setup},
 #endif
 #ifdef CONFIG_SCSI_NCR53C406A
        { "ncr53c406a=", NCR53c406a_setup},
@@ -297,9 +305,8 @@ void calibrate_delay(void)
        int ticks;
        int loopbit;
        int lps_precision = LPS_PREC;
-#ifdef __SMP__
+
        loops_per_sec = (1<<12);
-#endif         
 
        printk("Calibrating delay loop.. ");
        while (loops_per_sec <<= 1) {
@@ -350,10 +357,14 @@ void calibrate_delay(void)
 static void parse_options(char *line)
 {
        char *next;
+#ifdef CONFIG_ROOT_NFS
+       char *devnames[] = { "nfs", "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL };
+       int devnums[]    = { 0x0FF, 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0};
+#else
        static const char *devnames[] = { "hda", "hdb", "hdc", "hdd", "sda", "sdb", "sdc", "sdd", "sde", "fd", "xda", "xdb", NULL };
        static int devnums[]    = { 0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0};
+#endif
        int args, envs;
-
        if (!*line)
                return;
        args = 0;
@@ -384,6 +395,20 @@ static void parse_options(char *line)
                        }
                        continue;
                }
+#ifdef CONFIG_ROOT_NFS
+               if (!strncmp(line,"nfsroot=",8)) {
+                       int n = 255 - strlen (NFS_ROOT);
+                       line += 8;
+                       if (line [0] == '/' || (line [0] >= '0' && line [0] <= '9')){
+                               strncpy (nfs_root_name, line, 255);
+                               continue;
+                       }
+                       if (strlen (line) >= n)
+                               line [n] = 0;
+                       sprintf (nfs_root_name, NFS_ROOT, line);
+                       continue;
+               }
+#endif
                if (!strcmp(line,"ro")) {
                        root_mountflags |= MS_RDONLY;
                        continue;
index 73c950c79efdb609aeac6031b31e044350dd675e..2f39057ff62403cf89abe32d677d8f10ed1f91a8 100644 (file)
@@ -332,7 +332,7 @@ int is_orphaned_pgrp(int pgrp)
        return(1);      /* (sighing) "Often!" */
 }
 
-static int has_stopped_jobs(int pgrp)
+static inline int has_stopped_jobs(int pgrp)
 {
        struct task_struct * p;
 
@@ -345,7 +345,7 @@ static int has_stopped_jobs(int pgrp)
        return(0);
 }
 
-static void forget_original_parent(struct task_struct * father)
+static inline void forget_original_parent(struct task_struct * father)
 {
        struct task_struct * p;
 
@@ -358,7 +358,7 @@ static void forget_original_parent(struct task_struct * father)
        }
 }
 
-void exit_files(struct task_struct *tsk)
+static inline void __exit_files(struct task_struct *tsk)
 {
        struct files_struct * files = tsk->files;
 
@@ -378,7 +378,12 @@ void exit_files(struct task_struct *tsk)
        }
 }
 
-void exit_fs(struct task_struct *tsk)
+void exit_files(struct task_struct *tsk)
+{
+  __exit_files(tsk);
+}
+
+static inline void __exit_fs(struct task_struct *tsk)
 {
        struct fs_struct * fs = tsk->fs;
 
@@ -392,7 +397,12 @@ void exit_fs(struct task_struct *tsk)
        }
 }
 
-void exit_sighand(struct task_struct *tsk)
+void exit_fs(struct task_struct *tsk)
+{
+  __exit_fs(tsk);
+}
+
+static inline void __exit_sighand(struct task_struct *tsk)
 {
        struct signal_struct * sig = tsk->sig;
 
@@ -404,7 +414,12 @@ void exit_sighand(struct task_struct *tsk)
        }
 }
 
-static void exit_mm(void)
+void exit_sighand(struct task_struct *tsk)
+{
+  __exit_sighand(tsk);
+}
+
+static inline void exit_mm(void)
 {
        struct mm_struct * mm = current->mm;
 
@@ -497,9 +512,9 @@ fake_volatile:
        del_timer(&current->real_timer);
        sem_exit();
        exit_mm();
-       exit_files(current);
-       exit_fs(current);
-       exit_sighand(current);
+       __exit_files(current);
+       __exit_fs(current);
+       __exit_sighand(current);
        exit_thread();
        exit_notify();
        current->state = TASK_ZOMBIE;
@@ -544,6 +559,11 @@ asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct
                if (flag)
                        return flag;
        }
+       if (ru) {
+               flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru));
+               if (flag)
+                       return flag;
+       }
        add_wait_queue(&current->wait_chldexit,&wait);
 repeat:
        flag=0;
@@ -568,12 +588,12 @@ repeat:
                                        continue;
                                if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
                                        continue;
+                               if (ru != NULL)
+                                       getrusage(p, RUSAGE_BOTH, ru);
                                if (stat_addr)
                                        put_user((p->exit_code << 8) | 0x7f,
                                                stat_addr);
                                p->exit_code = 0;
-                               if (ru != NULL)
-                                       getrusage(p, RUSAGE_BOTH, ru);
                                retval = p->pid;
                                goto end_wait4;
                        case TASK_ZOMBIE:
@@ -581,9 +601,9 @@ repeat:
                                current->cstime += p->stime + p->cstime;
                                if (ru != NULL)
                                        getrusage(p, RUSAGE_BOTH, ru);
-                               flag = p->pid;
                                if (stat_addr)
                                        put_user(p->exit_code, stat_addr);
+                               retval = p->pid;
                                if (p->p_opptr != p->p_pptr) {
                                        REMOVE_LINKS(p);
                                        p->p_pptr = p->p_opptr;
@@ -594,7 +614,6 @@ repeat:
 #ifdef DEBUG_PROC_TREE
                                audit_ptree();
 #endif
-                               retval = flag;
                                goto end_wait4;
                        default:
                                continue;
index 22f5aba0b09f72bb6fe5f272a0b47de79359315f..9388432d7dcfdd58433c7a1d96a169bcc699d92b 100644 (file)
@@ -29,7 +29,7 @@
 int nr_tasks=1;
 int nr_running=1;
 
-static int find_empty_process(void)
+static inline int find_empty_process(void)
 {
        int i;
        struct task_struct *p;
@@ -75,7 +75,7 @@ repeat:
        return last_pid;
 }
 
-static int dup_mmap(struct mm_struct * mm)
+static inline int dup_mmap(struct mm_struct * mm)
 {
        struct vm_area_struct * mpnt, **p, *tmp;
 
@@ -110,7 +110,7 @@ static int dup_mmap(struct mm_struct * mm)
        return 0;
 }
 
-static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
 {
        if (clone_flags & CLONE_VM) {
                SET_PAGE_DIR(tsk, current->mm->pgd);
@@ -134,7 +134,7 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
        return 0;
 }
 
-static int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
 {
        if (clone_flags & CLONE_FS) {
                current->fs->count++;
@@ -152,7 +152,7 @@ static int copy_fs(unsigned long clone_flags, struct task_struct * tsk)
        return 0;
 }
 
-static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk)
 {
        int i;
 
@@ -175,7 +175,7 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
        return 0;
 }
 
-static int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
+static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
 {
        if (clone_flags & CLONE_SIGHAND) {
                current->sig->count++;
index 8d12332c8e941325befec1bacd6655e41395cd70..0fb500d777839f43c940f04232e03eeacfc53762 100644 (file)
@@ -126,7 +126,7 @@ extern void (* iABI_hook)(struct pt_regs * regs);
 
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
-#ifdef CONFIG_MODVERSIONS
+#ifdef MODVERSIONS
        { (void *)1 /* Version version :-) */,
                SYMBOL_NAME_STR (Using_Versions) },
 #endif
@@ -226,7 +226,8 @@ struct symbol_table symbol_table = {
        X(getblk),
        X(bread),
        X(breada),
-       X(brelse),
+       X(__brelse),
+       X(__bforget),
        X(ll_rw_block),
        X(__wait_on_buffer),
        X(dcache_lookup),
@@ -387,6 +388,7 @@ struct symbol_table symbol_table = {
        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)          
        /* If 8390 NIC support is built in, we will need these. */
        X(ei_open),
+       X(ei_close),
        X(ei_debug),
        X(ei_interrupt),
        X(ethdev_init),
index 5bcae597f500a6b2f0185ac98df844d8059819f0..53f7d09f9e4ac249c9f7c24c87fef9266d3511c7 100644 (file)
@@ -90,7 +90,7 @@ asmlinkage int sys_sigpending(sigset_t *set)
  * isn't actually ignored, but does automatic child reaping, while
  * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
  */
-static void check_pending(int signum)
+static inline void check_pending(int signum)
 {
        struct sigaction *p;
 
index 11a6c1f49e875100867fa3ba1cb3fb3572bab2ed..c7605b1aab6a27a767ee7e585f2ad729902b6a22 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
 #include <asm/system.h>
 #include <asm/pgtable.h>
 
-static int anon_map(struct inode *, struct file *, struct vm_area_struct *);
+/*
+ * Map memory not associated with any file into a process
+ * address space.  Adjacent memory is merged.
+ */
+static inline int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma)
+{
+       if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+               return -ENOMEM;
+       return 0;
+}
 
 /*
  * description of effects of mapping type and prot in current implementation.
@@ -212,7 +221,6 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
  *   vm_avl_height   1+max(heightof(left),heightof(right))
  * The empty tree is represented as NULL.
  */
-#define avl_empty      (struct vm_area_struct *) NULL
 
 /* Since the trees are balanced, their height will never be large. */
 #define avl_maxheight  41      /* why this? a small exercise */
@@ -225,64 +233,8 @@ unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
  *    foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.
  */
 
-/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
-struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
-{
-#if 0 /* equivalent, but slow */
-       struct vm_area_struct * vma;
-
-       if (!task->mm)
-               return NULL;
-       for (vma = task->mm->mmap ; ; vma = vma->vm_next) {
-               if (!vma)
-                       return NULL;
-               if (vma->vm_end > addr)
-                       return vma;
-       }
-#else
-       struct vm_area_struct * result = NULL;
-       struct vm_area_struct * tree;
-
-       if (!task->mm)
-               return NULL;
-       for (tree = task->mm->mmap_avl ; ; ) {
-               if (tree == avl_empty)
-                       return result;
-               if (tree->vm_end > addr) {
-                       if (tree->vm_start <= addr)
-                               return tree;
-                       result = tree;
-                       tree = tree->vm_avl_left;
-               } else
-                       tree = tree->vm_avl_right;
-       }
-#endif
-}
-
-/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
-   NULL if none.  Assume start_addr < end_addr. */
-struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
-{
-       struct vm_area_struct * vma;
-
-#if 0 /* equivalent, but slow */
-       for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
-               if (end_addr <= vma->vm_start)
-                       break;
-               if (start_addr < vma->vm_end)
-                       return vma;
-       }
-       return NULL;
-#else
-       vma = find_vma(task,start_addr);
-       if (!vma || end_addr <= vma->vm_start)
-               return NULL;
-       return vma;
-#endif
-}
-
 /* Look up the nodes at the left and at the right of a given node. */
-static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+static inline void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
 {
        vm_avl_key_t key = node->vm_avl_key;
 
@@ -328,7 +280,7 @@ static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct
  * nodes[0]..nodes[k-1] such that
  * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}.
  */
-static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
+static inline void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
 {
        for ( ; count > 0 ; count--) {
                struct vm_area_struct ** nodeplace = *--nodeplaces_ptr;
@@ -405,7 +357,7 @@ static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
 }
 
 /* Insert a node into a tree. */
-static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
+static inline void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
 {
        vm_avl_key_t key = new_node->vm_avl_key;
        struct vm_area_struct ** nodeplace = ptree;
@@ -432,7 +384,7 @@ static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct
 /* Insert a node into a tree, and
  * return the node to the left of it and the node to the right of it.
  */
-static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
+static inline void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
        struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
 {
        vm_avl_key_t key = new_node->vm_avl_key;
@@ -462,7 +414,7 @@ static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_a
 }
 
 /* Removes a node out of a tree. */
-static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
+static inline void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
 {
        vm_avl_key_t key = node_to_delete->vm_avl_key;
        struct vm_area_struct ** nodeplace = ptree;
@@ -958,14 +910,3 @@ void merge_segments (struct task_struct * task, unsigned long start_addr, unsign
                mpnt = prev;
        }
 }
-
-/*
- * Map memory not associated with any file into a process
- * address space.  Adjacent memory is merged.
- */
-static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma)
-{
-       if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
-               return -ENOMEM;
-       return 0;
-}
index b184ba4c2a77d028671e0213282613621eb3a185..d0c4b9f5a60548bc190f6e57d4e1fcd54552244a 100644 (file)
@@ -262,7 +262,7 @@ o   IPX uses sock_alloc_send_skb                    [TESTED]
 o      Recvmsg for all IP, sendmsg for TCP             [IN]
        (nearly ready to go all *msg())
 
--------->>>>> 1.3.38 <<<<<<--------
+-------->>>>> 1.3.41 <<<<<<--------
 
 o      ip udp/raw nonblock bug fixed                   [TESTED]
 o      ICMP lockup fix                                 [IN]
@@ -271,7 +271,15 @@ o  bind() for SOCK_PACKET                          [IN]
 o      set_mac_addr fixed up                           [IN]
 o      BSD SIOCSIFADDR, AF_UNSPEC behaviour            [IN]
 o      Updated this list                               [IN]
-
+o      Massive ARP/cache/routing rewrite [ANK]         [IN]
+o      AX.25 connect return fixed in using sock_error  [IN]
+o      Proper netlink device major(36)                 [IN]
+o      First parts of the SKIP support                 [IN, not useful]
+o      TCP ICMP (SOSS should work again)               [IN]
+o      IPFW support for TOS changing (Al Longyear)     [IN]
+o      DECNET PPP test code                            [IN]
+o      NFS root                                        [IN]
+o      Path MTU discovery [ANK]                        [IN]
 
 ---------- Things I thought Linus had for a while and not merged ----------------
 
@@ -280,33 +288,29 @@ o Updated this list                               [IN]
 
 o      Improved IPX support for lwared.
 o      Decnet pre pre pre pre pre Alpha 0.0.
-o      Faster routing/caching support.
 
 ---------- Things pending for me to merge --------------
 
-o      IPFW support for TOS changing (Al Longyear)
 o      AF_UNIX garbage collect code
 o      Faster closedown option for heavy use sites (me)
 o      Tom May's insw_and_checksum()                   
 o      IPX PPP support
-o      DECNET PPP test code
-o      SPARC patches [Dave]
+o      SPARC patches [Dave] [partly in]
+o      Path MTU and other goodies [ANK]
 
 --------------- Things That Need Doing Before 1.4 ------------------
 
-o      inet_error for other layers
 o      Finish merging the bridge code
 o      Fast checksum/copy on outgoing TCP
 o      Fast dev_grab_next() transmit reload function
-       and dev_push_failed() ??
-o      Faster ip_forward last hit cache                [PENDING(GuruA0)]
-o      L2 ip routing cache                             [PENDING(btv)]
+       and dev_push_failed() ??                        [Causes deadlock
+                                                       cases with slow box, heavy networking]
 o      Forwarding queue control (+ fairness algorithms ??)
 o      IP forward flow control.
 o      Clean up RAW AX.25 sockets.
 o      Finish 802.2 Class I code to be compliant to the oddities of 802.2
 o      Tidy BPQ support to use a bpqip tunnel device
-o      Strange eth0-eth3 bug 
+o      Strange eth0-eth3 bug                           [Matti fixed ??]
 o      Finish IPIP bug fixes                           [Done hopefully]
 o      Why doesnt the PROTO_UNREACH get sent ?         [Should now work]
 o      Kill off old ip_queue_xmit/ip_send stuff.
index 97df026923aea4f5da8f433274614b28221178e9..2d8abf6957a02f5f8db7e7260be3174b767cb612 100644 (file)
@@ -661,7 +661,7 @@ struct device *ax25rtr_get_dev(ax25_address *addr)
                                                return dev;
                                        break;
                                case ARPHRD_ETHER:
-                                       if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, ARPHRD_AX25))
+                                       if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, dev))
                                                if (ax25cmp(addr, &dev_addr) == 0)
                                                        return dev;
                                        break;
@@ -1669,7 +1669,7 @@ static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *
 
        skb->sk = NULL;         /* Initially we don't know who its for */
 
-       if (!arp_query((unsigned char *)&port_call, dev->pa_addr, ARPHRD_AX25)) {
+       if (!arp_query((unsigned char *)&port_call, dev->pa_addr, dev)) {
                kfree_skb(skb, FREE_READ);      /* We have no port callsign */
                return 0;
        }
index 844063fc4d64a5c81464603179a152b20b2ae30a..3c5c670b2132ead44df3f13f1dd19ab4f59872c8 100644 (file)
@@ -39,6 +39,8 @@
  *             Alan Cox        :       Device lock protection.
  *             Alan Cox        :       Fixed nasty side effect of device close changes.
  *             Rudi Cilibrasi  :       Pass the right thing to set_mac_address()
+ *             Dave Miller     :       32bit quantity for the device lock to make it work out
+ *                                     on a Sparc.
  *
  */
 
index 96f6e2b729192a8796baa06b897bd1a2a81abd59..518ac9edd0fb4879a3a8b0447c3e5ab88b6e9483 100644 (file)
@@ -89,7 +89,7 @@ void eth_setup(char *str, int *ints)
 int eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
           void *daddr, void *saddr, unsigned len)
 {
-       struct ethhdr *eth = (struct ethhdr *)skb_push(skb,14);
+       struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
 
        /* 
         *      Set the protocol type. For a packet of type ETH_P_802_3 we put the length
@@ -175,7 +175,7 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
        unsigned char *rawp;
        
        skb->mac.raw=skb->data;
-       skb_pull(skb,14);       
+       skb_pull(skb,dev->hard_header_len);
        eth= skb->mac.ethernet;
        
        if(*eth->h_dest&1)
@@ -218,28 +218,48 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
 }
 
 /*
- *     Header caching for ethernet. Try to find and cache a header to avoid arp overhead.
+ * Upper level calls this function to bind hardware header cache entry.
+ * If the call is successful, then corresponding Address Resolution Protocol
+ * (maybe, not ARP) takes responsibility for updating cache content.
  */
-void eth_header_cache(struct device *dev, struct sock *sk, unsigned long saddr, unsigned long daddr)
+
+void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
+                          unsigned short htype, __u32 daddr)
 {
-       int v=arp_find_cache(sk->ip_hcache_data, daddr, dev);
-       if(v!=1)
-               sk->ip_hcache_state=0;  /* Try when arp resolves */
-       else
+       struct hh_cache *hh;
+
+       if (htype != ETH_P_IP)
+       {
+               printk("eth_header_cache_bind: %04x cache is not implemented\n", htype);
+               return;
+       }
+       if (arp_bind_cache(hhp, dev, htype, daddr))
+               return;
+       if ((hh=*hhp) != NULL)
+       {
+               memcpy(hh->hh_data+6, dev->dev_addr, ETH_ALEN);
+               hh->hh_data[12] = htype>>8;
+               hh->hh_data[13] = htype&0xFF;
+       }
+}
+
+/*
+ * Called by Address Resolution module to notify changes in address.
+ */
+
+void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
+{
+       if (hh->hh_type != ETH_P_IP)
        {
-               memcpy(sk->ip_hcache_data+6, dev->dev_addr, ETH_ALEN);
-               sk->ip_hcache_data[12]=ETH_P_IP>>8;
-               sk->ip_hcache_data[13]=ETH_P_IP&0xFF;
-               sk->ip_hcache_state=1;
-               sk->ip_hcache_stamp=arp_cache_stamp;
-               sk->ip_hcache_ver=&arp_cache_stamp;
+               printk("eth_header_cache_update: %04x cache is not implemented\n", hh->hh_type);
+               return;
        }
+       memcpy(hh->hh_data, haddr, ETH_ALEN);
+       hh->hh_uptodate = 1;
 }
 
 /*
  *     Copy from an ethernet device memory space to an sk_buff while checksumming if IP
- *     The magic "34" is Rx_addr+Tx_addr+type_field+sizeof(struct iphdr) == 6+6+2+20.
  */
  
 void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base)
@@ -261,13 +281,15 @@ void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int
         * We have to use the smaller of length and ip_length because it
         * can happen that ip_length > length.
         */
-       memcpy(dest->data,src,34);      /* ethernet is always >= 34 */
-       length -= 34;
-       iph=(struct iphdr*)(src+14);    /* 14 = Rx_addr+Tx_addr+type_field */
+       memcpy(dest->data,src,sizeof(struct iphdr)+ETH_HLEN);   /* ethernet is always >= 34 */
+       length -= sizeof(struct iphdr) + ETH_HLEN;
+       iph=(struct iphdr*)(src+ETH_HLEN);
        ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr);
-       if (ip_length <= length)
+
+       /* Also watch out for bogons - min IP size is 8 (rfc-1042) */
+       if ((ip_length <= length) && (ip_length > 7))
                length=ip_length;
 
-       dest->csum=csum_partial_copy(src+34,dest->data+34,length,base);
+       dest->csum=csum_partial_copy(src+sizeof(struct iphdr)+ETH_HLEN,dest->data+sizeof(struct iphdr)+ETH_HLEN,length,base);
        dest->ip_summed=1;
 }
index ddb75a41810e737a3bebf7ff23c379bef5ecd82f..d9930d0e467a39fdd2da2c2bb281b46a1f4aba23 100644 (file)
@@ -20,7 +20,7 @@ fi
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
-bool 'IP: Assume subnets are local' CONFIG_INET_SNARL
+bool 'IP: Disable Path MTU Discovery (normally enabled)' CONFIG_NO_PATH_MTU_DISCOVERY
 bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
 bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
 bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
index e8dac8258a2d6bcbe6ae4f0c9acf45ac70588ed0..9c9648ece50a0d77bec68eee53dd26ccf01a8267 100644 (file)
@@ -402,6 +402,7 @@ void destroy_sock(struct sock *sk)
        {
                if(sk->opt)
                        kfree(sk->opt);
+               ip_rt_put(sk->ip_route_cache);
                /*
                 *      This one is pure paranoia. I'll take it out
                 *      later once I know the bug is buried.
@@ -915,6 +916,7 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
                sk->daddr = 0;
                sk->dummy_th.dest = 0;
        }
+       ip_rt_put(sk->ip_route_cache);
        sk->ip_route_cache=NULL;
        return(0);
 }
@@ -1236,6 +1238,9 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                case SIOCDARP:
                case SIOCGARP:
                case SIOCSARP:
+               case OLD_SIOCDARP:
+               case OLD_SIOCGARP:
+               case OLD_SIOCSARP:
                        return(arp_ioctl(cmd,(void *) arg));
                case SIOCDRARP:
                case SIOCGRARP:
@@ -1570,4 +1575,10 @@ void inet_proto_init(struct net_proto *pro)
                0, &proc_net_inode_operations,
                rt_get_info
        });
+       proc_net_register(&(struct proc_dir_entry) {
+               PROC_NET_RTCACHE, 8, "rt_cache",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+               0, &proc_net_inode_operations,
+               rt_cache_get_info
+       });
 }
index d75b4298e137a9b77036496b1cdd2900d14cf98e..9414f0772338489054850fa2fcdd9dc13e2ae76f 100644 (file)
@@ -105,12 +105,11 @@ struct arp_table
 {
        struct arp_table                *next;                  /* Linked entry list            */
        unsigned long                   last_used;              /* For expiry                   */
+       unsigned long                   last_updated;           /* For expiry                   */
        unsigned int                    flags;                  /* Control status               */
        u32                             ip;                     /* ip address of entry          */
        u32                             mask;                   /* netmask - used for generalised proxy arps (tridge)           */
        unsigned char                   ha[MAX_ADDR_LEN];       /* Hardware address             */
-       unsigned char                   hlen;                   /* Length of hardware address   */
-       unsigned short                  htype;                  /* Type of hardware in use      */
        struct device                   *dev;                   /* Device the entry is tied to  */
 
        /*
@@ -120,6 +119,7 @@ struct arp_table
        struct timer_list               timer;                  /* expire timer                 */
        int                             retries;                /* remaining retries            */
        struct sk_buff_head             skb;                    /* list of queued packets       */
+       struct hh_cache                 *hh;
 };
 
 
@@ -130,17 +130,18 @@ struct arp_table
 /*
  *     If an arp request is send, ARP_RES_TIME is the timeout value until the
  *     next request is send.
+ *     RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
+ *     The recommended minimum timeout is 1 second per destination.
+ *     This timeout is prolongated to ARP_DEAD_RES_TIME, if
+ *     destination does not respond.
  */
 
-/* RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST) */
-/* The recommended minimum timeout is 1 second per destination. */
-/* Is this a per-destination timeout? -- MS [YES AC]*/
-
-#define ARP_RES_TIME           (250*(HZ/10))
+#define ARP_RES_TIME           (5*HZ)
+#define ARP_DEAD_RES_TIME      (60*HZ)
 
 /*
  *     The number of times an arp request is send, until the host is
- *     considered unreachable.
+ *     considered temporarily unreachable.
  */
 
 #define ARP_MAX_TRIES          3
@@ -153,22 +154,30 @@ struct arp_table
 
 /*
  *     How often is the function 'arp_check_retries' called.
- *     An entry is invalidated in the time between ARP_TIMEOUT and
+ *     An unused entry is invalidated in the time between ARP_TIMEOUT and
  *     (ARP_TIMEOUT+ARP_CHECK_INTERVAL).
  */
 
-#define ARP_CHECK_INTERVAL     (60 * HZ)
+#define ARP_CHECK_INTERVAL     (60*HZ)
 
-enum proxy {
-   PROXY_EXACT=0,
-   PROXY_ANY,
-   PROXY_NONE,
-};
+/*
+ *     The entry is reconfirmed by sending point-to-point ARP
+ *     request after ARP_CONFIRM_INTERVAL. If destinations does not respond
+ *     for ARP_CONFIRM_TIMEOUT, normal broadcast resolution scheme is started.
+ */
 
-/* Forward declarations. */
-static void arp_check_expire (unsigned long);  
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type);
+#define ARP_CONFIRM_INTERVAL   (300*HZ)
+#define ARP_CONFIRM_TIMEOUT    ARP_RES_TIME
+
+static unsigned long arp_lock;
+static unsigned long arp_bh_mask;
+
+#define ARP_BH_BACKLOG 1
+
+static struct arp_table *arp_backlog;
 
+static void arp_run_bh(void);
+static void arp_check_expire (unsigned long);  
 
 static struct timer_list arp_timer =
        { NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -176,26 +185,17 @@ static struct timer_list arp_timer =
 /*
  * The default arp netmask is just 255.255.255.255 which means it's
  * a single machine entry. Only proxy entries can have other netmasks
- *
-*/
+ */
 
 #define DEF_ARP_NETMASK (~0)
 
-
 /*
  *     The size of the hash table. Must be a power of two.
  *     Maybe we should remove hashing in the future for arp and concentrate
  *     on Patrick Schaaf's Host-Cache-Lookup...
  */
 
-
 #define ARP_TABLE_SIZE  16
-
-/* The ugly +1 here is to cater for proxy entries. They are put in their 
-   own list for efficiency of lookup. If you don't want to find a proxy
-   entry then don't look in the last entry, otherwise do 
-*/
-
 #define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)
 
 struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
@@ -203,8 +203,7 @@ struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
        NULL,
 };
 
-unsigned long arp_cache_stamp;
-
+#define arp_proxy_list arp_tables[ARP_TABLE_SIZE]
 
 /*
  *     The last bits in the IP address are used for the cache lookup.
@@ -212,82 +211,356 @@ unsigned long arp_cache_stamp;
  */
 
 #define HASH(paddr)            (htonl(paddr) & (ARP_TABLE_SIZE - 1))
-#define PROXY_HASH ARP_TABLE_SIZE
+
+/*
+ * Lock/unlock arp_table chains.
+ */
+
+static __inline__ void arp_fast_lock(void)
+{
+       ATOMIC_INCR(&arp_lock);
+}
+
+static __inline__ void arp_fast_unlock(void)
+{
+       ATOMIC_DECR(&arp_lock);
+}
+
+static __inline__ void arp_unlock(void)
+{
+       if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask)
+               arp_run_bh();
+}
+
+/*
+ * Enqueue to FIFO list.
+ */
+
+static void arp_enqueue(struct arp_table **q, struct arp_table *entry)
+{
+       unsigned long flags;
+       struct arp_table * tail;
+
+       save_flags(flags);
+       cli();
+       tail = *q;
+       if (!tail)
+               entry->next = entry;
+       else
+       {
+               entry->next = tail->next;
+               tail->next = entry;
+       }
+       *q = entry;
+       restore_flags(flags);
+       return;
+}
+
+/*
+ * Dequeue from FIFO list,
+ * caller should mask interrupts.
+ */
+
+static struct arp_table * arp_dequeue(struct arp_table **q)
+{
+       struct arp_table * entry;
+
+       if (*q)
+       {
+               entry = (*q)->next;
+               (*q)->next = entry->next;
+               if (entry->next == entry)
+                       *q = NULL;
+               entry->next = NULL;
+               return entry;
+       }
+       return NULL;
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void arp_release_entry(struct arp_table *entry)
+{
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       /* Release the list of `skb' pointers. */
+       while ((skb = skb_dequeue(&entry->skb)) != NULL)
+       {
+               skb_device_lock(skb);
+               restore_flags(flags);
+               dev_kfree_skb(skb, FREE_WRITE);
+               cli();
+       }
+       restore_flags(flags);
+       return;
+}
+
+/*
+ *     Release the entry and all resources linked to it: skb's, hh's, timer
+ *     and certainly memory.
+ */
+
+static void arp_free_entry(struct arp_table *entry)
+{
+       unsigned long flags;
+       struct hh_cache *hh, *next;
+
+       del_timer(&entry->timer);
+
+       save_flags(flags);
+       cli();
+       arp_release_entry(entry);
+
+       for (hh = entry->hh; hh; hh = next)
+       {
+               next = hh->hh_next;
+               hh->hh_arp = NULL;
+               if (!--hh->hh_refcnt)
+                       kfree_s(hh, sizeof(struct(struct hh_cache)));
+       }
+       restore_flags(flags);
+
+       kfree_s(entry, sizeof(struct arp_table));
+       return;
+}
+
+/*
+ * How many users has this entry?
+ */
+
+static __inline__ int arp_count_hhs(struct arp_table * entry)
+{
+       struct hh_cache *hh, **hhp;
+       int count = 0;
+
+       hhp = &entry->hh;
+       while ((hh=*hhp) != NULL)
+       {
+               if (hh->hh_refcnt == 1)
+               {
+                       *hhp = hh->hh_next;
+                       kfree_s(hh, sizeof(struct hh_cache));
+                       continue;
+               }
+               count += hh->hh_refcnt-1;
+               hhp = &hh->hh_next;
+       }
+
+       return count;
+}
+
+/*
+ * Invalidate all hh's, so that higher level will not try to use it.
+ */
+
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
+{
+       struct hh_cache *hh;
+
+       for (hh=entry->hh; hh; hh=hh->hh_next)
+               hh->hh_uptodate = 0;
+}
+
+/*
+ * Signal to device layer, that hardware address may be changed.
+ */
+
+static __inline__ void arp_update_hhs(struct arp_table * entry)
+{
+       struct hh_cache *hh;
+
+       for (hh=entry->hh; hh; hh=hh->hh_next)
+               entry->dev->header_cache_update(hh, entry->dev, entry->ha);
+}
 
 /*
  *     Check if there are too old entries and remove them. If the ATF_PERM
  *     flag is set, they are always left in the arp cache (permanent entry).
- *     Note: Only fully resolved entries, which don't have any packets in
- *     the queue, can be deleted, since ARP_TIMEOUT is much greater than
- *     ARP_MAX_TRIES*ARP_RES_TIME.
+ *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
+ *     declare it invalid and send point-to-point ARP request.
+ *     If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
+ *     give it to shred by arp_expire_entry.
  */
 
-/* RFC1122: Looks good.  Prevents stale ARP entries, as per 2.3.2.1. (MUST) */
-
 static void arp_check_expire(unsigned long dummy)
 {
        int i;
        unsigned long now = jiffies;
-       unsigned long flags;
-       save_flags(flags);
-       cli();
 
-       for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
+       del_timer(&arp_timer);
+
+       if (!arp_lock)
        {
-               struct arp_table *entry;
-               struct arp_table **pentry = &arp_tables[i];
+               arp_fast_lock();
 
-               while ((entry = *pentry) != NULL)
+               for (i = 0; i < ARP_TABLE_SIZE; i++)
                {
-                       if ((now - entry->last_used) > ARP_TIMEOUT
-                               && !(entry->flags & ATF_PERM))
+                       struct arp_table *entry;
+                       struct arp_table **pentry;
+               
+                       pentry = &arp_tables[i];
+
+                       while ((entry = *pentry) != NULL)
                        {
-                               *pentry = entry->next;  /* remove from list */
-                               arp_cache_stamp++;
-                               del_timer(&entry->timer);       /* Paranoia */
-                               kfree_s(entry, sizeof(struct arp_table)); 
-                               /* Don't have to remove packets in entry->skb. */
-                               /* See comments above. */
+                               cli();
+                               if (now - entry->last_used > ARP_TIMEOUT
+                                   && !(entry->flags & ATF_PERM)
+                                   && !arp_count_hhs(entry))
+                               {
+                                       *pentry = entry->next;
+                                       sti();
+#if RT_CACHE_DEBUG >= 2
+                                       printk("arp_expire: %08x expired\n", entry->ip);
+#endif
+                                       arp_free_entry(entry);
+                               }
+                               else if (entry->last_updated
+                                        && now - entry->last_updated > ARP_CONFIRM_INTERVAL
+                                        && !(entry->flags & ATF_PERM))
+                               {
+                                       struct device * dev = entry->dev;
+                                       pentry = &entry->next;
+                                       entry->flags &= ~ATF_COM;
+                                       arp_invalidate_hhs(entry);
+                                       sti();
+                                       entry->retries = ARP_MAX_TRIES+1;
+                                       del_timer(&entry->timer);
+                                       entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
+                                       add_timer(&entry->timer);
+                                       arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
+                                                dev, dev->pa_addr, entry->ha,
+                                                dev->dev_addr, NULL);
+#if RT_CACHE_DEBUG >= 2
+                                       printk("arp_expire: %08x requires confirmation\n", entry->ip);
+#endif
+                               }
+                               else
+                                       pentry = &entry->next;  /* go to next entry */
                        }
-                       else
-                               pentry = &entry->next;  /* go to next entry */
                }
+               arp_unlock();
        }
-       restore_flags(flags);
+
+       ip_rt_check_expire();
 
        /*
         *      Set the timer again.
         */
 
-       del_timer(&arp_timer);
        arp_timer.expires = jiffies + ARP_CHECK_INTERVAL;
        add_timer(&arp_timer);
 }
 
-
 /*
- *     Release all linked skb's and the memory for this entry.
+ *     This function is called, if an entry is not resolved in ARP_RES_TIME.
+ *     When more than MAX_ARP_TRIES retries was done, release queued skb's,
+ *     but not discard entry itself if  it is in use.
  */
 
-static void arp_release_entry(struct arp_table *entry)
+static void arp_expire_request (unsigned long arg)
 {
-       struct sk_buff *skb;
+       struct arp_table *entry = (struct arp_table *) arg;
+       struct arp_table **pentry;
+       unsigned long hash;
        unsigned long flags;
 
        save_flags(flags);
        cli();
-       /* Release the list of `skb' pointers. */
-       while ((skb = skb_dequeue(&entry->skb)) != NULL)
+
+       /*
+        *      Since all timeouts are handled with interrupts enabled, there is a
+        *      small chance, that this entry has just been resolved by an incoming
+        *      packet. This is the only race condition, but it is handled...
+        */
+       
+       if (entry->flags & ATF_COM)
        {
-               skb_device_lock(skb);
                restore_flags(flags);
-               dev_kfree_skb(skb, FREE_WRITE);
-               cli();
+               return;
+       }
+
+       if (arp_lock)
+       {
+#if RT_CACHE_DEBUG >= 1
+               printk("arp_expire_request: %08x postponed\n", entry->ip);
+#endif
+               del_timer(&entry->timer);
+               entry->timer.expires = jiffies + HZ/10;
+               add_timer(&entry->timer);
+               restore_flags(flags);
+               return;
        }
+
+       arp_fast_lock();
        restore_flags(flags);
-       del_timer(&entry->timer);
-       kfree_s(entry, sizeof(struct arp_table));
-       return;
+
+       if (entry->last_updated && --entry->retries > 0)
+       {
+               struct device *dev = entry->dev;
+
+#if RT_CACHE_DEBUG >= 2
+               printk("arp_expire_request: %08x timed out\n", entry->ip);
+#endif
+               /* Set new timer. */
+               del_timer(&entry->timer);
+               entry->timer.expires = jiffies + ARP_RES_TIME;
+               add_timer(&entry->timer);
+               arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
+                        NULL, dev->dev_addr, NULL);
+               arp_unlock();
+               return;
+       }
+
+       arp_release_entry(entry);
+
+       cli();
+       if (arp_count_hhs(entry))
+       {
+               struct device *dev = entry->dev;
+#if RT_CACHE_DEBUG >= 2
+               printk("arp_expire_request: %08x is dead\n", entry->ip);
+#endif
+               arp_release_entry(entry);
+               entry->retries = ARP_MAX_TRIES;
+               restore_flags(flags);
+               entry->last_updated = 0;
+               del_timer(&entry->timer);
+               entry->timer.expires = jiffies + ARP_DEAD_RES_TIME;
+               add_timer(&entry->timer);
+               arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
+                        NULL, dev->dev_addr, NULL);
+               arp_unlock();
+               return;
+       }
+       restore_flags(flags);
+
+       hash = HASH(entry->ip);
+
+       pentry = &arp_tables[hash];
+
+       while (*pentry != NULL)
+       {
+               if (*pentry == entry)
+               {
+                       cli();
+                       *pentry = entry->next;
+                       restore_flags(flags);
+#if RT_CACHE_DEBUG >= 2
+                       printk("arp_expire_request: %08x is killed\n", entry->ip);
+#endif
+                       arp_free_entry(entry);
+                       arp_unlock();
+                       return;
+               }
+               pentry = &(*pentry)->next;
+       }
+       printk("arp_expire_request: bug: ARP entry is lost!\n");
+       arp_unlock();
 }
 
 /*
@@ -298,16 +571,19 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr
 {
        struct device *dev=ptr;
        int i;
-       unsigned long flags;
        
-       if(event!=NETDEV_DOWN)
+       if (event != NETDEV_DOWN)
                return NOTIFY_DONE;
        /*
         *      This is a bit OTT - maybe we need some arp semaphores instead.
         */
-        
-       save_flags(flags);
-       cli();
+
+#if RT_CACHE_DEBUG >= 1         
+       if (arp_lock)
+               printk("arp_device_event: bug\n");
+#endif
+       arp_fast_lock();
+
        for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
        {
                struct arp_table *entry;
@@ -315,18 +591,15 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr
 
                while ((entry = *pentry) != NULL)
                {
-                       if(entry->dev==dev)
+                       if (entry->dev == dev)
                        {
                                *pentry = entry->next;  /* remove from list */
-                               del_timer(&entry->timer);       /* Paranoia */
-                               kfree_s(entry, sizeof(struct arp_table));
+                               arp_free_entry(entry);
                        }
                        else
                                pentry = &entry->next;  /* go to next entry */
                }
        }
-       arp_cache_stamp++;
-       restore_flags(flags);
        return NOTIFY_DONE;
 }
 
@@ -338,7 +611,8 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr
 
 void arp_send(int type, int ptype, u32 dest_ip, 
              struct device *dev, u32 src_ip, 
-             unsigned char *dest_hw, unsigned char *src_hw)
+             unsigned char *dest_hw, unsigned char *src_hw,
+             unsigned char *target_hw)
 {
        struct sk_buff *skb;
        struct arphdr *arp;
@@ -348,7 +622,7 @@ void arp_send(int type, int ptype, u32 dest_ip,
         *      No arp on this interface.
         */
        
-       if(dev->flags&IFF_NOARP)
+       if (dev->flags&IFF_NOARP)
                return;
 
        /*
@@ -380,7 +654,7 @@ void arp_send(int type, int ptype, u32 dest_ip,
 #ifdef CONFIG_NETROM
        arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
 #else
-       arp->ar_pro = (dev->type != ARPHRD_AX25)? htons(ETH_P_IP) : htons(AX25_P_IP);
+       arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
 #endif
 #else
        arp->ar_pro = htons(ETH_P_IP);
@@ -395,8 +669,8 @@ void arp_send(int type, int ptype, u32 dest_ip,
        arp_ptr+=dev->addr_len;
        memcpy(arp_ptr, &src_ip,4);
        arp_ptr+=4;
-       if (dest_hw != NULL)
-               memcpy(arp_ptr, dest_hw, dev->addr_len);
+       if (target_hw != NULL)
+               memcpy(arp_ptr, target_hw, dev->addr_len);
        else
                memset(arp_ptr, 0, dev->addr_len);
        arp_ptr+=dev->addr_len;
@@ -405,124 +679,35 @@ void arp_send(int type, int ptype, u32 dest_ip,
        dev_queue_xmit(skb, dev, 0);
 }
 
-
 /*
- *     This function is called, if an entry is not resolved in ARP_RES_TIME.
- *     Either resend a request, or give it up and free the entry.
+ *     This will try to retransmit everything on the queue.
  */
 
-static void arp_expire_request (unsigned long arg)
+static void arp_send_q(struct arp_table *entry)
 {
-       struct arp_table *entry = (struct arp_table *) arg;
-       struct arp_table **pentry;
-       unsigned long hash;
-       unsigned long flags;
+       struct sk_buff *skb;
 
-       save_flags(flags);
-       cli();
+       unsigned long flags;
 
        /*
-        *      Since all timeouts are handled with interrupts enabled, there is a
-        *      small chance, that this entry has just been resolved by an incoming
-        *      packet. This is the only race condition, but it is handled...
+        *      Empty the entire queue, building its data up ready to send
         */
        
-       if (entry->flags & ATF_COM)
+       if(!(entry->flags&ATF_COM))
        {
-               restore_flags(flags);
+               printk("arp_send_q: incomplete entry for %s\n",
+                               in_ntoa(entry->ip));
+               /* Can't flush the skb, because RFC1122 says to hang on to */
+               /* at least one from any unresolved entry.  --MS */
+               /* Whats happened is that someone has 'unresolved' the entry
+                  as we got to use it - this 'can't happen' -- AC */
                return;
        }
 
-       if (--entry->retries > 0)
-       {
-               u32 ip = entry->ip;
-               struct device *dev = entry->dev;
-
-               /* Set new timer. */
-               del_timer(&entry->timer);
-               entry->timer.expires = jiffies + ARP_RES_TIME;
-               add_timer(&entry->timer);
-               restore_flags(flags);
-               arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, 
-                        NULL, dev->dev_addr);
-               return;
-       }
-
-       /*
-        *      Arp request timed out. Delete entry and all waiting packets.
-        *      If we give each entry a pointer to itself, we don't have to
-        *      loop through everything again. Maybe hash is good enough, but
-        *      I will look at it later.
-        */
-
-       hash = HASH(entry->ip);
-
-       /* proxy entries shouldn't really time out so this is really
-          only here for completeness
-       */
-
-       /* RFC1122: They *can* be timed out, according to 2.3.2.1. */
-       /* They recommend a minute. -- MS */
-       /* The world doesn't work this way -- AC */
-
-       if (entry->flags & ATF_PUBL)
-         pentry = &arp_tables[PROXY_HASH];
-       else
-         pentry = &arp_tables[hash];
-       while (*pentry != NULL)
-       {
-               if (*pentry == entry)
-               {
-                       *pentry = entry->next;  /* delete from linked list */
-                       del_timer(&entry->timer);
-                       restore_flags(flags);
-                       arp_release_entry(entry);
-                       arp_cache_stamp++;
-                       return;
-               }
-               pentry = &(*pentry)->next;
-       }
-       restore_flags(flags);
-       printk("Possible ARP queue corruption.\n");
-       /*
-        *      We should never arrive here.
-        */
-
-       /* Should we perhaps flush the ARP table (except the ones we're */
-       /* publishing, if we can trust the queue that much) at this */
-       /* point? -- MS */
-}
-
-
-/*
- *     This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
-{
-       struct sk_buff *skb;
-
-       unsigned long flags;
-
-       /*
-        *      Empty the entire queue, building its data up ready to send
-        */
-       
-       if(!(entry->flags&ATF_COM))
-       {
-               printk("arp_send_q: incomplete entry for %s\n",
-                               in_ntoa(entry->ip));
-               /* Can't flush the skb, because RFC1122 says to hang on to */
-               /* at least one from any unresolved entry.  --MS */
-               /* Whats happened is that someone has 'unresolved' the entry
-                  as we got to use it - this 'can't happen' -- AC */
-               return;
-       }
-
-       save_flags(flags);
-       
-       cli();
-       while((skb = skb_dequeue(&entry->skb)) != NULL)
+       save_flags(flags);
+       
+       cli();
+       while((skb = skb_dequeue(&entry->skb)) != NULL)
        {
                IS_SKB(skb);
                skb_device_lock(skb);
@@ -544,50 +729,29 @@ static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
  *     Delete an ARP mapping entry in the cache.
  */
 
-void arp_destroy(u32 ip_addr, int force)
+static void arp_destroy(struct arp_table * entry)
 {
-        int checked_proxies = 0;
-       struct arp_table *entry;
+       struct arp_table *entry1;
        struct arp_table **pentry;
-       unsigned long hash = HASH(ip_addr);
 
-ugly:
-       cli();
-       pentry = &arp_tables[hash];
-       if (! *pentry) /* also check proxy entries */
-         pentry = &arp_tables[PROXY_HASH];
+       if (entry->flags & ATF_PUBL)
+               pentry = &arp_proxy_list;
+       else
+               pentry = &arp_tables[HASH(entry->ip)];
 
-       while ((entry = *pentry) != NULL)
+       while ((entry1 = *pentry) != NULL)
        {
-               if (entry->ip == ip_addr)
+               if (entry1 == entry)
                {
-                       if ((entry->flags & ATF_PERM) && !force) {
-                               sti();
-                               return;
-                       }
-                       *pentry = entry->next;
+                       *pentry = entry1->next;
                        del_timer(&entry->timer);
-                       sti();
-                       arp_release_entry(entry);
-                       /* this would have to be cleaned up */
-                       goto ugly;
-                       /* perhaps like this ?
-                       cli();
-                       entry = *pentry;
-                       */
+                       arp_free_entry(entry);
+                       return;
                }
-               pentry = &entry->next;
-               if (!checked_proxies && ! *pentry)
-                 { /* ugly. we have to make sure we check proxy
-                      entries as well */
-                   checked_proxies = 1;
-                   pentry = &arp_tables[PROXY_HASH];
-                 }
+               pentry = &entry1->next;
        }
-       sti();
 }
 
-
 /*
  *     Receive an arp request by the device layer. Maybe I rewrite it, to
  *     use the incoming packet for the reply. The time for the current
@@ -604,7 +768,6 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        unsigned char *arp_ptr= (unsigned char *)(arp+1);
        struct arp_table *entry;
        struct arp_table *proxy_entry;
-       int hlen,htype;
        unsigned long hash;
        unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */
        unsigned char *sha,*tha;
@@ -636,7 +799,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  */
 /* Again, should this be an error/printk? -- MS */
 
-       switch(dev->type)
+       switch (dev->type)
        {
 #ifdef CONFIG_AX25
                case ARPHRD_AX25:
@@ -683,16 +846,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  *     Extract fields
  */
 
-       hlen  = dev->addr_len;
-       htype = dev->type;
-
        sha=arp_ptr;
-       arp_ptr+=hlen;
-       memcpy(&sip,arp_ptr,4);
-       arp_ptr+=4;
+       arp_ptr += dev->addr_len;
+       memcpy(&sip, arp_ptr, 4);
+       arp_ptr += 4;
        tha=arp_ptr;
-       arp_ptr+=hlen;
-       memcpy(&tip,arp_ptr,4);
+       arp_ptr += dev->addr_len;
+       memcpy(&tip, arp_ptr, 4);
   
 /* 
  *     Check for bad requests for 127.x.x.x and requests for multicast
@@ -721,47 +881,48 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  *  cache.
  */
 
-       if(arp->ar_op == htons(ARPOP_REQUEST))
+       if (arp->ar_op == htons(ARPOP_REQUEST))
        { 
 /*
  * Only reply for the real device address or when it's in our proxy tables
  */
-               if(tip!=dev->pa_addr)
+               if (tip != dev->pa_addr)
                {
 /*
  *     To get in here, it is a request for someone else.  We need to
  *     check if that someone else is one of our proxies.  If it isn't,
  *     we can toss it.
  */
-                       cli();
-                       for(proxy_entry=arp_tables[PROXY_HASH];
-                           proxy_entry;
-                           proxy_entry = proxy_entry->next)
+                       arp_fast_lock();
+
+                       for (proxy_entry=arp_proxy_list;
+                            proxy_entry;
+                            proxy_entry = proxy_entry->next)
                        {
-                         /* we will respond to a proxy arp request
-                            if the masked arp table ip matches the masked
-                            tip. This allows a single proxy arp table
-                            entry to be used on a gateway machine to handle
-                            all requests for a whole network, rather than
-                            having to use a huge number of proxy arp entries
-                            and having to keep them uptodate.
-                            */
-                         if (proxy_entry->dev != dev && proxy_entry->htype == htype &&
-                             !((proxy_entry->ip^tip)&proxy_entry->mask))
-                           break;
+                               /* we will respond to a proxy arp request
+                                  if the masked arp table ip matches the masked
+                                  tip. This allows a single proxy arp table
+                                  entry to be used on a gateway machine to handle
+                                  all requests for a whole network, rather than
+                                  having to use a huge number of proxy arp entries
+                                  and having to keep them uptodate.
+                                  */
+                               if (proxy_entry->dev == dev &&
+                                   !((proxy_entry->ip^tip)&proxy_entry->mask))
+                                       break;
 
                        }
                        if (proxy_entry)
                        {
-                               memcpy(ha, proxy_entry->ha, hlen);
-                               sti();
-                               arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha);
+                               memcpy(ha, proxy_entry->ha, dev->addr_len);
+                               arp_unlock();
+                               arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha);
                                kfree_skb(skb, FREE_READ);
                                return 0;
                        }
                        else
                        {
-                               sti();
+                               arp_unlock();
                                kfree_skb(skb, FREE_READ);
                                return 0;
                        }
@@ -771,7 +932,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 /*
  *     To get here, it must be an arp request for us.  We need to reply.
  */
-                       arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr);
+                       arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
                }
        }
 /*
@@ -791,21 +952,22 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  * there.
  */
 
+       arp_fast_lock();
+
        hash = HASH(sip);
-       cli();
-       for(entry=arp_tables[hash];entry;entry=entry->next)
-               if(entry->ip==sip && entry->htype==htype)
+
+       for (entry=arp_tables[hash]; entry; entry=entry->next)
+               if (entry->ip == sip && entry->dev == dev)
                        break;
 
-       if(entry)
+       if (entry)
        {
 /*
  *     Entry found; update it only if it is not a permanent entry.
  */
                if (!(entry->flags & ATF_PERM)) {
-                       memcpy(entry->ha, sha, hlen);
-                       entry->hlen = hlen;
-                       entry->last_used = jiffies;
+                       memcpy(entry->ha, sha, dev->addr_len);
+                       entry->last_updated = jiffies;
                }
                if (!(entry->flags & ATF_COM))
                {
@@ -815,17 +977,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  */
                        del_timer(&entry->timer);
                        entry->flags |= ATF_COM;
-                       sti();
+                       arp_update_hhs(entry);
 /* 
  *     Send out waiting packets. We might have problems, if someone is 
  *     manually removing entries right now -- entry might become invalid 
  *     underneath us.
  */
-                       arp_send_q(entry, sha);
-               }
-               else
-               {
-                       sti();
+                       arp_send_q(entry);
                }
        }
        else
@@ -836,94 +994,121 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
                if(entry == NULL)
                {
-                       sti();
+                       arp_unlock();
                        printk("ARP: no memory for new arp entry\n");
-
                        kfree_skb(skb, FREE_READ);
                        return 0;
                }
 
                 entry->mask = DEF_ARP_NETMASK;
                entry->ip = sip;
-               entry->hlen = hlen;
-               entry->htype = htype;
                entry->flags = ATF_COM;
+               entry->hh    = NULL;
                init_timer(&entry->timer);
-               memcpy(entry->ha, sha, hlen);
-               entry->last_used = jiffies;
+               entry->timer.function = arp_expire_request;
+               entry->timer.data = (unsigned long)entry;
+               memcpy(entry->ha, sha, dev->addr_len);
+               entry->last_updated = entry->last_used = jiffies;
                entry->dev = skb->dev;
                skb_queue_head_init(&entry->skb);
-               entry->next = arp_tables[hash];
-               arp_tables[hash] = entry;
-               sti();
+               if (arp_lock == 1)
+               {
+                       entry->next = arp_tables[hash];
+                       arp_tables[hash] = entry;
+               }
+               else
+               {
+#if RT_CACHE_DEBUG >= 1
+                       printk("arp_rcv: %08x backlogged\n", entry->ip);
+#endif
+                       arp_enqueue(&arp_backlog, entry);
+                       arp_bh_mask |= ARP_BH_BACKLOG;
+               }
        }
 
 /*
  *     Replies have been sent, and entries have been added.  All done.
  */
        kfree_skb(skb, FREE_READ);
+       arp_unlock();
        return 0;
 }
 
-
 /*
- *     Find an arp mapping in the cache. If not found, return false.
+ * Lookup ARP entry by (addr, dev) pair.
+ * Flags: ATF_PUBL - search for proxy entries
+ *       ATF_NETMASK - search for proxy network entry.
+ * NOTE:  should be called with locked ARP tables.
  */
 
-int arp_query(unsigned char *haddr, u32 paddr, unsigned short type)
+static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev)
 {
        struct arp_table *entry;
-       unsigned long hash = HASH(paddr);
 
-       /*
-        *      Find an entry
-        */
-       cli();
-
-       for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
-               if (entry->ip == paddr && entry->htype == type)
-                       break;
+       if (!(flags & ATF_PUBL))
+       {
+               for (entry = arp_tables[HASH(paddr)];
+                    entry != NULL; entry = entry->next)
+                       if (entry->ip == paddr && entry->dev == dev)
+                               break;
+               return entry;
+       }
 
-       if (entry != NULL) {
-               /*
-                *      Update the record
-                */
-               
-               entry->last_used = jiffies;
-               memcpy(haddr, entry->ha, entry->hlen);
-               sti();
-               return 1;
+       if (!(flags & ATF_NETMASK))
+       {
+               for (entry = arp_proxy_list;
+                    entry != NULL; entry = entry->next)
+                       if (entry->ip == paddr && entry->dev == dev)
+                               break;
+               return entry;
        }
 
-       sti();
-       return 0;
+       for (entry=arp_proxy_list; entry != NULL; entry = entry->next)
+               if (!((entry->ip^paddr)&entry->mask) && entry->dev == dev)
+                       break;
+       return entry;
 }
 
 /*
- *     Find an arp mapping in the cache. If not found, post a request.
+ *     Find an arp mapping in the cache. If not found, return false.
  */
 
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
-          u32 saddr, struct sk_buff *skb)
+int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
 {
        struct arp_table *entry;
-       unsigned long hash;
-#ifdef CONFIG_IP_MULTICAST
-       u32 taddr;
-#endif 
 
-       switch (ip_chk_addr(paddr))
+       arp_fast_lock();
+
+       entry = arp_lookup(paddr, 0, dev);
+
+       if (entry != NULL)
+       {
+               entry->last_used = jiffies;
+               if (entry->flags & ATF_COM)
+               {
+                       memcpy(haddr, entry->ha, dev->addr_len);
+                       arp_unlock();
+                       return 1;
+               }
+       }
+       arp_unlock();
+       return 0;
+}
+
+
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, __u32 paddr, struct device * dev)
+{
+       switch (addr_hint)
        {
                case IS_MYADDR:
                        printk("ARP: arp called for own IP address\n");
                        memcpy(haddr, dev->dev_addr, dev->addr_len);
-                       skb->arp = 1;
-                       return 0;
+                       return 1;
 #ifdef CONFIG_IP_MULTICAST
                case IS_MULTICAST:
                        if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
                        {
-                               /* What exactly does this do? -- MS */
+                               u32 taddr;
                                haddr[0]=0x01;
                                haddr[1]=0x00;
                                haddr[2]=0x5e;
@@ -933,7 +1118,7 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                                haddr[4]=taddr&0xff;
                                taddr=taddr>>8;
                                haddr[3]=taddr&0x7f;
-                               return 0;
+                               return 1;
                        }
                /*
                 *      If a device does not support multicast broadcast the stuff (eg AX.25 for now)
@@ -942,17 +1127,35 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                
                case IS_BROADCAST:
                        memcpy(haddr, dev->broadcast, dev->addr_len);
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ *     Find an arp mapping in the cache. If not found, post a request.
+ */
+
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+            u32 saddr, struct sk_buff *skb)
+{
+       struct arp_table *entry;
+       unsigned long hash;
+
+       if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
+       {
+               if (skb)
                        skb->arp = 1;
-                       return 0;
+               return 0;
        }
 
        hash = HASH(paddr);
-       cli();
+       arp_fast_lock();
 
        /*
         *      Find an entry
         */
-       entry = arp_lookup(paddr, PROXY_NONE, dev->type);
+       entry = arp_lookup(paddr, 0, dev);
 
        if (entry != NULL)      /* It exists */
        {
@@ -965,10 +1168,30 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                        
                        if (skb != NULL)
                        {
-                               skb_queue_tail(&entry->skb, skb);
-                               skb_device_unlock(skb);
+                               if (entry->last_updated)
+                               {
+                                       skb_queue_tail(&entry->skb, skb);
+                                       skb_device_unlock(skb);
+                               }
+                               /*
+                                * If last_updated==0 host is dead, so
+                                * drop skb's and set socket error.
+                                */
+                               else
+                               {
+                                       /*
+                                        * FIXME: ICMP HOST UNREACHABLE should be
+                                        *        sent in this situation. --ANK
+                                        */
+                                       if (skb->sk)
+                                       {
+                                               skb->sk->err = EHOSTDOWN;
+                                               skb->sk->error_report(skb->sk);
+                                       }
+                                       dev_kfree_skb(skb, FREE_WRITE);
+                               }
                        }
-                       sti();
+                       arp_unlock();
                        return 1;
                }
 
@@ -980,7 +1203,7 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                memcpy(haddr, entry->ha, dev->addr_len);
                if (skb)
                        skb->arp = 1;
-               sti();
+               arp_unlock();
                return 0;
        }
 
@@ -992,42 +1215,49 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                                        GFP_ATOMIC);
        if (entry != NULL)
        {
-               entry->next = arp_tables[hash];
-               entry->last_used = jiffies;
+               entry->last_updated = entry->last_used = jiffies;
                entry->flags = 0;
                entry->ip = paddr;
                entry->mask = DEF_ARP_NETMASK;
                memset(entry->ha, 0, dev->addr_len);
-               entry->hlen = dev->addr_len;
-               entry->htype = dev->type;
                entry->dev = dev;
+               entry->hh    = NULL;
                init_timer(&entry->timer);
                entry->timer.function = arp_expire_request;
                entry->timer.data = (unsigned long)entry;
                entry->timer.expires = jiffies + ARP_RES_TIME;
-               arp_tables[hash] = entry;
-               add_timer(&entry->timer);
-               entry->retries = ARP_MAX_TRIES;
                skb_queue_head_init(&entry->skb);
                if (skb != NULL)
                {
                        skb_queue_tail(&entry->skb, skb);
                        skb_device_unlock(skb);
                }
+               if (arp_lock == 1)
+               {
+                       entry->next = arp_tables[hash];
+                       arp_tables[hash] = entry;
+                       add_timer(&entry->timer);
+                       entry->retries = ARP_MAX_TRIES;
+               }
+               else
+               {
+#if RT_CACHE_DEBUG >= 1
+                       printk("arp_find: %08x backlogged\n", entry->ip);
+#endif
+                       arp_enqueue(&arp_backlog, entry);
+                       arp_bh_mask |= ARP_BH_BACKLOG;
+               }
        }
-       else
-       {
-               if (skb != NULL && skb->free)
-                       kfree_skb(skb, FREE_WRITE);
-       }
-       sti();
+       else if (skb != NULL)
+               dev_kfree_skb(skb, FREE_WRITE);
+       arp_unlock();
 
        /*
         *      If we didn't find an entry, we will try to send an ARP packet.
         */
        
        arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, 
-                dev->dev_addr);
+                dev->dev_addr, NULL);
 
        return 1;
 }
@@ -1042,7 +1272,6 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
 int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
        int len=0;
-       off_t begin=0;
        off_t pos=0;
        int size;
        struct arp_table *entry;
@@ -1050,12 +1279,13 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
        int i,j,k;
        const char hexbuf[] =  "0123456789ABCDEF";
 
-       size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask\n");
+       size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
 
        pos+=size;
        len+=size;
-         
-       cli();
+
+       arp_fast_lock();
+
        for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
        {
                for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
@@ -1065,17 +1295,17 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
  */
 #ifdef CONFIG_AX25
 #ifdef CONFIG_NETROM
-                       if (entry->htype == ARPHRD_AX25 || entry->htype == ARPHRD_NETROM)
+                       if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
                             strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
                        else {
 #else
-                       if(entry->htype==ARPHRD_AX25)
+                       if(entry->dev->type==ARPHRD_AX25)
                             strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
                        else {
 #endif
 #endif
 
-                       for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->hlen;j++)
+                       for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
                        {
                                hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
                                hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
@@ -1089,200 +1319,330 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
                        size = sprintf(buffer+len,
                                "%-17s0x%-10x0x%-10x%s",
                                in_ntoa(entry->ip),
-                               (unsigned int)entry->htype,
+                               (unsigned int)entry->dev->type,
                                entry->flags,
                                hbuffer);
+#if RT_CACHE_DEBUG < 2
                        size += sprintf(buffer+len+size,
-                                "     %-17s\n",
-                                 entry->mask==DEF_ARP_NETMASK?
-                                  "*":in_ntoa(entry->mask));
+                                "     %-17s %s\n",
+                                entry->mask==DEF_ARP_NETMASK ?
+                                "*" : in_ntoa(entry->mask), entry->dev->name);
+#else
+                       size += sprintf(buffer+len+size,
+                                "     %-17s %s\t%ld\t%1d\n",
+                                entry->mask==DEF_ARP_NETMASK ?
+                                "*" : in_ntoa(entry->mask), entry->dev->name, 
+                                entry->hh ? entry->hh->hh_refcnt : -1,
+                                entry->hh ? entry->hh->hh_uptodate : 0);
+#endif
        
-                       len+=size;
-                       pos=begin+len;
+                       len += size;
+                       pos += size;
                  
-                       if(pos<offset)
-                       {
+                       if (pos <= offset)
                                len=0;
-                               begin=pos;
-                       }
-                       if(pos>offset+length)
+                       if (pos >= offset+length)
                                break;
                }
        }
-       sti();
+       arp_unlock();
   
-       *start=buffer+(offset-begin);   /* Start of wanted data */
-       len-=(offset-begin);            /* Start slop */
-       if(len>length)
-               len=length;                     /* Ending slop */
+       *start = buffer+len-(pos-offset);       /* Start of wanted data */
+       len = pos-offset;                       /* Start slop */
+       if (len>length)
+               len = length;                   /* Ending slop */
        return len;
 }
 
 
-/*
- *     This will find an entry in the ARP table by looking at the IP address.
- *     If proxy is PROXY_EXACT then only exact IP matches will be allowed
- *     for proxy entries, otherwise the netmask will be used
- */
 
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type)
+int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
 {
        struct arp_table *entry;
-       unsigned long hash = HASH(paddr);
-       
-       for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
-               if (entry->ip == paddr && entry->htype == type)
-                       break;
+       struct hh_cache *hh = *hhp;
+       int addr_hint;
+       unsigned long flags;
 
-       /* it's possibly a proxy entry (with a netmask) */
-       if (!entry && proxy != PROXY_NONE)
-       for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next)
-         if ((proxy==PROXY_EXACT) ? (entry->ip==paddr)
-                                  : !((entry->ip^paddr)&entry->mask)) 
-           break;        
+       if (hh)
+               return 1;
 
-       return entry;
-}
+       if ((addr_hint = ip_chk_addr(paddr)) != 0)
+       {
+               unsigned char haddr[MAX_ADDR_LEN];
+               if (hh)
+                       return 1;
+               hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+               if (!hh)
+                       return 1;
+               arp_set_predefined(addr_hint, haddr, paddr, dev);
+               hh->hh_uptodate = 0;
+               hh->hh_refcnt = 1;
+               hh->hh_arp = NULL;
+               hh->hh_next = NULL;
+               hh->hh_type = htype;
+               *hhp = hh;
+               dev->header_cache_update(hh, dev, haddr);
+               return 0;
+       }
 
+       save_flags(flags);
 
-int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev)
-{      
-       /* 
-        *      We need the broadcast/multicast awareness here and the find routine split up.
-        */
-       struct arp_table *entry;
-#ifdef CONFIG_IP_MULTICAST
-       u32 taddr;
-#endif 
+       arp_fast_lock();
 
-       switch (ip_chk_addr(daddr))
+       entry = arp_lookup(paddr, 0, dev);
+
+       if (entry)
        {
-               case IS_MYADDR:
-                       printk("ARP: arp called for own IP address\n");
-                       memcpy(dp, dev->dev_addr, dev->addr_len);
+               cli();
+               for (hh = entry->hh; hh; hh=hh->hh_next)
+                       if (hh->hh_type == htype)
+                               break;
+               if (hh)
+               {
+                       hh->hh_refcnt++;
+                       *hhp = hh;
+                       restore_flags(flags);
+                       arp_unlock();
                        return 1;
-#ifdef CONFIG_IP_MULTICAST
-               case IS_MULTICAST:
-                       if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+               }
+               restore_flags(flags);
+       }
+
+       hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+       if (!hh)
+       {
+               arp_unlock();
+               return 1;
+       }
+
+       hh->hh_uptodate = 0;
+       hh->hh_refcnt = 1;
+       hh->hh_arp = NULL;
+       hh->hh_next = NULL;
+       hh->hh_type = htype;
+
+       if (entry)
+       {
+               dev->header_cache_update(hh, dev, entry->ha);
+               *hhp = hh;
+               cli();
+               hh->hh_arp = (void*)entry;
+               entry->hh = hh;
+               hh->hh_refcnt++;
+               restore_flags(flags);
+               entry->last_used = jiffies;
+               arp_unlock();
+               return 0;
+       }
+
+
+       /*
+        *      Create a new unresolved entry.
+        */
+       
+       entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+                                       GFP_ATOMIC);
+       if (entry == NULL)
+       {
+               kfree_s(hh, sizeof(struct hh_cache));
+               arp_unlock();
+               return 1;
+       }
+
+       entry->last_updated = entry->last_used = jiffies;
+       entry->flags = 0;
+       entry->ip = paddr;
+       entry->mask = DEF_ARP_NETMASK;
+       memset(entry->ha, 0, dev->addr_len);
+       entry->dev = dev;
+       entry->hh = hh;
+       ATOMIC_INCR(&hh->hh_refcnt);
+       init_timer(&entry->timer);
+       entry->timer.function = arp_expire_request;
+       entry->timer.data = (unsigned long)entry;
+       entry->timer.expires = jiffies + ARP_RES_TIME;
+       skb_queue_head_init(&entry->skb);
+
+       if (arp_lock == 1)
+       {
+               unsigned long hash = HASH(paddr);
+               cli();
+               entry->next = arp_tables[hash];
+               arp_tables[hash] = entry;
+               hh->hh_arp = (void*)entry;
+               entry->retries = ARP_MAX_TRIES;
+               restore_flags(flags);
+
+               add_timer(&entry->timer);
+               arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+       }
+       else
+       {
+#if RT_CACHE_DEBUG >= 1
+               printk("arp_cache_bind: %08x backlogged\n", entry->ip);
+#endif
+               arp_enqueue(&arp_backlog, entry);
+               arp_bh_mask |= ARP_BH_BACKLOG;
+       }
+       *hhp = hh;
+       arp_unlock();
+       return 0;
+}
+
+static void arp_run_bh()
+{
+       unsigned long flags;
+       struct arp_table *entry, *entry1;
+       struct hh_cache *hh;
+       __u32 sip;
+
+       save_flags(flags);
+       cli();
+       if (!arp_lock)
+       {
+               arp_fast_lock();
+
+               while ((entry = arp_dequeue(&arp_backlog)) != NULL)
+               {
+                       unsigned long hash;
+                       sti();
+                       sip = entry->ip;
+                       hash = HASH(sip);
+
+                       /* It's possible, that an entry with the same pair 
+                        * (addr,type) was already created. Our entry is older,
+                        * so it should be discarded.
+                        */
+                       for (entry1=arp_tables[hash]; entry1; entry1=entry1->next)
+                               if (entry1->ip==sip && entry1->dev == entry->dev)
+                                       break;
+
+                       if (!entry1)
                        {
-                               dp[0]=0x01;
-                               dp[1]=0x00;
-                               dp[2]=0x5e;
-                               taddr=ntohl(daddr);
-                               dp[5]=taddr&0xff;
-                               taddr=taddr>>8;
-                               dp[4]=taddr&0xff;
-                               taddr=taddr>>8;
-                               dp[3]=taddr&0x7f;
-                               return 1;
-                       }
-               /*
-                *      If a device does not support multicast broadcast the stuff (eg AX.25 for now)
-                */
+                               struct device  * dev = entry->dev;
+                               cli();
+                               entry->next = arp_tables[hash];
+                               arp_tables[hash] = entry;
+                               for (hh=entry->hh; hh; hh=hh->hh_next)
+                                       hh->hh_arp = (void*)entry;
+                               sti();
+                               del_timer(&entry->timer);
+                               entry->timer.expires = jiffies + ARP_RES_TIME;
+                               add_timer(&entry->timer);
+                               entry->retries = ARP_MAX_TRIES;
+                               arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+#if RT_CACHE_DEBUG >= 1
+                               printk("arp_run_bh: %08x reinstalled\n", sip);
 #endif
-               
-               case IS_BROADCAST:
-                       memcpy(dp, dev->broadcast, dev->addr_len);
-                       return 1;
-                       
-               default:
-                       entry=arp_lookup(daddr, PROXY_NONE, dev->type);
-                       if(entry)
+                       }
+                       else
                        {
-                               memcpy(dp,entry->ha, ETH_ALEN);
-                               return 1;
+                               struct sk_buff * skb;
+                               struct hh_cache * next;
+
+                               /* Discard entry, but preserve its hh's and
+                                * skb's.
+                                */
+                               cli();
+                               for (hh=entry->hh; hh; hh=next)
+                               {
+                                       next = hh->hh_next;
+                                       hh->hh_next = entry1->hh;
+                                       entry1->hh = hh;
+                                       hh->hh_arp = (void*)entry1;
+                               }
+                               entry->hh = NULL;
+
+                               /* Prune skb list from entry
+                                * and graft it to entry1.
+                                */
+                               while ((skb = skb_dequeue(&entry->skb)) != NULL)
+                               {
+                                       skb_device_lock(skb);
+                                       sti();
+                                       skb_queue_tail(&entry1->skb, skb);
+                                       skb_device_unlock(skb);
+                                       cli();
+                               }
+                               sti();
+                               
+#if RT_CACHE_DEBUG >= 1
+                               printk("arp_run_bh: entry %08x was born dead\n", entry->ip);
+#endif
+                               arp_free_entry(entry);
+
+                               if (entry1->flags & ATF_COM)
+                               {
+                                       arp_update_hhs(entry1);
+                                       arp_send_q(entry1);
+                               }
                        }
+                       cli();
+               }
+               arp_bh_mask  &= ~ARP_BH_BACKLOG;
+               arp_unlock();
        }
-       return 0;
+       restore_flags(flags);
 }
 
+
 /*
  *     Set (create) an ARP cache entry.
  */
 
-static int arp_req_set(struct arpreq *req)
+static int arp_req_set(struct arpreq *r, struct device * dev)
 {
-       struct arpreq r;
        struct arp_table *entry;
        struct sockaddr_in *si;
-       int htype, hlen;
        struct rtable *rt;
+       struct device * dev1;
        u32 ip;
 
-       memcpy_fromfs(&r, req, sizeof(r));
-
-       /* We only understand about IP addresses... */
-       if (r.arp_pa.sa_family != AF_INET)
-               return -EPFNOSUPPORT;
-
        /*
         * Find out about the hardware type.
         * We have to be compatible with BSD UNIX, so we have to
         * assume that a "not set" value (i.e. 0) means Ethernet.
+        *
+        * ANK: Hey, who wrote it? Do you really mean that BSD considers 
+        *      ARPHRD_NETROM as ARPHRD_ETHER, or somthing another?
         */
        
-       switch (r.arp_ha.sa_family) {
-               case ARPHRD_ETHER:
-                       htype = ARPHRD_ETHER;
-                       hlen = ETH_ALEN;
-                       break;
-
-               case ARPHRD_ARCNET:
-                       htype = ARPHRD_ARCNET;
-                       hlen = 1;       /* length of arcnet addresses */
-                       break;
-
-#ifdef CONFIG_AX25
-               case ARPHRD_AX25:
-                       htype = ARPHRD_AX25;
-                       hlen = AX25_ADDR_LEN;
-                       break;
-#endif
-#ifdef CONFIG_NETROM
-               case ARPHRD_NETROM:
-                       htype = ARPHRD_NETROM;
-                       hlen = AX25_ADDR_LEN;
-                       break;
-#endif
-               case ARPHRD_IEEE802:
-                       htype = ARPHRD_IEEE802;
-                       hlen = TR_ALEN;
-                       break;
-               default:
-                       return -EPFNOSUPPORT;
-       }
-
-       si = (struct sockaddr_in *) &r.arp_pa;
+       si = (struct sockaddr_in *) &r->arp_pa;
        ip = si->sin_addr.s_addr;
-       if (ip == 0)
-       {
-               printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
-               return -EINVAL;
-       }
 
        /*
-        *      Is it reachable directly ?
+        *      Is it reachable ?
         */
 
-       rt = ip_rt_route(ip, NULL, NULL);
-       if (rt == NULL)
+       rt = ip_rt_route(ip, 0);
+       if (!rt)
                return -ENETUNREACH;
+       dev1 = rt->rt_dev;
+       ip_rt_put(rt);
+
+       if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
+           (!(r->arp_flags & ATF_PUBL) && dev != dev1))
+               return -EINVAL;
+
+#if RT_CACHE_DEBUG >= 1
+       if (arp_lock)
+               printk("arp_req_set: bug\n");
+#endif
+       arp_fast_lock();
 
        /*
         *      Is there an existing entry for this address?
         */
-       
-       cli();
 
        /*
         *      Find the entry
         */
-       entry = arp_lookup(ip, PROXY_EXACT, htype);
-       if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL))
+       
+       entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev);
+
+       if (entry)
        {
-               sti();
-               arp_destroy(ip,1);
-               cli();
+               arp_destroy(entry);
                entry = NULL;
        }
 
@@ -1292,77 +1652,83 @@ static int arp_req_set(struct arpreq *req)
        
        if (entry == NULL)
        {
-               unsigned long hash = HASH(ip);
-               if (r.arp_flags & ATF_PUBL)
-                 hash = PROXY_HASH;
-
                entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
                                        GFP_ATOMIC);
                if (entry == NULL)
                {
-                       sti();
+                       arp_unlock();
                        return -ENOMEM;
                }
                entry->ip = ip;
-               entry->hlen = hlen;
-               entry->htype = htype;
+               entry->hh = NULL;
                init_timer(&entry->timer);
-               entry->next = arp_tables[hash];
-               arp_tables[hash] = entry;
+               entry->timer.function = arp_expire_request;
+               entry->timer.data = (unsigned long)entry;
+
+               if (r->arp_flags & ATF_PUBL)
+               {
+                       cli();
+                       entry->next = arp_proxy_list;
+                       arp_proxy_list = entry;
+                       sti();
+               }
+               else
+               {
+                       unsigned long hash = HASH(ip);
+                       cli();
+                       entry->next = arp_tables[hash];
+                       arp_tables[hash] = entry;
+                       sti();
+               }
                skb_queue_head_init(&entry->skb);
        }
        /*
         *      We now have a pointer to an ARP entry.  Update it!
         */
        
-       memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
-       entry->last_used = jiffies;
-       entry->flags = r.arp_flags | ATF_COM;
+       if ((r->arp_flags & ATF_COM) && !r->arp_ha.sa_data[0])
+               memcpy(&entry->ha, dev->dev_addr, dev->addr_len);
+       else
+               memcpy(&entry->ha, &r->arp_ha.sa_data, dev->addr_len);
+       entry->last_updated = entry->last_used = jiffies;
+       entry->flags = r->arp_flags | ATF_COM;
        if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK))
-         {
-           si = (struct sockaddr_in *) &r.arp_netmask;
-           entry->mask = si->sin_addr.s_addr;
-         }
+       {
+               si = (struct sockaddr_in *) &r->arp_netmask;
+               entry->mask = si->sin_addr.s_addr;
+       }
        else
-         entry->mask = DEF_ARP_NETMASK;
-       entry->dev = rt->rt_dev;
-       arp_cache_stamp++;
-       sti();
-
+               entry->mask = DEF_ARP_NETMASK;
+       entry->dev = dev;
+       arp_update_hhs(entry);
+       arp_unlock();
        return 0;
 }
 
 
+
 /*
  *     Get an ARP cache entry.
  */
 
-static int arp_req_get(struct arpreq *req)
+static int arp_req_get(struct arpreq *r, struct device *dev)
 {
-       struct arpreq r;
        struct arp_table *entry;
        struct sockaddr_in *si;
 
-       /*
-        *      We only understand about IP addresses...
-        */
-       
-       memcpy_fromfs(&r, req, sizeof(r));
+       si = (struct sockaddr_in *) &r->arp_pa;
 
-       if (r.arp_pa.sa_family != AF_INET)
-               return -EPFNOSUPPORT;
+#if RT_CACHE_DEBUG >= 1
+       if (arp_lock)
+               printk("arp_req_set: bug\n");
+#endif
+       arp_fast_lock();
 
-       /*
-        *      Is there an existing entry for this address?
-        */
-       
-       si = (struct sockaddr_in *) &r.arp_pa;
-       cli();
-       entry = arp_lookup(si->sin_addr.s_addr, PROXY_ANY, r.arp_ha.sa_family);
+       entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev);
 
        if (entry == NULL)
        {
-               sti();
+               arp_unlock();
                return -ENXIO;
        }
 
@@ -1370,19 +1736,54 @@ static int arp_req_get(struct arpreq *req)
         *      We found it; copy into structure.
         */
        
-       memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
-       r.arp_ha.sa_family = entry->htype;
-       r.arp_flags = entry->flags;
-       sti();
-
-       /*
-        *      Copy the information back
-        */
-       
-       memcpy_tofs(req, &r, sizeof(r));
+       memcpy(r->arp_ha.sa_data, &entry->ha, entry->dev->addr_len);
+       r->arp_ha.sa_family = entry->dev->type;
+       r->arp_flags = entry->flags;
+       strncpy(r->arp_dev, entry->dev->name, 16);
+       arp_unlock();
        return 0;
 }
 
+static int arp_req_delete(struct arpreq *r, struct device * dev)
+{
+       struct arp_table *entry;
+       struct sockaddr_in *si;
+
+       si = (struct sockaddr_in *) &r->arp_pa;
+#if RT_CACHE_DEBUG >= 1
+       if (arp_lock)
+               printk("arp_req_delete: bug\n");
+#endif
+       arp_fast_lock();
+
+       if (!(r->arp_flags & ATF_PUBL))
+       {
+               for (entry = arp_tables[HASH(si->sin_addr.s_addr)];
+                    entry != NULL; entry = entry->next)
+                       if (entry->ip == si->sin_addr.s_addr 
+                           && entry->dev == dev)
+                       {
+                               arp_destroy(entry);
+                               arp_unlock();
+                               return 0;
+                       }
+       }
+       else
+       {
+               for (entry = arp_proxy_list;
+                    entry != NULL; entry = entry->next)
+                       if (entry->ip == si->sin_addr.s_addr 
+                           && entry->dev == dev) 
+                       {
+                               arp_destroy(entry);
+                               arp_unlock();
+                               return 0;
+                       }
+       }
+
+       arp_unlock();
+       return -ENXIO;
+}
 
 /*
  *     Handle an ARP layer I/O control request.
@@ -1390,39 +1791,116 @@ static int arp_req_get(struct arpreq *req)
 
 int arp_ioctl(unsigned int cmd, void *arg)
 {
-       struct arpreq r;
-       struct sockaddr_in *si;
        int err;
+       struct arpreq r;
+
+       struct device * dev = NULL;
 
        switch(cmd)
        {
                case SIOCDARP:
+               case SIOCSARP:
                        if (!suser())
                                return -EPERM;
-                       err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
-                       if(err)
-                               return err;
-                       memcpy_fromfs(&r, arg, sizeof(r));
-                       if (r.arp_pa.sa_family != AF_INET)
-                               return -EPFNOSUPPORT;
-                       si = (struct sockaddr_in *) &r.arp_pa;
-                       arp_destroy(si->sin_addr.s_addr, 1);
-                       return 0;
                case SIOCGARP:
-                       err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
-                       if(err)
+                       err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+                       if (err)
                                return err;
-                       return arp_req_get((struct arpreq *)arg);
-               case SIOCSARP:
+                       memcpy_fromfs(&r, arg, sizeof(struct arpreq));
+                       break;
+               case OLD_SIOCDARP:
+               case OLD_SIOCSARP:
                        if (!suser())
                                return -EPERM;
-                       err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
-                       if(err)
+               case OLD_SIOCGARP:
+                       err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq_old));
+                       if (err)
                                return err;
-                       return arp_req_set((struct arpreq *)arg);
+                       memcpy_fromfs(&r, arg, sizeof(struct arpreq_old));
+                       memset(&r.arp_dev, 0, sizeof(r.arp_dev));
+                       break;
                default:
                        return -EINVAL;
        }
+
+       if (r.arp_pa.sa_family != AF_INET)
+               return -EPFNOSUPPORT;
+       if (((struct sockaddr_in *)&r.arp_pa)->sin_addr.s_addr == 0)
+               return -EINVAL;
+
+       if (r.arp_dev[0])
+       {
+               if ((dev = dev_get(r.arp_dev)) == NULL)
+                       return -ENODEV;
+
+               if (!r.arp_ha.sa_family)
+                       r.arp_ha.sa_family = dev->type;
+               else if (r.arp_ha.sa_family != dev->type)
+                       return -EINVAL;
+       }
+       else
+       {
+               /*
+                * Device was not specified. Take the first suitable one.
+                */
+               if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL)
+                       return -ENODEV;
+       }
+
+       switch(cmd)
+       {
+               case SIOCDARP:
+                       return arp_req_delete(&r, dev);
+               case SIOCSARP:
+                       return arp_req_set(&r, dev);
+               case OLD_SIOCDARP:
+                       /* old  SIOCDARP destoyes both
+                        * normal and proxy mappings
+                        */
+                       r.arp_flags &= ~ATF_PUBL;
+                       err = arp_req_delete(&r, dev);
+                       r.arp_flags |= ATF_PUBL;
+                       if (!err)
+                               arp_req_delete(&r, dev);
+                       else
+                               err = arp_req_delete(&r, dev);
+                       return err;
+               case OLD_SIOCSARP:
+                       err = arp_req_set(&r, dev);
+                       /* old SIOCSARP works so funny,
+                        * that its behaviour can be emulated
+                        * only approximately 8).
+                        * It should work. --ANK
+                        */
+                       if (r.arp_flags & ATF_PUBL)
+                       {       
+                               r.arp_flags &= ~ATF_PUBL;
+                               arp_req_delete(&r, dev);
+                       }
+                       return err;
+               case SIOCGARP:
+                       err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+                       if (err)
+                               return err;
+                       err = arp_req_get(&r, dev);
+                       if (!err)
+                               memcpy_tofs(arg, &r, sizeof(r));
+                       return err;
+               case OLD_SIOCGARP:
+                       err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq_old));
+                       if (err)
+                               return err;
+                       r.arp_flags &= ~ATF_PUBL;
+                       err = arp_req_get(&r, dev);
+                       if (err < 0)
+                       {
+                               r.arp_flags |= ATF_PUBL;
+                               err = arp_req_get(&r, dev);
+                       }
+                       if (!err)
+                               memcpy_tofs(arg, &r, sizeof(struct arpreq_old));
+                       return err;
+       }
        /*NOTREACHED*/
        return 0;
 }
index 1c91d9a7c911b4b60185a55d6ab14d9e887ae833..b761ba93f3c1210ba8f35ad36b4d0b06dab4b3c3 100644 (file)
@@ -227,3 +227,16 @@ struct device *ip_dev_find(unsigned long addr)
        }
        return NULL;
 }
+
+struct device *dev_getbytype(unsigned short type)
+{
+       struct device *dev;
+
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
+                       return(dev);
+       }
+       return(NULL);
+}
+
index 7e815223e67799bb4341062582da6f972fbfabc3..a6d0b43ec481d18f60625a178ea8862bf7dc80c1 100644 (file)
@@ -362,9 +362,51 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
                        case ICMP_PORT_UNREACH:
                                break;
                        case ICMP_FRAG_NEEDED:
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
                                printk("ICMP: %s: fragmentation needed and DF set.\n",
                                                                in_ntoa(iph->daddr));
                                break;
+#else
+                       {
+                               unsigned short old_mtu = ntohs(iph->tot_len);
+                               unsigned short new_mtu = ntohs(icmph->un.echo.sequence);
+
+                               if (new_mtu < 68 || new_mtu >= old_mtu)
+                               {
+                                       /*
+                                        *      It is either dumb router, which does not
+                                        *      understand Path MTU Disc. protocol
+                                        *      or broken (f.e. Linux<=1.3.37 8) router.
+                                        *      Try to guess...
+                                        *      The table is taken from RFC-1191.
+                                        */
+                                       if (old_mtu > 32000)
+                                               new_mtu = 32000;
+                                       else if (old_mtu > 17914)
+                                               new_mtu = 17914;
+                                       else if (old_mtu > 8166)
+                                               new_mtu = 8166;
+                                       else if (old_mtu > 4352)
+                                               new_mtu = 4352;
+                                       else if (old_mtu > 2002)
+                                               new_mtu = 2002;
+                                       else if (old_mtu > 1492)
+                                               new_mtu = 1492;
+                                       else if (old_mtu > 576)
+                                               new_mtu = 576;
+                                       else if (old_mtu > 296)
+                                               new_mtu = 296;
+                                       else
+                                               new_mtu = 68;
+                               }
+                               /*
+                                * Ugly trick to pass MTU to protocol layer.
+                                * Really we should add argument "info" to error handler.
+                                */
+                               iph->id = htons(new_mtu);
+                               break;
+                       }
+#endif
                        case ICMP_SR_FAILED:
                                printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
                                break;
@@ -427,9 +469,6 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
 
 static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 source, __u32 daddr, int len)
 {
-#ifndef CONFIG_IP_FORWARD
-       struct rtable *rt;
-#endif
        struct iphdr *iph;
        unsigned long ip;
 
@@ -472,16 +511,8 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct dev
                         *      (not some confused thing sending our
                         *      address)
                         */
-                       rt = ip_rt_route(ip, NULL, NULL);
-                       if (!rt)
-                               break;
-                       if (rt->rt_gateway != source || 
-                               ((icmph->un.gateway^dev->pa_addr)&dev->pa_mask) ||
-                               ip_chk_addr(icmph->un.gateway))
-                               break;
                        printk("ICMP redirect from %s\n", in_ntoa(source));
-                       ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY),
-                               ip, 0, icmph->un.gateway, dev,0, 0, 0, 0);
+                       ip_rt_redirect(source, ip, icmph->un.gateway, dev);
                        break;
                case ICMP_REDIR_NETTOS:
                case ICMP_REDIR_HOSTTOS:
index b61d3c6bbb652be80cc614801e71d9e0c74f78fd..56e0a673fbe840bf70e7379c7083b023f0a0167d 100644 (file)
@@ -103,7 +103,7 @@ static void igmp_send_report(struct device *dev, unsigned long address, int type
        if(skb==NULL)
                return;
        tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
-                               28 , 0, 1);
+                               28 , 0, 1, NULL);
        if(tmp<0)
        {
                kfree_skb(skb, FREE_WRITE);
index 3e1ef8bef9da54391c6296ceec40aeba8c7d6428..b6eec53f540c8a755c2ced1de449b54cdfba6765 100644 (file)
@@ -90,6 +90,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
        unsigned char *ptr;     /* Data pointer */
        unsigned long raddr;    /* Router IP address */
        struct   options * opt  = (struct options*)skb->proto_priv;
+       struct hh_cache *hh = NULL;
        int encap = 0;          /* Encap length */
 #ifdef CONFIG_FIREWALL
        int fw_res = 0;         /* Forwarding result */ 
@@ -159,7 +160,8 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                 * and give it to the IP sender for further processing.
                 */
 
-               rt = ip_rt_route(target_addr, NULL, NULL);
+               rt = ip_rt_route(target_addr, 0);
+
                if (rt == NULL)
                {
                        /*
@@ -181,31 +183,22 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
 
                raddr = rt->rt_gateway;
        
-               if (raddr != 0)
-               {
+               if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
                        /*
                         *      Strict routing permits no gatewaying
                         */
        
-                       if (opt->is_strictroute)
-                       {
-                               icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
-                               return -1;
-                       }
-               
-                       /*
-                        *      There is a gateway so find the correct route for it.
-                        *      Gateways cannot in turn be gatewayed.
-                        */
+                       ip_rt_put(rt);
+                       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+                       return -1;
                }
-               else
-                       raddr = target_addr;
 
                /*
                 *      Having picked a route we can now send the frame out.
                 */
 
                dev2 = rt->rt_dev;
+               hh = rt->rt_hh;
                /*
                 *      In IP you never have to forward a frame on the interface that it 
                 *      arrived upon. We now generate an ICMP HOST REDIRECT giving the route
@@ -227,6 +220,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                raddr=skb->raddr;
                if(is_frag&16)          /* VIFF_TUNNEL mode */
                        encap=20;
+               rt=NULL;
        }
 #endif 
        
@@ -250,7 +244,8 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
 
                if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
                  ip_statistics.IpFragFails++;
-                 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev);
+                 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
+                 ip_rt_put(rt);
                  return -1;
                }
 
@@ -271,6 +266,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                        if (skb2 == NULL)
                        {
                                NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
+                               ip_rt_put(rt);
                                return -1;
                        }
                
@@ -286,7 +282,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                        }
                        else
 #endif                 
-                               ip_send(skb2,raddr,skb->len,dev2,dev2->pa_addr);
+                               ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr);
 
                        /*
                         *      We have to copy the bytes over as the new header wouldn't fit
@@ -320,7 +316,18 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
 #endif
                                skb->arp=1;
                                skb->raddr=raddr;
-                               if(dev2->hard_header)
+                               if (hh)
+                               {
+                                       memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len);
+                                       if (!hh->hh_uptodate)
+                                       {
+#if RT_CACHE_DEBUG >= 2
+                                               printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway);
+#endif                                         
+                                               skb->arp = 0;
+                                       }
+                               }
+                               else if (dev2->hard_header)
                                {
                                        if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
                                                skb->arp=0;
@@ -418,7 +425,11 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                }
        }
        else
+       {
+               ip_rt_put(rt);
                return -1;
+       }
+       ip_rt_put(rt);
        
        /*
         *      Tell the caller if their buffer is free.
index 2f9c485c6d81fd01295c3d4668a43abfadbb8f72..4753a2653fdb7dce21c7a8e47afae40c916e2f0e 100644 (file)
@@ -196,8 +196,8 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int pol
        __u16                   src_port=0, dst_port=0, icmp_type=0;
        unsigned short          f_prt=0, prt;
        char                    notcpsyn=1, notcpack=1, match;
-       unsigned short          f_flag;
        unsigned short          offset;
+       int                     answer, priority;
 
        /*
         *      If the chain is empty follow policy. The BSD one
@@ -465,24 +465,39 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, struct ip_fw *chain, int pol
                        break;
        } /* Loop */
        
-       if(opt == 1)
-               return 0;
-
+       answer = FW_BLOCK;
+       
        /*
         * We rely on policy defined in the rejecting entry or, if no match
         * was found, we rely on the general policy variable for this type
         * of firewall.
         */
 
-       if(f!=NULL)     /* A match was found */
-               f_flag=f->fw_flg;
+       if(f!=NULL) 
+       {
+               policy=f->fw_flg;
+               priority=f->fw_priority;
+       }
        else
-               f_flag=policy;
-       if(f_flag&IP_FW_F_ACCEPT)
-               return ((f_flag&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT);
-       if(f_flag&IP_FW_F_ICMPRPL)
-               return FW_REJECT;
-       return FW_BLOCK;
+               priority=0xFF00;
+
+       if(opt != 1) 
+       {
+               if(policy&IP_FW_F_ACCEPT)
+                       answer=(policy&IP_FW_F_MASQ)?FW_MASQUERADE:FW_ACCEPT;
+               else
+                       if(policy&IP_FW_F_ICMPRPL)
+                               answer = FW_REJECT;
+       }
+
+       if (answer == 0) { /* Adjust priority and recompute checksum */
+               __u8 old_tos = ip->tos;
+               ip->tos = (old_tos & (priority>>8)) ^ priority;
+               if (ip->tos != old_tos)
+                       ip_send_check(ip);
+       }
+
+       return answer;
 }
 
 #ifdef CONFIG_IP_MASQUERADE
@@ -1022,7 +1037,7 @@ static void free_fw_chain(struct ip_fw *volatile* chainptr)
 
 /* Volatiles to keep some of the compiler versions amused */
 
-static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl)
+static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
 {
        struct ip_fw *ftmp;
        struct ip_fw *chtmp=NULL;
@@ -1046,8 +1061,11 @@ static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl)
                return( ENOMEM );
        }
 
-       memcpy(ftmp, frwl, sizeof( struct ip_fw ) );
+       memcpy(ftmp, frwl, len);
+       if (len == sizeof (struct ip_fw_old))
+               ftmp->fw_priority = 0xFF00; /* and_mask, xor_mask */
 
+       ftmp->fw_priority = (ftmp->fw_priority & 0xFFFC) | 0x0300;
        ftmp->fw_pcnt=0L;
        ftmp->fw_bcnt=0L;
 
@@ -1286,7 +1304,7 @@ static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl)
 struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
 {
 
-       if ( len != sizeof(struct ip_fw) )
+       if ( len != sizeof(struct ip_fw) && len != sizeof(struct ip_fw_old))
        {
 #ifdef DEBUG_CONFIG_IP_FIREWALL
                printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
@@ -1370,7 +1388,7 @@ int ip_acct_ctl(int stage, void *m, int len)
                switch (stage) 
                {
                        case IP_ACCT_ADD:
-                               return( add_to_chain(&ip_acct_chain,frwl));
+                               return( add_to_chain(&ip_acct_chain,frwl,len));
                        case IP_ACCT_DEL:
                                return( del_from_chain(&ip_acct_chain,frwl));
                        default:
@@ -1489,9 +1507,9 @@ int ip_fw_ctl(int stage, void *m, int len)
                switch (stage) 
                {
                        case IP_FW_ADD_BLK:
-                               return(add_to_chain(&ip_fw_blk_chain,frwl));
+                               return(add_to_chain(&ip_fw_blk_chain,frwl,len));
                        case IP_FW_ADD_FWD:
-                               return(add_to_chain(&ip_fw_fwd_chain,frwl));
+                               return(add_to_chain(&ip_fw_fwd_chain,frwl,len));
                        case IP_FW_DEL_BLK:
                                return(del_from_chain(&ip_fw_blk_chain,frwl));
                        case IP_FW_DEL_FWD: 
@@ -1565,6 +1583,7 @@ static int ip_chain_procinfo(int stage, char *buffer, char **start,
                        i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
                for (p = 0; p < IP_FW_MAX_PORTS; p++)
                        len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
+               len+=sprintf(buffer+len, " M%04X", i->fw_priority);
                buffer[len++]='\n';
                buffer[len]='\0';
                pos=begin+len;
index 5a320c00b5513bcddd9a025908ea6d14b672339f..8162e1949ea4c076f18be2214949c981218ff032 100644 (file)
@@ -118,14 +118,14 @@ int ip_options_echo(struct options * dopt, struct options * sopt,
                memcpy(dptr, sptr+sopt->ts, optlen);
                if (soffset <= optlen) 
                {
-                       if (dopt->ts_needaddr) 
+                       if (sopt->ts_needaddr) 
                        {
                                if (soffset + 3 > optlen)
                                        return -EINVAL;
                                dopt->ts_needaddr = 1;
                                soffset += 4;
                        }
-                       if (dopt->ts_needtime) 
+                       if (sopt->ts_needtime) 
                        {
                                if (soffset + 3 > optlen)
                                        return -EINVAL;
@@ -376,10 +376,8 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
                                      case IPOPT_TS_TSONLY:
                                        opt->ts = optptr - iph;
                                        if (skb) 
-                                       {
                                                timeptr = (__u32*)&optptr[ts->ptr-1];
-                                               opt->is_changed = 1;
-                                       }
+                                       opt->ts_needtime = 1;
                                        ts->ptr += 4;
                                        break;
                                      case IPOPT_TS_TSANDADDR:
index 1d05e2f7e632b11da3b1518b49ab339f7916ea27..5340b9acf21820bf7d638401a48b7f962bc05175 100644 (file)
@@ -86,7 +86,7 @@ static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
        /*
         *      Put a MAC header on the packet
         */
-       ip_send(newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
+       ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
        /*
         *      Add the rest of the data space. 
         */
@@ -110,7 +110,7 @@ static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
  *     Take an skb, and fill in the MAC header.
  */
 
-int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
 {
        int mac = 0;
 
@@ -123,6 +123,18 @@ int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32
                 *      (rebuild header will sort this out)
                 */
                skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are good */
+               if (rt && dev == rt->rt_dev && rt->rt_hh)
+               {
+                       memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+                       if (rt->rt_hh->hh_uptodate)
+                               return dev->hard_header_len;
+#if RT_CACHE_DEBUG >= 2
+                       printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
+#endif
+                       skb->arp = 0;
+                       skb->raddr = daddr;
+                       return -dev->hard_header_len;
+               }
                mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
                if (mac < 0)
                {
@@ -134,7 +146,7 @@ int ip_send(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32
        return mac;
 }
 
-static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
 {
        int mac = 0;
 
@@ -143,6 +155,18 @@ static int ip_send_room(struct sk_buff *skb, __u32 daddr, int len, struct device
        if (dev->hard_header)
        {
                skb_reserve(skb,MAX_HEADER);
+               if (rt && dev == rt->rt_dev && rt->rt_hh)
+               {
+                       memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+                       if (rt->rt_hh->hh_uptodate)
+                               return dev->hard_header_len;
+#if RT_CACHE_DEBUG >= 2
+                       printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
+#endif
+                       skb->arp = 0;
+                       skb->raddr = daddr;
+                       return -dev->hard_header_len;
+               }
                mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
                if (mac < 0)
                {
@@ -163,15 +187,16 @@ int ip_id_count = 0;
  * routing/ARP tables to select a device struct.
  */
 int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
-               struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
+               struct device **dev, int type, struct options *opt,
+               int len, int tos, int ttl, struct rtable ** rp)
 {
        struct rtable *rt;
        __u32 raddr;
        int tmp;
-       __u32 src;
        struct iphdr *iph;
        __u32 final_daddr = daddr;
 
+
        if (opt && opt->srr)
                daddr = opt->faddr;
 
@@ -183,12 +208,22 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
        if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
                *dev=dev_get(skb->sk->ip_mc_name);
 #endif
+       if (rp)
+       {
+               rt = ip_check_route(rp, daddr, skb->localroute);
+               /*
+                * If rp != NULL rt_put following below should not
+                * release route, so that...
+                */
+               if (rt)
+                       ATOMIC_INCR(&rt->rt_refcnt);
+       }
+       else
+               rt = ip_rt_route(daddr, skb->localroute);
+
+
        if (*dev == NULL)
        {
-               if(skb->localroute)
-                       rt = ip_rt_local(daddr, NULL, &src);
-               else
-                       rt = ip_rt_route(daddr, NULL, &src);
                if (rt == NULL)
                {
                        ip_statistics.IpOutNoRoutes++;
@@ -196,43 +231,24 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
                }
 
                *dev = rt->rt_dev;
-               /*
-                *      If the frame is from us and going off machine it MUST MUST MUST
-                *      have the output device ip address and never the loopback
-                */
-               if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-                       saddr = src;/*rt->rt_dev->pa_addr;*/
-               raddr = rt->rt_gateway;
-
        }
-       else
-       {
-               /*
-                *      We still need the address of the first hop.
-                */
-               if(skb->localroute)
-                       rt = ip_rt_local(daddr, NULL, &src);
-               else
-                       rt = ip_rt_route(daddr, NULL, &src);
-               /*
-                *      If the frame is from us and going off machine it MUST MUST MUST
-                *      have the output device ip address and never the loopback
-                */
-               if (LOOPBACK(saddr) && !LOOPBACK(daddr))
-                       saddr = src;/*rt->rt_dev->pa_addr;*/
 
-               raddr = (rt == NULL) ? 0 : rt->rt_gateway;
-       }
+       if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr)
+               saddr = rt ? rt->rt_src : (*dev)->pa_addr;
 
-       /*
-        *      No source addr so make it our addr
-        */
-       if (saddr == 0)
-               saddr = src;
+       raddr = rt ? rt->rt_gateway : 0;
+
+       if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY))
+       {
+               ip_rt_put(rt);
+               ip_statistics.IpOutNoRoutes++;
+               return -ENETUNREACH;
+       }
 
        /*
         *      No gateway so aim at the real destination
         */
+
        if (raddr == 0)
                raddr = daddr;
 
@@ -240,10 +256,12 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
         *      Now build the MAC header.
         */
 
-       if(type==IPPROTO_TCP)
-               tmp = ip_send_room(skb, raddr, len, *dev, saddr);
+       if (type==IPPROTO_TCP)
+               tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr);
        else
-               tmp = ip_send(skb, raddr, len, *dev, saddr);
+               tmp = ip_send(rt, skb, raddr, len, *dev, saddr);
+
+       ip_rt_put(rt);
 
        /*
         *      Book keeping
@@ -285,11 +303,6 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
 
        if (!opt || !opt->optlen)
                return sizeof(struct iphdr) + tmp;
-       if (opt->is_strictroute && rt && rt->rt_gateway) 
-       {
-               ip_statistics.IpOutNoRoutes++;
-               return -ENETUNREACH;
-       }
        iph->ihl += opt->optlen>>2;
        ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
        return iph->ihl*4 + tmp;
@@ -563,8 +576,9 @@ int ip_build_xmit(struct sock *sk,
        __u32 saddr;
        unsigned short id;
        struct iphdr *iph;
-       int local=0;
-       struct device *dev;
+       __u32 raddr;
+       struct device *dev = NULL;
+       struct hh_cache * hh=NULL;
        int nfrags=0;
        __u32 true_daddr = daddr;
 
@@ -588,60 +602,17 @@ int ip_build_xmit(struct sock *sk,
        else
        {
 #endif 
-               /*
-                *      Perform the IP routing decisions
-                */
-        
-               if(sk->localroute || flags&MSG_DONTROUTE)
-                       local=1;
-       
-               rt = sk->ip_route_cache;
-               
-               /*
-                *      See if the routing cache is outdated. We need to clean this up once we are happy it is reliable
-                *      by doing the invalidation actively in the route change and header change.
-                */
-       
-               saddr=sk->ip_route_saddr;        
-               if(!rt || sk->ip_route_stamp != rt_stamp ||
-                  daddr!=sk->ip_route_daddr || sk->ip_route_local!=local ||
-                  (sk->saddr && sk->saddr != saddr))
-               {
-                       if(local)
-                               rt = ip_rt_local(daddr, NULL, &saddr);
-                       else
-                               rt = ip_rt_route(daddr, NULL, &saddr);
-                       sk->ip_route_local=local;
-                       sk->ip_route_daddr=daddr;
-                       sk->ip_route_saddr=saddr;
-                       sk->ip_route_stamp=rt_stamp;
-                       sk->ip_route_cache=rt;
-                       sk->ip_hcache_ver=NULL;
-                       sk->ip_hcache_state= 0;
-               }
-               else if(rt)
-               {
-                       /*
-                        *      Attempt header caches only if the cached route is being reused. Header cache
-                        *      is not ultra cheap to set up. This means we only set it up on the second packet,
-                        *      so one shot communications are not slowed. We assume (seems reasonable) that 2 is
-                        *      probably going to be a stream of data.
-                        */
-                       if(rt->rt_dev->header_cache && sk->ip_hcache_state!= -1)
-                       {
-                               if(sk->ip_hcache_ver==NULL || sk->ip_hcache_stamp!=*sk->ip_hcache_ver)
-                                       rt->rt_dev->header_cache(rt->rt_dev,sk,saddr,daddr);
-                               else
-                                       /* Can't cache. Remember this */
-                                       sk->ip_hcache_state= -1;
-                       }
-               }
-               
+               rt = ip_check_route(&sk->ip_route_cache, daddr,
+                                   sk->localroute || (flags&MSG_DONTROUTE) ||
+                                   (opt && opt->is_strictroute));
                if (rt == NULL) 
                {
-                       ip_statistics.IpOutNoRoutes++;
+                       ip_statistics.IpOutNoRoutes++;
                        return(-ENETUNREACH);
                }
+               saddr = rt->rt_src;
+
+               hh = rt->rt_hh;
        
                if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
                        saddr = sk->saddr;
@@ -649,10 +620,13 @@ int ip_build_xmit(struct sock *sk,
                dev=rt->rt_dev;
 #ifdef CONFIG_IP_MULTICAST
        }
+       if (rt && !dev)
+               dev = rt->rt_dev;
 #endif         
        if (user_saddr)
                saddr = user_saddr;
 
+       raddr = rt ? rt->rt_gateway : daddr;
        /*
         *      Now compute the buffer space we require
         */ 
@@ -662,16 +636,10 @@ int ip_build_xmit(struct sock *sk,
         *      choice RAW frames within 20 bytes of maximum size(rare) to the long path
         */
 
-       length += 20;
+       length += sizeof(struct iphdr);
        if (!sk->ip_hdrincl && opt) 
-       {
                length += opt->optlen;
-               if (opt->is_strictroute && rt && rt->rt_gateway) 
-               {
-                       ip_statistics.IpOutNoRoutes++;
-                       return -ENETUNREACH;
-               }
-       }
+
        if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
        {       
                int error;
@@ -687,12 +655,19 @@ int ip_build_xmit(struct sock *sk,
                skb->sk=sk;
                skb->arp=0;
                skb->saddr=saddr;
-               skb->raddr=(rt&&rt->rt_gateway)?rt->rt_gateway:daddr;
+               skb->raddr = raddr;
                skb_reserve(skb,(dev->hard_header_len+15)&~15);
-               if(sk->ip_hcache_state>0)
+               if (hh)
                {
-                       memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data,dev->hard_header_len);
                        skb->arp=1;
+                       memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
+                       if (!hh->hh_uptodate)
+                       {
+                               skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+                               printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
+#endif                         
+                       }
                }
                else if(dev->hard_header)
                {
@@ -747,7 +722,7 @@ int ip_build_xmit(struct sock *sk,
                }
                return 0;
        }
-       length-=20;
+       length -= sizeof(struct iphdr);
        if (sk && !sk->ip_hdrincl && opt) 
        {
                length -= opt->optlen;
@@ -847,7 +822,7 @@ int ip_build_xmit(struct sock *sk,
                skb->sk = sk;
                skb->arp = 0;
                skb->saddr = saddr;
-               skb->raddr = (rt&&rt->rt_gateway) ? rt->rt_gateway : daddr;
+               skb->raddr = raddr;
                skb_reserve(skb,(dev->hard_header_len+15)&~15);
                data = skb_put(skb, fraglen-dev->hard_header_len);
 
@@ -858,10 +833,17 @@ int ip_build_xmit(struct sock *sk,
                 *      pointer to speed header cache builds for identical targets.
                 */
                 
-               if(sk->ip_hcache_state>0)
+               if (hh)
                {
-                       memcpy(skb_push(skb,dev->hard_header_len),sk->ip_hcache_data, dev->hard_header_len);
                        skb->arp=1;
+                       memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
+                       if (!hh->hh_uptodate)
+                       {
+                               skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+                               printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
+#endif                         
+                       }
                }
                else if (dev->hard_header)
                {
@@ -958,15 +940,15 @@ int ip_build_xmit(struct sock *sk,
                        if(sk==NULL || sk->ip_mc_loop)
                        {
                                if(skb->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-                                       ip_loopback(rt?rt->rt_dev:dev,skb);
+                                       ip_loopback(dev,skb);
                                else 
                                {
-                                       struct ip_mc_list *imc=rt?rt->rt_dev->ip_mc_list:dev->ip_mc_list;
+                                       struct ip_mc_list *imc=dev->ip_mc_list;
                                        while(imc!=NULL) 
                                        {
                                                if(imc->multiaddr==daddr) 
                                                {
-                                                       ip_loopback(rt?rt->rt_dev:dev,skb);
+                                                       ip_loopback(dev,skb);
                                                        break;
                                                }
                                                imc=imc->next;
index 9e70be68754d3d4d9d1dc736eee3af28c359892f..818b7269d7c42db41622d7209803ffd79771a604 100644 (file)
@@ -283,10 +283,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                                /*
                                 *      Not set so scan.
                                 */
-                               if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+                               if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
                                {
                                        dev=rt->rt_dev;
-                                       rt->rt_use--;
+                                       route_src = rt->rt_src;
+                                       ATOMIC_DECR(&rt->rt_use);
+                                       ip_rt_put(rt);
                                }
                        }
                        else
@@ -335,10 +337,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
  
                        if(mreq.imr_interface.s_addr==INADDR_ANY) 
                        {
-                               if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,NULL, &route_src))!=NULL)
+                               if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
                                {
                                        dev=rt->rt_dev;
-                                       rt->rt_use--;
+                                       ATOMIC_DECR(&rt->rt_use);
+                                       route_src = rt->rt_src;
+                                       ip_rt_put(rt);
                                }
                        }
                        else 
index e4b2140e584277ad9a17feef2bcdf707b514029a..650f02daabcc98092afc74cb42486be317e0edc6 100644 (file)
@@ -266,7 +266,7 @@ static int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
                sti();
 
                arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, 
-                       dev->dev_addr);
+                       dev->dev_addr, sha);
        }
        else
                sti();
@@ -288,6 +288,7 @@ static int rarp_req_set(struct arpreq *req)
        int htype, hlen;
        unsigned long ip;
        struct rtable *rt;
+       struct device * dev;
   
        memcpy_fromfs(&r, req, sizeof(r));
   
@@ -326,9 +327,11 @@ static int rarp_req_set(struct arpreq *req)
  *     Is it reachable directly ?
  */
   
-       rt = ip_rt_route(ip, NULL, NULL);
+       rt = ip_rt_route(ip, 0);
        if (rt == NULL)
                return -ENETUNREACH;
+       dev = rt->rt_dev;
+       ip_rt_put(rt);
 
 /*
  *     Is there an existing entry for this address?  Find out...
@@ -366,7 +369,7 @@ static int rarp_req_set(struct arpreq *req)
        entry->hlen = hlen;
        entry->htype = htype;
        memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
-       entry->dev = rt->rt_dev;
+       entry->dev = dev;
 
        sti();  
 
@@ -574,5 +577,4 @@ void cleanup_module(void)
                rarp_release_entry(rt);
        }
 }
-
 #endif
index 495ec9adbee79e95dad25636213878df40fef442..b7f9a6a54a47a0b5fce4714c2681417bd066162a 100644 (file)
@@ -35,6 +35,8 @@
  *             as published by the Free Software Foundation; either version
  *             2 of the License, or (at your option) any later version.
  */
+#include <linux/config.h> 
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <linux/types.h>
@@ -57,8 +59,6 @@
 #include <net/udp.h>
 #include <net/checksum.h>
 
-#include <linux/config.h>
-
 #ifdef CONFIG_IP_MROUTE
 struct sock *mroute_socket=NULL;
 #endif
@@ -240,8 +240,8 @@ static int raw_sendto(struct sock *sk, const unsigned char *from,
  *     Temporary
  */
  
-static int raw_sendmsg(struct sock *sk, struct msghdr *msg,
-       int len, int noblock, int flags)
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
+       int flags)
 {
        if(msg->msg_iovlen==1)
                return raw_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
index 6483db017a2fa8e950e33191566bb34d1174e250..d14fead2ea25870e97cbdc27584d5e5e15792cbb 100644 (file)
@@ -35,6 +35,8 @@
  *             Alan Cox        :       Aligned routing errors more closely with BSD
  *                                     our system is still very different.
  *             Alan Cox        :       Faster /proc handling
+ *     Alexey Kuznetsov        :       Massive rework to support tree based routing,
+ *                                     routing caches and better behaviour.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
  *             2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <asm/segment.h>
 #include <asm/system.h>
+#include <asm/bitops.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <net/netlink.h>
 
 /*
- *     The routing table list
+ * Forwarding Information Base definitions.
  */
 
-static struct rtable *rt_base = NULL;
-unsigned long rt_stamp = 1;            /* Routing table version stamp for caches ( 0 is 'unset' ) */
+struct fib_node
+{
+       struct fib_node         *fib_next;
+       __u32                   fib_dst;
+       unsigned long           fib_use;
+       struct fib_info         *fib_info;
+       short                   fib_metric;
+       unsigned char           fib_tos;
+};
+
+/*
+ * This structure contains data shared by many of routes.
+ */    
+
+struct fib_info
+{
+       struct fib_info         *fib_next;
+       struct fib_info         *fib_prev;
+       __u32                   fib_gateway;
+       struct device           *fib_dev;
+       int                     fib_refcnt;
+       unsigned long           fib_window;
+       unsigned short          fib_flags;
+       unsigned short          fib_mtu;
+       unsigned short          fib_irtt;
+};
+
+struct fib_zone
+{
+       struct fib_zone *fz_next;
+       struct fib_node **fz_hash_table;
+       struct fib_node *fz_list;
+       int             fz_nent;
+       int             fz_logmask;
+       __u32           fz_mask;
+};
+
+static struct fib_zone         *fib_zones[33];
+static struct fib_zone         *fib_zone_list;
+static struct fib_node         *fib_loopback = NULL;
+static struct fib_info         *fib_info_list;
 
 /*
- *     Pointer to the loopback route
+ * Backlogging.
  */
-static struct rtable *rt_loopback = NULL;
+
+#define RT_BH_REDIRECT         0
+#define RT_BH_GARBAGE_COLLECT  1
+#define RT_BH_FREE             2
+
+struct rt_req
+{
+       struct rt_req * rtr_next;
+       struct device *dev;
+       __u32 dst;
+       __u32 gw;
+       unsigned char tos;
+};
+
+int                    ip_rt_lock;
+unsigned               ip_rt_bh_mask;
+static struct rt_req   *rt_backlog;
 
 /*
- *     Remove a routing table entry.
+ * Route cache.
  */
 
-static int rt_del(__u32 dst, __u32 mask,
-               char *devname, __u32 gtw, short rt_flags, short metric)
+struct rtable          *ip_rt_hash_table[RT_HASH_DIVISOR];
+static int             rt_cache_size;
+static struct rtable   *rt_free_queue;
+struct wait_queue      *rt_wait;
+
+static void rt_kick_backlog(void);
+static void rt_cache_add(unsigned hash, struct rtable * rth);
+static void rt_cache_flush(void);
+static void rt_garbage_collect_1(void);
+
+/* 
+ * Evaluate mask length.
+ */
+
+static __inline__ int rt_logmask(__u32 mask)
 {
-       struct rtable *r, **rp;
-       unsigned long flags;
-       int found=0;
+       if (!(mask = ntohl(mask)))
+               return 32;
+       return ffz(~mask);
+}
 
-       rp = &rt_base;
-       
-       /*
-        *      This must be done with interrupts off because we could take
-        *      an ICMP_REDIRECT.
-        */
-        
-       save_flags(flags);
-       cli();
-       while((r = *rp) != NULL) 
+/* 
+ * Create mask from length.
+ */
+
+static __inline__ __u32 rt_mask(int logmask)
+{
+       if (logmask >= 32)
+               return 0;
+       return htonl(~((1<<logmask)-1));
+}
+
+static __inline__ unsigned fz_hash_code(__u32 dst, int logmask)
+{
+       return ip_rt_hash_code(ntohl(dst)>>logmask);
+}
+
+/*
+ * Free FIB node.
+ */
+
+static void fib_free_node(struct fib_node * f)
+{
+       struct fib_info * fi = f->fib_info;
+       if (!--fi->fib_refcnt)
        {
-               /*
-                *      Make sure the destination and netmask match.
-                *      metric, gateway and device are also checked
-                *      if they were specified.
-                */
-               if (r->rt_dst != dst ||
-                   (mask && r->rt_mask != mask) ||
-                   (gtw && r->rt_gateway != gtw) ||
-                   (metric >= 0 && r->rt_metric != metric) ||
-                   (devname && strcmp((r->rt_dev)->name,devname) != 0) )
+#if RT_CACHE_DEBUG >= 2
+               printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+               if (fi->fib_next)
+                       fi->fib_next->fib_prev = fi->fib_prev;
+               if (fi->fib_prev)
+                       fi->fib_prev->fib_next = fi->fib_next;
+               if (fi == fib_info_list)
+                       fib_info_list = fi->fib_next;
+       }
+       kfree_s(f, sizeof(struct fib_node));
+}
+
+/*
+ * Find gateway route by address.
+ */
+
+static struct fib_node * fib_lookup_gateway(__u32 dst)
+{
+       struct fib_zone * fz;
+       struct fib_node * f;
+
+       for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+       {
+               if (fz->fz_hash_table)
+                       f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+               else
+                       f = fz->fz_list;
+               
+               for ( ; f; f = f->fib_next)
                {
-                       rp = &r->rt_next;
-                       continue;
+                       if ((dst ^ f->fib_dst) & fz->fz_mask)
+                               continue;
+                       if (f->fib_info->fib_flags & RTF_GATEWAY)
+                               return NULL;
+                       return f;
                }
-               *rp = r->rt_next;
-               
-               /*
-                *      If we delete the loopback route update its pointer.
-                */
-                
-               if (rt_loopback == r)
-                       rt_loopback = NULL;
-               ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, rt_flags, metric, r->rt_dev->name);
-               kfree_s(r, sizeof(struct rtable));
-               found=1;
-       } 
-       rt_stamp++;             /* New table revision */
-       
-       restore_flags(flags);
-       
-       if(found)
-               return 0;
-       return -ESRCH;
+       }
+       return NULL;
 }
 
+/*
+ * Find local route by address.
+ * FIXME: I use "longest match" principle. If destination
+ *       has some non-local route, I'll not search shorter matches.
+ *       It's possible, I'm wrong, but I wanted to prevent following
+ *       situation:
+ *     route add 193.233.7.128 netmask 255.255.255.192 gw xxxxxx
+ *     route add 193.233.7.0   netmask 255.255.255.0 eth1
+ *       (Two ethernets connected by serial line, one is small and other is large)
+ *       Host 193.233.7.129 is locally unreachable,
+ *       but old (<=1.3.37) code will send packets destined for it to eth1.
+ *
+ */
+
+static struct fib_node * fib_lookup_local(__u32 dst)
+{
+       struct fib_zone * fz;
+       struct fib_node * f;
+
+       for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+       {
+               int longest_match_found = 0;
+
+               if (fz->fz_hash_table)
+                       f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+               else
+                       f = fz->fz_list;
+               
+               for ( ; f; f = f->fib_next)
+               {
+                       if ((dst ^ f->fib_dst) & fz->fz_mask)
+                               continue;
+                       if (!(f->fib_info->fib_flags & RTF_GATEWAY))
+                               return f;
+                       longest_match_found = 1;
+               }
+               if (longest_match_found)
+                       return NULL;
+       }
+       return NULL;
+}
 
 /*
- *     Remove all routing table entries for a device. This is called when
- *     a device is downed.
+ * Main lookup routine.
+ *     IMPORTANT NOTE: this algorithm has small difference from <=1.3.37 visible
+ *     by user. It doesn't route non-CIDR broadcasts by default.
+ *
+ *     F.e.
+ *             ifconfig eth0 193.233.7.65 netmask 255.255.255.192 broadcast 193.233.7.255
+ *     is valid, but if you really are not able (not allowed, do not want) to
+ *     use CIDR compliant broadcast 193.233.7.127, you should add host route:
+ *             route add -host 193.233.7.255 eth0
  */
-void ip_rt_flush(struct device *dev)
+
+static struct fib_node * fib_lookup(__u32 dst)
 {
-       struct rtable *r;
-       struct rtable **rp;
-       unsigned long flags;
+       struct fib_zone * fz;
+       struct fib_node * f;
 
-       rp = &rt_base;
-       save_flags(flags);
-       cli();
-       while ((r = *rp) != NULL) {
-               if (r->rt_dev != dev) {
-                       rp = &r->rt_next;
-                       continue;
+       for (fz = fib_zone_list; fz; fz = fz->fz_next) 
+       {
+               if (fz->fz_hash_table)
+                       f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+               else
+                       f = fz->fz_list;
+               
+               for ( ; f; f = f->fib_next)
+               {
+                       if ((dst ^ f->fib_dst) & fz->fz_mask)
+                               continue;
+                       return f;
                }
-               *rp = r->rt_next;
-               if (rt_loopback == r)
-                       rt_loopback = NULL;
-               kfree_s(r, sizeof(struct rtable));
-       } 
-       rt_stamp++;             /* New table revision */
-       restore_flags(flags);
+       }
+       return NULL;
+}
+
+static __inline__ struct device * get_gw_dev(__u32 gw)
+{
+       struct fib_node * f;
+       f = fib_lookup_gateway(gw);
+       if (f)
+               return f->fib_info->fib_dev;
+       return NULL;
 }
 
 /*
@@ -199,228 +347,1244 @@ static __u32 guess_mask(__u32 dst, struct device * dev)
 }
 
 
-/*
- *     Find the route entry through which our gateway will be reached
- */
-static inline struct device * get_gw_dev(__u32 gw)
+/*
+ *     Check if a mask is acceptable.
+ */
+static inline int bad_mask(__u32 mask, __u32 addr)
+{
+       if (addr & (mask = ~mask))
+               return 1;
+       mask = ntohl(mask);
+       if (mask & (mask+1))
+               return 1;
+       return 0;
+}
+
+
+static int fib_del_list(struct fib_node **fp, __u32 dst,
+               struct device * dev, __u32 gtw, short flags, short metric, __u32 mask)
+{
+       struct fib_node *f;
+       int found=0;
+
+       while((f = *fp) != NULL) 
+       {
+               struct fib_info * fi = f->fib_info;
+
+               /*
+                *      Make sure the destination and netmask match.
+                *      metric, gateway and device are also checked
+                *      if they were specified.
+                */
+               if (f->fib_dst != dst ||
+                   (gtw && fi->fib_gateway != gtw) ||
+                   (metric >= 0 && f->fib_metric != metric) ||
+                   (dev && fi->fib_dev != dev) )
+               {
+                       fp = &f->fib_next;
+                       continue;
+               }
+               cli();
+               *fp = f->fib_next;
+               if (fib_loopback == f)
+                       fib_loopback = NULL;
+               sti();
+               ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, flags, metric, fi->fib_dev->name);
+               fib_free_node(f);
+               found++;
+       }
+       return found;
+}
+
+static __inline__ int fib_del_1(__u32 dst, __u32 mask,
+               struct device * dev, __u32 gtw, short flags, short metric)
+{
+       struct fib_node **fp;
+       struct fib_zone *fz;
+       int found=0;
+
+       if (!mask)
+       {
+               for (fz=fib_zone_list; fz; fz = fz->fz_next)
+               {
+                       int tmp;
+                       if (fz->fz_hash_table)
+                               fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+                       else
+                               fp = &fz->fz_list;
+
+                       tmp = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
+                       fz->fz_nent -= tmp;
+                       found += tmp;
+               }
+       } 
+       else
+       {
+               if ((fz = fib_zones[rt_logmask(mask)]) != NULL)
+               {
+                       if (fz->fz_hash_table)
+                               fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
+                       else
+                               fp = &fz->fz_list;
+       
+                       found = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
+                       fz->fz_nent -= found;
+               }
+       }
+
+       if (found)
+       {
+               rt_cache_flush();
+               return 0;
+       }
+       return -ESRCH;
+}
+
+
+static struct fib_info * fib_create_info(__u32 gw, struct device * dev,
+                                        unsigned short flags, unsigned short mss,
+                                        unsigned long window, unsigned short irtt)
+{
+       struct fib_info * fi;
+
+       if (!(flags & RTF_MSS))
+       {
+               mss = dev->mtu;
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
+               /*
+                *      If MTU was not specified, use default.
+                *      If you want to increase MTU for some net (local subnet)
+                *      use "route add .... mss xxx".
+                *
+                *      The MTU isnt currently always used and computed as it
+                *      should be as far as I can tell. [Still verifying this is right]
+                */
+               if ((flags & RTF_GATEWAY) && mss > 576)
+                       mss = 576;
+#endif
+       }
+       if (!(flags & RTF_WINDOW))
+               window = 0;
+       if (!(flags & RTF_IRTT))
+               irtt = 0;
+
+       for (fi=fib_info_list; fi; fi = fi->fib_next)
+       {
+               if (fi->fib_gateway != gw ||
+                   fi->fib_dev != dev  ||
+                   fi->fib_flags != flags ||
+                   fi->fib_mtu != mss ||
+                   fi->fib_window != window ||
+                   fi->fib_irtt != irtt)
+                       continue;
+               fi->fib_refcnt++;
+#if RT_CACHE_DEBUG >= 2
+               printk("fib_create_info: fi %08x/%s is duplicate\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+               return fi;
+       }
+       fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
+       if (!fi)
+               return NULL;
+       memset(fi, 0, sizeof(struct fib_info));
+       fi->fib_flags = flags;
+       fi->fib_dev = dev;
+       fi->fib_gateway = gw;
+       fi->fib_mtu = mss;
+       fi->fib_window = window;
+       fi->fib_refcnt++;
+       fi->fib_next = fib_info_list;
+       fi->fib_prev = NULL;
+       if (fib_info_list)
+               fib_info_list->fib_prev = fi;
+       fib_info_list = fi;
+#if RT_CACHE_DEBUG >= 2
+       printk("fib_create_info: fi %08x/%s is created\n", fi->fib_gateway, fi->fib_dev->name);
+#endif
+       return fi;
+}
+
+
+static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask,
+       __u32 gw, struct device *dev, unsigned short mss,
+       unsigned long window, unsigned short irtt, short metric)
+{
+       struct fib_node *f, *f1;
+       struct fib_node **fp;
+       struct fib_node **dup_fp = NULL;
+       struct fib_zone * fz;
+       struct fib_info * fi;
+       int logmask;
+
+       if (flags & RTF_HOST) 
+               mask = 0xffffffff;
+       /*
+        * If mask is not specified, try to guess it.
+        */
+       else if (!mask)
+       {
+               if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
+               {
+                       mask = dev->pa_mask;
+                       flags &= ~RTF_GATEWAY;
+                       if (flags & RTF_DYNAMIC) 
+                       {
+                               printk("Dynamic route to my own net rejected\n");
+                               return;
+                       }
+               } 
+               else
+                       mask = guess_mask(dst, dev);
+               dst &= mask;
+       }
+       
+       /*
+        *      A gateway must be reachable and not a local address
+        */
+        
+       if (gw == dev->pa_addr)
+               flags &= ~RTF_GATEWAY;
+               
+       if (flags & RTF_GATEWAY) 
+       {
+               /*
+                *      Don't try to add a gateway we can't reach.. 
+                */
+                
+               if (dev != get_gw_dev(gw))
+                       return;
+                       
+               flags |= RTF_GATEWAY;
+       } 
+       else
+               gw = 0;
+               
+       /*
+        *      Allocate an entry and fill it in.
+        */
+        
+       f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
+       if (f == NULL)
+               return;
+
+       memset(f, 0, sizeof(struct fib_node));
+       f->fib_dst = dst;
+       f->fib_metric = metric;
+       f->fib_tos    = 0;
+
+       if  ((fi = fib_create_info(gw, dev, flags, mss, window, irtt)) == NULL)
+       {
+               kfree_s(f, sizeof(struct fib_node));
+               return;
+       }
+       f->fib_info = fi;
+
+       logmask = rt_logmask(mask);
+       fz = fib_zones[logmask];
+
+
+       if (!fz)
+       {
+               int i;
+               fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
+               if (!fz)
+               {
+                       fib_free_node(f);
+                       return;
+               }
+               memset(fz, 0, sizeof(struct fib_zone));
+               fz->fz_logmask = logmask;
+               fz->fz_mask = mask;
+               for (i=logmask-1; i>=0; i--)
+                       if (fib_zones[i])
+                               break;
+               cli();
+               if (i<0)
+               {
+                       fz->fz_next = fib_zone_list;
+                       fib_zone_list = fz;
+               }
+               else
+               {
+                       fz->fz_next = fib_zones[i]->fz_next;
+                       fib_zones[i]->fz_next = fz;
+               }
+               fib_zones[logmask] = fz;
+               sti();
+       }
+
+       /*
+        * If zone overgrows RTZ_HASHING_LIMIT, create hash table.
+        */
+
+       if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32)
+       {
+               struct fib_node ** ht;
+#if RT_CACHE_DEBUG
+               printk("fib_add_1: hashing for zone %d started\n", logmask);
+#endif
+               ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL);
+
+               if (ht)
+               {
+                       memset(ht, 0, RTZ_HASH_DIVISOR*sizeof(struct fib_node*));
+                       cli();
+                       f1 = fz->fz_list;
+                       while (f1)
+                       {
+                               struct fib_node * next;
+                               unsigned hash = fz_hash_code(f1->fib_dst, logmask);
+                               next = f1->fib_next;
+                               f1->fib_next = ht[hash];
+                               ht[hash] = f1;
+                               f1 = next;
+                       }
+                       fz->fz_list = NULL;
+                       fz->fz_hash_table = ht; 
+                       sti();
+               }
+       }
+
+       if (fz->fz_hash_table)
+               fp = &fz->fz_hash_table[fz_hash_code(dst, logmask)];
+       else
+               fp = &fz->fz_list;
+
+       /*
+        * Scan list to find the first route with the same destination
+        */
+       while ((f1 = *fp) != NULL)
+       {
+               if (f1->fib_dst == dst)
+                       break;
+               fp = &f1->fib_next;
+       }
+
+       /*
+        * Find route with the same destination and less (or equal) metric.
+        */
+       while ((f1 = *fp) != NULL && f1->fib_dst == dst)
+       {
+               if (f1->fib_metric >= metric)
+                       break;
+               /*
+                *      Record route with the same destination and gateway,
+                *      but less metric. We'll delete it 
+                *      after instantiation of new route.
+                */
+               if (f1->fib_info->fib_gateway == gw)
+                       dup_fp = fp;
+               fp = &f1->fib_next;
+       }
+
+       /*
+        * Is it already present?
+        */
+
+       if (f1 && f1->fib_metric == metric && f1->fib_info == fi)
+       {
+               fib_free_node(f);
+               return;
+       }
+       
+       /*
+        * Insert new entry to the list.
+        */
+
+       cli();
+       f->fib_next = f1;
+       *fp = f;
+       if (!fib_loopback && (fi->fib_dev->flags & IFF_LOOPBACK))
+               fib_loopback = f;
+       sti();
+       fz->fz_nent++;
+       ip_netlink_msg(RTMSG_NEWROUTE, dst, gw, mask, flags, metric, fi->fib_dev->name);
+
+       /*
+        *      Delete route with the same destination and gateway.
+        *      Note that we should have at most one such route.
+        */
+       if (dup_fp)
+               fp = dup_fp;
+       else
+               fp = &f->fib_next;
+
+       while ((f1 = *fp) != NULL && f1->fib_dst == dst)
+       {
+               if (f1->fib_info->fib_gateway == gw)
+               {
+                       cli();
+                       *fp = f1->fib_next;
+                       if (fib_loopback == f1)
+                               fib_loopback = NULL;
+                       sti();
+                       ip_netlink_msg(RTMSG_DELROUTE, dst, gw, mask, flags, metric, f1->fib_info->fib_dev->name);
+                       fib_free_node(f1);
+                       fz->fz_nent--;
+                       break;
+               }
+               fp = &f1->fib_next;
+       }
+       rt_cache_flush();
+       return;
+}
+
+static int rt_flush_list(struct fib_node ** fp, struct device *dev)
+{
+       int found = 0;
+       struct fib_node *f;
+
+       while ((f = *fp) != NULL) {
+               if (f->fib_info->fib_dev != dev) {
+                       fp = &f->fib_next;
+                       continue;
+               }
+               cli();
+               *fp = f->fib_next;
+               if (fib_loopback == f)
+                       fib_loopback = NULL;
+               sti();
+               fib_free_node(f);
+               found++;
+       }
+       return found;
+}
+
+static __inline__ void fib_flush_1(struct device *dev)
+{
+       struct fib_zone *fz;
+       int found = 0;
+
+       for (fz = fib_zone_list; fz; fz = fz->fz_next)
+       {
+               if (fz->fz_hash_table)
+               {
+                       int i;
+                       int tmp = 0;
+                       for (i=0; i<RTZ_HASH_DIVISOR; i++)
+                               tmp += rt_flush_list(&fz->fz_hash_table[i], dev);
+                       fz->fz_nent -= tmp;
+                       found += tmp;
+               }
+               else
+               {
+                       int tmp;
+                       tmp = rt_flush_list(&fz->fz_list, dev);
+                       fz->fz_nent -= tmp;
+                       found += tmp;
+               }
+       }
+               
+       if (found)
+               rt_cache_flush();
+}
+
+
+/* 
+ *     Called from the PROCfs module. This outputs /proc/net/route.
+ *
+ *     We preserve the old format but pad the buffers out. This means that
+ *     we can spin over the other entries as we read them. Remember the
+ *     gated BGP4 code could need to read 60,000+ routes on occasion (thats
+ *     about 7Mb of data). To do that ok we will need to also cache the
+ *     last route we got to (reads will generally be following on from
+ *     one another without gaps).
+ */
+int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+       struct fib_zone *fz;
+       struct fib_node *f;
+       int len=0;
+       off_t pos=0;
+       char temp[129];
+       int i;
+       
+       pos = 128;
+
+       if (offset<128)
+       {
+               sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
+               len = 128;
+       }
+       
+       while  (ip_rt_lock)
+               sleep_on(&rt_wait);
+       ip_rt_fast_lock();
+
+       for (fz=fib_zone_list; fz; fz = fz->fz_next)
+       {
+               int maxslot;
+               struct fib_node ** fp;
+
+               if (fz->fz_nent == 0)
+                       continue;
+
+               if (pos + 128*fz->fz_nent <= offset)
+               {
+                       pos += 128*fz->fz_nent;
+                       len = 0;
+                       continue;
+               }
+
+               if (fz->fz_hash_table)
+               {
+                       maxslot = RTZ_HASH_DIVISOR;
+                       fp      = fz->fz_hash_table;
+               }
+               else
+               {
+                       maxslot = 1;
+                       fp      = &fz->fz_list;
+               }
+                       
+               for (i=0; i < maxslot; i++, fp++)
+               {
+                       
+                       for (f = *fp; f; f = f->fib_next) 
+                       {
+                               struct fib_info * fi;
+                               /*
+                                *      Spin through entries until we are ready
+                                */
+                               pos += 128;
+
+                               if (pos <= offset)
+                               {
+                                       len=0;
+                                       continue;
+                               }
+                                       
+                               fi = f->fib_info;
+                               sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
+                                       fi->fib_dev->name, (unsigned long)f->fib_dst, (unsigned long)fi->fib_gateway,
+                                       fi->fib_flags, 0, f->fib_use, f->fib_metric,
+                                       (unsigned long)fz->fz_mask, (int)fi->fib_mtu, fi->fib_window, (int)fi->fib_irtt);
+                               sprintf(buffer+len,"%-127s\n",temp);
+
+                               len += 128;
+                               if (pos >= offset+length)
+                                       goto done;
+                       }
+               }
+        }
+
+done:
+       ip_rt_unlock();
+       wake_up(&rt_wait);
+       
+       *start = buffer+len-(pos-offset);
+       len = pos - offset;
+       if (len>length)
+               len = length;
+       return len;
+}
+
+int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+       int len=0;
+       off_t pos=0;
+       char temp[129];
+       struct rtable *r;
+       int i;
+
+       pos = 128;
+
+       if (offset<128)
+       {
+               sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tHH\tARP\n");
+               len = 128;
+       }
+       
+       
+       while  (ip_rt_lock)
+               sleep_on(&rt_wait);
+       ip_rt_fast_lock();
+
+       for (i = 0; i<RT_HASH_DIVISOR; i++)
+       {
+               for (r = ip_rt_hash_table[i]; r; r = r->rt_next) 
+               {
+                       /*
+                        *      Spin through entries until we are ready
+                        */
+                       pos += 128;
+
+                       if (pos <= offset)
+                       {
+                               len = 0;
+                               continue;
+                       }
+                                       
+                       sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%ld\t%lu\t%d\t%08lX\t%d\t%lu\t%u\t%ld\t%1d",
+                               r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
+                               r->rt_flags, r->rt_refcnt, r->rt_use, 0,
+                               (unsigned long)r->rt_src, (int)r->rt_mtu, r->rt_window, (int)r->rt_irtt, r->rt_hh ? r->rt_hh->hh_refcnt : -1, r->rt_hh ? r->rt_hh->hh_uptodate : 0);
+                       sprintf(buffer+len,"%-127s\n",temp);
+                       len += 128;
+                       if (pos >= offset+length)
+                               goto done;
+               }
+        }
+
+done:
+       ip_rt_unlock();
+       wake_up(&rt_wait);
+       
+       *start = buffer+len-(pos-offset);
+       len = pos-offset;
+       if (len>length)
+               len = length;
+       return len;
+}
+
+
+static void rt_free(struct rtable * rt)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if (!rt->rt_refcnt)
+       {
+               struct hh_cache * hh = rt->rt_hh;
+               rt->rt_hh = NULL;
+               if (hh && !--hh->hh_refcnt)
+               {
+                       restore_flags(flags);
+                       kfree_s(hh, sizeof(struct hh_cache));
+               }
+               restore_flags(flags);
+               kfree_s(rt, sizeof(struct rt_table));
+               return;
+       }
+       rt->rt_next = rt_free_queue;
+       rt->rt_flags &= ~RTF_UP;
+       rt_free_queue = rt;
+       ip_rt_bh_mask |= RT_BH_FREE;
+#if RT_CACHE_DEBUG >= 2
+       printk("rt_free: %08x\n", rt->rt_dst);
+#endif
+       restore_flags(flags);
+}
+
+/*
+ * RT "bottom half" handlers. Called with masked inetrrupts.
+ */
+
+static __inline__ void rt_kick_free_queue(void)
+{
+       struct rtable *rt, **rtp;
+
+       rtp = &rt_free_queue;
+
+       while ((rt = *rtp) != NULL)
+       {
+               if  (!rt->rt_refcnt)
+               {
+                       struct hh_cache * hh = rt->rt_hh;
+#if RT_CACHE_DEBUG >= 2
+                       __u32 daddr = rt->rt_dst;
+#endif
+                       *rtp = rt->rt_next;
+                       rt->rt_hh = NULL;
+                       if (hh && !--hh->hh_refcnt)
+                       {
+                               sti();
+                               kfree_s(hh, sizeof(struct hh_cache));
+                       }
+                       sti();
+                       kfree_s(rt, sizeof(struct rt_table));
+#if RT_CACHE_DEBUG >= 2
+                       printk("rt_kick_free_queue: %08x is free\n", daddr);
+#endif
+                       cli();
+                       continue;
+               }
+               rtp = &rt->rt_next;
+       }
+}
+
+void ip_rt_run_bh() {
+       unsigned long flags;
+       save_flags(flags);
+       cli();
+       if (ip_rt_bh_mask && !ip_rt_lock)
+       {
+               if (ip_rt_bh_mask & RT_BH_REDIRECT)
+                       rt_kick_backlog();
+
+               if (ip_rt_bh_mask & RT_BH_GARBAGE_COLLECT)
+               {
+                       ip_rt_fast_lock();
+                       ip_rt_bh_mask &= ~RT_BH_GARBAGE_COLLECT;
+                       sti();
+                       rt_garbage_collect_1();
+                       cli();
+                       ip_rt_fast_unlock();
+               }
+
+               if (ip_rt_bh_mask & RT_BH_FREE)
+                       rt_kick_free_queue();
+       }
+       restore_flags(flags);
+}
+
+
+void ip_rt_check_expire()
+{
+       ip_rt_fast_lock();
+       if (ip_rt_lock == 1)
+       {
+               int i;
+               struct rtable *rth, **rthp;
+               unsigned long flags;
+               unsigned long now = jiffies;
+
+               save_flags(flags);
+               for (i=0; i<RT_HASH_DIVISOR; i++)
+               {
+                       rthp = &ip_rt_hash_table[i];
+
+                       while ((rth = *rthp) != NULL)
+                       {
+                               struct rtable * rth_next = rth->rt_next;
+
+                               /*
+                                * Cleanup aged off entries.
+                                */
+
+                               cli();
+                               if (!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
+                               {
+                                       *rthp = rth_next;
+                                       sti();
+                                       rt_cache_size--;
+#if RT_CACHE_DEBUG >= 2
+                                       printk("rt_check_expire clean %02x@%08x\n", i, rth->rt_dst);
+#endif
+                                       rt_free(rth);
+                                       continue;
+                               }
+                               sti();
+
+                               if (!rth_next)
+                                       break;
+
+                               /*
+                                * LRU ordering.
+                                */
+
+                               if (rth->rt_lastuse + RT_CACHE_BUBBLE_THRESHOULD < rth_next->rt_lastuse ||
+                                   (rth->rt_lastuse < rth_next->rt_lastuse &&
+                                    rth->rt_use < rth_next->rt_use))
+                               {
+#if RT_CACHE_DEBUG >= 2
+                                       printk("rt_check_expire bubbled %02x@%08x<->%08x\n", i, rth->rt_dst, rth_next->rt_dst);
+#endif
+                                       cli();
+                                       *rthp = rth_next;
+                                       rth->rt_next = rth_next->rt_next;
+                                       rth_next->rt_next = rth;
+                                       sti();
+                                       rthp = &rth_next->rt_next;
+                                       continue;
+                               }
+                               rthp = &rth->rt_next;
+                       }
+               }
+               restore_flags(flags);
+               rt_kick_free_queue();
+       }
+       ip_rt_unlock();
+}
+
+static void rt_redirect_1(__u32 dst, __u32 gw, struct device *dev)
+{
+       struct rtable *rt;
+       unsigned long hash = ip_rt_hash_code(dst);
+
+       if (gw == dev->pa_addr)
+               return;
+       if (dev != get_gw_dev(gw))
+               return;
+       rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+       if (rt == NULL) 
+               return;
+       memset(rt, 0, sizeof(struct rtable));
+       rt->rt_flags = RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY | RTF_UP;
+       rt->rt_dst = dst;
+       rt->rt_dev = dev;
+       rt->rt_gateway = gw;
+       rt->rt_src = dev->pa_addr;
+       rt->rt_mtu = dev->mtu;
+#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
+       if (dev->mtu > 576)
+               rt->rt_mtu = 576;
+#endif
+       rt->rt_lastuse  = jiffies;
+       rt->rt_refcnt  = 1;
+       rt_cache_add(hash, rt);
+       ip_rt_put(rt);
+       return;
+}
+
+static void rt_cache_flush(void)
+{
+       int i;
+       struct rtable * rth, * next;
+
+       for (i=0; i<RT_HASH_DIVISOR; i++)
+       {
+               int nr=0;
+
+               cli();
+               if (!(rth = ip_rt_hash_table[i]))
+               {
+                       sti();
+                       continue;
+               }
+
+               ip_rt_hash_table[i] = NULL;
+               sti();
+
+               for (; rth; rth=next)
+               {
+                       next = rth->rt_next;
+                       rt_cache_size--;
+                       nr++;
+                       rth->rt_next = NULL;
+                       rt_free(rth);
+               }
+#if RT_CACHE_DEBUG >= 2
+               if (nr > 0)
+                       printk("rt_cache_flush: %d@%02x\n", nr, i);
+#endif
+       }
+#if RT_CACHE_DEBUG >= 1
+       if (rt_cache_size)
+       {
+               printk("rt_cache_flush: bug rt_cache_size=%d\n", rt_cache_size);
+               rt_cache_size = 0;
+       }
+#endif
+}
+
+static void rt_garbage_collect_1(void)
+{
+       int i;
+       unsigned expire = RT_CACHE_TIMEOUT>>1;
+       struct rtable * rth, **rthp;
+       unsigned long now = jiffies;
+
+       for (;;)
+       {
+               for (i=0; i<RT_HASH_DIVISOR; i++)
+               {
+                       if (!ip_rt_hash_table[i])
+                               continue;
+                       for (rthp=&ip_rt_hash_table[i]; (rth=*rthp); rthp=&rth->rt_next)
+                       {
+                               if (rth->rt_lastuse + expire*(rth->rt_refcnt+1) > now)
+                                       continue;
+                               rt_cache_size--;
+                               cli();
+                               *rthp=rth->rt_next;
+                               rth->rt_next = NULL;
+                               sti();
+                               rt_free(rth);
+                               break;
+                       }
+               }
+               if (rt_cache_size < RT_CACHE_SIZE_MAX)
+                       return;
+               expire >>= 1;
+       }
+}
+
+static __inline__ void rt_req_enqueue(struct rt_req **q, struct rt_req *rtr)
+{
+       unsigned long flags;
+       struct rt_req * tail;
+
+       save_flags(flags);
+       cli();
+       tail = *q;
+       if (!tail)
+               rtr->rtr_next = rtr;
+       else
+       {
+               rtr->rtr_next = tail->rtr_next;
+               tail->rtr_next = rtr;
+       }
+       *q = rtr;
+       restore_flags(flags);
+       return;
+}
+
+/*
+ * Caller should mask interrupts.
+ */
+
+static __inline__ struct rt_req * rt_req_dequeue(struct rt_req **q)
+{
+       struct rt_req * rtr;
+
+       if (*q)
+       {
+               rtr = (*q)->rtr_next;
+               (*q)->rtr_next = rtr->rtr_next;
+               if (rtr->rtr_next == rtr)
+                       *q = NULL;
+               rtr->rtr_next = NULL;
+               return rtr;
+       }
+       return NULL;
+}
+
+/*
+   Called with masked interrupts
+ */
+
+static void rt_kick_backlog()
+{
+       if (!ip_rt_lock)
+       {
+               struct rt_req * rtr;
+
+               ip_rt_fast_lock();
+
+               while ((rtr = rt_req_dequeue(&rt_backlog)) != NULL)
+               {
+                       sti();
+                       rt_redirect_1(rtr->dst, rtr->gw, rtr->dev);
+                       kfree_s(rtr, sizeof(struct rt_req));
+                       cli();
+               }
+
+               ip_rt_bh_mask &= ~RT_BH_REDIRECT;
+
+               ip_rt_fast_unlock();
+       }
+}
+
+/*
+ * rt_{del|add|flush} called only from USER process. Waiting is OK.
+ */
+
+static int rt_del(__u32 dst, __u32 mask,
+               struct device * dev, __u32 gtw, short rt_flags, short metric)
+{
+       int retval;
+
+       while (ip_rt_lock)
+               sleep_on(&rt_wait);
+       ip_rt_fast_lock();
+       retval = fib_del_1(dst, mask, dev, gtw, rt_flags, metric);
+       ip_rt_unlock();
+       wake_up(&rt_wait);
+       return retval;
+}
+
+static void rt_add(short flags, __u32 dst, __u32 mask,
+       __u32 gw, struct device *dev, unsigned short mss,
+       unsigned long window, unsigned short irtt, short metric)
+{
+       while (ip_rt_lock)
+               sleep_on(&rt_wait);
+       ip_rt_fast_lock();
+       fib_add_1(flags, dst, mask, gw, dev, mss, window, irtt, metric);
+       ip_rt_unlock();
+       wake_up(&rt_wait);
+}
+
+void ip_rt_flush(struct device *dev)
+{
+       while (ip_rt_lock)
+               sleep_on(&rt_wait);
+       ip_rt_fast_lock();
+       fib_flush_1(dev);
+       ip_rt_unlock();
+       wake_up(&rt_wait);
+}
+
+/*
+   Called by ICMP module.
+ */
+
+void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev)
+{
+       struct rt_req * rtr;
+       struct rtable * rt;
+
+       rt = ip_rt_route(dst, 0);
+       if (!rt)
+               return;
+
+       if (rt->rt_gateway != src ||
+           rt->rt_dev != dev ||
+           ((gw^dev->pa_addr)&dev->pa_mask) ||
+           ip_chk_addr(gw))
+       {
+               ip_rt_put(rt);
+               return;
+       }
+       ip_rt_put(rt);
+
+       ip_rt_fast_lock();
+       if (ip_rt_lock == 1)
+       {
+               rt_redirect_1(dst, gw, dev);
+               ip_rt_unlock();
+               return;
+       }
+
+       rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC);
+       if (rtr)
+       {
+               rtr->dst = dst;
+               rtr->gw = gw;
+               rtr->dev = dev;
+               rt_req_enqueue(&rt_backlog, rtr);
+               ip_rt_bh_mask |= RT_BH_REDIRECT;
+       }
+       ip_rt_unlock();
+}
+
+
+static __inline__ void rt_garbage_collect(void)
 {
-       struct rtable * rt;
-
-       for (rt = rt_base ; ; rt = rt->rt_next) 
+       if (ip_rt_lock == 1)
        {
-               if (!rt)
-                       return NULL;
-               if ((gw ^ rt->rt_dst) & rt->rt_mask)
-                       continue;
-               /* 
-                *      Gateways behind gateways are a no-no 
-                */
-                
-               if (rt->rt_flags & RTF_GATEWAY)
-                       return NULL;
-               return rt->rt_dev;
+               rt_garbage_collect_1();
+               return;
        }
+       ip_rt_bh_mask |= RT_BH_GARBAGE_COLLECT;
 }
 
-/*
- *     Rewrote rt_add(), as the old one was weird - Linus
- *
- *     This routine is used to update the IP routing table, either
- *     from the kernel (ICMP_REDIRECT) or via an ioctl call issued
- *     by the superuser.
- */
-void ip_rt_add(short flags, __u32 dst, __u32 mask,
-       __u32 gw, struct device *dev, unsigned short mtu,
-       unsigned long window, unsigned short irtt, short metric)
+static void rt_cache_add(unsigned hash, struct rtable * rth)
 {
-       struct rtable *r, *rt;
-       struct rtable **rp;
-       unsigned long cpuflags;
-       int duplicate = 0;
+       unsigned long   flags;
+       struct rtable   **rthp;
+       __u32           daddr = rth->rt_dst;
+       unsigned long   now = jiffies;
 
-       /*
-        *      A host is a unique machine and has no network bits.
-        */
-        
-       if (flags & RTF_HOST) 
+#if RT_CACHE_DEBUG >= 2
+       if (ip_rt_lock != 1)
        {
-               mask = 0xffffffff;
-       } 
-       
-       /*
-        *      Calculate the network mask
-        */
-        
-       else if (!mask) 
+               printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock);
+               return;
+       }
+#endif
+
+       save_flags(flags);
+
+       if (rth->rt_dev->header_cache_bind)
        {
-               if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
+               struct rtable * rtg = rth;
+
+               if (rth->rt_gateway != daddr)
                {
-                       mask = dev->pa_mask;
-                       flags &= ~RTF_GATEWAY;
-                       if (flags & RTF_DYNAMIC) 
+                       ip_rt_fast_unlock();
+                       rtg = ip_rt_route(rth->rt_gateway, 0);
+                       ip_rt_fast_lock();
+               }
+
+               if (rtg)
+               {
+                       if (rtg == rth)
+                               rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst);
+                       else
                        {
-                               /*printk("Dynamic route to my own net rejected\n");*/
-                               return;
+                               if (rtg->rt_hh)
+                                       ATOMIC_INCR(&rtg->rt_hh->hh_refcnt);
+                               rth->rt_hh = rtg->rt_hh;
+                               ip_rt_put(rtg);
                        }
-               } 
-               else
-                       mask = guess_mask(dst, dev);
-               dst &= mask;
+               }
        }
-       
-       /*
-        *      A gateway must be reachable and not a local address
-        */
-        
-       if (gw == dev->pa_addr)
-               flags &= ~RTF_GATEWAY;
-               
-       if (flags & RTF_GATEWAY) 
+
+       if (rt_cache_size >= RT_CACHE_SIZE_MAX)
+               rt_garbage_collect();
+
+       cli();
+       rth->rt_next = ip_rt_hash_table[hash];
+#if RT_CACHE_DEBUG >= 2
+       if (rth->rt_next)
        {
-               /*
-                *      Don't try to add a gateway we can't reach.. 
-                */
-                
-               if (dev != get_gw_dev(gw))
-                       return;
-                       
-               flags |= RTF_GATEWAY;
-       } 
-       else
-               gw = 0;
-               
+               struct rtable * trth;
+               printk("rt_cache @%02x: %08x", hash, daddr);
+               for (trth=rth->rt_next; trth; trth=trth->rt_next)
+                       printk(" . %08x", trth->rt_dst);
+               printk("\n");
+       }
+#endif
+       ip_rt_hash_table[hash] = rth;
+       rthp = &rth->rt_next;
+       sti();
+       rt_cache_size++;
+
        /*
-        *      Allocate an entry and fill it in.
+        * Cleanup duplicate (and aged off) entries.
         */
-        
-       rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
-       if (rt == NULL) 
+
+       while ((rth = *rthp) != NULL)
        {
-               return;
+
+               cli();
+               if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
+                   || rth->rt_dst == daddr)
+               {
+                       *rthp = rth->rt_next;
+                       rt_cache_size--;
+                       sti();
+#if RT_CACHE_DEBUG >= 2
+                       printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst);
+#endif
+                       rt_free(rth);
+                       continue;
+               }
+               sti();
+               rthp = &rth->rt_next;
        }
-       memset(rt, 0, sizeof(struct rtable));
-       rt->rt_flags = flags | RTF_UP;
-       rt->rt_dst = dst;
-       rt->rt_dev = dev;
-       rt->rt_gateway = gw;
-       rt->rt_mask = mask;
-       rt->rt_mss = dev->mtu - HEADER_SIZE;
-       rt->rt_metric = metric;
-       rt->rt_window = 0;      /* Default is no clamping */
+       restore_flags(flags);
+}
 
-       /* Are the MSS/Window valid ? */
+/*
+   RT should be already locked.
+   
+   We could improve this by keeping a chain of say 32 struct rtable's
+   last freed for fast recycling.
+   
+ */
 
-       if(rt->rt_flags & RTF_MSS)
-               rt->rt_mss = mtu;
-               
-       if(rt->rt_flags & RTF_WINDOW)
-               rt->rt_window = window;
-       if(rt->rt_flags & RTF_IRTT)
-               rt->rt_irtt = irtt;
+struct rtable * ip_rt_slow_route (__u32 daddr, int local)
+{
+       unsigned hash = ip_rt_hash_code(daddr)^local;
+       struct rtable * rth;
+       struct fib_node * f;
+       struct fib_info * fi;
+       __u32 saddr;
 
-       /*
-        *      What we have to do is loop though this until we have
-        *      found the first address which has a higher generality than
-        *      the one in rt.  Then we can put rt in right before it.
-        *      The interrupts must be off for this process.
-        */
+#if RT_CACHE_DEBUG >= 2
+       printk("rt_cache miss @%08x\n", daddr);
+#endif
 
-       save_flags(cpuflags);
-       cli();
+       rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+       if (!rth)
+       {
+               ip_rt_unlock();
+               return NULL;
+       }
 
-       /*
-        *      Remove old route if we are getting a duplicate. 
-        */
-        
-       rp = &rt_base;
-       while ((r = *rp) != NULL) 
+       if (local)
+               f = fib_lookup_local(daddr);
+       else
+               f = fib_lookup (daddr);
+
+       if (f)
        {
-               if (r->rt_dst != dst || 
-                   r->rt_mask != mask)
-               {
-                       rp = &r->rt_next;
-                       continue;
-               }
-               if (r->rt_metric != metric && r->rt_gateway != gw)
+               fi = f->fib_info;
+               f->fib_use++;
+       }
+
+       if (!f || (fi->fib_flags & RTF_REJECT))
+       {
+#if RT_CACHE_DEBUG >= 2
+               printk("rt_route failed @%08x\n", daddr);
+#endif
+               ip_rt_unlock();
+               kfree_s(rth, sizeof(struct rtable));
+               return NULL;
+       }
+
+       saddr = fi->fib_dev->pa_addr;
+
+       if (daddr == fi->fib_dev->pa_addr)
+       {
+               f->fib_use--;
+               if ((f = fib_loopback) != NULL)
                {
-                       duplicate = 1;
-                       rp = &r->rt_next;
-                       continue;
+                       f->fib_use++;
+                       fi = f->fib_info;
                }
-               *rp = r->rt_next;
-               if (rt_loopback == r)
-                       rt_loopback = NULL;
-               ip_netlink_msg(RTMSG_DELROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name);
-               kfree_s(r, sizeof(struct rtable));
        }
-       
-       /*
-        *      Add the new route 
-        */
-        
-       rp = &rt_base;
-       while ((r = *rp) != NULL) {
-               /*
-                * When adding a duplicate route, add it before
-                * the route with a higher metric.
-                */
-               if (duplicate &&
-                   r->rt_dst == dst &&
-                   r->rt_mask == mask &&
-                   r->rt_metric > metric)
-                       break;
-               else
-               /*
-                * Otherwise, just add it before the
-                * route with a higher generality.
-                */
-                       if ((r->rt_mask & mask) != mask)
-                               break;
-               rp = &r->rt_next;
+
+       if (!f)
+       {
+               ip_rt_unlock();
+               kfree_s(rth, sizeof(struct rtable));
+               return NULL;
        }
-       rt->rt_next = r;
-       *rp = rt;
-       
-       /*
-        *      Update the loopback route
-        */
-        
-       if ((rt->rt_dev->flags & IFF_LOOPBACK) && !rt_loopback)
-               rt_loopback = rt;
 
-       rt_stamp++;             /* New table revision */
-               
-       /*
-        *      Restore the interrupts and return
-        */
+       rth->rt_dst     = daddr;
+       rth->rt_src     = saddr;
+       rth->rt_lastuse = jiffies;
+       rth->rt_refcnt  = 1;
+       rth->rt_use     = 1;
+       rth->rt_next    = NULL;
+       rth->rt_hh      = NULL;
+       rth->rt_gateway = fi->fib_gateway;
+       rth->rt_dev     = fi->fib_dev;
+       rth->rt_mtu     = fi->fib_mtu;
+       rth->rt_window  = fi->fib_window;
+       rth->rt_irtt    = fi->fib_irtt;
+       rth->rt_tos     = f->fib_tos;
+       rth->rt_flags   = fi->fib_flags | RTF_HOST;
+       if (local)
+               rth->rt_flags   |= RTF_LOCAL;
 
-       restore_flags(cpuflags);
-       ip_netlink_msg(RTMSG_NEWROUTE, dst,gw, mask, flags, metric, rt->rt_dev->name);
-       return;
+       if (!(rth->rt_flags & RTF_GATEWAY))
+               rth->rt_gateway = rth->rt_dst;
+
+       if (ip_rt_lock == 1)
+               rt_cache_add(hash, rth);
+       else
+       {
+               rt_free(rth);
+#if RT_CACHE_DEBUG >= 1
+               printk("rt_cache: route to %08x was born dead\n", daddr);
+#endif
+       }
+
+       ip_rt_unlock();
+       return rth;
 }
 
+void ip_rt_put(struct rtable * rt)
+{
+       if (rt)
+               ATOMIC_DECR(&rt->rt_refcnt);
+}
 
-/*
- *     Check if a mask is acceptable.
- */
-static inline int bad_mask(__u32 mask, __u32 addr)
+struct rtable * ip_rt_route(__u32 daddr, int local)
 {
-       if (addr & (mask = ~mask))
-               return 1;
-       mask = ntohl(mask);
-       if (mask & (mask+1))
-               return 1;
-       return 0;
+       struct rtable * rth;
+
+       ip_rt_fast_lock();
+
+       for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
+       {
+               if (rth->rt_dst == daddr)
+               {
+                       rth->rt_lastuse = jiffies;
+                       ATOMIC_INCR(&rth->rt_use);
+                       ATOMIC_INCR(&rth->rt_refcnt);
+                       ip_rt_unlock();
+                       return rth;
+               }
+       }
+       return ip_rt_slow_route (daddr, local);
 }
 
+
 /*
- *     Process a route add request from the user
+ *     Process a route add request from the user, or from a kernel
+ *     task.
  */
  
-static int rt_new(struct rtentry *r)
+int ip_rt_new(struct rtentry *r)
 {
        int err;
        char * devname;
@@ -465,7 +1629,7 @@ static int rt_new(struct rtentry *r)
        /*
         *      BSD emulation: Permits route add someroute gw one-of-my-addresses
         *      to indicate which iface. Not as clean as the nice Linux dev technique
-        *      but people keep using it... 
+        *      but people keep using it...  (and gated likes it ;))
         */
         
        if (!dev && (flags & RTF_GATEWAY)) 
@@ -522,8 +1686,8 @@ static int rt_new(struct rtentry *r)
        /*
         *      Add the route
         */
-        
-       ip_rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
+
+       rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
        return 0;
 }
 
@@ -539,6 +1703,7 @@ static int rt_kill(struct rtentry *r)
        struct sockaddr_in *gtw;
        char *devname;
        int err;
+       struct device * dev = NULL;
 
        trg = (struct sockaddr_in *) &r->rt_dst;
        msk = (struct sockaddr_in *) &r->rt_genmask;
@@ -548,159 +1713,20 @@ static int rt_kill(struct rtentry *r)
                err = getname(devname, &devname);
                if (err)
                        return err;
+               dev = dev_get(devname);
+               putname(devname);
+               if (!dev)
+                       return -ENODEV;
        }
        /*
         * metric can become negative here if it wasn't filled in
         * but that's a fortunate accident; we really use that in rt_del.
         */
-       err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, devname,
+       err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, dev,
                (__u32)gtw->sin_addr.s_addr, r->rt_flags, r->rt_metric - 1);
-       if ( devname != NULL )
-               putname(devname);
        return err;
 }
 
-
-/* 
- *     Called from the PROCfs module. This outputs /proc/net/route.
- *
- *     We preserve the old format but pad the buffers out. This means that
- *     we can spin over the other entries as we read them. Remember the
- *     gated BGP4 code could need to read 60,000+ routes on occasion (thats
- *     about 7Mb of data). To do that ok we will need to also cache the
- *     last route we got to (reads will generally be following on from
- *     one another without gaps).
- */
-int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-       struct rtable *r;
-       int len=128;
-       off_t pos=0;
-       off_t begin=0;
-       char temp[129];
-
-       if(offset<128)
-               sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
-       pos=128;
-       
-       for (r = rt_base; r != NULL; r = r->rt_next) 
-       {
-               /*
-                *      Spin through entries until we are ready
-                */
-               if(pos+128<offset)
-               {
-                       pos+=128;
-                       continue;
-               }
-                                       
-               sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
-                       r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
-                       r->rt_flags, r->rt_refcnt, r->rt_use, r->rt_metric,
-                       (unsigned long)r->rt_mask, (int)r->rt_mss, r->rt_window, (int)r->rt_irtt);
-               sprintf(buffer+len,"%-127s\n",temp);
-               len+=128;
-               pos+=128;
-               if(pos<offset)
-               {
-                       len=0;
-                       begin=pos;
-               }
-               if(pos>offset+length)
-                       break;
-       }
-       
-       *start=buffer+(offset-begin);
-       len-=(offset-begin);
-       if(len>length)
-               len=length;
-       return len;
-}
-
-/*
- *     This is hackish, but results in better code. Use "-S" to see why.
- */
-#define early_out ({ goto no_route; 1; })
-
-/*
- *     Route a packet. This needs to be fairly quick. Florian & Co. 
- *     suggested a unified ARP and IP routing cache. Done right its
- *     probably a brilliant idea. I'd actually suggest a unified
- *     ARP/IP routing/Socket pointer cache. Volunteers welcome
- */
-struct rtable * ip_rt_route(__u32 daddr, struct options *opt, __u32 *src_addr)
-{
-       struct rtable *rt;
-
-       for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) 
-       {
-               if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
-                       break;
-               /*
-                *      broadcast addresses can be special cases.. 
-                */
-               if (rt->rt_flags & RTF_GATEWAY)
-                       continue;                
-               if ((rt->rt_dev->flags & IFF_BROADCAST) &&
-                   (rt->rt_dev->pa_brdaddr == daddr))
-                       break;
-       }
-       
-       if(rt->rt_flags&RTF_REJECT)
-               return NULL;
-       
-       if(src_addr!=NULL)
-               *src_addr= rt->rt_dev->pa_addr;
-               
-       if (daddr == rt->rt_dev->pa_addr) {
-               if ((rt = rt_loopback) == NULL)
-                       goto no_route;
-       }
-       rt->rt_use++;
-       return rt;
-no_route:
-       return NULL;
-}
-
-struct rtable * ip_rt_local(__u32 daddr, struct options *opt, __u32 *src_addr)
-{
-       struct rtable *rt;
-
-       for (rt = rt_base; rt != NULL || early_out ; rt = rt->rt_next) 
-       {
-               /*
-                *      No routed addressing.
-                */
-               if (rt->rt_flags&RTF_GATEWAY)
-                       continue;
-                       
-               if (!((rt->rt_dst ^ daddr) & rt->rt_mask))
-                       break;
-               /*
-                *      broadcast addresses can be special cases.. 
-                */
-                
-               if ((rt->rt_dev->flags & IFF_BROADCAST) &&
-                    rt->rt_dev->pa_brdaddr == daddr)
-                       break;
-       }
-       
-       if(src_addr!=NULL)
-               *src_addr= rt->rt_dev->pa_addr;
-               
-       if (daddr == rt->rt_dev->pa_addr) {
-               if ((rt = rt_loopback) == NULL)
-                       goto no_route;
-       }
-       rt->rt_use++;
-       return rt;
-no_route:
-       return NULL;
-}
-
 /*
  *     Handle IP routing ioctl calls. These are used to manipulate the routing tables
  */
@@ -720,8 +1746,15 @@ int ip_rt_ioctl(unsigned int cmd, void *arg)
                        if (err)
                                return err;
                        memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
-                       return (cmd == SIOCDELRT) ? rt_kill(&rt) : rt_new(&rt);
+                       return (cmd == SIOCDELRT) ? rt_kill(&rt) : ip_rt_new(&rt);
        }
 
        return -EINVAL;
 }
+
+void ip_rt_advice(struct rtable **rp, int advice)
+{
+       /* Thanks! */
+       return;
+}
+
index 6281566d9d0af70c5bf2009e87d6a90d41e73c2f..86a1075e3ff036dd7f06d6557d00ae80f254029f 100644 (file)
  *             Marc Tamsky     :       Closing in closing fixes.
  *             Mike Shaver     :       RFC1122 verifications.
  *             Alan Cox        :       rcv_saddr errors.
- *             Alan Cox        :       Block double connect()
+ *             Alan Cox        :       Block double connect().
+ *             Alan Cox        :       Small hooks for enSKIP.
+ *             Alexey Kuznetsov:       Path MTU discovery.
  *
  *
  * To Fix:
  *             so it doesn't iterate over the queue, also spot packets with no funny
  *             options arriving in order and process directly.
  *
- *             Implement RFC 1191 [Path MTU discovery]
- *             Look at the effect of implementing RFC 1337 suggestions and their impact.
  *             Rewrite output state machine to use a single queue and do low window
  *             situations as per the spec (RFC 1122)
  *             Speed up input assembly algorithm.
@@ -674,19 +674,25 @@ void tcp_do_retransmit(struct sock *sk, int all)
                 *      currently this is done (less efficiently) elsewhere.
                 */
 
-               iph->id = htons(ip_id_count++);
-               ip_send_check(iph);
-               
                /*
                 *      Put a MAC header back on (may cause ARPing)
                 */
                 
-               if(skb->localroute)
-                       rt=ip_rt_local(iph->daddr,NULL,NULL);
-               else
-                       rt=ip_rt_route(iph->daddr,NULL,NULL);
+               {
+                       /* ANK: UGLY, but the bug, that was here, should be fixed.
+                        */
+                       struct options *  opt = (struct options*)skb->proto_priv;
+                       rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute);
+               }
+
+               iph->id = htons(ip_id_count++);
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+               if (rt && ntohs(iph->tot_len) > rt->rt_mtu)
+                       iph->frag_off &= ~htons(IP_DF);
+#endif
+               ip_send_check(iph);
                        
-               if(rt==NULL)    /* Deep poo */
+               if (rt==NULL)   /* Deep poo */
                {
                        if(skb->sk)
                        {
@@ -698,11 +704,20 @@ void tcp_do_retransmit(struct sock *sk, int all)
                {
                        dev=rt->rt_dev;
                        skb->raddr=rt->rt_gateway;
-                       if(skb->raddr==0)
-                               skb->raddr=iph->daddr;
                        skb->dev=dev;
                        skb->arp=1;
-                       if(dev->hard_header)
+                       if (rt->rt_hh)
+                       {
+                               memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+                               if (!rt->rt_hh->hh_uptodate)
+                               {
+                                       skb->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+                                       printk("tcp_do_retransmit: hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway);
+#endif
+                               }
+                       }
+                       else if (dev->hard_header)
                        {
                                if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, skb->len)<0)
                                        skb->arp=0;
@@ -869,8 +884,7 @@ static int tcp_write_timeout(struct sock *sk)
                 *      Attempt to recover if arp has changed (unlikely!) or
                 *      a route has shifted (not supported prior to 1.3).
                 */
-               arp_destroy (sk->daddr, 0);
-               /*ip_route_check (sk->daddr);*/
+               ip_rt_advice(&sk->ip_route_cache, 0);
        }
        
        /*
@@ -1040,13 +1054,15 @@ static void retransmit_timer(unsigned long data)
 void tcp_err(int type, int code, unsigned char *header, __u32 daddr,
        __u32 saddr, struct inet_protocol *protocol)
 {
-       struct tcphdr *th;
+       struct tcphdr *th = (struct tcphdr *)header;
        struct sock *sk;
-       struct iphdr *iph=(struct iphdr *)header;
-  
-       header+=4*iph->ihl;
-   
-
+       
+       /*
+        *      This one is _WRONG_. FIXME urgently.
+        */
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY    
+       struct iphdr *iph=(struct iphdr *)(header-sizeof(struct iphdr));
+#endif  
        th =(struct tcphdr *)header;
        sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
 
@@ -1071,6 +1087,27 @@ void tcp_err(int type, int code, unsigned char *header, __u32 daddr,
                sk->error_report(sk);
        }
 
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+       {
+               struct rtable * rt;
+               /*
+                * Ugly trick to pass MTU to protocol layer.
+                * Really we should add argument "info" to error handler.
+                */
+               unsigned short new_mtu = ntohs(iph->id);
+
+               if ((rt = sk->ip_route_cache) != NULL)
+                       if (rt->rt_mtu > new_mtu)
+                               rt->rt_mtu = new_mtu;
+
+               if (sk->mtu > new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr))
+                       sk->mtu = new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+
+               return;
+       }
+#endif
+
        /*
         * If we've already connected we will keep trying
         * until we time out, or the user gives up.
@@ -1553,7 +1590,7 @@ static void tcp_send_ack(u32 sequence, u32 ack,
         */
         
        tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
-                               IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+                               IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
        if (tmp < 0) 
        {
                buff->free = 1;
@@ -1787,6 +1824,24 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg,
                /* 
                 *      Now we need to check if we have a half built packet. 
                 */
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+               /*
+                *      FIXME:  I'm almost sure that this fragment is BUG,
+                *              but it works... I do not know why 8) --ANK
+                *
+                *      Really, we should rebuild all the queues...
+                *      It's difficult. Temprorary hack is to send all
+                *      queued segments with allowed fragmentation.
+                */
+               {
+                       int new_mss = min(sk->mtu, sk->max_window);
+                       if (new_mss < sk->mss)
+                       {
+                               tcp_send_partial(sk);
+                               sk->mss = new_mss;
+                       }
+               }
+#endif
        
                        if ((skb = tcp_dequeue_partial(sk)) != NULL) 
                        {
@@ -1800,11 +1855,10 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg,
                                if (!(flags & MSG_OOB)) 
                                {
                                        copy = min(sk->mss - (skb->len - hdrlen), seglen);
-                                       /* FIXME: this is really a bug. */
                                        if (copy <= 0) 
                                        {
-                                               printk("TCP: **bug**: \"copy\" <= 0: %d - (%ld - %d) <= %d\n", sk->mss, skb->len, hdrlen, seglen);
-                                               copy = 0;
+                                               printk("TCP: **bug**: \"copy\" <= 0\n");
+                                               return -EFAULT;
                                        }                 
                                        memcpy_fromfs(skb_put(skb,copy), from, copy);
                                        from += copy;
@@ -1922,7 +1976,7 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg,
                         */
                
                        tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
-                                IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl);
+                                IPPROTO_TCP, sk->opt, skb->truesize,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
                        if (tmp < 0 ) 
                        {
                                sock_wfree(sk, skb);
@@ -1931,6 +1985,9 @@ static int tcp_sendmsg(struct sock *sk, struct msghdr *msg,
                                        return(copied);
                                return(tmp);
                        }
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+                       skb->ip_hdr->frag_off |= htons(IP_DF);
+#endif
                        skb->dev = dev;
                        skb->h.th =(struct tcphdr *)skb_put(skb,sizeof(struct tcphdr));
                        tmp = tcp_build_header(skb->h.th, sk, seglen-copy);
@@ -2038,7 +2095,7 @@ static void tcp_read_wakeup(struct sock *sk)
         */
 
        tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-                              IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+                              IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
        if (tmp < 0) 
        {
                buff->free = 1;
@@ -2560,7 +2617,7 @@ static void tcp_send_fin(struct sock *sk)
 
        tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
                           IPPROTO_TCP, sk->opt,
-                          sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
+                          sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
        if (tmp < 0) 
        {
                int t;
@@ -2715,7 +2772,7 @@ static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *t
         */
 
        tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
-                          sizeof(struct tcphdr),tos,ttl);
+                          sizeof(struct tcphdr),tos,ttl,NULL);
        if (tmp < 0) 
        {
                buff->free = 1;
@@ -2919,6 +2976,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
 
        memcpy(newsk, sk, sizeof(*newsk));
        newsk->opt = NULL;
+       newsk->ip_route_cache  = NULL;
        if (opt && opt->optlen) {
          sk->opt = (struct options*)kmalloc(sizeof(struct options)+opt->optlen, GFP_ATOMIC);
          if (!sk->opt) {
@@ -3022,7 +3080,8 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         *      Note use of sk->user_mss, since user has no direct access to newsk 
         */
 
-       rt=ip_rt_route(saddr, NULL,NULL);
+       rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+       newsk->ip_route_cache = rt;
        
        if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
                newsk->window_clamp = rt->rt_window;
@@ -3031,19 +3090,10 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
                
        if (sk->user_mss)
                newsk->mtu = sk->user_mss;
-       else if(rt!=NULL && (rt->rt_flags&RTF_MSS))
-               newsk->mtu = rt->rt_mss - sizeof(struct iphdr) - sizeof(struct tcphdr);
+       else if (rt)
+               newsk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
        else 
-       {
-#ifdef CONFIG_INET_SNARL       /* Sub Nets Are Local */
-               if ((saddr ^ daddr) & default_mask(saddr))
-#else
-               if ((saddr ^ daddr) & dev->pa_mask)
-#endif
-                       newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
-               else
-                       newsk->mtu = MAX_WINDOW;
-       }
+               newsk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
 
        /*
         *      But not bigger than device MTU 
@@ -3051,6 +3101,20 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
 
        newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 
+#ifdef CONFIG_SKIP
+       
+       /*
+        *      SKIP devices set their MTU to 65535. This is so they can take packets
+        *      unfragmented to security process then fragment. They could lie to the
+        *      TCP layer about a suitable MTU, but its easier to let skip sort it out
+        *      simply because the final package we want unfragmented is going to be
+        *
+        *      [IPHDR][IPSP][Security data][Modified TCP data][Security data]
+        */
+        
+       if(skip_pick_mtu!=NULL)         /* If SKIP is loaded.. */
+               sk->mtu=skip_pick_mtu(sk->mtu,dev);
+#endif
        /*
         *      This will min with what arrived in the packet 
         */
@@ -3080,7 +3144,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         */
 
        tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &ndev,
-                              IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
+                              IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&newsk->ip_route_cache);
 
        /*
         *      Something went wrong. 
@@ -3281,6 +3345,13 @@ static void tcp_write_xmit(struct sock *sk)
                        iph = skb->ip_hdr;
                        th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
                        size = skb->len - (((unsigned char *) th) - skb->data);
+#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
+                       if (size > sk->mtu - sizeof(struct iphdr))
+                       {
+                               iph->frag_off &= ~htons(IP_DF);
+                               ip_send_check(iph);
+                       }
+#endif
                        
                        th->ack_seq = ntohl(sk->acked_seq);
                        th->window = ntohs(tcp_select_window(sk));
@@ -4522,29 +4593,17 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
         *      Put in the IP header and routing stuff.
         */
         
-       if (sk->localroute)
-               rt=ip_rt_local(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr);
-       else
-               rt=ip_rt_route(sk->daddr, NULL, sk->saddr ? NULL : &sk->saddr);
-
-       /*
-        *      When we connect we enforce receive requirements too.
-        */
-        
-       sk->rcv_saddr=sk->saddr;
-       
-       /*
-        *      We need to build the routing stuff from the things saved in skb. 
-        */
-
        tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-                                       IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
+               IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
        if (tmp < 0) 
        {
                sock_wfree(sk, buff);
                release_sock(sk);
                return(-ENETUNREACH);
        }
+       if ((rt = sk->ip_route_cache) != NULL && !sk->saddr)
+               sk->saddr = rt->rt_src;
+       sk->rcv_saddr = sk->saddr;
 
        t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
 
@@ -4571,19 +4630,11 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
 
        if (sk->user_mss)
                sk->mtu = sk->user_mss;
-       else if(rt!=NULL && (rt->rt_flags&RTF_MSS))
-               sk->mtu = rt->rt_mss;
+       else if (rt)
+               sk->mtu = rt->rt_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
        else 
-       {
-#ifdef CONFIG_INET_SNARL
-               if ((sk->saddr ^ sk->daddr) & default_mask(sk->saddr))
-#else
-               if ((sk->saddr ^ sk->daddr) & dev->pa_mask)
-#endif
-                       sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
-               else
-                       sk->mtu = MAX_WINDOW;
-       }
+               sk->mtu = 576 - sizeof(struct iphdr) - sizeof(struct tcphdr);
+
        /*
         *      but not bigger than device MTU 
         */
@@ -4592,6 +4643,21 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
                sk->mtu = 32;   /* Sanity limit */
                
        sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
+
+#ifdef CONFIG_SKIP
+       
+       /*
+        *      SKIP devices set their MTU to 65535. This is so they can take packets
+        *      unfragmented to security process then fragment. They could lie to the
+        *      TCP layer about a suitable MTU, but its easier to let skip sort it out
+        *      simply because the final package we want unfragmented is going to be
+        *
+        *      [IPHDR][IPSP][Security data][Modified TCP data][Security data]
+        */
+        
+       if(skip_pick_mtu!=NULL)         /* If SKIP is loaded.. */
+               sk->mtu=skip_pick_mtu(sk->mtu,dev);
+#endif
        
        /*
         *      Put in the TCP options to say MTU. 
@@ -5222,7 +5288,7 @@ static void tcp_write_wakeup(struct sock *sk)
 
                tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
                                         IPPROTO_TCP, sk->opt, buff->truesize,
-                                        sk->ip_tos,sk->ip_ttl);
+                                        sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
                if (tmp < 0) 
                {
                        sock_wfree(sk, buff);
@@ -5321,7 +5387,7 @@ static void tcp_write_wakeup(struct sock *sk)
                 */
                 
                tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
-                               IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
+                               IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
                if (tmp < 0) 
                {
                        sock_wfree(sk, buff);
index aa1c5afc2def488fd19ca327bb7c515aa5daf0d8..e62cf1486f3f7affe34674e06645e4ca3b9d1812 100644 (file)
@@ -146,8 +146,6 @@ void net_timer (unsigned long data)
                        /* We've waited long enough, close the socket. */
                        sk->state = TCP_CLOSE;
                        delete_timer (sk);
-                       /* Kill the ARP entry in case the hardware has changed. */
-                       arp_destroy (sk->daddr, 0);
                        if (!sk->dead)
                                sk->state_change(sk);
                        sk->shutdown = SHUTDOWN_MASK;
index 88528625549cb7c14f5724cbde56f2b632293cdb..e33880ed9a55ad748092b4edef802956e6d37468 100644 (file)
@@ -400,8 +400,8 @@ static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int n
  *     Temporary
  */
  
-static int udp_sendmsg(struct sock *sk, struct msghdr *msg,
-       int len, int noblock, int flags)
+static int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
+       int flags)
 {
        if(msg->msg_iovlen==1)
                return udp_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
@@ -541,7 +541,6 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
 int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
 {
        struct rtable *rt;
-       __u32 sa;
        if (addr_len < sizeof(*usin)) 
                return(-EINVAL);
 
@@ -553,19 +552,18 @@ int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
        if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
                return -EACCES;                 /* Must turn broadcast on first */
        
-       rt=(sk->localroute?ip_rt_local:ip_rt_route)((__u32)usin->sin_addr.s_addr, NULL, &sa);
-       if(rt==NULL)
+       rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
+       if (rt==NULL)
                return -ENETUNREACH;
        if(!sk->saddr)
-               sk->saddr = sa;         /* Update source address */
+               sk->saddr = rt->rt_src;         /* Update source address */
        if(!sk->rcv_saddr)
-               sk->rcv_saddr = sa;
+               sk->rcv_saddr = rt->rt_src;
        sk->daddr = usin->sin_addr.s_addr;
        sk->dummy_th.dest = usin->sin_port;
        sk->state = TCP_ESTABLISHED;
        udp_cache_zap();
        sk->ip_route_cache = rt;
-       sk->ip_route_stamp = rt_stamp;
        return(0);
 }
 
index a10b8cf029f3ddba1b00075ba621020700fb0470..e5fbec680eb38d5cfa5b90b17544b8fd345ff50b 100644 (file)
@@ -304,7 +304,7 @@ ipxitf_down(ipx_interface *intrfc)
 }
 
 static int 
-ipxitf_device_event(unsigned long event, void *ptr)
+ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
 {
        struct device *dev = ptr;
        ipx_interface *i, *tmp;
index c72abe4342fd60b55bd534513647d579b52ca8c8..3b7d7fb00a21d19faea4696c731515b0c6dd21f3 100644 (file)
@@ -216,8 +216,8 @@ int init_netlink(void)
 {
        int ct;
 
-       if(register_chrdev(NET_MAJOR,"netlink", &netlink_fops)) {
-               printk("netlink: unable to get major %d\n", NET_MAJOR);
+       if(register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {
+               printk("netlink: unable to get major %d\n", NETLINK_MAJOR);
                return -EIO;
        }
        for(ct=0;ct<MAX_LINKS;ct++)
index c914fa3505dbc0afd7f2665abf9c8b7e70ce8360..d00d595ceae2a778398d83f7e86765530b609957 100644 (file)
@@ -112,7 +112,7 @@ static int nr_rebuild_header(void *buff, struct device *dev,
 
        skb_device_unlock(skb);
 
-       if (!arp_query(bp + 7, raddr, ARPHRD_NETROM)) {
+       if (!arp_query(bp + 7, raddr, dev)) {
                skb->free = 1;
                kfree_skb(skb, FREE_WRITE);
                return 1;
index ff4bb73b44f8514144d981b27e047fc1ccd1c947..2181e60e9d294b61236b04ddd0cd0313e7415bb3 100644 (file)
@@ -524,7 +524,7 @@ static struct device *nr_ax25_dev_get(char *devname)
                return dev;
 
        if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-               if (arp_query((unsigned char *)&callsign, dev->pa_addr, ARPHRD_AX25))
+               if (arp_query((unsigned char *)&callsign, dev->pa_addr, dev))
                        return dev;
        
        return NULL;
index 66765bcd07a167db47ce3314963ab883bae5d4f4..98baa2c59ef19838d9dded0ae530221e0394fffd 100644 (file)
@@ -878,12 +878,12 @@ asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags)
        if(err)
                return err;
                
+       iov.iov_base=buff;
+       iov.iov_len=len;
        msg.msg_name=NULL;
        msg.msg_iov=&iov;
        msg.msg_iovlen=1;
        msg.msg_accrights=NULL;
-       iov.iov_base=buff;
-       iov.iov_len=1;
        return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
 }