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
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
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
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:
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]
--------------------------
+ 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:
(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:
(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:
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:
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:
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*
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*
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,
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 41
+SUBLEVEL = 42
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
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.
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
$(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
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
pic &= pic - 1;
device_interrupt(j, j, regs);
}
+#endif
}
static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector,
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
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
#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.
CFLAGS = -O2 -DSTDC_HEADERS
+ifdef SMP
+CFLAGS := $(CFLAGS) -D__SMP__
+endif
+
ifdef CONFIG_KERNEL_ELF
TARGET=--target elf32-i386
INPUT_DATA=input_data
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
!
! 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
entry start
start:
+
! Bootlin depends on this being done early
mov ax,#0x01500
mov dl,#0x81
! 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
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
! 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
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
.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
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
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
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
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
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
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]
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
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
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
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
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
.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
#
# 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
# 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
#.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
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
$(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
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;
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;
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;
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);
}
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);
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
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) {
return -EINVAL;
}
- if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (_IOC_DIR(cmd) & _IOC_READ)
return fd_copyout((void *)param, outparam, size);
else
return 0;
}
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 */
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;
}
* 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;
/*
* 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;
*/
-#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>
= "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>
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
*
* 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
*
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 */
*/
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;
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;
}
Changelog:
Paul Gortmaker : add support for the 2nd 8kB of RAM on 16 bit cards.
+ Paul Gortmaker : multiple card support for module users.
*/
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++)
#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;
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;
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;
}
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);
return -EAGAIN;
}
}
+
el2_init_card(dev);
- return ei_open(dev);
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
}
static int
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;
}
}
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
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
#include "8390.h"
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;
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;
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
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
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
endif
endif
-ifneq ($(CONFIG_PPP),n)
+ifdef CONFIG_PPP
M_OBJS += bsd_comp.o
endif
#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
#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
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;
}
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);
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;
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;
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;
return -EAGAIN;
#endif
- rc = ei_open(dev);
- if (rc != 0) return rc;
+ ei_open(dev);
MOD_INC_USE_COUNT;
irq2dev_map[dev->irq] = 0;
#endif
- NS8390_init(dev, 0);
+ ei_close(dev);
MOD_DEC_USE_COUNT;
}
#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
{
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
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]);
} 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;
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;
e21_open(struct device *dev)
{
short ioaddr = dev->base_addr;
- int rc;
if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) {
return EBUSY;
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;
}
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);
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. */
{"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
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);
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");
}
}
- 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! */
/* 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
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
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
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);
{
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
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++)
}
}
+ /* 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;
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)
{
}
#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
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.
*/
/* 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. */
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);
int start_page, stop_page;
int neX000, ctron;
int reg0 = inb_p(ioaddr);
+ static unsigned version_printed = 0;
if (reg0 == 0xFF)
return ENODEV;
}
}
+ 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. */
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);
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 {
}
-
- 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);
/* 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;
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;
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
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
}
-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);
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;
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 =
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);
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";
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);
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;
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;
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;
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;
}
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
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;
}
#ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
static int tunnel_probe(struct device *dev)
{
Changelog:
- Paul Gortmaker : multiple card support for module users
+ Paul Gortmaker : multiple card support for module users, support
+ for non-standard memory sizes.
+
*/
|| (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);
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);
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. */
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;
}
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);
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,
},
};
-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;
{
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);
}
}
}
}
}
-
-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) {
&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;
}
--- /dev/null
+/*
+
+ 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);
+}
--- /dev/null
+/*
+
+ 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 */
+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
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
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
--- /dev/null
+ 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.
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
#endif
#ifdef CONFIG_SCSI_BUSLOGIC
-#include "buslogic.h"
+#include "BusLogic.h"
#endif
#ifdef CONFIG_SCSI_EATA_DMA
#ifdef CONFIG_SCSI_AHA152X
AHA152X,
#endif
-/* Buslogic must come before aha1542.c */
+/* BusLogic must come before aha1542.c */
#ifdef CONFIG_SCSI_BUSLOGIC
BUSLOGIC,
#endif
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),
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
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
#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, \
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
#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;
unsigned int elf_stack;
char passed_fileno[6];
- MOD_INC_USE_COUNT;
-
ibcs2_interpreter = 0;
status = 0;
load_addr = 0;
if (elf_ex.e_ident[0] != 0x7f ||
strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
- MOD_DEC_USE_COUNT;
return -ENOEXEC;
}
(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;
}
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;
}
elf_ex.e_phentsize * elf_ex.e_phnum, 1);
if (retval < 0) {
kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
return retval;
}
if (elf_exec_fileno < 0) {
kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
return elf_exec_fileno;
}
{
kfree (elf_phdata);
kfree(elf_interpreter);
- MOD_DEC_USE_COUNT;
return -EINVAL;
}
GFP_KERNEL);
if (elf_interpreter == NULL) {
kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
return -ENOMEM;
}
if(retval < 0) {
kfree (elf_phdata);
kfree(elf_interpreter);
- MOD_DEC_USE_COUNT;
return retval;
}
}
{
kfree(elf_interpreter);
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
return -ELIBBAD;
}
}
kfree(elf_interpreter);
}
kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
return -E2BIG;
}
}
printk("Unable to load interpreter\n");
kfree(elf_phdata);
send_sig(SIGSEGV, current, 0);
- MOD_DEC_USE_COUNT;
return 0;
}
}
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;
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);
if(j != 1) {
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
return -ENOEXEC;
}
SYS(close)(fd);
if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
return error;
}
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
*
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;
/*
* 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 */
/*
* 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);
* "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)
/*
* 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;
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) ||
#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;
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;
/*
* 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;
/*
* 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;
/*
* 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;
#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)
{
{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;
}
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;
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;
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);
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;
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)
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
--- /dev/null
+/*
+ * 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;
+}
* 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;
*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);
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;
}
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.
/*
* 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>
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;
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");
* 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) | \
* processes are run.
*/
-#define PROC_CHANGE_PENALTY 5 /* Schedule penalty */
+#define PROC_CHANGE_PENALTY 20 /* Schedule penalty */
#endif
#endif
/*
* 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
* 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 {
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.
*
* For definitions of the flags field, see tty.h
*/
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
struct cyclades_port {
int magic;
int type;
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.
#define CyMAX_CHAR_FIFO 12
/***************************************************************************/
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CYCLADES_H */
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);
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);
#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 */
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. */
*/
/*
- * ==FILEVERSION 4==
+ * ==FILEVERSION 5==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, increment the number above.
#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.
#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 */
#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 */
* 32 - philips/lms cm206 cdrom
* 33 - ide2
* 34 - z8530 driver ide3
+ * 36 - netlink
*/
#define UNNAMED_MAJOR 0
#define CM206_CDROM_MAJOR 32
#define IDE2_MAJOR 33
#define IDE3_MAJOR 34
+#define NETLINK_MAJOR 36
/*
* Tests for SCSI devices.
/* 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 *);
#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.
#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
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
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);
};
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);
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 */
* 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 */
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);
PROC_NET_NR_NEIGH,
PROC_NET_NR,
PROC_NET_SOCKSTAT,
+ PROC_NET_RTCACHE,
PROC_NET_LAST
};
/* 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 */
#ifndef _LINUX_TCP_H
#define _LINUX_TCP_H
-
-#define HEADER_SIZE 128 /* maximum header size */
-
-
struct tcphdr {
__u16 source;
__u16 dest;
#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 */
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
+#include <net/route.h>
#ifndef _SNMP_H
#include <net/snmp.h>
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,
#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);
* 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 */
#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
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 */
__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);
* 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__
#include <linux/ioport.h>
#include <linux/hdreg.h>
#include <linux/mm.h>
+#include <linux/major.h>
#include <asm/bugs.h>
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);
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, };
{ "aic7xxx=", aic7xxx_setup},
#endif
#ifdef CONFIG_SCSI_BUSLOGIC
- { "buslogic=", buslogic_setup},
+ { "BusLogic=", BusLogic_Setup},
#endif
#ifdef CONFIG_SCSI_NCR53C406A
{ "ncr53c406a=", NCR53c406a_setup},
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) {
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;
}
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;
return(1); /* (sighing) "Often!" */
}
-static int has_stopped_jobs(int pgrp)
+static inline int has_stopped_jobs(int pgrp)
{
struct task_struct * p;
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;
}
}
-void exit_files(struct task_struct *tsk)
+static inline void __exit_files(struct task_struct *tsk)
{
struct files_struct * files = tsk->files;
}
}
-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;
}
}
-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;
}
}
-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;
del_timer(¤t->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;
if (flag)
return flag;
}
+ if (ru) {
+ flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru));
+ if (flag)
+ return flag;
+ }
add_wait_queue(¤t->wait_chldexit,&wait);
repeat:
flag=0;
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:
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;
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
- retval = flag;
goto end_wait4;
default:
continue;
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;
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;
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);
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++;
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;
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++;
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
X(getblk),
X(bread),
X(breada),
- X(brelse),
+ X(__brelse),
+ X(__bforget),
X(ll_rw_block),
X(__wait_on_buffer),
X(dcache_lookup),
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),
* 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;
#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.
* 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 */
* 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;
* 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;
}
/* 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;
/* 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;
}
/* 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;
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;
-}
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]
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 ----------------
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.
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;
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;
}
* 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.
*
*/
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
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)
}
/*
- * 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)
* 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;
}
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
{
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.
sk->daddr = 0;
sk->dummy_th.dest = 0;
}
+ ip_rt_put(sk->ip_route_cache);
sk->ip_route_cache=NULL;
return(0);
}
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:
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
+ });
}
{
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 */
/*
struct timer_list timer; /* expire timer */
int retries; /* remaining retries */
struct sk_buff_head skb; /* list of queued packets */
+ struct hh_cache *hh;
};
/*
* 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
/*
* 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 };
/*
* 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] =
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.
*/
#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();
}
/*
{
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;
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;
}
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;
* No arp on this interface.
*/
- if(dev->flags&IFF_NOARP)
+ if (dev->flags&IFF_NOARP)
return;
/*
#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);
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;
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);
* 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
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;
*/
/* Again, should this be an error/printk? -- MS */
- switch(dev->type)
+ switch (dev->type)
{
#ifdef CONFIG_AX25
case ARPHRD_AX25:
* 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
* 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;
}
/*
* 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);
}
}
/*
* 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))
{
*/
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
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;
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)
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 */
{
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;
}
memcpy(haddr, entry->ha, dev->addr_len);
if (skb)
skb->arp = 1;
- sti();
+ arp_unlock();
return 0;
}
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;
}
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;
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)
*/
#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 ];
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;
}
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;
}
* 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.
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;
}
}
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);
+}
+
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;
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;
* (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:
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);
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 */
* 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)
{
/*
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
raddr=skb->raddr;
if(is_frag&16) /* VIFF_TUNNEL mode */
encap=20;
+ rt=NULL;
}
#endif
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;
}
if (skb2 == NULL)
{
NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
+ ip_rt_put(rt);
return -1;
}
}
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
#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;
}
}
else
+ {
+ ip_rt_put(rt);
return -1;
+ }
+ ip_rt_put(rt);
/*
* Tell the caller if their buffer is free.
__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
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
/* 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;
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;
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));
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:
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:
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;
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;
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:
/*
* 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.
*/
* 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;
* (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)
{
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;
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)
{
* 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;
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++;
}
*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;
* 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
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;
__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;
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;
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
*/
* 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;
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)
{
}
return 0;
}
- length-=20;
+ length -= sizeof(struct iphdr);
if (sk && !sk->ip_hdrincl && opt)
{
length -= opt->optlen;
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);
* 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)
{
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;
/*
* 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
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
sti();
arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha,
- dev->dev_addr);
+ dev->dev_addr, sha);
}
else
sti();
int htype, hlen;
unsigned long ip;
struct rtable *rt;
+ struct device * dev;
memcpy_fromfs(&r, req, sizeof(r));
* 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...
entry->hlen = hlen;
entry->htype = htype;
memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
- entry->dev = rt->rt_dev;
+ entry->dev = dev;
sti();
rarp_release_entry(rt);
}
}
-
#endif
* 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>
#include <net/udp.h>
#include <net/checksum.h>
-#include <linux/config.h>
-
#ifdef CONFIG_IP_MROUTE
struct sock *mroute_socket=NULL;
#endif
* 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);
* 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;
}
/*
}
-/*
- * 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;
/*
* 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))
/*
* 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;
}
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;
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
*/
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;
+}
+
* 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.
* 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)
{
{
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;
* 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);
}
/*
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);
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.
*/
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;
/*
* 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)
{
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;
*/
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);
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);
*/
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;
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;
*/
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;
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) {
* 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;
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
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
*/
*/
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.
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));
* 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));
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
*/
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.
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);
*/
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);
/* 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;
* 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);
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);
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);
}
}
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;
{
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++)
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;
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;
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));
}