]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Linux-0.98 (September 29, 1992) 0.98
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:05 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:05 +0000 (15:09 -0500)
Real networking (TCP) merged! This is the now-called "net-1" code by
Ross Biro.  Boy, was it ugly, but it made for a big jump from not having
any at all.

(And add support for up the 32MB of memory ;)

[Original announcement below]

Sorry for being late - I can't even show any great new features in 0.98,
but at least it's out now, and available at the normal place (ie at
nic.funet.fi, pub/OS/Linux/testing/Linus).  So far there is only a
full-source version available, although I'll probably make it available
as a patch too tomorrow or so (but the patch won't contain the tcp/ip
stuff).

0.98 is essentially the same as 0.97.pl6 - the changes are mostly:
 - tcp/ip (0.8.1) is in.  It's not compiled into the standard bootimage,
   and you'd better be on the tcpip mailing-list to use it, but it's
   there. I've been unable to test it further than just watch it
   compile...
 - extfs patch to correct the problem with big directories with holes.
 - mouse patches (ie improved detection-routines)
 - minor scsi patches (ultrastor driver change)
 - swiss keyboard
 - some serial driver patches
 - the 32mb patches are in, so if you aren't using a DMA-SCSI driver,
   and have more than 16MB physical memory, you can get it recognized.
 - edited hd.c
 - corrected core-dumping routines

I didn't get my mm patches working yet, so they'll have to wait.  The
above are almost 100% by others - I have edited some of the patches, but
there is nothing major new by me.  Most of it is minor bug-fixes, and
the only thing that might be a bit of a problem are the hd.c changes:
but I hope they'll solve more problems than they cause.  Knock wood.

At nic.funet.fi you can currently find (a) the full sources (b) a
bootimage (US keyboard, floppy root, no tcp/ip) and (c) the protocols.h
file needed for compiling the tcp/ip directory (which should go into
/usr/include/netinet/).  I hope people try it out, and that there are no
new problems with this release.

                Linus

53 files changed:
.version
Makefile
README
boot/head.S [new file with mode: 0644]
boot/head.s [deleted file]
fs/buffer.c
fs/exec.c
fs/ext/Makefile
fs/ext/dir.c
fs/ext/namei.c
include/linux/busmouse.h
include/linux/config.h
include/linux/socket.h
include/linux/vmm.h [new file with mode: 0644]
init/main.c
kernel/blk_drv/genhd.c
kernel/blk_drv/hd.c
kernel/blk_drv/scsi/ultrastor.c
kernel/chr_drv/busmouse.c
kernel/chr_drv/keyboard.c
kernel/chr_drv/serial.c
kernel/sys.c
mm/memory.c
net/Makefile
net/tcp/Makefile [new file with mode: 0644]
net/tcp/Space.c [new file with mode: 0644]
net/tcp/arp.c [new file with mode: 0644]
net/tcp/arp.h [new file with mode: 0644]
net/tcp/dev.c [new file with mode: 0644]
net/tcp/dev.h [new file with mode: 0644]
net/tcp/eth.c [new file with mode: 0644]
net/tcp/eth.h [new file with mode: 0644]
net/tcp/icmp.c [new file with mode: 0644]
net/tcp/icmp.h [new file with mode: 0644]
net/tcp/ip.c [new file with mode: 0644]
net/tcp/ip.h [new file with mode: 0644]
net/tcp/loopback.c [new file with mode: 0644]
net/tcp/pack_type.c [new file with mode: 0644]
net/tcp/packet.c [new file with mode: 0644]
net/tcp/protocols.c [new file with mode: 0644]
net/tcp/raw.c [new file with mode: 0644]
net/tcp/sock.c [new file with mode: 0644]
net/tcp/sock.h [new file with mode: 0644]
net/tcp/tcp.c [new file with mode: 0644]
net/tcp/tcp.h [new file with mode: 0644]
net/tcp/timer.c [new file with mode: 0644]
net/tcp/timer.h [new file with mode: 0644]
net/tcp/udp.c [new file with mode: 0644]
net/tcp/udp.h [new file with mode: 0644]
net/tcp/we.c [new file with mode: 0644]
net/tcp/wereg.h [new file with mode: 0644]
tools/build.c
tools/version.h

index 60d3b2f4a4cd5f1637eba020358bfe5ecb5edcf2..aabe6ec3909c9d4871f8f89ef88ca3b6795ebd29 100644 (file)
--- a/.version
+++ b/.version
@@ -1 +1 @@
-15
+21
index 5996fdd821a526cc8fbbf4db97b9fc9c262916d3..3bf497a5a216eb8e03e3d9c6edd3fc6145b4e6f3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,14 +4,14 @@
 # default of FLOPPY is used by 'build'.
 #
 
-ROOT_DEV = /dev/hdb1
+ROOT_DEV =# /dev/hdb1
 
 #
 # uncomment this if you want kernel profiling: the profile_shift is the
 # granularity of the profiling (5 = 32-byte granularity)
 #
 
-PROFILING = -DPROFILE_SHIFT=2
+PROFILING =# -DPROFILE_SHIFT=2
 
 #
 # uncomment the correct keyboard:
@@ -30,9 +30,9 @@ PROFILING = -DPROFILE_SHIFT=2
 # 0x08 - tilde (~)
 # 0x10 - dieresis (umlaut)
 
-KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
+KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
 # KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F
-KEYBOARD = -DKBD_US -DKBDFLAGS=0
+KEYBOARD = -DKBD_US -DKBDFLAGS=0
 # KEYBOARD = -DKBD_GR -DKBDFLAGS=0
 # KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F
 # KEYBOARD = -DKBD_FR -DKBDFLAGS=0
@@ -43,6 +43,8 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
 # KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0
 # KEYBOARD = -DKBD_SG -DKBDFLAGS=0
 # KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x9F
+# KEYBOARD = -DKBD_SF -DKBDFLAGS=0
+# KEYBOARD = -DKBD_SF_LATIN1 -DKBDFLAGS=0x9F
 # KEYBOARD = -DKDB_NO
 
 #
@@ -51,11 +53,24 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
 
 MATH_EMULATION = -DKERNEL_MATH_EMULATION
 
+#
+# Maximum memory used by the kernel. This is normally 16MB - some of the
+# SCSI drivers may have problems with anything else due to DMA limits. The
+# drivers should check, but they don't. The ONLY valid values for
+# MAX_MEGABYTES are 16 and 32 - anything else needs kernel diffs.
+#
+# EVEN IF YOU HAVE > 16MB, YOU SHOULD EDIT THIS ONLY IF YOU ARE 100%
+# SURE YOU AREN'T USING ANY DEVICE THAT DOES UNCHECKED DMA!!  THE
+# FLOPPY DRIVER IS OK, BUT OTHERS MIGHT HAVE PROBLEMS.
+#
+
+MAX_MEGABYTES = 16
+
 #
 # standard CFLAGS
 #
 
-CFLAGS =-Wall -O6 -fomit-frame-pointer
+CFLAGS =-Wall -O6 -fomit-frame-pointer -DMAX_MEGABYTES=$(MAX_MEGABYTES)
 
 #
 # if you want the ram-disk device, define this to be the
@@ -80,7 +95,7 @@ LD    =ld
 HOSTCC =gcc -static
 CC     =gcc -DKERNEL
 MAKE   =make
-CPP    =$(CC) -E
+CPP    =$(CC) -E -DMAX_MEGABYTES=$(MAX_MEGABYTES)
 AR     =ar
 
 ARCHIVES       =kernel/kernel.o mm/mm.o fs/fs.o net/net.o
@@ -112,7 +127,7 @@ linuxsubdirs: dummy
 
 Version:
        @./makever.sh
-       @echo \#define UTS_RELEASE \"0.97.pl6-`cat .version`\" > tools/version.h
+       @echo \#define UTS_RELEASE \"0.98-`cat .version`\" > tools/version.h
        @echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h
        @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
        @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
@@ -134,6 +149,9 @@ tools/build: tools/build.c
 
 boot/head.o: boot/head.s
 
+boot/head.s: boot/head.S
+       $(CPP) -traditional boot/head.S -o boot/head.s
+
 tools/version.o: tools/version.c tools/version.h
 
 init/main.o: init/main.c
@@ -173,7 +191,7 @@ kernel: dummy
 
 clean:
        rm -f Image System.map tmp_make core boot/bootsect boot/setup \
-               boot/bootsect.s boot/setup.s init/main.s
+               boot/bootsect.s boot/setup.s boot/head.s init/main.s
        rm -f init/*.o tools/system tools/build boot/*.o tools/*.o
        for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
 
diff --git a/README b/README
index 9868ea511abbb20ed8a238965ded6dcff19f1ce7..3bc7d7f493afab81ef963c56006afc2c991a4a4e 100644 (file)
--- a/README
+++ b/README
@@ -72,10 +72,24 @@ want to include these in the kernel, do the following:
        - remove the corresponding lines in the initialization of
          file_systems in fs/super.c.
 
-5.  To configure more ptys do this:
-       - change NR_PTYS in include/linux/tty.h to the number you want
-       - create the new files in /dev
-       - recompile the kernel
+5.  The TCP/IP code is in the standard sources as of version 0.98, but
+it is not compiled into the normal binary. To get TCP/IP working, you
+have to:
+
+       - edit 'net/Makefile', defining the SUBDIRS and SOCK_FLAGS
+         variables correctly (currently commented out).  Likewise, you
+         have to edit the rule for linking net.o in the Makefile (again
+         removing a '#' to make tcp/tcpip.o active)
+       - make sure you have the /usr/include/netinet/protocols.h header
+         file.  If you don't have it, you should be able to find it at
+         the same place you got the kernel, or with a newer compiler
+         version.
+       - remove all object (*.o) files in the net/ subdirectory, making
+         sure that they are recompiled with the correct Makefile
+         definitions.
+       - Additionally, you obviously need the tcp/ip programs to make any
+         use of the kernel feature.  If you haven't joined the TCP/IP
+         mailing list, do so. 
 
 
 * Running make
@@ -105,7 +119,8 @@ copy.  This requires dd.
 
 "make dep" updates all dependencies.  This requires sed.  It modifies
 the makefiles directly (the end of them, starting at the ###Dependencies
--line at the end).
+-line at the end).  "make dep" is required after patching, or the kernel
+may not compile cleanly. 
 
 "make clean" will remove all object files and other files created by the
 compilation.  This requires basename.
@@ -120,4 +135,4 @@ The tee part is so that you can check what is going on while the
 compilation runs.  If you have GNU emacs and use M-x compile you don't
 need this, of course.
 
-               Lars Wirzeniu
+               Lars Wirzenius
diff --git a/boot/head.S b/boot/head.S
new file mode 100644 (file)
index 0000000..e55b467
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ *  linux/boot/head.s
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#if (MAX_MEGABYTES != 16) && (MAX_MEGABYTES != 32)
+#error "MAX_MEGABYTES must be 16 or 32"
+#endif
+
+/*
+ *  head.s contains the 32-bit startup code.
+ *
+ * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
+ * the page directory will exist. The startup code will be overwritten by
+ * the page directory.
+ */
+.text
+.globl _idt,_gdt,_swapper_pg_dir,_tmp_floppy_area,_floppy_track_buffer
+/*
+ * swapper_pg_dir is the main page directory, address 0x00000000
+ */
+_swapper_pg_dir:
+startup_32:
+       cld
+       movl $0x10,%eax
+       mov %ax,%ds
+       mov %ax,%es
+       mov %ax,%fs
+       mov %ax,%gs
+       lss _stack_start,%esp
+       call setup_idt
+       xorl %eax,%eax
+1:     incl %eax               # check that A20 really IS enabled
+       movl %eax,0x000000      # loop forever if it isn't
+       cmpl %eax,0x100000
+       je 1b
+/* check if it is 486 or 386. */
+       movl %esp,%edi          # save stack pointer
+       andl $0xfffffffc,%esp   # align stack to avoid AC fault
+       pushfl                  # push EFLAGS
+       popl %eax               # get EFLAGS
+       movl %eax,%ecx          # save original EFLAGS
+       xorl $0x40000,%eax      # flip AC bit in EFLAGS
+       pushl %eax              # copy to EFLAGS
+       popfl                   # set EFLAGS
+       pushfl                  # get new EFLAGS
+       popl %eax               # put it in eax
+       xorl %ecx,%eax          # check if AC bit is changed. zero is 486.
+       jz 1f                   # 486
+       pushl %ecx              # restore original EFLAGS
+       popfl
+       movl %edi,%esp          # restore esp
+       movl %cr0,%eax          # 386
+       andl $0x80000011,%eax   # Save PG,PE,ET
+       orl $2,%eax             # set MP
+       jmp 2f  
+/*
+ * NOTE! 486 should set bit 16, to check for write-protect in supervisor
+ * mode. Then it would be unnecessary with the "verify_area()"-calls.
+ * 486 users probably want to set the NE (#5) bit also, so as to use
+ * int 16 for math errors.
+ */
+1:     pushl %ecx              # restore original EFLAGS
+       popfl
+       movl %edi,%esp          # restore esp
+       movl %cr0,%eax          # 486
+       andl $0x80000011,%eax   # Save PG,PE,ET
+       orl $0x10022,%eax       # set NE and MP
+2:     movl %eax,%cr0
+       call check_x87
+       jmp after_page_tables
+
+/*
+ * We depend on ET to be correct. This checks for 287/387.
+ */
+check_x87:
+       fninit
+       fstsw %ax
+       cmpb $0,%al
+       je 1f
+       movl %cr0,%eax          /* no coprocessor: have to set bits */
+       xorl $6,%eax            /* reset MP, set EM */
+       movl %eax,%cr0
+       ret
+.align 2
+1:     .byte 0xDB,0xE4         /* fsetpm for 287, ignored by 387 */
+       ret
+
+/*
+ *  setup_idt
+ *
+ *  sets up a idt with 256 entries pointing to
+ *  ignore_int, interrupt gates. It doesn't actually load
+ *  idt - that can be done only after paging has been enabled
+ *  and the kernel moved to 0xC0000000. Interrupts
+ *  are enabled elsewhere, when we can be relatively
+ *  sure everything is ok. This routine will be over-
+ *  written by the page tables.
+ */
+setup_idt:
+       lea ignore_int,%edx
+       movl $0x00080000,%eax
+       movw %dx,%ax            /* selector = 0x0008 = cs */
+       movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */
+
+       lea _idt,%edi
+       mov $256,%ecx
+rp_sidt:
+       movl %eax,(%edi)
+       movl %edx,4(%edi)
+       addl $8,%edi
+       dec %ecx
+       jne rp_sidt
+       ret
+
+/*
+ * I put the kernel page tables right after the page directory,
+ * using 4 of them to span 16 Mb of physical memory. People with
+ * more than 16MB will have to expand this.
+ * When MAX_MEGABYTES == 32, this is set up for a maximum of 32 MB
+ * (ref: 17Apr92)  (redone for 0.97 kernel changes, 1Aug92, ref)
+ */
+.org 0x1000
+pg0:
+
+.org 0x2000
+pg1:
+
+.org 0x3000
+pg2:
+
+.org 0x4000
+pg3:
+
+.org 0x5000
+#if MAX_MEGABYTES == 32
+pg4:
+
+.org 0x6000
+pg5:
+
+.org 0x7000
+pg6:
+
+.org 0x8000
+pg7:
+
+.org 0x9000
+#endif
+/*
+ * empty_bad_page is a bogus page that will be used when out of memory,
+ * so that a process isn't accidentally killed due to a page fault when
+ * it is running in kernel mode..
+ */
+.globl _empty_bad_page
+_empty_bad_page:
+
+#if MAX_MEGABYTES == 32
+.org 0xa000
+#else
+.org 0x6000
+#endif
+/*
+ * empty_bad_page_table is similar to the above, but is used when the
+ * system needs a bogus page-table
+ */
+.globl _empty_bad_page_table
+_empty_bad_page_table:
+
+#if MAX_MEGABYTES == 32
+.org 0xb000
+#else
+.org 0x7000
+#endif
+/*
+ * tmp_floppy_area is used by the floppy-driver when DMA cannot
+ * reach to a buffer-block. It needs to be aligned, so that it isn't
+ * on a 64kB border.
+ */
+_tmp_floppy_area:
+       .fill 1024,1,0
+/*
+ * floppy_track_buffer is used to buffer one track of floppy data: it
+ * has to be separate from the tmp_floppy area, as otherwise a single-
+ * sector read/write can mess it up. It can contain one full track of
+ * data (18*2*512 bytes).
+ */
+_floppy_track_buffer:
+       .fill 512*2*18,1,0
+
+after_page_tables:
+       call setup_paging
+       lgdt gdt_descr
+       lidt idt_descr
+       ljmp $0x08,$1f
+1:     movl $0x10,%eax         # reload all the segment registers
+       mov %ax,%ds             # after changing gdt.
+       mov %ax,%es
+       mov %ax,%fs
+       mov %ax,%gs
+       lss _stack_start,%esp
+       pushl $0                # These are the parameters to main :-)
+       pushl $0
+       pushl $0
+       cld                     # gcc2 wants the direction flag cleared at all times
+       call _start_kernel
+L6:
+       jmp L6                  # main should never return here, but
+                               # just in case, we know what happens.
+
+/* This is the default interrupt "handler" :-) */
+int_msg:
+       .asciz "Unknown interrupt\n\r"
+.align 2
+ignore_int:
+       cld
+       pushl %eax
+       pushl %ecx
+       pushl %edx
+       push %ds
+       push %es
+       push %fs
+       movl $0x10,%eax
+       mov %ax,%ds
+       mov %ax,%es
+       mov %ax,%fs
+       pushl $int_msg
+       call _printk
+       popl %eax
+       pop %fs
+       pop %es
+       pop %ds
+       popl %edx
+       popl %ecx
+       popl %eax
+       iret
+
+
+/*
+ * Setup_paging
+ *
+ * This routine sets up paging by setting the page bit
+ * in cr0. The page tables are set up, identity-mapping
+ * the first 16MB. The pager assumes that no illegal
+ * addresses are produced (ie >4Mb on a 4Mb machine).
+ *
+ * NOTE! Although all physical memory should be identity
+ * mapped by this routine, only the kernel page functions
+ * use the >1Mb addresses directly. All "normal" functions
+ * use just the lower 1Mb, or the local data space, which
+ * will be mapped to some other place - mm keeps track of
+ * that.
+ *
+ * For those with more memory than 16 Mb - tough luck. I've
+ * not got it, why should you :-) The source is here. Change
+ * it. (Seriously - it shouldn't be too difficult. Mostly
+ * change some constants etc. I left it at 16Mb, as my machine
+ * even cannot be extended past that (ok, but it was cheap :-)
+ * I've tried to show which constants to change by having
+ * some kind of marker at them (search for "16Mb"), but I
+ * won't guarantee that's all :-( )
+ *
+ * (ref: added support for up to 32mb, 17Apr92)  -- Rik Faith
+ * (ref: update, 25Sept92)  -- croutons@crunchy.uucp 
+ */
+.align 2
+setup_paging:
+#if MAXMEGABYTES == 32
+       movl $1024*9,%ecx               /* 9 pages - swapper_pg_dir+8 page tables */
+#else 
+       movl $1024*5,%ecx               /* 5 pages - swapper_pg_dir+4 page tables */
+#endif
+       xorl %eax,%eax
+       xorl %edi,%edi                  /* swapper_pg_dir is at 0x000 */
+       cld;rep;stosl
+/* Identity-map the kernel in low 4MB memory for ease of transition */
+       movl $pg0+7,_swapper_pg_dir             /* set present bit/user r/w */
+/* But the real place is at 0xC0000000 */
+       movl $pg0+7,_swapper_pg_dir+3072        /* set present bit/user r/w */
+       movl $pg1+7,_swapper_pg_dir+3076        /*  --------- " " --------- */
+       movl $pg2+7,_swapper_pg_dir+3080        /*  --------- " " --------- */
+       movl $pg3+7,_swapper_pg_dir+3084        /*  --------- " " --------- */
+#if MAX_MEGABYTES == 32
+       movl $pg4+7,_swapper_pg_dir+3088        /*  --------- " " --------- */
+       movl $pg5+7,_swapper_pg_dir+3092        /*  --------- " " --------- */
+       movl $pg6+7,_swapper_pg_dir+3096        /*  --------- " " --------- */
+       movl $pg7+7,_swapper_pg_dir+3100        /*  --------- " " --------- */
+
+       movl $pg7+4092,%edi
+       movl $0x1fff007,%eax            /*  32Mb - 4096 + 7 (r/w user,p) */
+#else 
+       movl $pg3+4092,%edi
+       movl $0x0fff007,%eax            /*  16Mb - 4096 + 7 (r/w user,p) */
+#endif
+       std
+1:     stosl                   /* fill pages backwards - more efficient :-) */
+       subl $0x1000,%eax
+       jge 1b
+       cld
+       xorl %eax,%eax          /* swapper_pg_dir is at 0x0000 */
+       movl %eax,%cr3          /* cr3 - page directory start */
+       movl %cr0,%eax
+       orl $0x80000000,%eax
+       movl %eax,%cr0          /* set paging (PG) bit */
+       ret                     /* this also flushes prefetch-queue */
+
+/*
+ * The interrupt descriptor table has room for 256 idt's
+ */
+.align 4
+.word 0
+idt_descr:
+       .word 256*8-1           # idt contains 256 entries
+       .long 0xc0000000+_idt
+
+.align 4
+_idt:
+       .fill 256,8,0           # idt is uninitialized
+
+/*
+ * The real GDT is also 256 entries long - no real reason
+ */
+.align 4
+.word 0
+gdt_descr:
+       .word 256*8-1
+       .long 0xc0000000+_gdt
+
+/*
+ * This gdt setup gives the kernel a 1GB address space at virtual
+ * address 0xC0000000 - space enough for expansion, I hope.
+ */
+.align 4
+_gdt:
+       .quad 0x0000000000000000        /* NULL descriptor */
+       .quad 0xc0c39a000000ffff        /* 1GB at 0xC0000000 */
+       .quad 0xc0c392000000ffff        /* 1GB */
+       .quad 0x0000000000000000        /* TEMPORARY - don't use */
+       .fill 252,8,0                   /* space for LDT's and TSS's etc */
diff --git a/boot/head.s b/boot/head.s
deleted file mode 100644 (file)
index 2444502..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- *  linux/boot/head.s
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- *  head.s contains the 32-bit startup code.
- *
- * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
- * the page directory will exist. The startup code will be overwritten by
- * the page directory.
- */
-.text
-.globl _idt,_gdt,_swapper_pg_dir,_tmp_floppy_area,_floppy_track_buffer
-/*
- * swapper_pg_dir is the main page directory, address 0x00000000
- */
-_swapper_pg_dir:
-startup_32:
-       cld
-       movl $0x10,%eax
-       mov %ax,%ds
-       mov %ax,%es
-       mov %ax,%fs
-       mov %ax,%gs
-       lss _stack_start,%esp
-       call setup_idt
-       xorl %eax,%eax
-1:     incl %eax               # check that A20 really IS enabled
-       movl %eax,0x000000      # loop forever if it isn't
-       cmpl %eax,0x100000
-       je 1b
-/* check if it is 486 or 386. */
-       movl %esp,%edi          # save stack pointer
-       andl $0xfffffffc,%esp   # align stack to avoid AC fault
-       pushfl                  # push EFLAGS
-       popl %eax               # get EFLAGS
-       movl %eax,%ecx          # save original EFLAGS
-       xorl $0x40000,%eax      # flip AC bit in EFLAGS
-       pushl %eax              # copy to EFLAGS
-       popfl                   # set EFLAGS
-       pushfl                  # get new EFLAGS
-       popl %eax               # put it in eax
-       xorl %ecx,%eax          # check if AC bit is changed. zero is 486.
-       jz 1f                   # 486
-       pushl %ecx              # restore original EFLAGS
-       popfl
-       movl %edi,%esp          # restore esp
-       movl %cr0,%eax          # 386
-       andl $0x80000011,%eax   # Save PG,PE,ET
-       orl $2,%eax             # set MP
-       jmp 2f  
-/*
- * NOTE! 486 should set bit 16, to check for write-protect in supervisor
- * mode. Then it would be unnecessary with the "verify_area()"-calls.
- * 486 users probably want to set the NE (#5) bit also, so as to use
- * int 16 for math errors.
- */
-1:     pushl %ecx              # restore original EFLAGS
-       popfl
-       movl %edi,%esp          # restore esp
-       movl %cr0,%eax          # 486
-       andl $0x80000011,%eax   # Save PG,PE,ET
-       orl $0x10022,%eax       # set NE and MP
-2:     movl %eax,%cr0
-       call check_x87
-       jmp after_page_tables
-
-/*
- * We depend on ET to be correct. This checks for 287/387.
- */
-check_x87:
-       fninit
-       fstsw %ax
-       cmpb $0,%al
-       je 1f
-       movl %cr0,%eax          /* no coprocessor: have to set bits */
-       xorl $6,%eax            /* reset MP, set EM */
-       movl %eax,%cr0
-       ret
-.align 2
-1:     .byte 0xDB,0xE4         /* fsetpm for 287, ignored by 387 */
-       ret
-
-/*
- *  setup_idt
- *
- *  sets up a idt with 256 entries pointing to
- *  ignore_int, interrupt gates. It doesn't actually load
- *  idt - that can be done only after paging has been enabled
- *  and the kernel moved to 0xC0000000. Interrupts
- *  are enabled elsewhere, when we can be relatively
- *  sure everything is ok. This routine will be over-
- *  written by the page tables.
- */
-setup_idt:
-       lea ignore_int,%edx
-       movl $0x00080000,%eax
-       movw %dx,%ax            /* selector = 0x0008 = cs */
-       movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */
-
-       lea _idt,%edi
-       mov $256,%ecx
-rp_sidt:
-       movl %eax,(%edi)
-       movl %edx,4(%edi)
-       addl $8,%edi
-       dec %ecx
-       jne rp_sidt
-       ret
-
-/*
- * I put the kernel page tables right after the page directory,
- * using 4 of them to span 16 Mb of physical memory. People with
- * more than 16MB will have to expand this.
- */
-.org 0x1000
-pg0:
-
-.org 0x2000
-pg1:
-
-.org 0x3000
-pg2:
-
-.org 0x4000
-pg3:
-
-.org 0x5000
-/*
- * empty_bad_page is a bogus page that will be used when out of memory,
- * so that a process isn't accidentally killed due to a page fault when
- * it is running in kernel mode..
- */
-.globl _empty_bad_page
-_empty_bad_page:
-
-.org 0x6000
-/*
- * empty_bad_page_table is similar to the above, but is used when the
- * system needs a bogus page-table
- */
-.globl _empty_bad_page_table
-_empty_bad_page_table:
-
-.org 0x7000
-/*
- * tmp_floppy_area is used by the floppy-driver when DMA cannot
- * reach to a buffer-block. It needs to be aligned, so that it isn't
- * on a 64kB border.
- */
-_tmp_floppy_area:
-       .fill 1024,1,0
-/*
- * floppy_track_buffer is used to buffer one track of floppy data: it
- * has to be separate from the tmp_floppy area, as otherwise a single-
- * sector read/write can mess it up. It can contain one full track of
- * data (18*2*512 bytes).
- */
-_floppy_track_buffer:
-       .fill 512*2*18,1,0
-
-after_page_tables:
-       call setup_paging
-       lgdt gdt_descr
-       lidt idt_descr
-       ljmp $0x08,$1f
-1:     movl $0x10,%eax         # reload all the segment registers
-       mov %ax,%ds             # after changing gdt.
-       mov %ax,%es
-       mov %ax,%fs
-       mov %ax,%gs
-       lss _stack_start,%esp
-       pushl $0                # These are the parameters to main :-)
-       pushl $0
-       pushl $0
-       cld                     # gcc2 wants the direction flag cleared at all times
-       call _start_kernel
-L6:
-       jmp L6                  # main should never return here, but
-                               # just in case, we know what happens.
-
-/* This is the default interrupt "handler" :-) */
-int_msg:
-       .asciz "Unknown interrupt\n\r"
-.align 2
-ignore_int:
-       cld
-       pushl %eax
-       pushl %ecx
-       pushl %edx
-       push %ds
-       push %es
-       push %fs
-       movl $0x10,%eax
-       mov %ax,%ds
-       mov %ax,%es
-       mov %ax,%fs
-       pushl $int_msg
-       call _printk
-       popl %eax
-       pop %fs
-       pop %es
-       pop %ds
-       popl %edx
-       popl %ecx
-       popl %eax
-       iret
-
-
-/*
- * Setup_paging
- *
- * This routine sets up paging by setting the page bit
- * in cr0. The page tables are set up, identity-mapping
- * the first 16MB. The pager assumes that no illegal
- * addresses are produced (ie >4Mb on a 4Mb machine).
- *
- * NOTE! Although all physical memory should be identity
- * mapped by this routine, only the kernel page functions
- * use the >1Mb addresses directly. All "normal" functions
- * use just the lower 1Mb, or the local data space, which
- * will be mapped to some other place - mm keeps track of
- * that.
- *
- * For those with more memory than 16 Mb - tough luck. I've
- * not got it, why should you :-) The source is here. Change
- * it. (Seriously - it shouldn't be too difficult. Mostly
- * change some constants etc. I left it at 16Mb, as my machine
- * even cannot be extended past that (ok, but it was cheap :-)
- * I've tried to show which constants to change by having
- * some kind of marker at them (search for "16Mb"), but I
- * won't guarantee that's all :-( )
- */
-.align 2
-setup_paging:
-       movl $1024*5,%ecx               /* 5 pages - swapper_pg_dir+4 page tables */
-       xorl %eax,%eax
-       xorl %edi,%edi                  /* swapper_pg_dir is at 0x000 */
-       cld;rep;stosl
-/* Identity-map the kernel in low 4MB memory for ease of transition */
-       movl $pg0+7,_swapper_pg_dir             /* set present bit/user r/w */
-/* But the real place is at 0xC0000000 */
-       movl $pg0+7,_swapper_pg_dir+3072        /* set present bit/user r/w */
-       movl $pg1+7,_swapper_pg_dir+3076        /*  --------- " " --------- */
-       movl $pg2+7,_swapper_pg_dir+3080        /*  --------- " " --------- */
-       movl $pg3+7,_swapper_pg_dir+3084        /*  --------- " " --------- */
-       movl $pg3+4092,%edi
-       movl $0xfff007,%eax             /*  16Mb - 4096 + 7 (r/w user,p) */
-       std
-1:     stosl                   /* fill pages backwards - more efficient :-) */
-       subl $0x1000,%eax
-       jge 1b
-       cld
-       xorl %eax,%eax          /* swapper_pg_dir is at 0x0000 */
-       movl %eax,%cr3          /* cr3 - page directory start */
-       movl %cr0,%eax
-       orl $0x80000000,%eax
-       movl %eax,%cr0          /* set paging (PG) bit */
-       ret                     /* this also flushes prefetch-queue */
-
-/*
- * The interrupt descriptor table has room for 256 idt's
- */
-.align 4
-.word 0
-idt_descr:
-       .word 256*8-1           # idt contains 256 entries
-       .long 0xc0000000+_idt
-
-.align 4
-_idt:
-       .fill 256,8,0           # idt is uninitialized
-
-/*
- * The real GDT is also 256 entries long - no real reason
- */
-.align 4
-.word 0
-gdt_descr:
-       .word 256*8-1
-       .long 0xc0000000+_gdt
-
-.align 4
-_gdt:
-       .quad 0x0000000000000000        /* NULL descriptor */
-       .quad 0xc0c09a0000000fff        /* 16Mb at 0xC0000000 */
-       .quad 0xc0c0920000000fff        /* 16Mb */
-       .quad 0x0000000000000000        /* TEMPORARY - don't use */
-       .fill 252,8,0                   /* space for LDT's and TSS's etc */
index 62517dab0413fa8c9a17be19ada6f4da9250355b..06ac604d500df80087b888222811b2356a1070c3 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 
-#ifdef CONFIG_BLK_DEV_SR
+#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
 extern int check_cdrom_media_change(int, int);
 #endif
 
@@ -133,7 +133,7 @@ void check_disk_change(int dev)
                brelse(bh);
                break;
 
-#ifdef CONFIG_BLK_DEV_SR
+#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
          case 11: /* CDROM */
                i = check_cdrom_media_change(dev, 0);
                if (i) printk("Flushing buffers and inodes for CDROM\n");
index 8ce737f26f32516e44f6586440bc50578f9c30a0..452981b6cd4445b2026cc2166d59b053ae80c2a2 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -101,13 +101,16 @@ int core_dump(long signr, struct pt_regs * regs)
        if (!file.f_op->write)
                goto close_coredump;
        has_dumped = 1;
-/* write and seek example: from kernel space */
-       __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+/* changed the size calculations - should hopefully work better. lbt */
        dump.magic = CMAGIC;
-       dump.u_tsize = current->end_code / PAGE_SIZE;
-       dump.u_dsize = (current->brk - current->end_code) / PAGE_SIZE;
-       dump.u_ssize =((current->start_stack +(PAGE_SIZE-1)) / PAGE_SIZE) -
-         (regs->esp/ PAGE_SIZE);
+       dump.start_code = 0;
+       dump.start_stack = regs->esp & ~(PAGE_SIZE - 1);
+       dump.u_tsize = ((unsigned long) current->end_code) >> 12;
+       dump.u_dsize = ((unsigned long) (current->brk + (PAGE_SIZE-1))) >> 12;
+       dump.u_dsize -= dump.u_tsize;
+       dump.u_ssize = 0;
+       if (dump.start_stack < TASK_SIZE)
+               dump.u_ssize = ((unsigned long) (TASK_SIZE - dump.start_stack)) >> 12;
 /* If the size of the dump file exceeds the rlimit, then see what would happen
    if we wrote the stack, but not the data area.  */
        if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE/1024 >
@@ -121,8 +124,6 @@ int core_dump(long signr, struct pt_regs * regs)
        dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump)));
        dump.signal = signr;
        dump.regs = *regs;
-       dump.start_code = 0;
-       dump.start_stack = regs->esp & ~(PAGE_SIZE - 1);
 /* Flag indicating the math stuff is valid. */
        if (dump.u_fpvalid = current->used_math) {
                if (last_task_used_math == current)
@@ -130,6 +131,7 @@ int core_dump(long signr, struct pt_regs * regs)
                else
                        memcpy(&dump.i387,&current->tss.i387,sizeof(dump.i387));
        };
+       __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
        DUMP_WRITE(&dump,sizeof(dump));
        DUMP_SEEK(sizeof(dump));
  /* Dump the task struct.  Not be used by gdb, but could be useful */
@@ -140,14 +142,14 @@ int core_dump(long signr, struct pt_regs * regs)
        __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17));
 /* Dump the data area */
        if (dump.u_dsize != 0) {
-               dump_start = current->end_code;
-               dump_size = current->brk - current->end_code;
+               dump_start = dump.u_tsize << 12;
+               dump_size = dump.u_dsize << 12;
                DUMP_WRITE(dump_start,dump_size);
        };
 /* Now prepare to dump the stack area */
        if (dump.u_ssize != 0) {
-               dump_start = regs->esp & ~(PAGE_SIZE - 1);
-               dump_size = dump.u_ssize * PAGE_SIZE;
+               dump_start = dump.start_stack;
+               dump_size = dump.u_ssize << 12;
                DUMP_WRITE(dump_start,dump_size);
        };
 close_coredump:
index 1fdcc6cf5503bbdb02278ebb7014b718ac1fa174..04dc41c4ca6e811950a583d4cc2c8c5d6d71d6a0 100644 (file)
@@ -50,12 +50,12 @@ chrdev.o : chrdev.c /usr/include/linux/sched.h /usr/include/linux/head.h /usr/in
   /usr/include/linux/vm86.h /usr/include/linux/ext_fs.h /usr/include/linux/tty.h \
   /usr/include/linux/termios.h /usr/include/asm/system.h /usr/include/linux/stat.h \
   /usr/include/linux/fcntl.h /usr/include/linux/errno.h 
-dir.o : dir.c /usr/include/asm/segment.h /usr/include/linux/errno.h /usr/include/linux/fs.h \
-  /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
-  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
-  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
-  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
-  /usr/include/linux/ext_fs.h /usr/include/linux/stat.h 
+dir.o : dir.c /usr/include/asm/segment.h /usr/include/linux/errno.h /usr/include/linux/kernel.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+  /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+  /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+  /usr/include/linux/msdos_fs_sb.h /usr/include/linux/ext_fs.h /usr/include/linux/stat.h 
 fifo.o : fifo.c /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
   /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
   /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
index 9de7af7ea67c91f0029f684dcf288a9a78e12b6c..1412e291d26df3bd1c8a0882af2cf05e477373cd 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/segment.h>
 
 #include <linux/errno.h>
+#include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/ext_fs.h>
 #include <linux/stat.h>
@@ -73,6 +74,13 @@ static int ext_readdir(struct inode * inode, struct file * filp,
                while (offset < 1024 && filp->f_pos < inode->i_size) {
                        offset += de->rec_len;
                        filp->f_pos += de->rec_len;
+                       if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
+                           de->rec_len < de->name_len + 8) {
+                               printk ("ext_readdir: bad directory entry\n");
+                               printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n",
+                                       inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+                               return 0;
+                       }
                        if (de->inode) {
                                for (i = 0; i < de->name_len; i++)
                                        if (c = de->name[i])
index c5a1d61db018d546d02ed65fced16ef63e3f92e9..8354e4cc255a556104a681ede7c93655d6e7a6db 100644 (file)
@@ -122,6 +122,14 @@ static struct buffer_head * ext_find_entry(struct inode * dir,
                        if (prev_dir)
                                *prev_dir = NULL;
                }
+               if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
+                   de->rec_len < de->name_len + 8) {
+                       printk ("ext_find_entry: bad dir entry\n");
+                       printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n",
+                               dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+                       brelse (bh);
+                       return NULL;
+               }
                if (ext_match(namelen,name,de)) {
                        *res_dir = de;
                        if (next_dir)
@@ -253,6 +261,14 @@ printk ("ext_add_entry : creating next block\n");
                        dir->i_dirt = 1;
                        dir->i_ctime = CURRENT_TIME;
                }
+               if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
+                   de->rec_len < de->name_len + 8) {
+                       printk ("ext_addr_entry: bad dir entry\n");
+                       printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n",
+                               dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+                       brelse (bh);
+                       return NULL;
+               }
                if (!de->inode && de->rec_len >= rec_len) {
                        if (de->rec_len > rec_len
                            && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) {
@@ -471,6 +487,14 @@ static int empty_dir(struct inode * inode)
                        }
                        de = (struct ext_dir_entry *) bh->b_data;
                }
+               if (de->rec_len < 8 || de->rec_len %4 != 0 ||
+                   de->rec_len < de->name_len + 8) {
+                       printk ("empty_dir: bad dir entry\n");
+                       printk ("dev=%d, dir=%d, offset=%d, rec_len=%d, name_len=%d\n",
+                               inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+                       brelse (bh);
+                       return 1;
+               }
                if (de->inode) {
                        brelse(bh);
                        return 0;
index d83768eb3b4da89b295473050a10f13fc9acaf19..fb555c65f94fbe13ce42110ec2b687b4d9893349 100644 (file)
@@ -60,7 +60,7 @@
 /*--------- MICROSOFT BUSMOUSE ITEMS -------------*/
 
 #define        MS_MSE_DATA_PORT                0x23d
-#define        MS_MSE_SIGNATURE_PORT           0x23d
+#define        MS_MSE_SIGNATURE_PORT           0x23e
 #define        MS_MSE_CONTROL_PORT             0x23c
 #define        MS_MSE_CONFIG_PORT              0x23f
 
index 6f79980a8afadd228a7fb4751d003f5b193f7c5d..abfbf7e8280b364f8a9567d446ed014875917e7c 100644 (file)
@@ -22,7 +22,7 @@
 #define DEF_INITSEG    0x9000
 #define DEF_SYSSEG     0x1000
 #define DEF_SETUPSEG   0x9020
-#define DEF_SYSSIZE    0x5000
+#define DEF_SYSSIZE    0x7000
 
 /*
  * The root-device is no longer hard-coded. You can change the default
index 3c1da05282b9b816528146a0a23f971e739e6726..f11e8d064e8cae2d0dff5538ab45f7e3b377a1ad 100644 (file)
@@ -48,6 +48,13 @@ struct sockaddr {
 #define SO_KEEPALIVE   9
 #define SO_OOBINLINE   10
 #define SO_NO_CHECK    11
+#define SO_PRIORITY    12
+#define SO_LINGER      13
+
+/* the different priorities */
+#define SOPRI_INTERACTIVE      0
+#define SOPRI_NORMAL           1
+#define SOPRI_BACKGROUND       2
 
 /* setsockoptions level */
 #define SOL_SOCKET     1
diff --git a/include/linux/vmm.h b/include/linux/vmm.h
new file mode 100644 (file)
index 0000000..ad31dda
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _LINUX_VMM_H
+#define _LINUX_VMM_H
+
+/*
+ * Linux kernel virtual memory manager primitives.
+ * The idea being to have a "virtual" mm in the same way
+ * we have a virtual fs - giving a cleaner interface to the
+ * mm details, and allowing different kinds of memory mappings
+ * (from shared memory to executable loading to arbitrary
+ * mmap() functions).
+ */
+
+/*
+ * This struct defines a memory VMM memory area. There is one of these
+ * per VM-area/task.  A VM area is any part of the process virtual memory
+ * space that has a special rule for the page-fault handlers (ie a shared
+ * library, the executable area etc).
+ */
+struct vm_area_struct {
+       unsigned long vm_start;                 /* VM area parameters */
+       unsigned long vm_end;
+       struct vm_area_struct * vm_next;        /* ordered linked list */
+       struct vm_area_struct * vm_share;       /* circular linked list */
+       struct inode * vm_inode;
+       unsigned long vm_offset;
+       struct vm_operations_struct * vm_ops;
+       unsigned long vm_flags;
+};
+
+struct vm_operations_struct {
+       void (*open)(struct task_struct * tsk, struct vm_area_struct * area);
+       void (*close)(struct task_struct * tsk, struct vm_area_struct * area);
+       void (*nopage)(struct task_struct * tsk, struct vm_area_struct * area, unsigned long address);
+       void (*wppage)(struct task_struct * tsk, struct vm_area_struct * area, unsigned long address);
+};
+
+#endif
index 110a569f060570f6837d5b35fd667f1edd00a75c..10121140f40dd484b553a6a2f0f106f82faa461f 100644 (file)
@@ -162,8 +162,8 @@ void start_kernel(void)
        envp_init[1] = term;
        memory_end = (1<<20) + (EXT_MEM_K<<10);
        memory_end &= 0xfffff000;
-       if (memory_end > 16*1024*1024)
-               memory_end = 16*1024*1024;
+       if (memory_end > MAX_MEGABYTES*1024*1024)
+               memory_end = MAX_MEGABYTES*1024*1024;
        memory_start = 1024*1024;
        low_memory_start = (unsigned long) &end;
        low_memory_start += 0xfff;
index 6618ce17ab04b37cd812187f8f73fd554a12fe48..6addf899fa3d0827f0c804efaa09449b86151823 100644 (file)
@@ -63,7 +63,7 @@ static void extended_partition(struct gendisk *hd, int dev)
                                goto done;  /* shouldn't happen */
                        hd->part[current_minor].start_sect = this_sector + p->start_sect;
                        printk("  Logical part %d start %d size %d end %d\n\r", 
-                              current_minor, hd->part[current_minor].start_sect, 
+                              mask & current_minor, hd->part[current_minor].start_sect, 
                               hd->part[current_minor].nr_sects,
                               hd->part[current_minor].start_sect + 
                               hd->part[current_minor].nr_sects - 1);
@@ -96,6 +96,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
        struct buffer_head *bh;
        struct partition *p;
        unsigned long first_sector;
+       int mask = (1 << hd->minor_shift) - 1;
 
        first_sector = hd->part[MINOR(dev)].start_sect;
 
@@ -103,7 +104,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
                printk("Unable to read partition table of device %04x\n",dev);
                return;
        }
-       printk("%s%d :\n\r", hd->major_name, minor >> hd->minor_shift);
+       printk("%s%c :\n\r", hd->major_name, 'a'+(minor >> hd->minor_shift));
        current_minor += 4;  /* first "extra" minor */
        if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
                p = 0x1BE + (void *)bh->b_data;
@@ -127,14 +128,14 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
                        p = 0x1BE + (void *)bh->b_data;
                        for (i = 4 ; i < 16 ; i++, current_minor++) {
                                p--;
-                               if ((current_minor & 0x3f) >= 60)
+                               if ((current_minor & mask) >= mask-2)
                                        break;
                                if (!(p->start_sect && p->nr_sects))
                                        continue;
                                hd->part[current_minor].start_sect = p->start_sect;
                                hd->part[current_minor].nr_sects = p->nr_sects;
                                printk(" DM part %d start %d size %d end %d\n\r",
-                                      current_minor,
+                                      current_minor & mask,
                                       hd->part[current_minor].start_sect, 
                                       hd->part[current_minor].nr_sects,
                                       hd->part[current_minor].start_sect + 
index 3652c965a287382398109ce9a7257a380971d8f7..39a4fc1923f4ba703202b2d617cead17d4dff235 100644 (file)
@@ -251,23 +251,27 @@ static inline int wait_DRQ(void)
 static void read_intr(void)
 {
        int i;
+       int retries = 100000;
 
-       i = (unsigned) inb_p(HD_STATUS);
-       if ((i & STAT_MASK) != STAT_OK) {
-               printk("HD: read_intr: status = 0x%02x\n",i);
-               goto bad_read;
-       }
-       if (wait_DRQ()) {
-               printk("HD: read_intr: no DRQ\n");
-               goto bad_read;
+       do {
+               i = (unsigned) inb_p(HD_STATUS);
+               if ((i & STAT_MASK) != STAT_OK)
+                       break;
+               if (i & DRQ_STAT)
+                       goto ok_to_read;
+       } while (--retries > 0);
+       sti();
+       printk("HD: read_intr: status = 0x%02x\n",i);
+       if (i & ERR_STAT) {
+               i = (unsigned) inb(HD_ERROR);
+               printk("HD: read_intr: error = 0x%02x\n",i);
        }
+       bad_rw_intr();
+       cli();
+       do_hd_request();
+       return;
+ok_to_read:
        port_read(HD_DATA,CURRENT->buffer,256);
-       i = (unsigned) inb_p(HD_STATUS);
-       if (!(i & BUSY_STAT))
-               if ((i & STAT_MASK) != STAT_OK) {
-                       printk("HD: read_intr: second status = 0x%02x\n",i);
-                       goto bad_read;
-               }
        CURRENT->errors = 0;
        CURRENT->buffer += 512;
        CURRENT->sector++;
@@ -290,29 +294,31 @@ static void read_intr(void)
 #endif
        do_hd_request();
        return;
-bad_read:
-       if (i & ERR_STAT) {
-               i = (unsigned) inb(HD_ERROR);
-               printk("HD: read_intr: error = 0x%02x\n",i);
-       }
-       bad_rw_intr();
-       do_hd_request();
-       return;
 }
 
 static void write_intr(void)
 {
        int i;
+       int retries = 100000;
 
-       i = (unsigned) inb_p(HD_STATUS);
-       if ((i & STAT_MASK) != STAT_OK) {
-               printk("HD: write_intr: status = 0x%02x\n",i);
-               goto bad_write;
-       }
-       if (CURRENT->nr_sectors > 1 && wait_DRQ()) {
-               printk("HD: write_intr: no DRQ\n");
-               goto bad_write;
+       do {
+               i = (unsigned) inb_p(HD_STATUS);
+               if ((i & STAT_MASK) != STAT_OK)
+                       break;
+               if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
+                       goto ok_to_write;
+       } while (--retries > 0);
+       sti();
+       printk("HD: write_intr: status = 0x%02x\n",i);
+       if (i & ERR_STAT) {
+               i = (unsigned) inb(HD_ERROR);
+               printk("HD: write_intr: error = 0x%02x\n",i);
        }
+       bad_rw_intr();
+       cli();
+       do_hd_request();
+       return;
+ok_to_write:
        CURRENT->sector++;
        i = --CURRENT->nr_sectors;
        --CURRENT->current_nr_sectors;
@@ -330,16 +336,6 @@ static void write_intr(void)
                do_hd_request();
        }
        return;
-bad_write:
-       sti();
-       if (i & ERR_STAT) {
-               i = (unsigned) inb(HD_ERROR);
-               printk("HD: write_intr: error = 0x%02x\n",i);
-       }
-       bad_rw_intr();
-       cli();
-       do_hd_request();
-       return;
 }
 
 static void recal_intr(void)
@@ -587,7 +583,7 @@ static void hd_interrupt(int unused)
 }
 
 /*
- * This is the harddisk IRQ descruption. The SA_INTERRUPT in sa_flags
+ * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
  * means we run the IRQ-handler with interrupts disabled: this is bad for
  * interrupt latency, but anything else has led to problems on some
  * machines...
index 279448f054c0f3179ac70e5f10c95af3226e7e19..6b6b1723cc97fffe0f855e744c544ae607537f0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *     ultrastor.c     Copyright (C) 1991, 1992 David B. Gentzel
+ *     ultrastor.c     Copyright (C) 1992 David B. Gentzel
  *     Low-level SCSI driver for UltraStor 14F
  *     by David B. Gentzel, Whitfield Software Services, Carnegie, PA
  *         (gentzel@nova.enet.dec.com)
@@ -24,9 +24,8 @@
 /*
  * CAVEATS: ???
  *    This driver is VERY stupid.  It takes no advantage of much of the power
- *    of the UltraStor controller.  We just sit-and-spin while waiting for
- *    commands to complete.  I hope to go back and beat it into shape, but
- *    PLEASE, anyone else who would like to, please make improvements!
+ *    of the UltraStor controller.  I hope to go back and beat it into shape,
+ *    but PLEASE, anyone else who would like to, please make improvements!
  *
  *    By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature
  *    of the mid-level SCSI driver.  Once I'm satisfied that the queueing
@@ -155,7 +154,7 @@ static const unsigned short ultrastor_ports[] = {
 };
 #endif
 
-void ultrastor_interrupt(void);
+static void ultrastor_interrupt(int cpl);
 
 static void (*ultrastor_done)(int, int) = 0;
 
@@ -292,11 +291,19 @@ int ultrastor_14f_detect(int hostnum)
     host_number = hostnum;
     scsi_hosts[hostnum].this_id = config.ha_scsi_id;
 #ifndef NO_QUEUEING
-    set_intr_gate(0x20 + config.interrupt, ultrastor_interrupt);
-    /* gate to PIC 2 */
-    outb_p(inb_p(0x21) & ~BIT(2), 0x21);
-    /* enable the interrupt */
-    outb(inb_p(0xA1) & ~BIT(config.interrupt - 8), 0xA1);
+    {
+       struct sigaction sa;
+
+       sa.sa_handler = ultrastor_interrupt;
+       sa.sa_mask = 0;
+       sa.sa_flags = SA_INTERRUPT;     /* ??? Do we really need this? */
+       sa.sa_restorer = 0;
+       if (irqaction(config.interrupt, &sa)) {
+           printk("Unable to get IRQ%u for UltraStor controller\n",
+                  config.interrupt);
+           return FALSE;
+       }
+    }
 #endif
     return TRUE;
 }
@@ -309,7 +316,7 @@ const char *ultrastor_14f_info(void)
 }
 
 static struct mscp mscp = {
-    OP_SCSI, DTD_SCSI, FALSE, TRUE, FALSE      /* This stuff doesn't change */
+    OP_SCSI, DTD_SCSI, 0, 1, 0         /* This stuff doesn't change */
 };
 
 int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd,
@@ -437,15 +444,15 @@ int ultrastor_14f_reset(void)
 }
 
 #ifndef NO_QUEUEING
-void ultrastor_interrupt_service(void)
+static void ultrastor_interrupt(int cpl)
 {
 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
-    printk("US14F: interrupt_service: called: status = %08X\n",
+    printk("US14F: interrupt: called: status = %08X\n",
           (mscp.adapter_status << 16) | mscp.target_status);
 #endif
 
     if (ultrastor_done == 0)
-       panic("US14F: interrupt_service: unexpected interrupt!\n");
+       panic("US14F: interrupt: unexpected interrupt!\n");
     else {
        void (*done)(int, int);
 
@@ -464,40 +471,9 @@ void ultrastor_interrupt_service(void)
     }
 
 #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
-    printk("US14F: interrupt_service: returning\n");
+    printk("US14F: interrupt: returning\n");
 #endif
 }
-
-__asm__("
-_ultrastor_interrupt:
-       cld
-       pushl %eax
-       pushl %ecx
-       pushl %edx
-       push %ds
-       push %es
-       push %fs
-       movl $0x10,%eax
-       mov %ax,%ds
-       mov %ax,%es
-       movl $0x17,%eax
-       mov %ax,%fs
-       movb $0x20,%al
-       outb %al,$0xA0          # EOI to interrupt controller #1
-       outb %al,$0x80          # give port chance to breathe
-       outb %al,$0x80
-       outb %al,$0x80
-       outb %al,$0x80
-       outb %al,$0x20
-       call _ultrastor_interrupt_service
-       pop %fs
-       pop %es
-       pop %ds
-       popl %edx
-       popl %ecx
-       popl %eax
-       iret
-");
 #endif
 
 #endif
index 1b7277a12aa70fd82f24e84d4f5cf22de34453f9..5b51d7107c214ffc343e1dceb86ca0f23c8bd8fd 100644 (file)
  *           Added a couple of new functions to handle differences in using
  *             MS vs. Logitech (where the int variable wasn't appropriate).
  *
- * version 0.2
+ * Modified by Peter Cervasio (address above) (26SEP92)
+ * Changes:  Included code to (properly?) detect when a Microsoft mouse is
+ *           really attached to the machine.  Don't know what this does to
+ *           Logitech bus mice, but all it does is read ports.
+ *
+ * version 0.3
  */
 
 #include       <linux/kernel.h>
@@ -254,9 +259,40 @@ long bus_mouse_init(long kmem_start)
        return kmem_start;
 }
 
+#define MS_DELAY 100000
+
 long ms_bus_mouse_init(long kmem_start)
-{      
-       
+{
+       register int mse_byte;
+       int i, delay_val, msfound = 1;
+
+       if (inb(MS_MSE_SIGNATURE_PORT) == 0xde) {
+           for (delay_val=0; delay_val<MS_DELAY;) delay_val++;
+
+           mse_byte = inb(MS_MSE_SIGNATURE_PORT);
+           for (delay_val=0; delay_val<MS_DELAY; ) delay_val++;
+
+           for (i = 0; i < 4; i++) {
+               for (delay_val=0; delay_val<MS_DELAY;) delay_val++;
+               if (inb(MS_MSE_SIGNATURE_PORT) == 0xde) {
+
+                   for (delay_val=0; delay_val<MS_DELAY; ) delay_val++;
+                   if (inb(MS_MSE_SIGNATURE_PORT) == mse_byte)
+                       msfound = 0;
+                   else
+                       msfound = 1;
+               }
+               else
+                   msfound = 1;
+           }
+       }
+
+       if (msfound == 1) {
+           printk("No Microsoft bus mouse detected.\n");
+           mouse.present = 0;
+           return kmem_start;
+       }
+
        MS_MSE_INT_OFF();
        
        mouse.present = 1;
index e3f24a77b897613b1cd96b91f64207179e36a1e4..2a4d86e6ac6a4059ed9629e4117c9b69f50669d9 100644 (file)
@@ -160,7 +160,7 @@ static void puts_queue(char *cp)
        struct tty_struct *tty = TTY_TABLE(0);
        unsigned long new_head;
        char ch;
-       
+
        wake_up(&keypress_wait);
        if (!tty)
                return;
@@ -963,6 +963,95 @@ static unsigned char alt_map[] = {
          0,    0,    0,    0,    0,    0,    0,    0,
          0 };
 
+#elif defined KBD_SF
+
+static unsigned char key_map[] = {
+         0,   27,  '1',  '2',  '3',  '4',  '5',  '6',
+       '7',  '8',  '9',  '0', '\'',  '^',  127,    9,
+       'q',  'w',  'e',  'r',  't',  'z',  'u',  'i',
+       'o',  'p',    0,    0,   13,    0,  'a',  's',
+       'd',  'f',  'g',  'h',  'j',  'k',  'l',    0,
+         0,    0,   0,   '$',  'y',  'x',  'c',  'v',
+       'b',  'n',  'm',  ',',  '.',  '-',    0,  '*',
+         0,   32,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,  '-',    0,    0,    0,  '+',    0,
+         0,    0,    0,    0,    0,    0,  '<',    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
+static unsigned char shift_map[] = {
+         0,   27,  '+',  '"',  '*',    0,  '%',  '&',
+       '/',  '(',  ')',  '=',  '?',  '`',  127,    9,
+       'Q',  'W',  'E',  'R',  'T',  'Z',  'U',  'I',
+       'O',  'P',    0,  '!',   13,    0,  'A',  'S',
+       'D',  'F',  'G',  'H',  'J',  'K',  'L',    0,
+         0,    0,    0,    0,  'Y',  'X',  'C',  'V',
+       'B',  'N',  'M',  ';',  ':',  '_',    0,  '*',
+         0,   32,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,  '-',    0,    0,    0,  '+',    0,
+         0,    0,    0,    0,    0,    0,  '>',    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
+static unsigned char alt_map[] = {
+         0,    0,    0,  '@',  '#',    0,    0,    0,
+       '|',    0,    0,    0,  '\'', '~',    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,   '[',  ']',  13,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+        '{',   0,    0,   '}',   0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,  '\\',   0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
+
+#elif defined KBD_SF_LATIN1
+
+static unsigned char key_map[] = {
+         0,   27,  '1',  '2',  '3',  '4',  '5',  '6',
+       '7',  '8',  '9',  '0', '\'',  '^',  127,    9,
+       'q',  'w',  'e',  'r',  't',  'z',  'u',  'i',
+       'o',  'p',  232,  168,   13,    0,  'a',  's',
+       'd',  'f',  'g',  'h',  'j',  'k',  'l',  233,
+       224,  167,    0,  '$',  'y',  'x',  'c',  'v',
+       'b',  'n',  'm',  ',',  '.',  '-',    0,  '*',
+         0,   32,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,  '-',    0,    0,    0,  '+',    0,
+         0,    0,    0,    0,    0,    0,  '<',    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
+static unsigned char shift_map[] = {
+         0,   27,  '+',  '"',  '*',  231,  '%',  '&',
+       '/',  '(',  ')',  '=',  '?',  '`',  127,    9,
+       'Q',  'W',  'E',  'R',  'T',  'Z',  'U',  'I',
+       'O',  'P',  252,  '!',   13,    0,  'A',  'S',
+       'D',  'F',  'G',  'H',  'J',  'K',  'L',  246,
+       228,  176,    0,  163,  'Y',  'X',  'C',  'V',
+       'B',  'N',  'M',  ';',  ':',  '_',    0,  '*',
+         0,   32,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,  '-',    0,    0,    0,  '+',    0,
+         0,    0,    0,    0,    0,    0,  '>',    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
+static unsigned char alt_map[] = {
+         0,    0,    0,  '@',  '#',    0,    0,  172,
+       '|',   162,   0,    0,  180,  '~',    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,   '[',  ']',  13,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+        '{',   0,    0,   '}',   0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0,    0,    0,    0,    0,    0,  '\\',   0,
+         0,    0,    0,    0,    0,    0,    0,    0,
+         0 };
 #else
 #error "KBD-type not defined"
 #endif
index ca4a61576f1bfa7bc7019d8906acec44babe2aab..26eda93afcbbaa777ea3d157ebe71e6d7c21184c 100644 (file)
@@ -328,7 +328,7 @@ static void init(struct async_struct * info)
        outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
        status1 = inb_p(UART_MSR + port) & 0xF0;
        outb_p(scratch, UART_MCR + port);
-       outb_p(scratch, UART_MSR + port);
+       outb_p(scratch2, UART_MSR + port);
        if (status1 != 0x90) {
                info->type = PORT_UNKNOWN;
                return;
@@ -379,9 +379,7 @@ static void init(struct async_struct * info)
  */
 void rs_write(struct tty_struct * tty)
 {
-       int line = tty->line - 64;
-
-       do_rs_write(rs_table+line);
+       do_rs_write(rs_table+DEV_TO_SL(tty->line));
 }
 
 /*
@@ -571,6 +569,10 @@ static int set_serial_info(struct async_struct * info,
        irq = ISR->irq;
        if (irq == 2)
                irq = 9;
+       if (!new_irq)
+               new_irq = irq;
+       if (!new_port)
+               new_port = info->port;
        if (irq != new_irq) {
                /*
                 * We need to change the IRQ for this board.  OK, if
@@ -582,7 +584,7 @@ static int set_serial_info(struct async_struct * info,
                        sa.sa_flags = (SA_INTERRUPT);
                        sa.sa_mask = 0;
                        sa.sa_restorer = NULL;
-                       retval = irqaction(irq,&sa);
+                       retval = irqaction(new_irq,&sa);
                        if (retval)
                                return retval;
                }
@@ -608,6 +610,7 @@ static int set_serial_info(struct async_struct * info,
                if (ISR->next_ISR)
                        ISR->next_ISR->prev_ISR = ISR;
                IRQ_ISR[new_irq] = ISR;
+               ISR->irq = new_irq;
        }
        cli();
        if (new_port != info->port) {
index 0df6ff205cdc8135c8c91cebc1f9b338f8bb574e..90a558dcf301a8c01ff665ed9f84bd8a896fcfeb 100644 (file)
@@ -155,7 +155,7 @@ static void mark_screen_rdonly(struct task_struct * tsk)
                tmp = *(unsigned long *) tmp;
                if (tmp & PAGE_PRESENT) {
                        tmp &= 0xfffff000;
-                       pg_table = (0xA00000 >> PAGE_SHIFT) + (unsigned long *) tmp;
+                       pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp;
                        tmp = 32;
                        while (tmp--) {
                                if (PAGE_PRESENT & *pg_table)
index fc2ad1c868ad7065602e11c7d2e2e0ed81a71d5b..d556e4f28dd168878639cfeebe5fe4c6f949ff2d 100644 (file)
@@ -792,6 +792,10 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
 
        /* get the address */
        __asm__("movl %%cr2,%0":"=r" (address));
+       if (address >= TASK_SIZE) {
+               printk("Unable to handle kernel paging request at address %08x\n",address);
+               do_exit(SIGSEGV);
+       }
        if (esp[2] & VM_MASK) {
                unsigned int bit;
 
index aff3dad007c0d3512b051d99ea6737d75da9d538..f69daf052ea3c55d6afc8479712d7738ea9363fb 100644 (file)
@@ -32,6 +32,7 @@ subdirs: dummy
 clean:
        rm -f core *.o *.a tmp_make
        for i in *.c;do rm -f `basename $$i .c`.s;done
+       @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) clean) || exit; done
 
 dep:
        sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
diff --git a/net/tcp/Makefile b/net/tcp/Makefile
new file mode 100644 (file)
index 0000000..74c7d0f
--- /dev/null
@@ -0,0 +1,222 @@
+#
+# Makefile for the linux networking.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+.c.o:
+       $(CC) $(CFLAGS) \
+       -c -o $*.o $<
+.s.o:
+       $(AS) -o $*.o $<
+.c.s:
+       $(CC) $(CFLAGS) \
+       -S -o $*.s $<
+
+OBJS   = sock.o tcp.o ip.o timer.o we.o arp.o udp.o eth.o Space.o loopback.o \
+         icmp.o protocols.o raw.o pack_type.o dev.o packet.o
+
+tcpip.o: $(OBJS)
+       $(LD) -r -o tcpip.o $(OBJS)
+
+subdirs: dummy
+       for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
+
+
+clean:
+       rm -f core *.o *.a tmp_make
+       for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+       sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+       $(CPP) -M *.c >> tmp_make
+       cp tmp_make Makefile
+
+tar:
+       tar -cvf /dev/f1 .
+
+### Dependencies:
+Space.o : Space.c dev.h /usr/include/linux/stddef.h 
+arp.o : arp.c /usr/include/linux/types.h /usr/include/linux/string.h /usr/include/linux/kernel.h \
+  /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+  /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/dirent.h \
+  /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+  /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+  /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+  /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
+  /usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h /usr/include/asm/system.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h arp.h 
+dev.o : dev.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
+  /usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+  /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+  /usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
+  /usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
+  /usr/include/asm/memory.h dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h tcp.h sock.h /usr/include/linux/errno.h arp.h 
+eth.o : eth.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
+  /usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+  /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+  /usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
+  /usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
+  /usr/include/asm/memory.h dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h tcp.h sock.h /usr/include/linux/errno.h arp.h 
+icmp.o : icmp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
+  /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+  ../kern_sock.h icmp.h 
+ip.o : ip.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
+  /usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+  /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+  /usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
+  /usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
+  timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
+  eth.h tcp.h sock.h /usr/include/linux/errno.h arp.h icmp.h 
+loopback.o : loopback.c /usr/include/linux/config.h /usr/include/linux/config.dist.h \
+  /usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+  /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+  /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+  /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/tty.h /usr/include/linux/termios.h \
+  /usr/include/asm/system.h /usr/include/linux/ptrace.h /usr/include/asm/segment.h \
+  /usr/include/asm/io.h /usr/include/asm/memory.h /usr/include/errno.h /usr/include/traditional.h \
+  /usr/include/linux/errno.h /usr/include/linux/fcntl.h /usr/include/netinet/in.h \
+  /usr/include/features.h /usr/include/sys/socket.h /usr/include/linux/socket.h \
+  dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
+  tcp.h sock.h arp.h ../kern_sock.h 
+pack_type.o : pack_type.c /usr/include/linux/stddef.h dev.h eth.h 
+packet.o : packet.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
+  /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+  ../kern_sock.h 
+protocols.o : protocols.c /usr/include/asm/segment.h /usr/include/asm/system.h \
+  /usr/include/linux/types.h /usr/include/linux/kernel.h /usr/include/linux/sched.h \
+  /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+  /usr/include/linux/wait.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+  /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+  /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+  /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/string.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h icmp.h 
+raw.o : raw.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
+  /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+  ../kern_sock.h 
+sock.o : sock.c /usr/include/linux/errno.h /usr/include/linux/types.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h /usr/include/linux/kernel.h /usr/include/linux/sched.h \
+  /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+  /usr/include/linux/wait.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+  /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+  /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+  /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/timer.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/asm/memory.h ../kern_sock.h timer.h ip.h dev.h /usr/include/netinet/protocols.h \
+  eth.h tcp.h udp.h sock.h /usr/include/asm/segment.h /usr/include/asm/system.h \
+  /usr/include/linux/fcntl.h 
+tcp.o : tcp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/asm/memory.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h /usr/include/linux/fcntl.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h icmp.h tcp.h sock.h /usr/include/linux/errno.h \
+  /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+  ../kern_sock.h 
+timer.o : timer.c /usr/include/linux/types.h /usr/include/linux/errno.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h /usr/include/linux/kernel.h /usr/include/linux/sched.h \
+  /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+  /usr/include/linux/wait.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+  /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+  /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+  /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/timer.h /usr/include/asm/system.h \
+  timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
+  eth.h tcp.h sock.h arp.h ../kern_sock.h 
+udp.o : udp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+  /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+  /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+  /usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
+  /usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
+  /usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
+  /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
+  /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+  ../kern_sock.h udp.h icmp.h 
+we.o : we.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/include/linux/kernel.h \
+  /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+  /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
+  /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+  /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+  /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+  /usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+  /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+  /usr/include/linux/tty.h /usr/include/linux/termios.h /usr/include/asm/system.h \
+  /usr/include/linux/ptrace.h /usr/include/asm/segment.h /usr/include/asm/io.h \
+  /usr/include/asm/memory.h /usr/include/errno.h /usr/include/traditional.h /usr/include/linux/errno.h \
+  /usr/include/linux/fcntl.h /usr/include/netinet/in.h /usr/include/features.h \
+  /usr/include/sys/socket.h /usr/include/linux/socket.h dev.h eth.h timer.h ip.h \
+  /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h tcp.h sock.h \
+  arp.h wereg.h 
diff --git a/net/tcp/Space.c b/net/tcp/Space.c
new file mode 100644 (file)
index 0000000..c6777bb
--- /dev/null
@@ -0,0 +1,79 @@
+/* Space.c */
+/* Holds initial configuration information for devices. */
+#include "dev.h"
+#include <linux/stddef.h>
+
+
+extern void wd8003_init(struct device *);
+
+static struct device wd8003_dev =
+{
+  "eth0",
+  0xd2000,   /* recv memory end. */
+  0xd0600,   /* recv memory start. */
+  0xd2000,  /* memory end. */
+  0xd0000,  /* memory start. */
+  0x280,    /* base i/o address. */
+  5,       /* irq */
+  0,0,0,0,0, /* flags */
+  NULL, /* next device */
+  wd8003_init,
+  /* wd8003_init should set up the rest. */
+  0,  /* trans start. */
+  {NULL}, /* buffs */
+  NULL, /* backlog */
+  NULL, /* open */
+  NULL, /* stop */
+  NULL, /* hard_start_xmit */
+  NULL, /* hard_header */
+  NULL, /* add arp */
+  NULL, /* queue xmit */
+  NULL, /* rebuild header */
+  NULL, /* type_trans */
+  NULL, /* send_packet */
+  NULL, /* private */
+  0,    /* type. */
+  0,    /* hard_header_len */
+  0,    /* mtu */
+  {0,}, /* broadcast address */
+  {0,}, /* device address */
+  0     /* addr len */
+};
+
+extern void loopback_init(struct device *dev);
+
+static struct device loopback_dev =
+{
+  "loopback",
+  -1,       /* recv memory end. */
+  0x0,      /* recv memory start. */
+  -1,       /* memory end. */
+  0,        /* memory start. */
+  0,        /* base i/o address. */
+  0,       /* irq */
+  0,0,1,0,0, /* flags */
+  &wd8003_dev, /* next device */
+  loopback_init,
+  /* loopback_init should set up the rest. */
+  0,  /* trans start. */
+  {NULL}, /* buffs */
+  NULL, /* backlog */
+  NULL, /* open */
+  NULL, /* stop */
+  NULL, /* hard_start_xmit */
+  NULL, /* hard_header */
+  NULL, /* add arp */
+  NULL, /* queue xmit */
+  NULL, /* rebuild header */
+  NULL, /* type_trans */
+  NULL, /* send_packet */
+  NULL, /* private */
+  0,    /* type. */
+  0,    /* hard_header_len */
+  0,    /* mtu */
+  {0,}, /* broadcast address */
+  {0,}, /* device address */
+  0     /* addr len */
+};
+
+struct device *dev_base = &loopback_dev;
diff --git a/net/tcp/arp.c b/net/tcp/arp.c
new file mode 100644 (file)
index 0000000..ad3a7dc
--- /dev/null
@@ -0,0 +1,485 @@
+/* arp.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <asm/system.h>
+
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include "arp.h"
+
+#undef ARP_DEBUG
+#ifdef  ARP_DEBUG
+#define PRINTK printk
+#else
+#define PRINTK dummy_routine
+#endif
+
+static struct arp_table *arp_table[ARP_TABLE_SIZE] ={NULL, };
+static struct sk_buff *arp_q=NULL;
+
+/* this will try to retransmit everything on the queue. */
+static void
+send_arp_q(void)
+{
+   struct sk_buff *skb;
+   if (arp_q == NULL) return;
+
+   skb = arp_q;
+   do {
+      if (!skb->dev->rebuild_header (skb+1, skb->dev))
+       {
+          if (skb->next == skb)
+            {
+               arp_q = NULL;
+            }
+          else
+            {
+               skb->next->prev = skb->prev;
+               skb->prev->next = skb->next;
+               arp_q = skb->next;
+            }
+          skb->next = NULL;
+          skb->prev = NULL;
+          skb->arp  = 1;
+          skb->dev->queue_xmit (skb, skb->dev, 0);
+          if (arp_q == NULL) break;
+          skb = arp_q;
+          continue;
+       }
+      skb=skb->next;
+   } while (skb != arp_q);
+
+}
+
+static  void
+print_arp(struct arp *arp)
+{
+  int i;
+  unsigned long *lptr;
+  unsigned char *ptr;
+  PRINTK ("arp: \n");
+  PRINTK ("   hrd = %d\n",net16(arp->hrd));
+  PRINTK ("   pro = %d\n",net16(arp->pro));
+  PRINTK ("   hlen = %d plen = %d\n",arp->hlen, arp->plen);
+  PRINTK ("   op = %d\n", net16(arp->op));
+  ptr = (unsigned char *)(arp+1);
+  PRINTK ("   sender haddr = ");
+  for (i = 0; i < arp->hlen; i++)
+    {
+      PRINTK ("0x%02X ",*ptr++);
+    }
+  lptr = (void *)ptr;
+  PRINTK (" send paddr = %X\n",*lptr);
+  lptr ++;
+  ptr = (void *)lptr;
+  PRINTK ("   destination haddr = ");
+  for (i = 0; i < arp->hlen; i++)
+    {
+      PRINTK ("0x%02X ",*ptr++);
+    }
+  lptr = (void *)ptr;
+  PRINTK (" destination paddr = %X\n",*lptr);
+}
+
+static  unsigned char *
+arp_sourceh(struct arp *arp)
+{
+  unsigned char *ptr;
+  ptr = (unsigned char *)(arp + 1);
+  return (ptr);
+}
+
+static  unsigned char *
+arp_targeth(struct arp *arp)
+{
+  unsigned char *ptr;
+  ptr = (unsigned char *)(arp + 1);
+  ptr += arp->hlen+4;
+  return (ptr);
+}
+
+static  unsigned long *
+arp_sourcep(struct arp *arp)
+{
+  unsigned long *lptr;
+  unsigned char *ptr;
+  ptr = (unsigned char *)(arp + 1);
+  ptr += arp->hlen;
+  lptr = (unsigned long *)ptr;
+  return (lptr);
+}
+
+
+static  unsigned long *
+arp_targetp(struct arp *arp)
+{
+  unsigned long *lptr;
+  unsigned char *ptr;
+  ptr = (unsigned char *)(arp + 1);
+  ptr += 2*arp->hlen+4;
+  lptr = (unsigned long *)ptr;
+  return (lptr);
+}
+
+static  void
+arp_free (void *ptr, unsigned long len)
+{
+  free_s(ptr, len);
+}
+
+static  void *
+arp_malloc (unsigned long amount)
+{
+  return (malloc (amount));
+}
+
+static  int
+arp_response (struct arp *arp1, struct device *dev)
+{
+  struct arp *arp2;
+  struct sk_buff *skb;
+  int tmp;
+
+  /* get some mem and initialize it for the return trip. */
+  skb = arp_malloc (sizeof (*skb) + sizeof (*arp2) +
+                   2*arp1->hlen + 2*arp1->plen + dev->hard_header_len);
+  if (skb == NULL) return (1);
+
+  skb->mem_addr = skb;
+  skb->mem_len = sizeof (*skb) + sizeof (*arp2) + 2*arp1->hlen + 
+    2*arp1->plen + dev->hard_header_len;
+  skb->len = sizeof (*arp2) + 2*arp1->hlen + 
+    2*arp1->plen + dev->hard_header_len;
+
+  tmp = dev->hard_header((unsigned char *)(skb+1), dev,
+                        ETHERTYPE_ARP, *arp_sourcep(arp1),
+                        *arp_targetp(arp1),skb->len);
+
+  if (tmp < 0) return (1);
+
+  arp2 =(struct arp *) ((unsigned char *)skb+sizeof (*skb) + tmp );
+  memcpy (arp2, arp1, sizeof (*arp2));
+
+  /* now swap the addresses. */
+  *arp_sourcep(arp2) = *arp_targetp(arp1);
+  memcpy(arp_sourceh(arp2), dev->dev_addr, arp1->hlen);
+
+  *arp_targetp(arp2) = *arp_sourcep(arp1);
+  memcpy(arp_targeth(arp2), arp_sourceh(arp1), arp1->hlen);
+
+  arp2->op = NET16(ARP_REPLY);
+  skb->free = 1;
+  skb->arp = 1; /* so the code will know it's not waiting on an arp. */
+  skb->sk = NULL;
+  skb->next = NULL;
+  PRINTK (">>");
+  print_arp(arp2);
+  /* send it. */
+  dev->queue_xmit (skb, dev, 0);
+  return (0);
+}
+
+/* This will find an entry in the arp table by looking at the ip
+   address. */
+static  struct arp_table *
+arp_lookup (unsigned long paddr)
+{
+  unsigned long hash;
+  struct arp_table *apt;
+  PRINTK ("arp_lookup(paddr=%X)\n", paddr);
+  /* we don't want to arp ourselves. */
+  if (my_ip_addr(paddr)) return (NULL);
+  hash = net32(paddr) & (ARP_TABLE_SIZE - 1);
+  cli();
+  for (apt = arp_table[hash]; apt != NULL; apt = apt->next)
+    {
+      if (apt->ip == paddr)
+       {
+          sti();
+          return (apt);
+       }
+    }
+  sti();
+  return (NULL);
+}
+
+void
+arp_destroy(unsigned long paddr)
+{
+  unsigned long hash;
+  struct arp_table *apt;
+  struct arp_table *lapt;
+  PRINTK ("arp_destroy (paddr=%X)\n",paddr);
+  /* we don't want to destroy are own arp */
+  if (my_ip_addr(paddr)) return;
+  hash = net32(paddr) & (ARP_TABLE_SIZE - 1);
+
+  cli(); /* can't be interrupted. */
+  /* make sure there is something there. */
+  if (arp_table[hash] == NULL) return;
+
+  /* check the first one. */
+  if (arp_table[hash]->ip == paddr)
+    {
+      apt = arp_table[hash];
+      arp_table[hash] = arp_table[hash]->next;
+      arp_free (apt, sizeof (*apt));
+      sti();
+      return;
+    }
+
+  /* now deal with it any where else in the chain. */
+  lapt = arp_table[hash];
+  for (apt = arp_table[hash]->next; apt != NULL; apt = apt->next)
+    {
+      if (apt->ip == paddr) 
+       {
+         lapt->next = apt->next;
+         arp_free (apt, sizeof (*apt));
+         sti();
+         return;
+       }
+    }
+  sti();
+}
+
+/* this routine does not check for duplicates.  It assumes the caller
+   does. */
+static  struct arp_table *
+create_arp (unsigned long paddr, unsigned char *addr, int hlen)
+{
+  struct arp_table *apt;
+  unsigned long hash;
+  apt = arp_malloc (sizeof (*apt));
+  if (apt == NULL) return (NULL);
+
+  hash = net32(paddr) & (ARP_TABLE_SIZE - 1);
+  apt->ip = paddr;
+  apt->hlen =hlen;
+  memcpy (apt->hard, addr, hlen);
+  apt->last_used=timer_seq;
+  sti();
+  apt->next = arp_table[hash];
+  arp_table[hash]=apt;
+  cli();
+  return (apt);
+}
+
+int
+arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+   struct arp *arp;
+   struct arp_table *tbl;
+   int ret;
+
+   PRINTK ("<<\n");
+   arp = skb->h.arp;
+   print_arp(arp);
+
+  /* if this test doesn't pass, something fishy is going on. */
+  if (arp->hlen != dev->addr_len || dev->type !=NET16( arp->hrd))
+    {
+       free_skb(skb, FREE_READ);
+       return (0);
+    }
+
+  /* for now we will only deal with ip addresses. */
+  if (arp->pro != NET16(ARP_IP_PROT) || arp->plen != 4)
+    {
+       free_skb (skb, FREE_READ);
+       return (0);
+    }
+
+  /* now look up the ip address in the table. */
+  tbl = arp_lookup (*arp_sourcep(arp));
+  if (tbl != NULL)
+    {
+       memcpy (tbl->hard, arp+1, arp->hlen);
+       tbl->hlen = arp->hlen;
+       tbl->last_used = timer_seq;
+    }
+
+  if (!my_ip_addr(*arp_targetp(arp)))
+    {
+       free_skb (skb, FREE_READ);
+       return (0);
+    }
+
+  if (tbl == NULL)
+    create_arp (*arp_sourcep(arp), arp_sourceh(arp), arp->hlen);
+
+   /* now see if we can send anything. */
+   send_arp_q();
+     
+  if (arp->op != NET16(ARP_REQUEST))
+    {
+       free_skb (skb, FREE_READ);
+       return (0);
+    }
+
+  /* now we need to create a new packet. */
+   ret = arp_response(arp, dev);
+   free_skb (skb, FREE_READ);
+   return (ret);
+}
+
+void
+arp_snd (unsigned long paddr, struct device *dev, unsigned long saddr)
+{
+  struct sk_buff *skb;
+  struct arp *arp;
+  struct arp_table *apt;
+  int tmp;
+  PRINTK ("arp_snd (paddr=%X, dev=%X, saddr=%X)\n",paddr, dev, saddr);
+
+  /* first we build a dummy arp table entry. */
+  apt = create_arp (paddr, NULL, 0);
+  if (apt == NULL) return;
+
+  skb = arp_malloc (sizeof (*arp) + sizeof (*skb) + dev->hard_header_len +
+                   2*dev->addr_len+8);
+  if (skb == NULL) return;
+  
+  skb->sk = NULL;
+  skb->mem_addr = skb;
+  skb->mem_len = sizeof (*arp) + sizeof (*skb) + dev->hard_header_len +
+                   2*dev->addr_len+8;
+  skb->arp = 1;
+  skb->dev = dev;
+  skb->len = sizeof (*arp) + dev->hard_header_len + 2*dev->addr_len+8;
+  skb->next = NULL;
+
+  tmp = dev->hard_header ((unsigned char *)(skb+1), dev,
+                         ETHERTYPE_ARP, 0, saddr, skb->len);
+  if (tmp < 0)
+    {
+       arp_free (skb->mem_addr, skb->mem_len);
+       return;
+    }
+
+  arp =(struct arp *) ((unsigned char *)skb+sizeof (*skb) + tmp );
+  arp->hrd = net16(dev->type);
+  arp->pro = NET16(ARP_IP_PROT);
+  arp->hlen = dev->addr_len;
+  arp->plen = 4;
+  arp->op = NET16(ARP_REQUEST);
+  *arp_sourcep(arp) = saddr;
+  *arp_targetp(arp) = paddr;
+  memcpy (arp_sourceh(arp), dev->dev_addr, dev->addr_len);
+  memcpy (arp_targeth(arp), dev->broadcast, dev->addr_len);
+  PRINTK(">>\n");
+  print_arp(arp);
+  dev->queue_xmit (skb, dev, 0);
+}
+
+int
+arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
+          unsigned long saddr)
+{
+  struct arp_table *apt;
+  PRINTK ("arp_find(haddr=%X, paddr=%X, dev=%X, saddr=%X)\n",
+         haddr, paddr, dev, saddr);
+  if (my_ip_addr (paddr))
+    {
+      memcpy (haddr, dev->dev_addr, dev->addr_len);
+      return (0);
+    }
+  apt = arp_lookup (paddr);
+  if (apt != NULL)
+    {
+       /* make sure it's not too old. If it is too old, we will
+         just pretend we did not find it, and then arp_snd
+         will verify the address for us. */
+       if (!before (apt->last_used, timer_seq+ARP_TIMEOUT) &&
+          apt->hlen != 0)
+        {
+           apt->last_used=timer_seq;
+           memcpy (haddr, apt->hard, dev->addr_len);
+           return (0);
+        }
+    }
+
+  /* if we didn't find an entry, we will try to 
+     send an arp packet. */
+  if (apt == NULL || after (timer_seq, apt->last_used+ARP_RES_TIME))
+    arp_snd(paddr,dev,saddr);
+
+  /* this assume haddr are atleast 4 bytes.
+     If this isn't true we can use a lookup
+     table, one for every dev. */
+  *(unsigned long *)haddr = paddr;
+  return (1);
+}
+
+
+void
+arp_add (unsigned long addr, unsigned char *haddr, struct device *dev)
+{
+   struct arp_table *apt;
+   /* first see if the address is already in the table. */
+   apt = arp_lookup (addr);
+   if (apt != NULL)
+     {
+       apt->last_used = timer_seq;
+       memcpy (apt->hard, haddr , dev->addr_len);
+       return;
+     }
+   create_arp (addr, haddr, dev->addr_len);
+}
+
+void
+arp_add_broad (unsigned long addr, struct device *dev)
+{
+  arp_add (addr,  dev->broadcast , dev);
+}
+
+
+void
+arp_queue(struct sk_buff *skb)
+{
+   cli();
+   if (arp_q == NULL)
+     {
+       arp_q = skb;
+       skb->next = skb;
+       skb->prev = skb;
+     }
+   else
+    {
+      skb->next = arp_q;
+      skb->prev = arp_q->prev;
+      skb->next->prev = skb;
+      skb->prev->next = skb;
+    }
+  sti();
+
+}
+
diff --git a/net/tcp/arp.h b/net/tcp/arp.h
new file mode 100644 (file)
index 0000000..e9c6498
--- /dev/null
@@ -0,0 +1,61 @@
+/* arp.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_ARP_H
+#define _TCP_ARP_H
+
+struct arp
+{
+  unsigned short hrd;
+  unsigned short pro;
+  unsigned char hlen;
+  unsigned char plen;
+  unsigned short op;
+};
+
+
+struct arp_table
+{
+  struct arp_table *next;
+  unsigned long last_used;
+  unsigned long ip;
+  unsigned char hlen;
+  unsigned char hard[MAX_ADDR_LEN];
+};
+
+
+
+int arp_rcv(struct sk_buff *, struct device *, struct packet_type *);
+void arp_snd (unsigned long, struct device *, unsigned long);
+int arp_find (unsigned char *, unsigned long, struct device *dev,
+             unsigned long);
+void arp_add_broad (unsigned long, struct device *dev);
+void arp_destroy (unsigned long);
+void arp_add (unsigned long addr, unsigned char *haddr, struct device *dev);
+void arp_queue (struct sk_buff *skb);
+
+#define ARP_TABLE_SIZE 16
+#define ARP_IP_PROT ETHERTYPE_IP
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+#define ARP_TIMEOUT 8640000 /* about 8 hours. */
+#define ARP_RES_TIME 250 /* 2.5 seconds. */
+#endif
diff --git a/net/tcp/dev.c b/net/tcp/dev.c
new file mode 100644 (file)
index 0000000..fd6e8b6
--- /dev/null
@@ -0,0 +1,394 @@
+/* dev.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <asm/memory.h>
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include "arp.h"
+
+#undef DEV_DEBUG
+#ifdef DEV_DEBUG
+#define PRINTK printk
+#else
+#define PRINTK dummy_routine
+#endif
+
+
+static  unsigned long
+min(unsigned long a, unsigned long b)
+{
+   if (a < b) return (a);
+   return (b);
+}
+
+void
+dev_add_pack (struct packet_type *pt)
+{
+   struct packet_type *p1;
+   pt->next = ptype_base;
+
+   /* see if we need to copy it. */
+   for (p1 = ptype_base; p1 != NULL; p1 = p1->next)
+     {
+       if (p1->type == pt->type)
+         {
+            pt->copy = 1;
+            break;
+         }
+     }
+
+   ptype_base = pt;
+   
+}
+
+void
+dev_remove_pack (struct packet_type *pt)
+{
+   struct packet_type *lpt, *pt1;
+   if (pt == ptype_base)
+     {
+       ptype_base = pt->next;
+       return;
+     }
+
+   lpt = NULL;
+
+   for (pt1 = ptype_base; pt1->next != NULL; pt1=pt1->next)
+     {
+       if (pt1->next == pt )
+         {
+            cli();
+            if (!pt->copy && lpt) 
+              lpt->copy = 0;
+            pt1->next = pt->next;
+            sti();
+            return;
+         }
+
+       if (pt1->next -> type == pt ->type)
+         {
+            lpt = pt1->next;
+         }
+     }
+}
+
+struct device *
+get_dev (char *name)
+{
+   struct device *dev;
+   for (dev = dev_base; dev != NULL; dev=dev->next)
+     {
+       if (strcmp (dev->name, name) == 0) return (dev);
+     }
+   return (NULL);
+}
+
+void
+dev_queue_xmit (struct sk_buff *skb, struct device *dev, int pri)
+{
+  struct sk_buff *skb2;
+  PRINTK ("eth_queue_xmit (skb=%X, dev=%X, pri = %d)\n", skb, dev, pri);
+  skb->dev = dev;
+  if (pri < 0 || pri >= DEV_NUMBUFFS)
+    {
+       printk ("bad priority in dev_queue_xmit.\n");
+       pri = 1;
+    }
+
+  if (dev->hard_start_xmit(skb, dev) == 0)
+    {
+       return;
+    }
+
+  if (skb->next != NULL)
+    {
+       printk ("retransmitted packet still on queue. \n");
+       return;
+    }
+
+  /* used to say it is not currently on a send list. */
+  skb->next = NULL;
+
+
+  /* put skb into a bidirectional circular linked list. */
+  PRINTK ("eth_queue dev->buffs[%d]=%X\n",pri, dev->buffs[pri]);
+  /* interrupts should already be cleared by hard_start_xmit. */
+  cli();
+  if (dev->buffs[pri] == NULL)
+    {
+      dev->buffs[pri]=skb;
+      skb->next = skb;
+      skb->prev = skb;
+    }
+  else
+    {
+      skb2=dev->buffs[pri];
+      skb->next = skb2;
+      skb->prev = skb2->prev;
+      skb->next->prev = skb;
+      skb->prev->next = skb;
+    }
+  sti();
+
+}
+
+
+/* this routine now just gets the data out of the card and returns.
+   it's return values now mean.
+
+   1 <- exit even if you have more packets.
+   0 <- call me again no matter what.
+  -1 <- last packet not processed, try again. */
+
+int
+dev_rint(unsigned char *buff, unsigned long len, int flags,
+        struct device * dev)
+{
+   struct sk_buff *skb=NULL;
+   struct packet_type *ptype;
+   unsigned short type;
+   unsigned char flag =0;
+   unsigned char *to;
+   int amount;
+
+   /* try to grab some memory. */
+   if (len > 0 && buff != NULL)
+     {
+       skb = malloc (sizeof (*skb) + len);
+       skb->mem_len = sizeof (*skb) + len;
+       skb->mem_addr = skb;
+     }
+
+   /* firs we copy the packet into a buffer, and save it for later. */
+   if (buff != NULL && skb != NULL)
+     {
+       if ( !(flags & IN_SKBUFF))
+         {
+            to = (unsigned char *)(skb+1);
+            while (len > 0)
+              {
+                 amount = min (len, (unsigned long) dev->rmem_end -
+                               (unsigned long) buff);
+                 memcpy (to, buff, amount);
+                 len -= amount;
+                 buff += amount;
+                 to += amount;
+                 if ((unsigned long)buff == dev->rmem_end)
+                   buff = (unsigned char *)dev->rmem_start;
+              }
+         }
+       else
+         {
+            free_s (skb->mem_addr, skb->mem_len);
+            skb = (struct sk_buff *)buff;
+         }
+
+       skb->len = len;
+       skb->dev = dev;
+       skb->sk = NULL;
+
+       /* now add it to the dev backlog. */
+       cli();
+       if (dev-> backlog == NULL)
+         {
+            skb->prev = skb;
+            skb->next = skb;
+            dev->backlog = skb;
+         }
+       else
+         {
+            skb ->prev = dev->backlog->prev;
+            skb->next = dev->backlog;
+            skb->next->prev = skb;
+            skb->prev->next = skb;
+         }
+       sti();
+       return (0);
+     }
+
+   if (skb != NULL) 
+     free_s (skb->mem_addr, skb->mem_len);
+
+   /* anything left to process? */
+
+   if (dev->backlog == NULL)
+     {
+       if (buff == NULL)
+         {
+            sti();
+            return (1);
+         }
+
+       if (skb != NULL)
+         {
+            sti();
+            return (-1);
+         }
+
+       sti();
+       printk ("dev_rint:Dropping packets due to lack of memory\n");
+       return (1);
+     }
+
+   skb= dev->backlog;
+   if (skb->next == skb)
+     {
+       dev->backlog = NULL;
+     }
+   else
+     {
+       dev->backlog = skb->next;
+       skb->next->prev = skb->prev;
+       skb->prev->next = skb->next;
+     }
+   sti();
+
+   /* bump the pointer to the next structure. */
+   skb->h.raw = (unsigned char *)(skb+1) + dev->hard_header_len;
+   skb->len -= dev->hard_header_len;
+
+   /* convert the type to an ethernet type. */
+   type = dev->type_trans (skb, dev);
+
+   /* if there get to be a lot of types we should changes this to
+      a bunch of linked lists like we do for ip protocols. */
+   for (ptype = ptype_base; ptype != NULL; ptype=ptype->next)
+     {
+       if (ptype->type == type)
+         {
+            struct sk_buff *skb2;
+            /* copy the packet if we need to. */
+            if (ptype->copy)
+              {
+                 skb2 = malloc (skb->mem_len);
+                 if (skb2 == NULL) continue;
+                 memcpy (skb2, skb, skb->mem_len);
+                 skb2->mem_addr = skb2;
+              }
+            else
+              {
+                 skb2 = skb;
+                 flag = 1;
+              }
+              
+            ptype->func (skb2, dev, ptype);
+         }
+     }
+
+   if (!flag)
+     {
+       PRINTK ("discarding packet type = %X\n", type);
+       free_skb (skb, FREE_READ);
+     }
+
+     if (buff == NULL)
+       return (0);
+     else
+       return (-1);
+}
+
+/* This routine is called when an device interface is ready to
+   transmit a packet.  Buffer points to where the packet should
+   be put, and the routine returns the length of the packet.  A
+   length of zero is interrpreted to mean the transmit buffers
+   are empty, and the transmitter should be shut down. */
+
+unsigned long
+dev_tint(unsigned char *buff,  struct device *dev)
+{
+  int i;
+  int tmp;
+  struct sk_buff *skb;
+  for (i=0; i < DEV_NUMBUFFS; i++)
+    {
+      while (dev->buffs[i]!=NULL)
+       {
+         cli();
+         skb=dev->buffs[i];
+         if (skb->next == skb)
+           {
+             dev->buffs[i] = NULL;
+           }
+         else
+           {
+             dev->buffs[i]=skb->next;
+             skb->prev->next = skb->next;
+             skb->next->prev = skb->prev;
+           }
+         skb->next = NULL;
+         skb->prev = NULL;
+         sti();
+         tmp = skb->len;
+         if (!skb->arp)
+           {
+              if (dev->rebuild_header (skb+1, dev))
+                {
+                   skb->dev = dev;
+                   arp_queue (skb);
+                   continue;
+                }
+           }
+            
+         if (tmp <= dev->mtu)
+           {
+              if (dev->send_packet != NULL)
+                {
+                   dev->send_packet(skb, dev);
+                }
+              if (buff != NULL)
+                memcpy (buff, skb + 1, tmp);
+
+              PRINTK (">>\n");
+              print_eth ((struct enet_header *)(skb+1));
+           }
+         else
+           {
+              printk ("**** bug len bigger than mtu. \n");
+           }
+
+         if (skb->free)
+           {
+                 free_skb(skb, FREE_WRITE);
+           }
+
+         if (tmp != 0)
+           return (tmp);
+       }
+    }
+  PRINTK ("dev_tint returning 0 \n");
+  return (0);
+}
+
diff --git a/net/tcp/dev.h b/net/tcp/dev.h
new file mode 100644 (file)
index 0000000..16fe2b1
--- /dev/null
@@ -0,0 +1,96 @@
+/* dev.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_DEV_H
+#define _TCP_DEV_H
+/* for future expansion when we will have different priorities. */
+#define DEV_NUMBUFFS 3
+#define MAX_ADDR_LEN 6
+#define MAX_HEADER 14
+#define MAX_ROUTE 16
+
+struct device
+{
+  char *name;
+  unsigned long rmem_end;
+  unsigned long rmem_start;
+  unsigned long mem_end;
+  unsigned long mem_start;
+  unsigned short base_addr;
+  unsigned char irq;
+  unsigned char start:1,
+                tbusy:1,
+                loopback:1,
+                interrupt:1,
+                up:1;
+  struct device *next;
+  void (*init)(struct device *dev);
+  unsigned long trans_start;
+  struct sk_buff *buffs[DEV_NUMBUFFS];
+  struct sk_buff *backlog;
+  int  (*open)(struct device *dev);
+  int  (*stop)(struct device *dev);
+  int (*hard_start_xmit) (struct sk_buff *skb, struct device *dev);
+  int (*hard_header) (unsigned char *buff, struct device *dev,
+                     unsigned short type, unsigned long daddr,
+                     unsigned long saddr, unsigned len);
+  void (*add_arp) (unsigned long addr, struct sk_buff *skb,
+                  struct device *dev);
+  void (*queue_xmit)(struct sk_buff *skb, struct device *dev, int pri);
+  int (*rebuild_header)(void *eth, struct device *dev);
+  unsigned short (*type_trans) (struct sk_buff *skb, struct device *dev);
+  void (*send_packet)(struct sk_buff *skb, struct device *dev);
+  void *private;
+
+  unsigned short type;
+  unsigned short hard_header_len;
+  unsigned short mtu;
+  unsigned char broadcast[MAX_ADDR_LEN];
+  unsigned char dev_addr[MAX_ADDR_LEN];
+  unsigned char addr_len;
+};
+
+extern struct device *dev_base;
+
+struct packet_type
+{
+   unsigned short type; /* This is really NET16(ether_type) other devices
+                          will have to translate appropriately. */
+   unsigned short copy:1;
+   int (*func) (struct sk_buff *, struct device *, struct packet_type *);
+   void *data;
+   struct packet_type *next;
+};
+
+/* used by dev_rint */
+#define IN_SKBUFF 1
+
+extern struct packet_type *ptype_base;
+void dev_queue_xmit (struct sk_buff *skb, struct device *dev, int pri);
+int dev_rint (unsigned char *buff, unsigned long len, int flags,
+             struct device *dev);
+unsigned long dev_tint (unsigned char *buff, struct device *dev);
+void dev_add_pack (struct packet_type *pt);
+void dev_remove_pack (struct packet_type *pt);
+struct device *get_dev (char *name);
+
+
+#endif
diff --git a/net/tcp/eth.c b/net/tcp/eth.c
new file mode 100644 (file)
index 0000000..3e6fccd
--- /dev/null
@@ -0,0 +1,120 @@
+/* eth.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <asm/memory.h>
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include "arp.h"
+
+#undef ETH_DEBUG
+#ifdef ETH_DEBUG
+#define PRINTK printk
+#else
+#define PRINTK dummy_routine
+#endif
+
+void
+print_eth (struct enet_header *eth)
+{
+  int i;
+  PRINTK ("ether source addr: ");
+  for (i =0 ; i < ETHER_ADDR_LEN; i++)
+    {
+      PRINTK ("0x%2X ",eth->saddr[i]);
+    }
+  PRINTK ("\n");
+
+  PRINTK ("ether dest addr: ");
+  for (i =0 ; i < ETHER_ADDR_LEN; i++)
+    {
+      PRINTK ("0x%2X ",eth->daddr[i]);
+    }
+  PRINTK ("\n");
+  PRINTK ("ethertype = %X\n",net16(eth->type));
+}
+
+int
+eth_hard_header (unsigned char *buff, struct device *dev,
+                unsigned short type, unsigned long daddr,
+                unsigned long saddr, unsigned len)
+{
+  struct enet_header *eth;
+  eth = (struct enet_header *)buff;
+  eth->type = net16(type);
+  memcpy (eth->saddr, dev->dev_addr, dev->addr_len);
+  if (daddr == 0)
+    {
+      memset (eth->daddr, 0xff, dev->addr_len);
+      return (14);
+    }
+  if (!arp_find (eth->daddr, daddr, dev, saddr))
+    {
+      return (14);
+    }
+  else
+    {
+      *(unsigned long *)eth->saddr = saddr;
+       return (-14);
+    }
+}
+
+int
+eth_rebuild_header (void *buff, struct device *dev)
+{
+  struct enet_header *eth;
+  eth = buff;
+  if (arp_find(eth->daddr, *(unsigned long*)eth->daddr, dev, 
+                  *(unsigned long *)eth->saddr))
+    return (1);
+  memcpy (eth->saddr, dev->dev_addr, dev->addr_len);
+  return (0);
+}
+
+void
+eth_add_arp (unsigned long addr, struct sk_buff *skb, struct device *dev)
+{
+   struct enet_header *eh;
+   eh = (struct enet_header *)(skb + 1);
+   arp_add (addr, eh->saddr, dev);
+}
+
+unsigned short
+eth_type_trans (struct sk_buff *skb, struct device *dev)
+{
+   struct enet_header *eh;
+   eh = (struct enet_header *)(skb + 1);
+   return (eh->type);
+}
diff --git a/net/tcp/eth.h b/net/tcp/eth.h
new file mode 100644 (file)
index 0000000..a849d3b
--- /dev/null
@@ -0,0 +1,93 @@
+/* eth.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+
+#ifndef _TCP_ETH_H
+#define _TCP_ETH_H
+
+#define ETHER_MIN_LEN 64
+#define ETHER_ADDR_LEN 6
+
+#define ETHERTYPE_ARP 0x806
+#define ETHERTYPE_IP  0x800
+#define ETHER_TYPE 1
+
+/* Reciever modes */
+#define ETH_MODE_MONITOR               1       /* Monitor mode - no receive */
+#define ETH_MODE_PHYSICAL              2       /* Physical address receive only */
+#define ETH_MODE_BROADCAST             3       /* Broadcast receive + mode 2 */
+#define ETH_MODE_MULTICAST             4       /* Multicast receive + mode 3 */
+#define ETH_MODE_PROMISCUOUS           5       /* Promiscuous mode - receive all */
+
+#define WD_RX_SAVE_ERRORS      1       /* save error packets */
+#define WD_RX_RUNT             2       /* accept runt packets */
+#define WD_RX_BROAD            4       /* accept broadcast packets */
+#define WD_RX_MULTI            8       /* accept multicast packets */
+#define WD_RX_PROM             0x10    /* accept all packets */
+#define WD_RX_MON              0x20    /* monitor mode (just count packets) */
+
+#define NET16(x) (((x&0xff)<<8)|((x>>8)&0xff))
+
+struct enet_header
+{
+  unsigned char daddr[ETHER_ADDR_LEN];
+  unsigned char saddr[ETHER_ADDR_LEN];
+  unsigned short type;
+};
+
+#define ETHER_HEADER sizeof(struct enet_header)
+
+struct enet_statistics{
+  int rx_packets;      /* total packets received */
+  int tx_packets;      /* total packets transmitted */
+  int rx_errors;       /* bad packets received */
+  int tx_errors;       /* packet transmit problems */
+  int rx_dropped;      /* no space in linux buffers */
+  int tx_dropped;      /* no space available in linux */
+  int collisions;      /* total number of collisions */
+  int multicast;       /* multicast packets received */
+                       /* detailed rx_errors: */
+  int rx_length_errors;
+  int rx_over_errors;  /* receiver overwrote ring buffer in card */
+  int rx_crc_errors;   /* received packet with crc error */
+  int rx_frame_errors;         /* received frame alignment error */
+  int rx_fifo_errors;  /* receiver fifo overrun */
+  int rx_missed_errors; /* receiver missed packet */
+                       /* detailed tx_errors */
+  int tx_aborted_errors;
+  int tx_carrier_errors;
+  int tx_fifo_errors;
+  int tx_heartbeat_errors;
+  int tx_window_errors;
+};
+
+void print_eth(struct enet_header *eth);
+int eth_hard_header (unsigned char *buff, struct device *dev,
+                    unsigned short type, unsigned long daddr,
+                    unsigned long saddr, unsigned len);
+
+int eth_rebuild_header(void *eth, struct device *dev);
+void eth_add_arp (unsigned long addr, struct sk_buff *skb,
+                 struct device *dev);
+unsigned short eth_type_trans (struct sk_buff *skb, struct device *dev);
+
+#endif
diff --git a/net/tcp/icmp.c b/net/tcp/icmp.c
new file mode 100644 (file)
index 0000000..9c75ea1
--- /dev/null
@@ -0,0 +1,283 @@
+/* Internet Control Message Protocol (ICMP) icmp.c */
+
+/*
+    Copyright (C) 1992  Bob Harris
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author of tcpip package may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+
+    The author of this file may be reached at rth@sparta.com or Sparta, Inc.
+    7926 Jones Branch Dr. Suite 900, McLean Va 22102.
+*/
+
+/* modified by Ross Biro bir7@leland.stanford.edu to do more than just
+   echo responses. */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>      /* free_s */
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include "../kern_sock.h" /* for PRINTK */
+#include "icmp.h"
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/* an array of errno for error messages from dest unreach. */
+struct icmp_err icmp_err_convert[]=
+{
+   {ENETUNREACH, 1},
+   {EHOSTUNREACH, 1},
+   {ENOPROTOOPT, 1},
+   {ECONNREFUSED, 1},
+   {EOPNOTSUPP, 0},
+   {EOPNOTSUPP, 0},
+   {ENETUNREACH, 1},
+   {EHOSTDOWN, 1},
+   {ENONET, 1},
+   {ENETUNREACH, 1},
+   {EHOSTUNREACH, 1},
+   {EOPNOTSUPP, 0},
+   {EOPNOTSUPP, 0}
+};
+
+void
+print_icmph (struct icmp_header *icmph)
+{
+   PRINTK ("  type = %d, code = %d, checksum = %X\n", icmph->type,
+          icmph->code, icmph->checksum);
+   PRINTK (" gateway = %X\n", icmph->un.gateway);
+}
+
+/* sends an icmp message in response to a packet. */
+void
+icmp_reply (struct sk_buff *skb_in,  int type, int code, struct device *dev)
+{
+   struct sk_buff *skb;
+   struct ip_header *iph;
+   int offset;
+   struct icmp_header *icmph;
+
+   int len;
+   /* get some memory for the replay. */
+   len = sizeof (*skb) + 8 /* amount of header to return. */ +
+         sizeof (struct icmp_header) +
+        64 /* enough for an ip header. */ +
+        dev->hard_header_len;
+          
+   skb = malloc (len);
+   if (skb == NULL) return;
+
+   skb->mem_addr = skb;
+   skb->mem_len = len;
+
+   len -= sizeof (*skb);
+
+   /* find the ip header. */
+   iph = (struct ip_header *)(skb_in+1);
+   iph = (struct ip_header *)((unsigned char *)iph + dev->hard_header_len);
+
+   /* Build Layer 2-3 headers for message back to source */
+   offset = ip_build_header( skb, iph->daddr, iph->saddr,
+                           &dev, IP_ICMP, NULL, len );
+
+   if (offset < 0)
+     {
+       skb->sk = NULL;
+       free_skb (skb, FREE_READ);
+       return;
+     }
+
+   /* Readjust length according to actual IP header size */
+   skb->len = offset + sizeof (struct icmp_header) + 8;
+   
+   icmph = (struct icmp_header *)((unsigned char *)(skb+1) + offset);
+   icmph->type = type;
+   icmph->code = code;
+   icmph->checksum = 0; /* we don't need to compute this. */
+   icmph->un.gateway = 0; /* might as well 0 it. */
+   memcpy (icmph+1, iph+1, 8);
+   /* send it and free it. */
+   ip_queue_xmit (NULL, dev, skb, 1);
+   
+}
+
+/* deals with incoming icmp packets. */
+
+int
+icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
+       unsigned long daddr, unsigned short len,
+       unsigned long saddr, int redo, struct ip_protocol *protocol )
+{
+   int size, offset;
+   struct icmp_header *icmph, *icmphr;
+   struct sk_buff *skb;
+   unsigned char *buff;
+
+
+   /* drop broadcast packets.  */
+   if ((daddr & 0xff000000) == 0 || (daddr & 0xff000000) == 0xff000000)
+     {
+       skb1->sk = NULL;
+       free_skb (skb1, FREE_READ);
+       return (0);
+     }
+
+   buff = skb1->h.raw;
+
+   icmph = (struct icmp_header *)buff;
+
+   /* Validate the packet first */
+   if( icmph->checksum )
+     { /* Checksums Enabled? */
+       if( ip_compute_csum( (unsigned char *)icmph, len ) )
+         {
+            /* Failed checksum! */
+            PRINTK("\nICMP ECHO failed checksum!");
+            skb1->sk = NULL;
+            free_skb (skb1, FREE_READ);
+            return (0);
+         }
+     }
+
+   print_icmph(icmph);
+
+   /* Parse the ICMP message */
+   switch( icmph->type )
+     {
+       case ICMP_DEST_UNREACH:
+       case ICMP_SOURCE_QUENCH:
+       {
+          struct ip_header *iph;
+          struct ip_protocol *ipprot;
+          unsigned char hash;
+          int err;
+
+          err = icmph->type << 8 | icmph->code;
+
+          /* we need to cause the socket to be closed and the error message
+             to be set appropriately. */
+          iph = (struct ip_header *)(icmph+1);
+
+          /* get the protocol(s) */
+          hash = iph->protocol & (MAX_IP_PROTOS -1 );
+          for (ipprot = ip_protos[hash]; ipprot != NULL; ipprot=ipprot->next)
+            {
+               /* pass it off to everyone who wants it. */
+               ipprot->err_handler (err, (unsigned char *)iph+4*iph->ihl,
+                                    iph->daddr, iph->saddr, ipprot);
+            }
+          skb1->sk = NULL;
+          free_skb (skb1, FREE_READ);
+          return (0);
+       }
+
+       case ICMP_REDIRECT:
+       {
+          /* we need to put a new route in the routing table. */
+          struct rtable *rt; /* we will add a new route. */
+          struct ip_header *iph;
+
+          iph = (struct ip_header *)(icmph+1);
+          rt = malloc (sizeof (*rt));
+          if (rt != NULL)
+            {
+               rt->net = iph->daddr;
+               /* assume class C network.  Technically this is incorrect,
+                  but will give it a try. */
+               if ((icmph->code & 1) == 0) rt->net &= 0x00ffffff;
+               rt->dev = dev;
+               rt->router = icmph->un.gateway;
+               add_route (rt);
+            }
+          skb1->sk = NULL;
+          free_skb (skb1, FREE_READ);
+          return (0);
+       }
+
+       case ICMP_ECHO: 
+       
+       /* Allocate an sk_buff response buffer (assume 64 byte IP header) */
+
+       size = sizeof( struct sk_buff ) + dev->hard_header_len + 64 + len;
+       skb = malloc( size );
+       if (skb == NULL)
+         {
+            skb1->sk = NULL;
+            free_skb (skb1, FREE_READ);
+            return (0);
+         }
+       skb->sk = NULL;
+       skb->mem_addr = skb;
+       skb->mem_len = size;
+
+       /* Build Layer 2-3 headers for message back to source */
+       offset = ip_build_header( skb, daddr, saddr, &dev, IP_ICMP, opt, len );
+       if (offset < 0)
+         {
+            /* Problems building header */
+            PRINTK("\nCould not build IP Header for ICMP ECHO Response");
+            free_s (skb->mem_addr, skb->mem_len);
+            skb1->sk = NULL;
+            free_skb (skb1, FREE_READ);
+            return( 0 ); /* just toss the received packet */
+         }
+
+       /* Readjust length according to actual IP header size */
+       skb->len = offset + len;
+
+       /* Build ICMP_ECHO Response message */
+       icmphr = (struct icmp_header *)( (char *)( skb + 1 ) + offset );
+       memcpy( (char *)icmphr, (char *)icmph, len );
+       icmphr->type = ICMP_ECHOREPLY;
+       icmphr->code = 0;
+       icmphr->checksum = 0;
+
+       if( icmph->checksum )
+         { /* Calculate Checksum */
+            icmphr->checksum = ip_compute_csum( (void *)icmphr, len );
+         }
+
+       /* Ship it out - free it when done */
+       ip_queue_xmit( (volatile struct sock *)NULL, dev, skb, 1 );
+       
+       skb1->sk = NULL;
+       free_skb (skb1, FREE_READ);
+       return( 0 );
+
+       default:
+       PRINTK("\nUnsupported ICMP type = x%x", icmph->type );
+       skb1->sk = NULL;
+       free_skb (skb1, FREE_READ);
+       return( 0 ); /* just toss the packet */
+     }
+
+   /* should be unecessary, but just in case. */
+   skb1->sk = NULL;
+   free_skb (skb1, FREE_READ);
+   return( 0 ); /* just toss the packet */
+}
+
diff --git a/net/tcp/icmp.h b/net/tcp/icmp.h
new file mode 100644 (file)
index 0000000..ada5286
--- /dev/null
@@ -0,0 +1,64 @@
+/* Internet Control Message Protocol (ICMP) header file */
+
+
+#define ICMP_ECHOREPLY         0
+#define ICMP_DEST_UNREACH      3
+#define ICMP_SOURCE_QUENCH     4
+#define ICMP_REDIRECT          5
+#define ICMP_ECHO              8
+#define ICMP_TIME_EXCEEDED     11
+#define ICMP_PARAMETERPROB     12
+#define ICMP_TIMESTAMP        13
+#define ICMP_TIMESTAMPREPLY    14
+#define ICMP_INFO_REQUEST      15
+#define ICMP_INFO_REPLY               16
+
+
+/* used by unreachable. */
+
+#define ICMP_NET_UNREACH       0
+#define ICMP_HOST_UNREACH      1
+#define ICMP_PROT_UNREACH      2
+#define ICMP_PORT_UNREACH      3 /* lots of room for confusion. */
+#define ICMP_FRAG_NNEDED       4
+#define ICMP_SR_FAILED         5
+#define ICMP_NET_UNKNOWN       6
+#define ICMP_HOST_UNKNOWN      7
+#define ICMP_HOST_ISOLATED     8
+#define ICMP_NET_ANO           9
+#define ICMP_HOST_ANO         10
+#define ICMP_NET_UNR_TOS       11
+#define ICMP_HOST_UNR_TOS      12
+
+
+struct icmp_header
+{
+       unsigned char type;
+       unsigned char code;
+       unsigned short checksum;
+       union 
+         {
+            struct
+              {
+                 unsigned short id;
+                 unsigned short sequence;
+              } echo;
+            unsigned long gateway;
+         } un;
+};
+
+struct icmp_err 
+{
+   int errno;
+   unsigned fatal:1;
+};
+
+extern struct icmp_err icmp_err_convert[];
+
+int
+icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+       unsigned long daddr, unsigned short len,
+       unsigned long saddr, int redo, struct ip_protocol *protocol);
+
+void
+icmp_reply (struct sk_buff *skb_in,  int type, int code, struct device *dev);
diff --git a/net/tcp/ip.c b/net/tcp/ip.c
new file mode 100644 (file)
index 0000000..35d2a73
--- /dev/null
@@ -0,0 +1,931 @@
+/* ip.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include "arp.h"
+#include "icmp.h"
+
+unsigned long ip_addr[MAX_IP_ADDRES]={0,0,0};
+
+#undef IP_DEBUG
+#ifdef IP_DEBUG
+#define PRINTK printk
+#else
+#define PRINTK dummy_routine
+#endif
+
+static struct rtable *rt_base=NULL; /* used to base all the routing data. */
+
+struct ip_protocol *ip_protos[MAX_IP_PROTOS] = { NULL, };
+
+#if 0
+static  struct ip_protocol *
+get_protocol(unsigned char prot)
+{
+   unsigned char hash;
+   struct ip_protocol *p;
+   PRINTK ("get_protocol (%d)\n ", prot);
+   hash = prot & (MAX_IP_PROTOS -1);
+   for (p = ip_protos[hash] ; p != NULL; p=p->next)
+     {
+       PRINTK ("trying protocol %d\n", p->protocol);
+       if (p->protocol == prot)
+            return (p);
+     }
+   return (NULL);
+    
+}
+#endif
+
+void
+add_ip_protocol (struct ip_protocol *prot)
+{
+   unsigned char hash;
+   struct ip_protocol *p2;
+   hash = prot->protocol & (MAX_IP_PROTOS-1);
+   prot ->next = ip_protos[hash];
+   ip_protos[hash] = prot;
+   prot->copy = 0;
+   /* set the copy bit if we need to. */
+   for (p2 = prot->next; p2 != NULL; p2=p2->next)
+     {
+       if (p2->protocol == prot->protocol)
+         {
+            prot->copy = 1;
+            break;
+         }
+     }
+
+}
+
+int
+delete_ip_protocol (struct ip_protocol *prot)
+{
+   struct ip_protocol *p;
+   struct ip_protocol *lp=NULL;
+   unsigned char hash;
+
+
+   hash = prot->protocol & (MAX_IP_PROTOS -1);
+   if (prot == ip_protos[hash])
+     {
+        ip_protos[hash]=ip_protos[hash]->next;
+       return (0);
+     }
+
+   for (p = ip_protos[hash]; p != NULL; p = p->next)
+     {
+       /* we have to worry if the protocol being deleted is the
+          last one on the list, then we may need to reset someones
+          copied bit. */
+       if (p->next != NULL && p->next == prot)
+         {
+            /* if we are the last one with this protocol and
+               there is a previous one, reset its copy bit. */
+
+            if (p->copy == 0 && lp != NULL)
+              lp->copy = 0;
+            p->next = prot->next;
+            return (0);
+         }
+
+       if (p->next != NULL && p->next->protocol == prot->protocol)
+         {
+            lp = p;
+         }
+     }
+   return (-1);
+}
+
+/* addr1 is the address which may or may not be broadcast etc.
+   addr2 is the "real addr." */
+
+int
+ip_addr_match (unsigned long addr1, unsigned long addr2)
+{
+  int i;
+  if (addr1 == addr2) return (1);
+  for (i = 0; i < 4; i++, addr1 >>= 8, addr2 >>= 8)
+    {
+      if ((addr1 & 0xff) != (addr2 & 0xff))
+       {
+         /* the only way this could be a match is for the rest of
+            addr1 to be 0. */
+         if (addr1 != 0) 
+           {
+             return (0);
+           }
+         return (1);
+       }
+    }
+  return (1);
+}
+
+int
+my_ip_addr(unsigned long addr)
+{
+  int i;
+  for (i = 0; i < MAX_IP_ADDRES; i++)
+    {
+      if (ip_addr[i] == 0) return (0);
+      if (ip_addr_match (addr, ip_addr[i])) return (1);
+    }
+  return (0);
+}
+
+/* these two routines will do routining. */
+static  void
+strict_route(struct ip_header *iph, struct options *opt)
+{
+}
+
+static  void
+loose_route(struct ip_header *iph, struct options *opt)
+{
+}
+
+void
+print_rt(struct rtable *rt)
+{
+  PRINTK ("net = %08X router = %08X\n",rt->net, rt->router);
+  PRINTK ("dev = %X, next = %X\n",rt->dev, rt->next);
+}
+
+void
+print_ipprot (struct ip_protocol *ipprot)
+{
+   PRINTK ("handler = %X, protocol = %d\n",
+          ipprot->handler, ipprot->protocol);
+}
+
+/* This assumes that address are all in net order. */
+static  struct device *
+ip_route(struct options *opt, unsigned long daddr , unsigned long *raddr)
+{
+  struct rtable *rt;
+  /* look through the routing table for some
+     kind of match. */
+  for (rt=rt_base; rt != NULL; rt=rt->next)
+    {
+      /* see if we found one. */
+      if (ip_addr_match (rt->net, daddr))
+       {
+         *raddr = rt->router;
+         return (rt->dev);
+       }
+    }
+  return (NULL);
+};
+
+void
+add_route (struct rtable *rt)
+{
+  int mask;
+  struct rtable *r;
+  struct rtable *r1;
+  PRINTK ("add_route (rt=%X):\n",rt);
+  print_rt(rt);
+
+  if (rt_base == NULL)
+    {
+      rt->next = NULL;
+      rt_base = rt;
+      return;
+    }
+
+  /* what we have to do is loop though this until we have found the
+     first address which has the same generality as the one in rt.  Then
+     we can put rt in after it. */
+  for (mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask)
+    {
+      if (mask & rt->net)
+       {
+         mask = mask << 8;
+         break;
+       }
+    }
+  PRINTK ("mask = %X\n",mask);
+  r1=rt_base;
+  for (r=rt_base; r != NULL; r=r->next)
+    {
+       /* see if we are getting a duplicate. */
+       if (r->net == rt->net)
+        {
+           if (r == rt_base)
+             {
+                rt->next = r->next;
+                rt_base = rt;
+             }
+           else
+             {
+                rt->next = r->next;
+                r1->next = rt;
+             }
+           free_s (r, sizeof (*r));
+           return;
+        }
+
+      if (!(r->net & mask))
+       {
+          PRINTK("adding before r=%X\n",r);
+          print_rt(r);
+          if (r == rt_base)
+            {
+               rt->next = rt_base;
+               rt_base = rt;
+               return;
+            }
+          rt->next = r;
+          r1->next = rt;
+          return;
+       }
+      r1 = r;
+    }
+  PRINTK ("adding after r1=%X\n",r1);
+  print_rt(r1);
+  /* goes at the end. */
+  rt->next = NULL;
+  r1->next = rt;
+}
+
+int
+ip_set_dev (struct ip_config *u_ipc)
+{
+  struct rtable *rt;
+  struct device *dev;
+  struct ip_config ipc;
+  static int ip_ads = 0;
+
+  if (ip_ads >= MAX_IP_ADDRES) return (-EINVAL);
+
+  verify_area (u_ipc, sizeof (ipc));
+  memcpy_fromfs(&ipc, u_ipc, sizeof (ipc));
+  ipc.name[MAX_IP_NAME-1] = 0;
+  dev = get_dev (ipc.name);
+
+  if (dev == NULL) return (-EINVAL);
+
+  /* see if we need to add a broadcast address. */
+  if (ipc.net != -1)
+    {
+       arp_add_broad (ipc.net, dev);
+       rt = malloc (sizeof (*rt));
+       if (rt == NULL) return (-ENOMEM);
+
+       rt->net = ipc.net;
+       rt->dev = dev;
+       rt->router = 0;
+       add_route (rt);
+/*     dev->net = ipc.net;*/
+    }
+
+  if (ipc.router != -1)
+    {
+       rt = malloc (sizeof (*rt));
+       if (rt == NULL) return (-ENOMEM);
+       rt->net = 0;
+       rt->dev = dev;
+       rt->router = ipc.router;
+       add_route (rt);
+    }
+
+  if (dev->loopback)
+    {
+       rt = malloc (sizeof (*rt));
+       if (rt == NULL) return (-ENOMEM);
+       rt->net = ipc.paddr;
+       rt->dev = dev;
+       rt->router = 0;
+       add_route (rt);
+
+    }
+
+
+  if (!my_ip_addr (ipc.paddr))
+    ip_addr[ip_ads++] = ipc.paddr;
+
+  dev->up = ipc.up;
+  if (dev->up)
+    {
+       if (dev->open)
+        dev->open(dev);
+    }
+  else
+    {
+       if (dev->stop)
+        dev->stop(dev);
+    }
+  return (0);
+
+}
+
+/* this routine will check to see if we have lost a gateway. */
+void
+ip_route_check (unsigned long daddr)
+{
+}
+
+/* this routine puts the options at the end of an ip header. */
+static  int
+build_options (struct ip_header *iph, struct options *opt)
+{
+  unsigned char *ptr;
+  /* currently we don't support any options. */
+  ptr = (unsigned char *)(iph+1);
+  *ptr = 0;
+  return (4);
+}
+
+/* This routine builds the appropriate hardware/ip headers for
+   the routine.  It assumes that if *prot != NULL then the
+   protocol knows what it's doing, otherwise it uses the
+   routing/arp tables to select a protocol struct. */
+
+int
+ip_build_header (struct sk_buff *skb, unsigned long saddr,
+                unsigned long daddr, struct device **dev, int type,
+                struct options *opt, int len)
+{
+  static struct options optmem;
+  struct ip_header *iph;
+  unsigned char *buff;
+  static int count = 0;
+  unsigned long raddr; /* for the router. */
+  int tmp;
+  if (saddr == 0) saddr = MY_IP_ADDR;
+  PRINTK ("ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n"
+         "                 type=%d, opt=%X, len = %d)\n",
+         skb, saddr, daddr, *dev, type, opt, len);
+  buff = (unsigned char *)(skb + 1);
+  /* see if we need to look up the device. */
+  if (*dev == NULL)
+    {
+      *dev = ip_route(&optmem,daddr, &raddr);
+      if (*dev == NULL)
+       {
+         return (-ENETUNREACH);
+       }
+      opt = &optmem;
+    }
+  else
+    {
+      /* we still need the address of the first hop. */
+      ip_route (&optmem, daddr, &raddr);
+    }
+  if (raddr == 0) raddr = daddr;
+  /* now build the header. */
+  /* we need to worry about routing in here.  daddr should
+     really be the address of the next hop. */
+  /* but raddr is . */
+  if ((*dev)->hard_header)
+    {
+       tmp = (*dev)->hard_header(buff, *dev, ETHERTYPE_IP, raddr, saddr, len);
+    }
+  else
+    {
+       tmp = 0;
+    }
+  if (tmp < 0)
+    {
+       tmp = -tmp;
+       skb->arp = 0;
+    }
+  else
+    {
+       skb->arp = 1;
+    }
+  buff += tmp;
+  len -= tmp;
+  skb->dev = *dev;
+  /* now build the ip header. */
+  iph = (struct ip_header *)buff;
+  iph->version = 4;
+  iph->tos = 0;
+  iph->frag_off = 0;
+  iph->ttl = 32;
+  iph->daddr = daddr;
+  iph->saddr = saddr;
+  iph->protocol=type;
+  iph->ihl = 5;
+  iph->id = net16(count++);
+  /* build_options (iph, opt);*/
+  return (20+tmp);
+}
+
+static  int
+do_options(struct ip_header *iph, struct options *opt)
+{
+  unsigned char *buff;
+  int done = 0;
+  int len=sizeof (*iph);
+  int i;
+  /* zero  out the options. */
+  opt->record_route.route_size = 0;
+  opt->loose_route.route_size = 0;
+  opt->strict_route.route_size = 0;
+  opt->tstamp.ptr = 0;
+  opt->security = 0;
+  opt->compartment = 0;
+  opt->handling = 0;
+  opt->stream = 0;
+  opt->tcc = 0;
+  return (0);
+  /* advance the pointer to start at the options. */
+  buff = (unsigned char *)(iph + 1);
+
+  /*now start the processing. */
+  while (!done && len < iph->ihl*4)
+    {
+      switch (*buff)
+       {
+       case IPOPT_END:
+         done=1;
+         break;
+
+       case IPOPT_NOOP:
+         buff++;
+         len ++;
+         break;
+
+       case IPOPT_SEC:
+         buff++;
+         if (*buff != 11)
+           return (1);
+         buff++;
+         opt->security = net16(*(unsigned short *)buff);
+         buff += 2;
+         opt->compartment = net16(*(unsigned short *)buff);
+         buff += 2;
+         opt-> handling = net16(*(unsigned short *)buff);
+         buff += 2;
+         opt->tcc = ((*buff) << 16) + net16(*(unsigned short *)(buff+1));
+         buff += 3;
+         len += 11;
+         break;
+
+       case IPOPT_LSRR:
+         buff ++;
+         if ((*buff - 3)% 4 != 0) return (1);
+         len += *buff;
+         opt->loose_route.route_size = (*buff -3)/4;
+         buff ++;
+         if (*buff % 4 != 0) return (1);
+         opt->loose_route.pointer = *buff/4 - 1;
+         buff ++;
+         buff ++;
+         for (i = 0; i < opt->loose_route.route_size; i++)
+           {
+             opt->loose_route.route[i]=*(unsigned long *)buff;
+             buff += 4;
+           }
+         break;
+
+
+       case IPOPT_SSRR:
+         buff ++;
+         if ((*buff - 3)% 4 != 0) return (1);
+         len += *buff;
+         opt->strict_route.route_size = (*buff -3)/4;
+         buff ++;
+         if (*buff % 4 != 0) return (1);
+         opt->strict_route.pointer = *buff/4 - 1;
+         buff ++;
+         buff ++;
+         for (i = 0; i < opt->strict_route.route_size; i++)
+           {
+             opt->strict_route.route[i]=*(unsigned long *)buff;
+             buff += 4;
+           }
+         break;
+
+       case IPOPT_RR:
+         buff ++;
+         if ((*buff - 3)% 4 != 0) return (1);
+         len += *buff;
+         opt->record_route.route_size = (*buff -3)/4;
+         buff ++;
+         if (*buff % 4 != 0) return (1);
+         opt->record_route.pointer = *buff/4 - 1;
+         buff ++;
+         buff ++;
+         for (i = 0; i < opt->record_route.route_size; i++)
+           {
+             opt->record_route.route[i]=*(unsigned long *)buff;
+             buff += 4;
+           }
+         break;
+
+       case IPOPT_SID:
+         len += 4;
+         buff +=2;
+         opt->stream = *(unsigned short *)buff;
+         buff += 2;
+         break;
+
+       case IPOPT_TIMESTAMP:
+         buff ++;
+         len += *buff;
+         if (*buff % 4 != 0) return (1);
+         opt->tstamp.len = *buff / 4 - 1;
+         buff ++;
+         if ((*buff - 1) % 4 != 0) return (1);
+         opt->tstamp.ptr = (*buff-1)/4;
+         buff ++;
+         opt->tstamp.x.full_char = *buff;
+         buff ++;
+         for (i = 0; i < opt->tstamp.len; i++)
+           {
+             opt->tstamp.data[i] = *(unsigned long *)buff;
+             buff += 4;
+           }
+         break;
+
+       default:
+         return (1);
+       }
+    }
+  if (opt->record_route.route_size == 0)
+    {
+      if (opt->strict_route.route_size != 0)
+       {
+         memcpy (&(opt->record_route), &(opt->strict_route),
+                 sizeof (opt->record_route));
+       }
+      else if (opt->loose_route.route_size != 0)
+       {
+         memcpy (&(opt->record_route), &(opt->loose_route),
+                 sizeof (opt->record_route));
+       }
+    }
+
+  if (opt->strict_route.route_size != 0 &&
+      opt->strict_route.route_size != opt->strict_route.pointer)
+    {
+      strict_route (iph, opt);
+      return (0);
+    }
+
+  if (opt->loose_route.route_size != 0 &&
+      opt->loose_route.route_size != opt->loose_route.pointer)
+    {
+      loose_route (iph, opt);
+      return (0);
+    }
+
+  return (0);
+}
+
+
+/* This routine does all the checksum computations that don't require
+   anything special (like copying or special headers.) */
+
+unsigned short
+ip_compute_csum(unsigned char * buff, int len)
+{
+  unsigned long sum = 0;
+  if (len > 3)
+    {
+       /* do the first multiple of 4 bytes and convert to 16 bits. */
+       __asm__("\t clc\n"
+              "1:\n"
+              "\t lodsl\n"
+              "\t adcl %%eax, %%ebx\n"
+              "\t loop 1b\n"
+              "\t adcl $0, %%ebx\n"
+              "\t movl %%ebx, %%eax\n"
+              "\t shrl $16, %%eax\n"
+              "\t addw %%ax, %%bx\n"
+              "\t adcw $0, %%bx\n"
+              : "=b" (sum) , "=S" (buff)
+              : "0" (sum), "c" (len >> 2) ,"1" (buff)
+              : "ax", "cx", "si", "bx" );
+    }
+  if (len & 2)
+    {
+       __asm__("\t lodsw\n"
+              "\t addw %%ax, %%bx\n"
+              "\t adcw $0, %%bx\n"
+              : "=b" (sum), "=S" (buff)
+              : "0" (sum), "1" (buff)
+              : "bx", "ax", "si");
+    }
+  if (len & 1)
+    {
+       __asm__("\t lodsb\n"
+              "\t movb $0, %%ah\n"
+              "\t addw %%ax, %%bx\n"
+              "\t adcw $0, %%bx\n"
+              : "=b" (sum), "=S" (buff)
+              : "0" (sum), "1" (buff)
+              : "bx", "ax", "si");
+    }
+  sum =~sum;
+  return (sum&0xffff);
+}
+
+static  int
+ip_csum(struct ip_header *iph)
+{
+  if (iph->check == 0) return (0);
+  if (ip_compute_csum((unsigned char *)iph, iph->ihl*4) == 0)  return (0);
+  return (1);
+}
+
+static  void
+ip_send_check(struct ip_header *iph)
+{
+   iph->check = 0;
+   iph->check = ip_compute_csum((unsigned char *)iph, iph->ihl*4);
+}
+
+int
+ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+  struct ip_header *iph;
+  unsigned char hash;
+  unsigned char flag=0;
+  static struct options opt; /* since we don't use these yet, and they
+                               take up stack space. */
+  struct ip_protocol *ipprot;
+
+  iph=skb->h.iph;
+
+  PRINTK("<<\n");
+  print_iph(iph);
+
+  if (ip_csum (iph) || do_options (iph,&opt) || iph->version != 4)
+    {
+       PRINTK ("ip packet thrown out. \n");
+       skb->sk = NULL;
+       free_skb(skb, 0);
+       return (0);
+    }
+
+  /* for now we will only deal with packets meant for us. */
+  if (!my_ip_addr(iph->daddr))
+    {
+       PRINTK ("packet meant for someone else.\n");
+       skb->sk = NULL;
+       free_skb(skb, 0);
+       return (0);
+    }
+
+  /* deal with fragments.  or don't for now.*/
+  if ((iph->frag_off & 64) || (net16(iph->frag_off)&0x1fff))
+    {
+       printk ("packet fragmented. \n");
+       skb->sk = NULL;
+       free_skb(skb, 0);
+       return(0);
+    }
+
+  skb->h.raw += iph->ihl*4;
+
+  /* add it to the arp table if it's talking to us.  That way we
+     will be able to talk to them also. */
+
+  hash = iph->protocol & (MAX_IP_PROTOS -1);
+  for (ipprot = ip_protos[hash]; ipprot != NULL; ipprot=ipprot->next)
+    {
+       struct sk_buff *skb2;
+       PRINTK ("Using protocol = %X:\n", ipprot);
+       print_ipprot (ipprot);
+       /* pass it off to everyone who wants it. */
+       /* we should check the return values here. */
+       /* see if we need to make a copy of it.  This will
+         only be set if more than one protpocol wants it. 
+         and then not for the last one. */
+
+       if (ipprot->copy)
+        {
+           skb2 = malloc (skb->mem_len);
+           if (skb2 == NULL) continue;
+           memcpy (skb2, skb, skb->mem_len);
+           skb2->mem_addr = skb2;
+        }
+       else
+        {
+           skb2 = skb;
+        }
+       flag = 1;
+       ipprot->handler (skb2, dev, &opt, iph->daddr,
+                       net16(iph->tot_len) - iph->ihl*4,
+                       iph->saddr, 0, ipprot);
+
+    }
+  if (!flag)
+    {
+       icmp_reply (skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+       skb->sk = NULL;
+       free_skb (skb, 0);
+    }
+
+
+  return (0);
+}
+
+
+/* queues a packet to be sent, and starts the transmitter if
+   necessary.  if free = 1 then we free the block after transmit,
+   otherwise we don't. */
+/* This routine also needs to put in the total length, and compute
+   the checksum. */
+void
+ip_queue_xmit (volatile struct sock *sk, struct device *dev, 
+              struct sk_buff *skb, int free)
+{
+  struct ip_header *iph;
+  unsigned char *ptr;
+  if (sk == NULL) free = 1;
+  skb->free = free;
+  skb->dev = dev;
+  skb->when = jiffies;
+  PRINTK(">>\n");
+  ptr = (unsigned char *)(skb + 1);
+  ptr += dev->hard_header_len;
+  iph = (struct ip_header *)ptr;
+  iph->tot_len = net16(skb->len-dev->hard_header_len);
+  ip_send_check (iph);
+  print_iph(iph);
+  skb->next = NULL;
+  if (!free)
+    {
+      skb->link3 = NULL;
+      sk->packets_out++;
+      cli();
+      if (sk->send_tail == NULL)
+       {
+         sk->send_tail = skb;
+         sk->send_head = skb;
+       }
+      else
+       {
+         sk->send_tail->link3 = skb;
+         sk->send_tail = skb;
+       }
+      sti();
+      sk->time_wait.len = sk->rtt*2;
+      sk->timeout=TIME_WRITE;
+      reset_timer ((struct timer *)&sk->time_wait);
+   }
+  else
+    {
+       skb->sk = sk;
+    }
+  if (dev->up)
+    {
+       if (sk)
+        dev->queue_xmit(skb, dev, sk->priority);
+       else
+        dev->queue_xmit (skb, dev, SOPRI_NORMAL);
+    }
+  else
+    {
+       if (free) 
+        free_skb (skb, FREE_WRITE);
+    }
+}
+
+void
+ip_retransmit (volatile struct sock *sk, int all)
+{
+  struct sk_buff * skb;
+  struct proto *prot;
+  struct device *dev;
+
+  prot = sk->prot;
+  skb = sk->send_head;
+  while (skb != NULL)
+    {
+      dev = skb->dev;
+      /* rebuild_header sees if the arp is done.  If not it sends a new
+        arp, and if so it builds the header. */
+      if (!skb->arp)
+       {
+         if (dev->rebuild_header ((struct enet_header *)(skb+1),dev))
+           {
+              if (!all) break;
+              skb=skb->link3;
+              continue;
+           }
+       }
+      skb->arp = 1;
+      skb->when = jiffies;
+
+      if (dev->up)
+       if (sk)
+         dev->queue_xmit(skb, dev, sk->priority);
+       else
+         dev->queue_xmit(skb, dev, SOPRI_NORMAL );
+
+      sk->retransmits++;
+      sk->prot->retransmits ++;
+      if (!all) break;
+      /* this should cut it off before we send too
+        many packets. */
+      if (sk->retransmits > sk->cong_window) break;
+      skb=skb->link3;
+    }
+  sk->time_wait.len = sk->rtt*2;
+  sk->timeout = TIME_WRITE;
+  reset_timer ((struct timer *)&sk->time_wait);
+}
+
+void
+print_iph (struct ip_header *ip)
+{
+  PRINTK ("ip header:\n");
+  PRINTK ("  ihl = %d, version = %d, tos = %d, tot_len = %d\n",
+         ip->ihl, ip->version, ip->tos, net16(ip->tot_len));
+  PRINTK ("  id = %x, ttl = %d, prot = %d, check=%x\n",
+         ip->id, ip->ttl, ip->protocol, ip->check);
+  PRINTK (" frag_off=%d\n", ip->frag_off);
+  PRINTK ("  saddr = %X, daddr = %X\n",ip->saddr, ip->daddr);
+}
+
+#if 0
+int
+ip_handoff (volatile struct sock *sk)
+{
+   struct ip_protocol *p;
+   struct sk_buff *skb;
+   p = get_protocol (sk->protocol);
+
+   if (p == NULL)
+     {
+       /* this can never happen. */
+       printk ("sock_ioctl: protocol not found. \n");
+       /* what else can I do, I suppose I could send a sigkill. */
+       return (-EIO);
+     }
+
+   while (p->handler != sk->prot->rcv)
+     {
+       p=p->next;
+       if (p == NULL)
+         {
+            /* this can never happen. */
+            printk ("sock_ioctl: protocol not found. \n");
+            /* what else can I do, I suppose I could send a sigkill. */
+            return (-EIO);
+         }
+     }
+   p = p-> next;
+   sk->inuse = 1;
+   
+   /* now we have to remove the top sock buff.  If there are none, then
+      we return. */
+   if (sk->rqueue == NULL) return (0);
+   skb = sk->rqueue;
+   if (skb->next == skb)
+     {
+       sk->rqueue = NULL;
+     }
+   else
+     {
+       sk->rqueue = skb->next;
+       skb->next->prev = skb->prev;
+       skb->prev->next = skb->next;
+     }
+   if (p != NULL)
+     {
+       p->handler ((unsigned char *)(skb+1), skb->dev, NULL, skb->saddr,
+                   skb->len, skb->daddr, p->protocol, 0);
+     }
+   free_skb (skb, FREE_READ);
+   release_sock (sk);
+   return (0);
+}
+
+#endif
diff --git a/net/tcp/ip.h b/net/tcp/ip.h
new file mode 100644 (file)
index 0000000..f3faad5
--- /dev/null
@@ -0,0 +1,170 @@
+/* ip.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_IP_H
+#define _TCP_IP_H
+
+#include "dev.h"
+#include <linux/sock_ioctl.h>
+#include <netinet/protocols.h>
+
+struct rtable
+{
+  unsigned long net;
+  unsigned long router;
+  struct device *dev;
+  struct rtable *next;
+};
+
+struct route
+{
+  char route_size;
+  char pointer;
+  unsigned long route[MAX_ROUTE];
+};
+
+struct timestamp
+{
+  unsigned char len;
+  unsigned char ptr;
+  union
+    {
+      unsigned char flags:4, overflow:4;
+      unsigned char full_char;
+    } x;
+  unsigned long data[9];
+};
+
+struct options
+{
+  struct route record_route;
+  struct route loose_route;
+  struct route strict_route;
+  struct timestamp tstamp;
+  unsigned short security;
+  unsigned short compartment;
+  unsigned short handling;
+  unsigned short stream;
+  unsigned tcc;
+};
+
+
+
+struct ip_header
+{
+  unsigned char ihl:4, version:4;
+  unsigned char tos;
+  unsigned short tot_len;
+  unsigned short id;
+  unsigned short frag_off;
+  unsigned char ttl;
+  unsigned char protocol;
+  unsigned short check;
+  unsigned long saddr;
+  unsigned long daddr;
+  /*The options start here. */
+};
+
+
+#define IPOPT_END 0
+#define IPOPT_NOOP 1
+#define IPOPT_SEC 130
+#define IPOPT_LSRR 131
+#define IPOPT_SSRR 137
+#define IPOPT_RR 7
+#define IPOPT_SID 136
+#define IPOPT_TIMESTAMP 68
+#define IP_LOOPBACK_ADDR 0x0100007f
+static inline unsigned short
+net16(unsigned short x)
+{
+  __asm__("xchgb %%cl,%%ch": "=c" (x) : "0" (x) : "cx");
+  return (x);
+}
+
+static inline unsigned long
+net32(unsigned long x)
+{
+  __asm__("xchgb %%cl,%%ch\n"
+         "\t roll $16,%%ecx\n"
+         "\t xchgb %%cl,%%ch":"=c" (x):"0"(x):"cx");
+  return (x);
+}
+
+/* change the name of this. */
+#define MAX_IP_PROTOS 32 /* Must be a power of 2 */
+
+/* This is used to register protocols. */
+struct ip_protocol 
+{
+   int (*handler) (struct sk_buff *skb, struct device *dev,
+                  struct options *opt, unsigned long daddr,
+                  unsigned short len, unsigned long saddr,
+                  int redo, struct ip_protocol *protocol);
+   void (*err_handler) (int err, unsigned char *buff, unsigned long daddr,
+                       unsigned long saddr, struct ip_protocol *ipprot);
+   struct ip_protocol *next;
+   unsigned char protocol;
+   unsigned char copy:1;
+   void *data;
+};
+
+extern struct ip_protocol *ip_protocol_base;
+extern struct ip_protocol *ip_protos[MAX_IP_PROTOS];
+
+#define MAX_IP_ADDRES 5
+extern unsigned long ip_addr[MAX_IP_ADDRES];
+#define MY_IP_ADDR ip_addr[0];
+int my_ip_addr(unsigned long);
+
+#include "eth.h"
+
+void
+print_iph (struct ip_header *);
+
+void
+print_eth (struct enet_header *);
+
+int ip_set_dev (struct ip_config *);
+
+int ip_build_header(struct sk_buff *skb, unsigned long saddr,
+                   unsigned long daddr, struct device **dev, int type,
+                   struct options *opt, int len);
+void ip_queue_xmit (volatile struct sock *sk, struct device *dev, 
+                   struct sk_buff *skb, int free);
+void ip_retransmit(volatile struct sock *sk, int all);
+int ip_rcv(struct sk_buff *buff, struct device *dev, struct packet_type *);
+void add_ip_protocol (struct ip_protocol *);
+int delete_ip_protocol (struct ip_protocol *);
+int ip_handoff (volatile struct sock *sk);
+unsigned short ip_compute_csum (unsigned char *buff, int len);
+int ip_addr_match (unsigned long, unsigned long);
+void add_route (struct rtable *rt);
+void ip_route_check (unsigned long daddr);
+
+#endif
+
+
+
+
+
+
diff --git a/net/tcp/loopback.c b/net/tcp/loopback.c
new file mode 100644 (file)
index 0000000..d9f8b4a
--- /dev/null
@@ -0,0 +1,113 @@
+/* loopback.c contains the loopback device functions. */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/tty.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/memory.h>
+#include <errno.h>
+#include <linux/fcntl.h>
+#include <netinet/in.h>
+
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include "arp.h"
+
+#include "../kern_sock.h" /* for PRINTK */
+
+
+
+static int
+loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+  static int inuse=0;
+  struct enet_header *eth;
+  int i;
+  int done;
+  static unsigned char buff[2048];
+  unsigned char *tmp;
+
+  PRINTK ("loopback_xmit (dev = %X)\n", dev);
+  cli();
+  if (inuse)
+    {
+       sti();
+       return (1);
+    }
+  inuse = 1;
+  sti();
+  tmp = NULL;
+  done = dev_rint ((unsigned char *)(skb+1), skb->len, 0, dev);
+
+  if (skb->free)
+    free_skb (skb, FREE_WRITE);
+
+  while (done != 1)
+        {
+           if (done != -1 && (i = dev_tint (buff,dev)) != 0)
+             {
+                /* print out the buffer. */
+                PRINTK ("ethernet xmit: \n");
+                eth = (struct enet_header *)buff;
+                print_eth (eth);
+                tmp = buff;
+                done = dev_rint (buff, i, 0, dev);
+                if (done != -1) tmp = NULL;
+             }
+           else
+             {
+                done = dev_rint (tmp, 0, 0, dev);
+             }
+           
+        }
+  inuse = 0;
+  return (0);
+}
+
+void
+loopback_init(struct device *dev)
+{
+   printk ("Loopback device init\n");
+  /* initialize the rest of the device structure. */
+  dev->mtu = 2000; /* mtu */
+  dev->hard_start_xmit = loopback_xmit;
+  dev->open = NULL;
+  dev->hard_header = eth_hard_header;
+  dev->add_arp = NULL;
+  dev->hard_header_len = sizeof (struct enet_header);
+  dev->addr_len = ETHER_ADDR_LEN;
+  dev->type = ETHER_TYPE;
+  dev->queue_xmit = dev_queue_xmit;
+  dev->rebuild_header = eth_rebuild_header;
+  dev->type_trans = eth_type_trans;
+  dev->loopback = 1;
+}
diff --git a/net/tcp/pack_type.c b/net/tcp/pack_type.c
new file mode 100644 (file)
index 0000000..780fe26
--- /dev/null
@@ -0,0 +1,56 @@
+/* pack_type.c - implements raw packet sockets. */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <linux/stddef.h>
+#include "dev.h"
+#include "eth.h"
+
+extern int arp_rcv (struct sk_buff *skb, struct device *dev,
+                   struct packet_type *pt);
+
+static struct packet_type arp_packet_type=
+{
+   NET16(ETHERTYPE_ARP),
+   0, /* copy */
+   arp_rcv,
+   NULL,
+   NULL /* next */
+};
+
+extern int ip_rcv (struct sk_buff *skb, struct device *dev,
+                  struct packet_type *pt);
+
+static struct packet_type ip_packet_type=
+{
+   NET16(ETHERTYPE_IP),
+   0, /* copy */
+   ip_rcv,
+   NULL,
+   &arp_packet_type
+};
+   
+struct packet_type *ptype_base = &ip_packet_type;
+
+
+
+
+
diff --git a/net/tcp/packet.c b/net/tcp/packet.c
new file mode 100644 (file)
index 0000000..ffad524
--- /dev/null
@@ -0,0 +1,315 @@
+/* packet.c - implements raw packet sockets. */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include "../kern_sock.h" /* for PRINTK */
+
+extern struct proto raw_prot;
+
+static  unsigned long
+min(unsigned long a, unsigned long b)
+{
+   if (a < b) return (a);
+   return (b);
+}
+
+/* this should be the easiest of all, all we do is copy it into
+   a buffer. */
+int
+packet_rcv (struct sk_buff *skb, struct device *dev,  struct packet_type *pt)
+{
+   volatile struct sock *sk;
+
+   sk = pt->data;
+   skb->dev = dev;
+   skb->len += dev->hard_header_len;
+
+   /* now see if we are in use. */
+   cli();
+   if (sk->inuse)
+     {
+       sti();
+       /* drop any packets if we can't currently deal with them.
+          Assume that the other end will retransmit if it was
+          important. */
+       skb->sk = NULL;
+       free_skb (skb, FREE_READ);
+       return (0);
+
+     }
+   sk->inuse = 1;
+   sti ();
+
+   skb->sk = sk;
+
+   /* charge it too the socket. */
+   if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
+     {
+       skb->sk = NULL;
+       free_skb (skb, FREE_READ);
+       return (0);
+     }
+            
+   sk->rmem_alloc += skb->mem_len;
+
+   /* now just put it onto the queue. */
+   if (sk->rqueue == NULL)
+     {
+       sk->rqueue = skb;
+       skb->next = skb;
+       skb->prev = skb;
+     }
+   else
+     {
+       skb->next = sk->rqueue;
+       skb->prev = sk->rqueue->prev;
+       skb->prev->next = skb;
+       skb->next->prev = skb;
+     }
+   wake_up (sk->sleep);
+   release_sock (sk);
+   return (0);
+}
+
+/* this will do terrible things if len + ipheader + devheader > dev->mtu */
+static int
+packet_sendto (volatile struct sock *sk, unsigned char *from, int len,
+              int noblock,
+              unsigned flags, struct sockaddr_in *usin, int addr_len)
+{
+   struct sk_buff *skb;
+   struct device *dev;
+   struct sockaddr saddr;
+
+   /* check the flags. */
+   if (flags) return (-EINVAL);
+   if (len < 0) return (-EINVAL);
+
+   /* get and verify the address. */
+   if (usin)
+     {
+       if (addr_len < sizeof (saddr))
+         return (-EINVAL);
+       verify_area (usin, sizeof (saddr));
+       memcpy_fromfs (&saddr, usin, sizeof(saddr));
+     }
+   else
+     return (-EINVAL);
+
+   skb = sk->prot->wmalloc (sk, len+sizeof (*skb) + sk->prot->max_header, 0);
+   /* this shouldn't happen, but it could. */
+   if (skb == NULL)
+     {
+       PRINTK ("packet_sendto: write buffer full?\n");
+       print_sk (sk);
+       return (-EAGAIN);
+     }
+   skb->mem_addr = skb;
+   skb->mem_len = len + sizeof (*skb) +sk->prot->max_header;
+   skb->sk = sk;
+   skb->free = 1;
+   saddr.sa_data[13] = 0;
+   dev = get_dev (saddr.sa_data);
+   if (dev == NULL)
+     {
+       sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
+       return (-ENXIO);
+     }
+   verify_area (from, len);
+   memcpy_fromfs (skb+1, from, len);
+   skb->len = len;
+   skb->next = NULL;
+   if (dev->up)
+     dev->queue_xmit (skb, dev, sk->priority);
+   else
+     free_skb (skb, FREE_WRITE);
+   return (len);
+}
+
+static int
+packet_write (volatile struct sock *sk, unsigned char *buff, 
+             int len, int noblock,  unsigned flags)
+{
+   return (packet_sendto (sk, buff, len, noblock, flags, NULL, 0));
+}
+
+static void
+packet_close (volatile struct sock *sk, int timeout)
+{
+   sk->inuse = 1;
+   sk->state = TCP_CLOSE;
+   dev_remove_pack ((struct packet_type *)sk->pair);
+   free_s ((void *)sk->pair, sizeof (struct packet_type));
+   release_sock (sk);
+}
+
+static int
+packet_init (volatile struct sock *sk)
+{
+   struct packet_type *p;
+   p = malloc (sizeof (*p));
+   if (p == NULL) return (-ENOMEM);
+
+   p->func = packet_rcv;
+   p->type = sk->num;
+   p->data = (void *)sk;
+   dev_add_pack (p);
+   
+   /* we need to remember this somewhere. */
+   sk->pair = (volatile struct sock *)p;
+
+   return (0);
+}
+
+
+int
+packet_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
+             int noblock,
+             unsigned flags, struct sockaddr_in *sin, int *addr_len)
+{
+       /* this should be easy, if there is something there we
+          return it, otherwise we block. */
+       int copied=0;
+       struct sk_buff *skb;
+       struct sockaddr *saddr;
+       saddr = (struct sockaddr *)sin;
+
+       if (len == 0) return (0);
+       if (len < 0) return (-EINVAL);
+       if (addr_len)
+         {
+                 verify_area (addr_len, sizeof(*addr_len));
+                 put_fs_long (sizeof (*saddr), addr_len);
+         }
+
+       sk->inuse = 1;
+       while (sk->rqueue == NULL)
+         {
+            if (noblock)
+              {
+                 release_sock (sk);
+                 return (-EAGAIN);
+              }
+            release_sock (sk);
+            cli();
+            if (sk->rqueue == NULL)
+              {
+                 interruptible_sleep_on (sk->sleep);
+                 if (current->signal & ~current->blocked)
+                   {
+                      return (-ERESTARTSYS);
+                   }
+              }
+            sti();
+         }
+       skb = sk->rqueue;
+
+       if (!(flags & MSG_PEEK))
+         {
+                 if (skb->next == skb )
+                   {
+                           sk->rqueue = NULL;
+                   }
+                 else
+                   {
+                           sk->rqueue = sk->rqueue ->next;
+                           skb->prev->next = skb->next;
+                           skb->next->prev = skb->prev;
+                   }
+         }
+       copied = min (len, skb->len);
+       verify_area (to, copied);
+       memcpy_tofs (to, skb+1,  copied);
+       /* copy the address. */
+       if (saddr)
+         {
+                 struct sockaddr addr;
+                 addr.sa_family = skb->dev->type;
+                 memcpy (addr.sa_data,skb->dev->name, 14);
+                 verify_area (saddr, sizeof (*saddr));
+                 memcpy_tofs(saddr, &addr, sizeof (*saddr));
+         }
+
+       if (!(flags & MSG_PEEK))
+         {
+            free_skb (skb, FREE_READ);
+         }
+
+       release_sock (sk);
+       return (copied);
+
+}
+
+int
+packet_read (volatile struct sock *sk, unsigned char *buff,
+            int len, int noblock, unsigned flags)
+{
+       return (packet_recvfrom (sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+
+int udp_connect (volatile struct sock *sk, struct sockaddr_in *usin,
+                int addr_len);
+
+int udp_select (volatile struct sock *sk, int sel_type, select_table *wait);
+
+
+struct proto packet_prot =
+{
+  sock_wmalloc,
+  sock_rmalloc,
+  sock_wfree,
+  sock_rfree,
+  sock_rspace,
+  sock_wspace,
+  packet_close,
+  packet_read,
+  packet_write,
+  packet_sendto,
+  packet_recvfrom,
+  ip_build_header,
+  udp_connect,
+  NULL,
+  ip_queue_xmit,
+  ip_retransmit,
+  NULL,
+  NULL,
+  NULL, 
+  udp_select,
+  NULL,
+  packet_init,
+  128,
+  0,
+  {NULL,}
+};
diff --git a/net/tcp/protocols.c b/net/tcp/protocols.c
new file mode 100644 (file)
index 0000000..d0942a7
--- /dev/null
@@ -0,0 +1,69 @@
+/* protocols.c */
+/* these headers are overkill, but until I clean up the socket header
+   files, this is the best way. */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include "icmp.h"
+
+int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+           unsigned long daddr, unsigned short len,
+           unsigned long saddr, int redo, struct ip_protocol *protocol);
+
+void udp_err  (int err, unsigned char *header, unsigned long daddr,
+              unsigned long saddr, struct ip_protocol *protocol);
+
+
+int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+           unsigned long daddr, unsigned short len,
+           unsigned long saddr, int redo, struct ip_protocol *protocol);
+
+void tcp_err  (int err, unsigned char *header, unsigned long daddr,
+              unsigned long saddr, struct ip_protocol *protocol);
+
+int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+            unsigned long daddr, unsigned short len,
+            unsigned long saddr, int redo, struct ip_protocol *protocol);
+
+
+static struct ip_protocol tcp_protocol =
+{
+   tcp_rcv,
+   tcp_err,
+   NULL,
+   IP_TCP,
+   0, /* copy */
+   NULL
+};
+
+static struct ip_protocol udp_protocol =
+{
+   udp_rcv,
+   udp_err,
+   &tcp_protocol,
+   IP_UDP,
+   0, /* copy */
+   NULL
+};
+
+static struct ip_protocol icmp_protocol =
+{
+   icmp_rcv,
+   NULL,
+   &udp_protocol,
+   IP_ICMP,
+   0, /* copy */
+   NULL
+};
+
+struct ip_protocol *ip_protocol_base = &icmp_protocol;
diff --git a/net/tcp/raw.c b/net/tcp/raw.c
new file mode 100644 (file)
index 0000000..8cd116e
--- /dev/null
@@ -0,0 +1,367 @@
+/* raw.c - implements raw ip sockets. */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include "../kern_sock.h" /* for PRINTK */
+
+extern struct proto raw_prot;
+
+static  unsigned long
+min(unsigned long a, unsigned long b)
+{
+   if (a < b) return (a);
+   return (b);
+}
+
+/* this should be the easiest of all, all we do is copy it into
+   a buffer. */
+int
+raw_rcv (struct sk_buff *skb, struct device *dev, struct options *opt,
+        unsigned long daddr, unsigned short len, unsigned long saddr,
+        int redo, struct ip_protocol *protocol)
+{
+
+   volatile struct sock *sk;
+
+   sk = protocol->data;
+   
+   /* now we need to copy this into memory. */
+   if (!redo )
+     {
+       skb->dev = dev;
+       skb->saddr = daddr;
+       skb->daddr = saddr;
+       /* now see if we are in use. */
+       cli();
+       if (sk->inuse)
+         {
+            PRINTK ("raw_rcv adding to backlog. \n");
+            if (sk->back_log == NULL)
+              {
+                 sk->back_log = skb;
+                 skb->next = skb;
+                 skb->prev = skb;
+              }
+            else
+              {
+                 skb->next = sk->back_log;
+                 skb->prev = sk->back_log->prev;
+                 skb->prev->next = skb;
+                 skb->next->prev = skb;
+              }
+            sti();
+            return (0);
+         }
+       sk->inuse = 1;
+       sti();
+     }
+
+   skb->sk = sk;
+   skb->len = len;
+
+   /* charge it too the socket. */
+   if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
+     {
+       skb->sk = NULL;
+       free_skb (skb, FREE_READ);
+       return (0);
+     }
+            
+   sk->rmem_alloc += skb->mem_len;
+
+   /* now just put it onto the queue. */
+   if (sk->rqueue == NULL)
+     {
+       sk->rqueue = skb;
+       skb->next = skb;
+       skb->prev = skb;
+     }
+   else
+     {
+       skb->next = sk->rqueue;
+       skb->prev = sk->rqueue->prev;
+       skb->prev->next = skb;
+       skb->next->prev = skb;
+     }
+   skb->len = len;
+   wake_up (sk->sleep);
+   release_sock (sk);
+   return (0);
+}
+
+static  int
+raw_loopback (volatile struct sock *sk, int prot, char *from, int len, 
+             unsigned long daddr)
+{
+   /* just pretend it just came in. */
+   struct sk_buff *skb;
+   int err;
+   skb = malloc (len+sizeof (*skb));
+   if (skb == NULL) return (-ENOMEM);
+
+   skb->mem_addr = skb;
+   skb->mem_len = len + sizeof (*skb);
+   skb->h.raw = (unsigned char *)(skb+1);
+   verify_area (from, len);
+   memcpy_fromfs (skb+1, from, len);
+   err = raw_rcv (skb, NULL, NULL, daddr, len, sk->saddr, prot, 0);
+   return (err);
+}
+
+/* this will do terrible things if len + ipheader + devheader > dev->mtu */
+static int
+raw_sendto (volatile struct sock *sk, unsigned char *from, int len,
+           int noblock,
+           unsigned flags, struct sockaddr_in *usin, int addr_len)
+{
+   struct sk_buff *skb;
+   struct device *dev=NULL;
+   struct sockaddr_in sin;
+   int tmp;
+
+   /* check the flags. */
+   if (flags) return (-EINVAL);
+   if (len < 0) return (-EINVAL);
+
+   /* get and verify the address. */
+   if (usin)
+     {
+       if (addr_len < sizeof (sin))
+         return (-EINVAL);
+       verify_area (usin, sizeof (sin));
+       memcpy_fromfs (&sin, usin, sizeof(sin));
+       if (sin.sin_family &&
+           sin.sin_family != AF_INET)
+         return (-EINVAL);
+     }
+   else
+     {
+       if (sk->state != TCP_ESTABLISHED)
+         return (-EINVAL);
+       sin.sin_family = AF_INET;
+       sin.sin_port = sk->protocol;
+       sin.sin_addr.s_addr = sk->daddr;
+     }
+   if (sin.sin_port == 0) sin.sin_port = sk->protocol;
+
+   if ((sin.sin_addr.s_addr & 0xff000000) == 0)
+     {
+       int err;
+       err = raw_loopback (sk, sin.sin_port, from,  len,
+                           sin.sin_addr.s_addr);
+       if (err < 0) return (err);
+     }
+
+   sk->inuse = 1;
+   skb = sk->prot->wmalloc (sk, len+sizeof (*skb) + sk->prot->max_header, 0);
+   /* this shouldn't happen, but it could. */
+   if (skb == NULL)
+     {
+       PRINTK ("raw_sendto: write buffer full?\n");
+       print_sk (sk);
+       release_sock (sk);
+       return (-EAGAIN);
+     }
+   skb->mem_addr = skb;
+   skb->mem_len = len + sizeof (*skb) +sk->prot->max_header;
+   skb->sk = sk;
+   skb->free = 1; /* these two should be unecessary. */
+   skb->arp = 0;
+   tmp = sk->prot->build_header (skb, sk->saddr, 
+                                sin.sin_addr.s_addr, &dev,
+                                sk->protocol, sk->opt, skb->mem_len);
+   if (tmp < 0)
+     {
+       sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
+       release_sock (sk);
+       return (tmp);
+     }
+   verify_area (from, len);
+   memcpy_fromfs (skb+1, from, len);
+   skb->len = tmp + len;
+   sk->prot->queue_xmit (sk, dev, skb, 1);
+   return (len);
+}
+
+static int
+raw_write (volatile struct sock *sk, unsigned char *buff, int len, int noblock,
+          unsigned flags)
+{
+   return (raw_sendto (sk, buff, len, noblock, flags, NULL, 0));
+}
+
+static void
+raw_close (volatile struct sock *sk, int timeout)
+{
+   sk->inuse = 1;
+   sk->state = TCP_CLOSE;
+   delete_ip_protocol ((struct ip_protocol *)sk->pair);
+   free_s ((void *)sk->pair, sizeof (struct ip_protocol));
+   release_sock (sk);
+}
+
+static int
+raw_init (volatile struct sock *sk)
+{
+   struct ip_protocol *p;
+   p = malloc (sizeof (*p));
+   if (p == NULL) return (-ENOMEM);
+
+   p->handler = raw_rcv;
+   p->protocol = sk->protocol;
+   p->data = (void *)sk;
+   add_ip_protocol (p);
+   
+   /* we need to remember this somewhere. */
+   sk->pair = (volatile struct sock *)p;
+
+   return (0);
+}
+
+
+int
+raw_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
+             int noblock,
+             unsigned flags, struct sockaddr_in *sin, int *addr_len)
+{
+       /* this should be easy, if there is something there we
+          return it, otherwise we block. */
+       int copied=0;
+       struct sk_buff *skb;
+       if (len == 0) return (0);
+       if (len < 0) return (-EINVAL);
+       if (addr_len)
+         {
+                 verify_area (addr_len, sizeof(*addr_len));
+                 put_fs_long (sizeof (*sin), addr_len);
+         }
+       sk->inuse = 1;
+       while (sk->rqueue == NULL)
+         {
+            if (noblock)
+              {
+                 release_sock (sk);
+                 return (-EAGAIN);
+              }
+            release_sock (sk);
+            cli();
+            if (sk->rqueue == NULL)
+              {
+                 interruptible_sleep_on (sk->sleep);
+                 if (current->signal & ~current->blocked)
+                   {
+                      return (-ERESTARTSYS);
+                   }
+              }
+            sti();
+         }
+       skb = sk->rqueue;
+
+       if (!(flags & MSG_PEEK))
+         {
+                 if (skb->next == skb )
+                   {
+                           sk->rqueue = NULL;
+                   }
+                 else
+                   {
+                           sk->rqueue = sk->rqueue ->next;
+                           skb->prev->next = skb->next;
+                           skb->next->prev = skb->prev;
+                   }
+         }
+       copied = min (len, skb->len);
+       verify_area (to, copied);
+       memcpy_tofs (to, skb->h.raw,  copied);
+       /* copy the address. */
+       if (sin)
+         {
+                 struct sockaddr_in addr;
+                 addr.sin_family = AF_INET;
+                 addr.sin_addr.s_addr = skb->daddr;
+                 verify_area (sin, sizeof (*sin));
+                 memcpy_tofs(sin, &addr, sizeof (*sin));
+         }
+
+       if (!(flags & MSG_PEEK))
+         {
+            free_skb (skb, FREE_READ);
+         }
+       release_sock (sk);
+       return (copied);
+
+}
+
+int
+raw_read (volatile struct sock *sk, unsigned char *buff, int len, int noblock,
+         unsigned flags)
+{
+       return (raw_recvfrom (sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+
+int udp_connect (volatile struct sock *sk, struct sockaddr_in *usin,
+                int addr_len);
+
+int udp_select (volatile struct sock *sk, int sel_type, select_table *wait);
+
+
+struct proto raw_prot =
+{
+  sock_wmalloc,
+  sock_rmalloc,
+  sock_wfree,
+  sock_rfree,
+  sock_rspace,
+  sock_wspace,
+  raw_close,
+  raw_read,
+  raw_write,
+  raw_sendto,
+  raw_recvfrom,
+  ip_build_header,
+  udp_connect,
+  NULL,
+  ip_queue_xmit,
+  ip_retransmit,
+  NULL,
+  NULL,
+  raw_rcv,
+  udp_select,
+  NULL,
+  raw_init,
+  128,
+  0,
+  {NULL,}
+};
diff --git a/net/tcp/sock.c b/net/tcp/sock.c
new file mode 100644 (file)
index 0000000..09ee78f
--- /dev/null
@@ -0,0 +1,1661 @@
+/* sock.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/sock_ioctl.h>
+#include <asm/memory.h>
+#include "../kern_sock.h"
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "udp.h"
+#include "sock.h"
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+
+#ifdef MEM_DEBUG
+#define MPRINTK printk
+#else
+#define MPRINTK dummy_routine
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+#define swap(a,b) {unsigned long c; c=a; a=b; b=c;}
+
+extern struct proto tcp_prot;
+extern struct proto udp_prot;
+extern struct proto raw_prot;
+extern struct proto packet_prot;
+
+static int ip_proto_init(void);
+static int ip_proto_create(struct socket *sock, int protocol);
+static int ip_proto_dup(struct socket *newsock, struct socket *oldsock);
+static int ip_proto_release(struct socket *sock, struct socket *peer);
+static int ip_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+                          int sockaddr_len);
+static int ip_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+                             int sockaddr_len, int flags);
+static int ip_proto_socketpair(struct socket *sock1, struct socket *sock2);
+static int ip_proto_accept(struct socket *sock, struct socket *newsock, int flags);
+static int ip_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+                             int *usockaddr_len, int peer);
+static int ip_proto_read(struct socket *sock, char *ubuf, int size,
+                          int nonblock);
+static int ip_proto_write(struct socket *sock, char *ubuf, int size,
+                           int nonblock);
+static int ip_proto_select(struct socket *sock, int which, select_table *wait);
+static int ip_proto_ioctl(struct socket *sock, unsigned int cmd,
+                           unsigned long arg);
+static int ip_proto_listen(struct socket *sock, int backlog);
+
+static int ip_proto_send (struct socket *sock, void *buff, int len,
+                         int nonblock, unsigned flags);
+static int ip_proto_recv (struct socket *sock, void *buff, int len,
+                         int nonblock, unsigned flags);
+static int ip_proto_sendto (struct socket *sock, void *buff, int len,
+                           int nonblock, unsigned flags,
+                           struct sockaddr *addr, int addr_len);
+static int ip_proto_recvfrom (struct socket *sock, void *buff, int len,
+                             int nonblock, unsigned flags,
+                             struct sockaddr *addr, int *addr_len);
+
+static int ip_proto_shutdown (struct socket *sock, int how);
+
+
+static int ip_proto_setsockopt (struct socket *sock, int level, int optname,
+                               char *optval, int optlen);
+static int ip_proto_getsockopt (struct socket *sock, int level, int optname,
+                               char *optval, int *optlen);
+static int ip_proto_fcntl (struct socket *sock, unsigned int cmd,
+                          unsigned long arg);
+
+
+struct proto_ops inet_proto_ops = 
+{
+  ip_proto_init,
+  ip_proto_create,
+  ip_proto_dup,
+  ip_proto_release,
+  ip_proto_bind,
+  ip_proto_connect,
+  ip_proto_socketpair,
+  ip_proto_accept,
+  ip_proto_getname, 
+  ip_proto_read,
+  ip_proto_write,
+  ip_proto_select,
+  ip_proto_ioctl,
+  ip_proto_listen,
+  ip_proto_send,
+  ip_proto_recv,
+  ip_proto_sendto,
+  ip_proto_recvfrom,
+  ip_proto_shutdown,
+  ip_proto_setsockopt,
+  ip_proto_getsockopt,
+  ip_proto_fcntl
+};
+
+void
+print_sk (volatile struct sock *sk)
+{
+  PRINTK ("  wmem_alloc = %d\n", sk->wmem_alloc);
+  PRINTK ("  rmem_alloc = %d\n", sk->rmem_alloc);
+  PRINTK ("  send_head = %X\n", sk->send_head);
+  PRINTK ("  state = %d\n",sk->state);
+  PRINTK ("  wback = %X, rqueue = %X\n", sk->wback, sk->rqueue);
+  PRINTK ("  wfront = %X\n", sk->wfront);
+  PRINTK ("  daddr = %X, saddr = %X\n", sk->daddr,sk->saddr);
+  PRINTK ("  num = %d", sk->num);
+  PRINTK (" next = %X\n", sk->next);
+  PRINTK ("  send_seq = %d, acked_seq = %d, copied_seq = %d\n",
+         sk->send_seq, sk->acked_seq, sk->copied_seq);
+  PRINTK ("  rcv_ack_seq = %d, window_seq = %d, fin_seq = %d\n",
+         sk->rcv_ack_seq, sk->window_seq, sk->fin_seq);
+  PRINTK ("  prot = %X\n", sk->prot);
+  PRINTK ("  pair = %X, back_log = %X\n", sk->pair,sk->back_log);
+  PRINTK ("  inuse = %d , blog = %d\n", sk->inuse, sk->blog);
+  PRINTK ("  dead = %d delay_acks=%d\n", sk->dead, sk->delay_acks);
+  PRINTK ("  retransmits = %d, timeout = %d\n", sk->retransmits, sk->timeout);
+  PRINTK ("  cong_window = %d, packets_out = %d\n", sk->cong_window,
+         sk->packets_out);
+}
+
+void
+print_skb(struct sk_buff *skb)
+{
+  PRINTK ("  prev = %X, next = %X\n", skb->prev, skb->next);
+  PRINTK ("  sk = %X link3 = %X\n", skb->sk, skb->link3);
+  PRINTK ("  mem_addr = %X, mem_len = %d\n", skb->mem_addr, skb->mem_len);
+  PRINTK ("  used = %d free = %d\n", skb->used,skb->free);
+}
+
+/* just used to reference some pointers to keep gcc from over optimizing
+   my code so that it doesn't work. */
+void dummy_routine(void *dummy, ...)
+{
+   return;
+}
+
+void
+lock_skb (struct sk_buff *skb)
+{
+   if (skb->lock)
+     {
+       printk ("*** bug more than one lock on sk_buff. \n");
+     }
+   skb->lock = 1;
+}
+
+
+void
+free_skb (struct sk_buff *skb, int rw)
+{
+   if (skb->lock)
+     {
+       skb->free = 1;
+       return;
+     }
+   if (skb->sk)
+     {
+       if (rw)
+         {
+            skb->sk->prot->rfree (skb->sk, skb->mem_addr, skb->mem_len);
+         }
+       else
+         {
+            skb->sk->prot->wfree (skb->sk, skb->mem_addr, skb->mem_len);
+         }
+     }
+   else
+     {
+       free_s (skb->mem_addr, skb->mem_len);
+     }
+}
+
+void
+unlock_skb (struct sk_buff *skb, int rw)
+{
+   if (skb->lock != 1)
+     {
+       printk ("*** bug unlocking non-locked sk_buff. \n");
+     }
+   skb->lock = 0;
+   if (skb->free)
+     free_skb (skb, rw);
+}
+
+static  int
+sk_inuse( struct proto *prot, int num)
+{
+  volatile struct sock *sk;
+  for (sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
+       sk != NULL; sk=sk->next)
+    {
+      if (sk->dummy_th.source == num) return (1);
+    }
+  return (0);
+}
+
+unsigned short
+get_new_socknum(struct proto *prot, unsigned short base)
+{
+  static int start=0;
+  /* used to cycle through the port numbers so the chances of
+     a confused connection drop. */
+
+  int i,j;
+  int best=0;
+  int size=32767; /* a big num. */
+  volatile struct sock *sk;
+  start++;
+  if (base == 0) base = PROT_SOCK+1+(start % 1024);
+  if (base <= PROT_SOCK)
+    {
+      base += PROT_SOCK+(start % 1024);
+    }
+
+  /* now look through the entire array and try to find an empty
+     ptr. */
+  for (i = 0; i < SOCK_ARRAY_SIZE; i++)
+    {
+      j = 0;
+      sk = prot->sock_array[(i+base+1) & (SOCK_ARRAY_SIZE -1)];
+      while (sk != NULL)
+       {
+         sk = sk->next;
+         j++;
+       }
+      if (j == 0) return (i+base+1);
+      if (j < size) 
+       {
+         best = i;
+         size = j;
+       }
+    }
+  /* now make sure the one we want is not in use. */
+  while (sk_inuse (prot, base +best+1))
+    {
+      best += SOCK_ARRAY_SIZE;
+    }
+  return (best+base+1);
+  
+}
+
+void
+put_sock(unsigned short num, volatile struct sock *sk)
+{
+   volatile struct sock *sk1;
+   volatile struct sock *sk2;
+   int mask;
+
+   PRINTK ("put_sock (num = %d, sk = %X\n", num, sk);
+   sk->num = num;
+   sk->next = NULL;
+   num = num & (SOCK_ARRAY_SIZE -1);
+
+   /* we can't have an interupt renter here. */
+   cli();
+   if (sk->prot->sock_array[num] == NULL)
+     {
+       sk->prot->sock_array[num] = sk;
+       sti();
+       return;
+     }
+   sti();
+   for (mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask)
+     {
+       if (mask & sk->saddr)
+         {
+            mask = mask << 8;
+            break;
+         }
+     }
+
+   PRINTK ("mask = %X\n", mask);
+
+   cli();
+   sk1 = sk->prot->sock_array[num];
+   for (sk2 = sk1; sk2 != NULL; sk2=sk2->next)
+     {
+       if (!(sk2->saddr & mask))
+         {
+            if (sk2 == sk1)
+              {
+                 sk->next = sk->prot->sock_array[num];
+                 sk->prot->sock_array[num] = sk;
+                 sti();
+                 return;
+              }
+            sk->next = sk2;
+            sk1->next= sk;
+            sti();
+            return;
+         }
+       sk1 = sk2;
+     }
+   /* goes at the end. */
+   sk->next = NULL;
+   sk1->next = sk;
+   sti();
+}
+
+
+static void
+remove_sock(volatile struct sock *sk1)
+{
+  volatile struct sock *sk2;
+  PRINTK ("remove_sock(sk1=%X)\n",sk1);
+
+  /* we can't have this changing out from under us. */
+  cli();
+  sk2=sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)];
+  if (sk2 == sk1)
+    {
+       sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)] = sk1->next;
+       sti();
+       return;
+    }
+  while (sk2->next != sk1)
+    {
+      if (sk2 == NULL)
+       {
+         sti();
+         PRINTK ("remove_sock: sock  not found.\n");
+         return;
+       }
+      sk2=sk2->next;
+    }
+  sk2->next = sk1->next;
+  sti();
+}
+
+void
+destroy_sock(volatile struct sock *sk)
+{
+
+  struct sk_buff *skb;
+  PRINTK ("destroying socket %X\n",sk);
+  /* just to be safe. */
+  sk->inuse = 1;
+
+  remove_sock (sk);
+  /* now we can no longer get new packets. */
+
+  delete_timer((struct timer *)&sk->time_wait);
+
+  /* cleanup up the write buffer. */
+  for (skb = sk->wfront; skb != NULL; )
+    {
+      struct sk_buff *skb2;
+      skb2=skb->next;
+      free_skb(skb, FREE_WRITE);
+      skb=skb2;
+    }
+
+  sk->wfront = NULL;
+
+  if (sk->rqueue != NULL)
+    {
+       skb = sk->rqueue;
+       do {
+         struct sk_buff *skb2;
+         skb2=skb->next;
+         /* this will take care of closing sockets that were
+            listening and didn't accept everything. */
+
+         if (skb->sk != NULL && skb->sk != sk)
+           {
+              skb->sk->dead = 1;
+              skb->sk->prot->close (skb->sk, 0);
+           }
+         free_skb(skb, FREE_READ);
+         skb=skb2;
+       } while (skb != sk->rqueue);
+    }
+
+  sk->rqueue = NULL;
+
+  /* now we need to clean up the send head. */
+  for (skb = sk->send_head; skb != NULL; )
+    {
+      struct sk_buff *skb2;
+      /* we need to remove skb from the transmit queue. */
+      cli();
+      /* see if it's in a transmit queue. */
+      if (skb->next != NULL)
+       {
+          if (skb->next != skb)
+            {
+               skb->next->prev = skb->prev;
+               skb->prev->next = skb->next;
+            }
+          else
+            {
+               int i;
+               for (i = 0; i < DEV_NUMBUFFS; i++)
+                 {
+                    if (skb->dev && skb->dev->buffs[i] == skb)
+                      {
+                         skb->dev->buffs[i]= NULL;
+                         break;
+                      }
+                 }
+            }
+       }
+      sti();
+      skb2=skb->link3;
+      free_skb(skb, FREE_WRITE);
+      skb=skb2;
+    }
+
+  sk->send_head = NULL;
+
+  /* and now the backlog. */
+
+  if (sk->back_log != NULL)
+    {
+       /* this should never happen. */
+       printk ("cleaning back_log. \n");
+       cli();
+       skb = sk->back_log;
+       do {
+         struct sk_buff *skb2;
+         skb2=skb->next;
+         free_skb(skb, FREE_READ);
+         skb=skb2;
+       } while (skb != sk->back_log);
+       sti();
+    }
+
+  sk->back_log = NULL;
+
+  /* now if everything is gone we can free the socket structure, 
+     otherwise we need to keep it around until everything is gone. */
+  if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0)
+    {
+       free_s ((void *)sk,sizeof (*sk));
+    }
+  else
+    {
+       /* this should never happen. */
+       /* actually it can if an ack has just been sent. */
+       PRINTK ("possible memory leak in socket = %X\n", sk);
+       print_sk (sk);
+       sk->destroy = 1;
+       sk->ack_backlog = 0;
+       sk->inuse = 0;
+       sk->time_wait.len = SOCK_DESTROY_TIME;
+       sk->timeout = TIME_DESTROY;
+       reset_timer ((struct timer *)&sk->time_wait);
+    }
+  
+}
+
+
+static int
+ip_proto_fcntl (struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+   volatile struct sock *sk;
+   sk=sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+   switch (cmd)
+     {
+       case F_SETOWN:
+       sk->proc = arg;
+       return (0);
+
+       case F_GETOWN:
+       return (sk->proc);
+
+       default:
+       return (-EINVAL);
+     }
+}
+
+static int
+ip_proto_setsockopt(struct socket *sock, int level, int optname,
+                   char *optval, int optlen)
+{
+    volatile struct sock *sk;
+    int val;
+    /* This should really pass things on to the other levels. */
+    if (level != SOL_SOCKET) return (-EOPNOTSUPP);
+    sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+    verify_area (optval, sizeof (int));
+    val = get_fs_long ((unsigned long *)optval);
+    switch (optname)
+      {
+       case SO_TYPE:
+       case SO_ERROR:
+       default:
+         return (-ENOPROTOOPT);
+
+       case SO_DEBUG: /* not implemented. */
+       case SO_DONTROUTE:
+       case SO_BROADCAST:
+       case SO_SNDBUF:
+       case SO_RCVBUF:
+         return (0);
+
+       case SO_REUSEADDR:
+         if (val)
+           sk->reuse = 1;
+         else 
+           sk->reuse = 1;
+         return (0);
+
+       case SO_KEEPALIVE:
+         if (val)
+           sk->keepopen = 1;
+         else
+           sk->keepopen = 0;
+         return (0);
+
+        case SO_OOBINLINE:
+         if (val)
+           sk->urginline = 1;
+         else
+           sk->urginline = 0;
+         return (0);
+
+        case SO_NO_CHECK:
+         if (val)
+           sk->no_check = 1;
+         else
+           sk->no_check = 0;
+         return (0);
+
+        case SO_PRIORITY:
+         if (val >= 0 && val < DEV_NUMBUFFS)
+           {
+              sk->priority = val;
+           }
+         else
+           {
+              return (-EINVAL);
+           }
+         return (0);
+
+      }
+}
+
+static int
+ip_proto_getsockopt(struct socket *sock, int level, int optname,
+                   char *optval, int *optlen)
+{
+    volatile struct sock *sk;
+    int val;
+    /* This should really pass things on to the other levels. */
+    if (level != SOL_SOCKET) return (-EOPNOTSUPP);
+    sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+    switch (optname)
+      {
+       default:
+         return (-ENOPROTOOPT);
+
+       case SO_DEBUG: /* not implemented. */
+       case SO_DONTROUTE:
+       case SO_BROADCAST:
+       case SO_SNDBUF:
+       case SO_RCVBUF:
+         val = 0;
+         break;
+
+       case SO_REUSEADDR:
+         val = sk->reuse;
+         break;
+
+       case SO_KEEPALIVE:
+         val = sk->keepopen;
+         break;
+
+       case SO_TYPE:
+         if (sk->prot == &tcp_prot)
+           val = SOCK_STREAM;
+         else
+           val = SOCK_DGRAM;
+         break;
+
+       case SO_ERROR:
+         val = sk->err;
+         sk->err = 0;
+         break;
+
+        case SO_OOBINLINE:
+         val = sk->urginline;
+         break;
+
+        case SO_NO_CHECK:
+         val = sk->no_check;
+         break;
+
+        case SO_PRIORITY:
+         val = sk->priority;
+         break;
+      }
+    verify_area (optlen, sizeof (int));
+    put_fs_long (sizeof(int),(unsigned long *) optlen);
+
+    verify_area(optval, sizeof (int));
+    put_fs_long (val, (unsigned long *)optval);
+    return (0);
+}
+
+static int
+ip_proto_listen(struct socket *sock, int backlog)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  sk->state = TCP_LISTEN;
+  return (0);
+}
+
+/* Hardware should be inited here. */
+static int ip_proto_init(void)
+{
+  int i;
+  struct device *dev;
+  struct ip_protocol *p;
+  seq_offset = CURRENT_TIME*250;
+  /* add all the protocols. */
+  for (i = 0; i < SOCK_ARRAY_SIZE; i++)
+    {
+       tcp_prot.sock_array[i] = NULL;
+       udp_prot.sock_array[i] = NULL;
+       raw_prot.sock_array[i] = NULL;
+    }
+
+  for (p = ip_protocol_base; p != NULL;)
+    {
+       struct ip_protocol *tmp;
+       /* add all the protocols. */
+       tmp = p->next;
+       add_ip_protocol (p);
+       p = tmp;
+    }
+
+  /* add the devices */
+  for (dev = dev_base; dev != NULL; dev=dev->next)
+    {
+       if (dev->init)
+        dev->init(dev);
+    }
+  timer_table[NET_TIMER].fn = net_timer;
+  return (0);
+}
+
+static int
+ip_proto_create (struct socket *sock, int protocol)
+{
+  volatile struct sock *sk;
+  struct proto *prot;
+  int err;
+
+  sk = malloc (sizeof (*sk));
+  if (sk == NULL)
+    return (-ENOMEM);
+  sk->num = 0;
+
+
+  switch (sock->type)
+    {
+    case SOCK_STREAM:
+    case SOCK_SEQPACKET:
+       if (protocol && protocol != IP_TCP)
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPROTONOSUPPORT);
+        }
+       sk->no_check = TCP_NO_CHECK;
+       prot = &tcp_prot;
+       break;
+
+    case SOCK_DGRAM:
+       if (protocol && protocol != IP_UDP)
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPROTONOSUPPORT);
+        }
+       sk->no_check = UDP_NO_CHECK;
+       prot=&udp_prot;
+       break;
+      
+     case SOCK_RAW:
+       if (!suser())
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPERM);
+        }
+
+       if (!protocol)
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPROTONOSUPPORT);
+        }
+       prot = &raw_prot;
+       sk->reuse = 1;
+       sk->no_check = 0; /* doesn't matter no checksum is preformed
+                           anyway. */
+       sk->num = protocol;
+       break;
+
+    case SOCK_PACKET:
+       if (!suser())
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPERM);
+        }
+
+       if (!protocol)
+        {
+           free_s ((void *)sk, sizeof (*sk));
+           return (-EPROTONOSUPPORT);
+        }
+       prot = &packet_prot;
+       sk->reuse = 1;
+       sk->no_check = 0; /* doesn't matter no checksum is preformed
+                           anyway. */
+       sk->num = protocol;
+       break;
+
+      
+    default:
+       free_s ((void *)sk, sizeof (*sk));
+       return (-ESOCKTNOSUPPORT);
+
+    }
+  sk->protocol = protocol;
+  sk->wmem_alloc = 0;
+  sk->rmem_alloc = 0;
+  sk->pair = NULL;
+  sk->opt = NULL;
+  sk->send_seq = 0;
+  sk->acked_seq = 0;
+  sk->copied_seq = 0;
+  sk->fin_seq = 0;
+  sk->proc = 0;
+  sk->rtt = TCP_WRITE_TIME;
+  sk->packets_out = 0;
+  sk->cong_window = 1; /* start with only sending one packet at a time. */
+  sk->exp_growth = 1;  /* if set cong_window grow exponentially every time
+                         we get an ack. */
+  sk->urginline = 0;
+  sk->intr = 0;
+  sk->linger = 0;
+  sk->destroy = 0;
+  sk->reuse = 0;
+  sk->priority = 1;
+  sk->shutdown = 0;
+  sk->urg = 0;
+  sk->keepopen = 0;
+  sk->done = 0;
+  sk->ack_backlog = 0;
+  sk->window = 0;
+  sk->bytes_rcv = 0;
+  sk->state = TCP_CLOSE;
+  sk->dead = 0;
+  sk->ack_timed = 0;
+
+  /* this is how many unacked bytes we will accept for
+     this socket.  */
+
+  sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
+
+  /* how many packets we should send before forcing an ack. 
+     if this is set to zero it is the same as sk->delay_acks = 0 */
+
+  sk->max_ack_backlog = MAX_ACK_BACKLOG;
+  sk->inuse = 0;
+  sk->delay_acks = 1; /* default to waiting a while before sending
+                        acks.  */
+  sk->wback = NULL;
+  sk->wfront = NULL;
+  sk->rqueue = NULL;
+  sk->mtu = 576;
+  sk->prot = prot;
+  sk->sleep = sock->wait;
+  sk->daddr = 0;
+  sk->saddr = MY_IP_ADDR;
+  sk->err = 0;
+  sk->next = NULL;
+  sk->pair = NULL;
+  sk->send_tail = NULL;
+  sk->send_head = NULL;
+  sk->time_wait.len = TCP_CONNECT_TIME;
+  sk->time_wait.when = 0;
+  sk->time_wait.sk = sk;
+  sk->time_wait.next = NULL;
+  sk->timeout = 0;
+  sk->back_log = NULL;
+  sk->blog = 0;
+  sock->data =(void *) sk;
+  sk->dummy_th.doff = sizeof (sk->dummy_th)/4;
+  sk->dummy_th.res1=0;
+  sk->dummy_th.res2=0;
+  sk->dummy_th.urg_ptr = 0;
+  sk->dummy_th.fin = 0;
+  sk->dummy_th.syn = 0;
+  sk->dummy_th.rst = 0;
+  sk->dummy_th.psh = 0;
+  sk->dummy_th.ack = 0;
+  sk->dummy_th.urg = 0;
+  sk->dummy_th.dest = 0;
+  if (sk->num)
+    {
+       put_sock (sk->num, sk);
+    }
+  else
+    {
+       sk->num = get_new_socknum(sk->prot, 0);
+    }
+  /* make sure there was a free socket. */
+  if (sk->num == 0)
+    {
+      destroy_sock(sk);
+      return (-EAGAIN);
+    }
+  put_sock(sk->num, sk);
+  sk->dummy_th.source = net16(sk->num);
+  if (sk->prot->init)
+    {
+       err = sk->prot->init(sk);
+       if (err != 0)
+        {
+           destroy_sock (sk);
+           return (err);
+        }
+    }
+  return (0);
+}
+
+static int
+ip_proto_dup (struct socket *newsock, struct socket *oldsock)
+{
+  return (ip_proto_create (newsock,
+                          ((volatile struct sock *)(oldsock->data))->protocol));
+}
+
+/* the peer socket should always be NULL. */
+static int
+ip_proto_release(struct socket *sock, struct socket *peer)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+  if (sk == NULL) return (0);
+  wake_up (sk->sleep);
+  /* start closing the connection.  This may take a while. */
+  /* if linger is set, we don't return until the close is
+     complete.  Other wise we return immediately.  The
+     actually closing is done the same either way. */
+  if (sk->linger == 0)
+    {
+       sk->prot->close(sk,0);
+       sk->dead = 1;
+    }
+  else
+    {
+       sk->prot->close(sk, 0);
+       cli();
+       while (sk->state != TCP_CLOSE)
+        {
+           interruptible_sleep_on (sk->sleep);
+           if (current->signal & ~current->blocked)
+             {
+                sti();
+                return (-ERESTARTSYS);
+             }
+        }
+       sti();
+       sk->dead = 1;
+    }
+
+  sk->inuse = 1;
+  /* this will destroy it. */
+  release_sock (sk);
+  sock->data = NULL;
+  return (0);
+}
+
+
+static int
+ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
+              int addr_len)
+{
+  struct sockaddr_in addr;
+  volatile struct sock *sk, *sk2;
+  unsigned short snum;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  /* check this error. */
+  if (sk->state != TCP_CLOSE) return (-EIO);
+  verify_area (uaddr, addr_len);
+  memcpy_fromfs (&addr, uaddr, min (sizeof (addr), addr_len));
+  if (addr.sin_family && addr.sin_family != AF_INET)
+    return (-EIO); /* this needs to be changed. */
+  snum = net16(addr.sin_port);
+  PRINTK ("bind sk =%X to port = %d\n", sk, snum);
+  print_sk (sk);
+  sk = sock->data;
+
+  /* we can't just leave the socket bound wherever it is, it might be bound
+     to a priveledged port. However, since there seems to be a bug here,
+     we will leave it if the port is not priveledged(sp?) */
+
+  if (snum == 0)
+    {
+       if ( sk->num > PROT_SOCK) return (0);
+       snum = get_new_socknum (sk->prot, 0);
+    }
+
+  if (snum <= PROT_SOCK && !suser())
+    return (-EPERM);
+
+  if (my_ip_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0)
+    sk->saddr = addr.sin_addr.s_addr;
+  PRINTK ("sock_array[%d] = %X:\n", snum & (SOCK_ARRAY_SIZE -1),
+         sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]);
+  print_sk (sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]);
+
+  /* make sure we are allowed to bind here. */
+  for (sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
+       sk2 != NULL;
+       sk2 = sk2->next)
+    {
+       if (sk2->num != snum) continue;
+       if (sk2->saddr != sk->saddr) continue;
+       if (!sk->reuse) return (-EADDRINUSE);
+       if (!sk2->reuse) return (-EADDRINUSE);
+    }
+  remove_sock (sk);
+  put_sock(snum, sk);
+  sk->dummy_th.source = net16(sk->num);
+  sk->daddr = 0;
+  sk->dummy_th.dest = 0;
+  return (0);
+}
+
+static int
+ip_proto_connect (struct socket *sock, struct sockaddr * uaddr,
+                 int addr_len, int flags)
+{
+  volatile struct sock *sk;
+  int err;
+  sock->conn = NULL;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->prot->connect == NULL)
+    return (-EOPNOTSUPP);
+
+  if (sk->intr == 0)
+    {
+      err = sk->prot->connect (sk, (struct sockaddr_in *)uaddr, addr_len);
+      if (err < 0) return (err);
+    }
+
+  sock->state = SS_CONNECTED;
+
+  if (flags & O_NONBLOCK) return (0);
+
+  cli(); /* avoid the race condition */
+
+  while (sk->state != TCP_ESTABLISHED && sk->state < TCP_CLOSING)
+    {
+      interruptible_sleep_on (sk->sleep);
+      if (current->signal & ~current->blocked)
+       {
+          sti();
+          sk->intr = 1;
+          return (-ERESTARTSYS);
+       }
+    }
+  sti();
+  sk->intr = 0;
+  if (sk->state != TCP_ESTABLISHED && sk->err)
+    {
+      return (-sk->err);
+    }
+  return (0);
+}
+
+static int
+ip_proto_socketpair (struct socket *sock1, struct socket *sock2)
+{
+  return (-EOPNOTSUPP);
+}
+
+static int
+ip_proto_accept (struct socket *sock, struct socket *newsock, int flags)
+{
+  volatile struct sock *sk1, *sk2;
+  sk1= sock->data;
+   if (sk1 == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  newsock->data = NULL;
+  if (sk1->prot->accept == NULL) return (-EOPNOTSUPP);
+  /* restore the state if we have been interrupted, and
+     then returned. */
+  if (sk1->pair != NULL )
+    {
+      sk2 = sk1->pair;
+      sk1->pair = NULL;
+    }
+  else
+    {
+      sk2 = sk1->prot->accept (sk1,flags);
+      if (sk2 == NULL)
+       return (-sk1->err);
+    }
+  newsock->data = (void *)sk2;
+  sk2->sleep = (void *)newsock->wait;
+  newsock->conn = NULL;
+  if (flags & O_NONBLOCK)
+    return (0);
+
+  cli(); /* avoid the race. */
+  while (sk2->state == TCP_SYN_RECV)
+    {
+      interruptible_sleep_on (sk2->sleep);
+      if (current->signal & ~current->blocked)
+       {
+          sti();
+          sk1->pair = sk2;
+          sk2->sleep = NULL;
+          newsock->data = NULL;
+          return (-ERESTARTSYS);
+       }
+    }
+  sti();
+
+  if (sk2->state != TCP_ESTABLISHED && sk2->err)
+    {
+      int err;
+      err = -sk2->err;
+      destroy_sock (sk2);
+      newsock->data = NULL;
+      return (err);
+    }
+  newsock->state = SS_CONNECTED;
+  return (0);
+}
+
+static int
+ip_proto_getname(struct socket *sock, struct sockaddr *uaddr,
+                int *uaddr_len, int peer)
+{
+  struct sockaddr_in sin;
+  volatile struct sock *sk;
+  int len;
+  verify_area(uaddr_len, sizeof (len));
+  len = get_fs_long(uaddr_len);
+  /* check this error. */
+  if (len < sizeof (sin)) return (-EINVAL);
+  verify_area (uaddr, len);
+  sin.sin_family=AF_INET;
+  sk = sock->data;
+  if (sk == NULL)
+    {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+    }
+  if (peer)
+    {
+      if (sk->state != TCP_ESTABLISHED)
+       return (-ENOTCONN);
+      sin.sin_port = sk->dummy_th.dest;
+      sin.sin_addr.s_addr = sk->daddr;
+      }
+  else
+    {
+      sin.sin_port = sk->dummy_th.source;
+      sin.sin_addr.s_addr = sk->saddr;
+    }
+  len = sizeof (sin);
+  memcpy_tofs(uaddr, &sin, sizeof (sin));
+  put_fs_long (len, uaddr_len);
+  return (0);
+}
+
+static int
+ip_proto_read (struct socket *sock, char *ubuf, int size, int noblock)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & RCV_SHUTDOWN)
+    return (-EIO);
+  return (sk->prot->read (sk, ubuf, size, noblock,0));
+}
+
+static int
+ip_proto_recv (struct socket *sock, void *ubuf, int size, int noblock,
+              unsigned flags)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & RCV_SHUTDOWN)
+    return (-EIO);
+  return (sk->prot->read (sk, ubuf, size, noblock, flags));
+}
+
+static int
+ip_proto_write (struct socket *sock, char *ubuf, int size, int noblock)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & SEND_SHUTDOWN)
+    return (-EIO);
+  return (sk->prot->write (sk, ubuf, size, noblock, 0));
+}
+
+
+static int
+ip_proto_send (struct socket *sock, void *ubuf, int size, int noblock, 
+              unsigned flags)
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & SEND_SHUTDOWN)
+    return (-EIO);
+  return (sk->prot->write (sk, ubuf, size, noblock, flags));
+}
+
+
+static int
+ip_proto_sendto (struct socket *sock, void *ubuf, int size, int noblock, 
+                unsigned flags, struct sockaddr *sin, int addr_len )
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & SEND_SHUTDOWN)
+    return (-EIO);
+  if (sk->prot->sendto == NULL) return (-EOPNOTSUPP);
+  return (sk->prot->sendto (sk, ubuf, size, noblock, flags, 
+                           (struct sockaddr_in *)sin, addr_len));
+}
+
+static int
+ip_proto_recvfrom (struct socket *sock, void *ubuf, int size, int noblock, 
+                  unsigned flags, struct sockaddr *sin, int *addr_len )
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+  if (sk->shutdown & RCV_SHUTDOWN)
+    return (-EIO);
+  if (sk->prot->recvfrom == NULL) return (-EOPNOTSUPP);
+  return (sk->prot->recvfrom (sk, ubuf, size, noblock, flags,
+                             (struct sockaddr_in*)sin, addr_len));
+}
+
+static int
+ip_proto_shutdown (struct socket *sock, int how)
+{
+       volatile struct sock *sk;
+       /* this should really check to make sure the socket is
+          a tcp socket. */
+       how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
+                      1->2 bit 2 snds.
+                      2->3 */
+       if (how & ~SHUTDOWN_MASK) return (-EINVAL);
+       sk = sock->data;
+       if (sk == NULL)
+         {
+            printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+            return (0);
+         }
+       if (sk->state != TCP_ESTABLISHED) return (-ENOTCONN);
+       sk->shutdown |= how;
+       return (0);
+}
+
+static int
+ip_proto_select (struct socket *sock, int sel_type, select_table *wait )
+{
+  volatile struct sock *sk;
+  sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+
+  if (sk->prot->select == NULL)
+    {
+       PRINTK ("select on non-selectable socket. \n");
+       return (0);
+    }
+  return (sk->prot->select(sk, sel_type, wait));
+}
+
+/* these should be distributed to the different protocol routines. */
+static int
+ip_proto_ioctl (struct socket *sock, unsigned int cmd, 
+               unsigned long arg)
+{
+   volatile struct sock *sk;
+   sk = sock->data;
+   if (sk == NULL)
+     {
+       printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
+       return (0);
+     }
+
+  PRINTK ("in ip_proto_ioctl\n");
+  switch (cmd)
+    {
+
+      case IP_SET_DEV:
+       if (!suser())
+        return (-EPERM);
+       return (ip_set_dev((struct ip_config *)arg));
+#if 0
+    case IP_ADD_ROUTE:
+      ip_add_route ((struct rtable *) arg);
+      return (0);
+#endif
+    default:
+       if (!sk->prot->ioctl)
+        return (-EINVAL);
+       return (sk->prot->ioctl (sk, cmd, arg));
+    }
+}
+
+#ifdef MEM_DEBUG
+
+struct mem
+{
+   unsigned long check;
+   struct mem *other;
+   unsigned long len;
+   unsigned short buff[10];
+};
+
+static void
+print_mem (struct mem *m)
+{
+   int i;
+   MPRINTK("mem:\n");
+   MPRINTK("  check=%X, other = %X\n", m->check, m->other);
+   MPRINTK("  len=%d buff:\n " , m->len);
+   for (i = 0; i < 10; i++)
+     {
+       MPRINTK ("0x%02X ",m->buff[i]);
+     }
+   MPRINTK ("\n");
+}
+
+static void *
+smalloc (unsigned long size)
+{
+   struct mem *head, *tail;
+   static unsigned short count;
+   int i;
+   int sum;
+   unsigned char *ptr;
+
+   MPRINTK ("smalloc (size = %d)\n",size);
+   head = malloc (size + 2*sizeof (*head));
+   if (head == NULL) return (NULL);
+   tail = (struct mem *)((unsigned char *)(head+1) + size); 
+
+   head->other = tail;
+   tail->other = head;
+
+   tail->len = size;
+   head->len = size;
+   for (i = 0; i < 10; i++)
+     {
+       tail->buff[i]=count++;
+       head->buff[i]=count;
+     }
+
+   ptr = (unsigned char *)head;
+   head->check = 0;
+   sum = 0;
+
+   for (i = 0; i < sizeof (*head); i ++)
+     {
+       sum+= ptr[i]; 
+     }
+
+   head->check = ~sum;
+   ptr = (unsigned char *)tail;
+   tail->check = 0;
+   sum = 0;
+
+   for (i = 0; i < sizeof (*head); i ++)
+     {
+       sum+= ptr[i]; 
+     }
+
+   tail->check = ~sum;
+   MPRINTK ("head = %X:\n", head);
+   print_mem(head);
+   MPRINTK ("tail = %X:\n", tail);
+   print_mem(tail);
+   return (head+1);
+}
+
+void
+sfree (void *data, unsigned long len)
+{
+   int i;
+   int sum;
+   int csum;
+   unsigned char *ptr;
+   int bad = 0;
+   struct mem *head, *tail;
+   MPRINTK ("sfree(data=%X, len = %d)\n", data, len);
+   head = data;
+   head--;
+   tail = (struct mem *)((unsigned char *)(head+1) + len);
+   print_mem (head);
+   print_mem (tail);
+   if (head->other != tail)
+     {
+       MPRINTK ("sfree: head->other != tail:\n");
+       bad = 1;
+     }
+   if (tail->other != head)
+     {
+       MPRINTK ("sfree: tail->other != head:\n");
+       bad =1 ;
+     }
+   if (head ->len != len)
+     {
+       MPRINTK ("sfree: head->len != len");
+       bad = 1;
+     }
+   if (tail ->len != len)
+     {
+       MPRINTK ("sfree: tail->len != len");
+       bad = 1;
+     }
+   csum = head->check;
+   ptr = (unsigned char *)head;
+   head->check = 0;
+   sum = 0;
+   for (i = 0; i < sizeof (*head); i ++)
+     {
+       sum+= ptr[i]; 
+     }
+   if (csum != ~sum)
+     {
+       MPRINTK ("sfree: head failed checksum\n");
+       bad = 1;
+     }
+   csum = tail->check;
+   ptr = (unsigned char *)tail;
+   tail->check = 0;
+   sum = 0;
+   for (i = 0; i < sizeof (*head); i ++)
+     {
+       sum+= ptr[i]; 
+     }
+   if (csum != ~sum)
+     {
+       MPRINTK ("sfree: tail failed checksum\n");
+       bad = 1;
+     }
+   if (!bad)
+     free_s (head, len+2*sizeof (*head));
+   else
+     schedule();
+}
+#else
+static  void *
+smalloc (unsigned long size)
+{
+   return (malloc (size));
+}
+static  void
+sfree(void *data, unsigned long len)
+{
+   free_s(data,len);
+}
+#endif
+
+void *
+sock_wmalloc(volatile struct sock *sk, unsigned long size, int force)
+{
+  void *tmp;
+  if (sk)
+    {
+       if (sk->wmem_alloc + size >= SK_WMEM_MAX && !force)
+        {
+           MPRINTK ("sock_wmalloc(%X,%d,%d) returning NULL\n",
+                    sk, size, force);
+           return (NULL);
+        }
+      cli();
+      sk->wmem_alloc+= size;
+      sti();
+    }
+   if (sk)
+     tmp = smalloc (size);
+   else
+     tmp = malloc (size);
+
+  MPRINTK ("sock_wmalloc(%X,%d,%d) returning %X\n",sk, size, force, tmp);
+  return (tmp);
+}
+
+void *
+sock_rmalloc(volatile struct sock *sk, unsigned long size, int force)
+{
+   struct mem *tmp;
+   if (sk )
+     {
+       if (sk->rmem_alloc + size >= SK_RMEM_MAX && !force)
+         {
+            MPRINTK ("sock_rmalloc(%X,%d,%d) returning NULL\n",sk,size,force);
+            return (NULL);
+         }
+       cli();
+       sk->rmem_alloc+= size;
+       sti();
+     }
+   if (sk)
+     tmp = smalloc (size);
+   else
+     tmp = malloc (size);
+
+   MPRINTK ("sock_rmalloc(%X,%d,%d) returning %X\n",sk, size, force, tmp);
+   return (tmp);
+}
+
+
+unsigned long
+sock_rspace (volatile struct sock *sk)
+{
+   int amt;
+   if (sk != NULL)
+     {
+       if (sk->rmem_alloc >= SK_RMEM_MAX-2*MIN_WINDOW) return (0);
+       amt = min ((SK_RMEM_MAX-sk->rmem_alloc)/2-MIN_WINDOW, MAX_WINDOW);
+       if (amt < 0) return (0);
+       return (amt);
+     }
+   return (0);
+}
+
+unsigned long
+sock_wspace (volatile struct sock *sk)
+{
+  if (sk != NULL)
+    {
+       if (sk->shutdown & SEND_SHUTDOWN) return (0);
+       if (sk->wmem_alloc >= SK_WMEM_MAX) return (0);
+       return (SK_WMEM_MAX-sk->wmem_alloc );
+    }
+  return (0);
+}
+
+
+void
+sock_wfree (volatile struct sock *sk, void *mem, unsigned long size)
+{
+   MPRINTK ("sock_wfree (sk=%X, mem=%X, size=%d)\n",sk, mem, size);
+   if (sk)
+     {
+       sk->wmem_alloc -= size;
+       sfree(mem,size);
+       /* in case it might be waiting for more memory. */
+       if (!sk->dead && sk->wmem_alloc > SK_WMEM_MAX/2) wake_up(sk->sleep);
+       if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0)
+         {
+            MPRINTK ("recovered lost memory, destroying sock = %X\n",sk);
+            delete_timer ((struct timer *)&sk->time_wait);
+            free_s ((void *)sk, sizeof (*sk));
+         }
+     }
+   else
+     {
+       free_s (mem, size);
+     }
+}
+
+void
+sock_rfree (volatile struct sock *sk, void *mem, unsigned long size)
+{
+   MPRINTK ("sock_rfree (sk=%X, mem=%X, size=%d)\n",sk, mem, size);
+   if (sk)
+     {
+       sk->rmem_alloc -= size;
+       sfree(mem,size);
+       if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0)
+         {
+            delete_timer ((struct timer *)&sk->time_wait);
+            free_s ((void *)sk, sizeof (*sk));
+         }
+     }
+   else
+     {
+       free_s (mem, size);
+     }
+}
+
+
+/* This routine must find a socket given a tcp header.  Everyhting
+   is assumed to be in net order. */
+
+volatile struct sock *get_sock (struct proto *prot, unsigned short num,
+                               unsigned long raddr,
+                               unsigned short rnum, unsigned long laddr)
+{
+  volatile struct sock *s;
+  PRINTK ("get_sock (prot=%X, num=%d, raddr=%X, rnum=%d, laddr=%X)\n",
+         prot, num, raddr, rnum, laddr);
+
+  /* SOCK_ARRAY_SIZE must be a power of two.  This will work better
+     than a prime unless 3 or more sockets end up using the same
+     array entry.  This should not be a problem because most
+     well known sockets don't overlap that much, and for
+     the other ones, we can just be careful about picking our
+     socket number when we choose an arbitrary one. */
+
+  for (s=prot->sock_array[num&(SOCK_ARRAY_SIZE-1)]; s != NULL; s=s->next)
+    {
+      if (s->num == num)
+       {
+         /* we need to see if this is the socket that we want. */
+         if (!ip_addr_match (s->daddr, raddr))
+           continue;
+         if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0)
+           continue;
+         if (!ip_addr_match (s->saddr, laddr))
+           continue;
+         return (s);
+       }
+    }
+  return (NULL);
+}
+
+void release_sock (volatile struct sock *sk)
+{
+  if (sk->blog) return;
+  /* see if we have any packets built up. */
+
+  cli();
+  sk->inuse = 1;
+  while (sk->back_log != NULL)
+    {
+      struct sk_buff *skb;
+      sk->blog = 1;
+      skb = sk->back_log;
+      PRINTK ("release_sock: skb = %X:\n",skb);
+      print_skb(skb);
+      if (skb->next != skb)
+       {
+         sk->back_log = skb->next;
+         skb->prev->next = skb->next;
+         skb->next->prev = skb->prev;
+       }
+      else
+       {
+         sk->back_log = NULL;
+       }
+      sti();
+      PRINTK ("sk->back_log = %X\n",sk->back_log);
+      if (sk->prot->rcv)
+       sk->prot->rcv(skb, skb->dev, sk->opt,
+                     skb->saddr, skb->len, skb->daddr, 1,
+                     /* only used for/by raw sockets. */
+                     (struct ip_protocol *)sk->pair); 
+      cli();
+    }
+  sk->blog = 0;
+  sk->inuse = 0;
+  sti();
+  if (sk->dead && sk->state == TCP_CLOSE)
+    {
+        /* should be about 2 rtt's */
+       sk->time_wait.len = min (sk->rtt * 2, TCP_DONE_TIME);
+       sk->timeout = TIME_DONE;
+       reset_timer ((struct timer *)&sk->time_wait);
+    }
+}
diff --git a/net/tcp/sock.h b/net/tcp/sock.h
new file mode 100644 (file)
index 0000000..1642509
--- /dev/null
@@ -0,0 +1,182 @@
+/* sock.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_SOCK_H
+#define _TCP_SOCK_H
+
+#define SOCK_ARRAY_SIZE 64
+
+/* This structure really needs to be cleaned up.  Most of it is
+   for tcp, and not used by any of the other protocols. */
+
+struct sock 
+{
+  struct options *opt;
+  unsigned long wmem_alloc;
+  unsigned long rmem_alloc;
+  unsigned long send_seq;
+  unsigned long acked_seq;
+  unsigned long copied_seq;
+  unsigned long rcv_ack_seq;
+  unsigned long window_seq;
+  unsigned long fin_seq;
+  unsigned long inuse:1, dead:1, urginline:1,
+                intr:1, blog:1, done:1, reuse:1, keepopen:1, linger:1,
+                delay_acks:1, timeout:3, destroy:1, ack_timed:1, no_check:1,
+                exp_growth:1;
+  int proc;
+  volatile struct sock *next;
+  volatile struct sock *pair;
+  struct sk_buff *send_tail;
+  struct sk_buff *send_head;
+  struct sk_buff *back_log;
+  long retransmits;
+  struct sk_buff *wback, *wfront, *rqueue;
+  struct proto *prot;
+  struct wait_queue **sleep;
+  unsigned long daddr;
+  unsigned long saddr;
+  unsigned short max_unacked;
+  unsigned short window;
+  unsigned short bytes_rcv;
+  unsigned short mtu;
+  unsigned short num;
+  unsigned short cong_window;
+  unsigned short packets_out;
+  unsigned short urg;
+  unsigned short shutdown;
+  short rtt;
+  unsigned char protocol;
+  unsigned char state;
+  unsigned char ack_backlog;
+  unsigned char err;
+  unsigned char max_ack_backlog;
+  unsigned char priority;
+  struct tcp_header dummy_th; /* I may be able to get rid of this. */
+  struct timer time_wait;
+};
+
+struct proto 
+{
+  void *(*wmalloc)(volatile struct sock *sk, unsigned long size, int force);
+  void *(*rmalloc)(volatile struct sock *sk, unsigned long size, int force);
+  void (*wfree)(volatile struct sock *sk, void *mem, unsigned long size);
+  void (*rfree)(volatile struct sock *sk, void *mem, unsigned long size);
+  unsigned long (*rspace)(volatile struct sock *sk);
+  unsigned long (*wspace)(volatile struct sock *sk);
+  void (*close)(volatile struct sock *sk, int timeout);
+  int (*read)(volatile struct sock *sk, unsigned char *to, int len,
+             int nonblock, unsigned flags);
+  int (*write)(volatile struct sock *sk, unsigned char *to, int len,
+              int nonblock, unsigned flags);
+  int (*sendto) (volatile struct sock *sk, unsigned char *from, int len,
+                int noblock, unsigned flags, struct sockaddr_in *usin,
+                int addr_len);
+  int (*recvfrom) (volatile struct sock *sk, unsigned char *from, int len,
+                  int noblock, unsigned flags, struct sockaddr_in *usin,
+                  int *addr_len);
+  int (*build_header) (struct sk_buff *skb, unsigned long saddr,
+                      unsigned long daddr, struct device **dev, int type,
+                      struct options *opt, int len);
+  int (*connect) (volatile struct sock *sk, struct sockaddr_in *usin,
+                 int addr_len);
+  volatile struct sock *(*accept) (volatile struct sock *sk, int flags);
+  void  (*queue_xmit) (volatile struct sock *sk, struct device *dev, 
+                      struct sk_buff *skb, int free);
+  void (*retransmit) (volatile struct sock *sk, int all);
+  void (*write_wakeup) (volatile struct sock *sk);
+  void (*read_wakeup) (volatile struct sock *sk);
+  int (*rcv)(struct sk_buff *buff, struct device *dev, struct options *opt,
+            unsigned long daddr, unsigned short len,
+            unsigned long saddr, int redo, struct ip_protocol *protocol);
+  int (*select)(volatile struct sock *sk, int which, select_table *wait);
+  int (*ioctl) (volatile struct sock *sk, int cmd, unsigned long arg);
+  int (*init) (volatile struct sock *sk);
+  unsigned short max_header;
+  unsigned long retransmits;
+  volatile struct sock *sock_array[SOCK_ARRAY_SIZE];
+};
+
+#define TIME_WRITE 1
+#define TIME_CLOSE 2
+#define TIME_KEEPOPEN 3
+#define TIME_DESTROY 4
+#define TIME_DONE 5 /* used to absorb those last few packets. */
+#define SOCK_DESTROY_TIME 1000 /* about 10 seconds. */
+
+/* used with free skb */
+#define FREE_READ 1
+#define FREE_WRITE 0
+
+struct sk_buff
+{
+  struct sk_buff *next;
+  struct sk_buff *prev;
+  struct sk_buff *link3;
+  volatile struct sock *sk;
+  unsigned long when; /* used to compute rtt's. */
+  struct device *dev;
+  void *mem_addr;
+  union
+    {
+       struct tcp_header *th;
+       struct enet_header *eth;
+       struct ip_header *iph;
+       struct udp_header *uh;
+       struct arp *arp;
+       unsigned char *raw;
+       unsigned long seq;
+    } h;
+  unsigned long mem_len;
+  unsigned long len;
+  unsigned long saddr;
+  unsigned long daddr;
+  unsigned long acked:1,used:1,free:1,arp:1, urg_used:1, lock:1;
+};
+
+#define PROT_SOCK 1024
+#define SK_WMEM_MAX 8192
+#define SK_RMEM_MAX 32767
+#define SHUTDOWN_MASK 3
+#define RCV_SHUTDOWN 1
+#define SEND_SHUTDOWN 2
+
+void destroy_sock (volatile struct sock *sk);
+unsigned short get_new_socknum (struct proto *, unsigned short);
+void put_sock (unsigned short, volatile struct sock *); 
+void release_sock (volatile struct sock *sk);
+volatile struct sock *get_sock(struct proto *, unsigned short, unsigned long,
+                              unsigned short, unsigned long);
+void print_sk (volatile struct sock *);
+void print_skb (struct sk_buff *);
+void *sock_wmalloc(volatile struct sock *sk, unsigned long size, int force);
+void *sock_rmalloc(volatile struct sock *sk, unsigned long size, int force);
+void sock_wfree(volatile struct sock *sk, void *mem, unsigned long size);
+void sock_rfree(volatile struct sock *sk, void *mem, unsigned long size);
+unsigned long sock_rspace(volatile struct sock *sk);
+unsigned long sock_wspace(volatile struct sock *sk);
+void free_skb (struct sk_buff *skb, int rw);
+void lock_skb (struct sk_buff *skb);
+void unlock_skb (struct sk_buff *skb, int rw);
+
+void dummy_routine(void *, ... );
+
+#endif
diff --git a/net/tcp/tcp.c b/net/tcp/tcp.c
new file mode 100644 (file)
index 0000000..db36788
--- /dev/null
@@ -0,0 +1,2524 @@
+ /* tcp.c */
+ /*
+     Copyright (C) 1992  Ross Biro
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2, or (at your option)
+     any later version.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+     The Author may be reached as bir7@leland.stanford.edu or
+     C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/memory.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <linux/fcntl.h>
+#include "timer.h"
+#include "ip.h"
+#include "icmp.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+/* #include <signal.h>*/
+#include "../kern_sock.h" /* for PRINTK */
+
+#define tmax(a,b) (before ((a),(b)) ? (b) : (a))
+#define swap(a,b) {unsigned long c; c=a; a=b; b=c;}
+
+extern struct proto tcp_prot;
+
+static  int 
+min (unsigned int a, unsigned int b)
+{
+   if (a < b) return (a);
+   return (b);
+}
+
+void
+print_th (struct tcp_header *th)
+{
+   unsigned char *ptr;
+   ptr = (unsigned char *)(th + 1);
+   PRINTK ("tcp header:\n");
+   PRINTK ("  source=%d, dest=%d, seq =%d, ack_seq = %d\n",
+          net16(th->source), net16(th->dest), net32(th->seq),
+          net32(th->ack_seq));
+   PRINTK ("  fin=%d, syn=%d, rst=%d, psh=%d, ack=%d, urg=%d res1=%d res2=%d\n"
+          ,th->fin, th->syn, th->rst, th->psh, th->ack, th->urg,
+          th->res1, th->res2);
+   PRINTK ("  window = %d, check = %d urg_ptr = %d\n",
+          net16(th->window), net16(th->check), net16(th->urg_ptr));
+   PRINTK ("  doff = %d\n",th->doff);
+   PRINTK ("options = %d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3]);
+ }
+
+ /* This routine grabs the first thing off of a rcv queue. */
+static  struct sk_buff *
+get_firstr(volatile struct sock *sk)
+{
+   struct sk_buff *skb;
+   skb = sk->rqueue;
+   if (skb == NULL) return (NULL);
+   sk->rqueue = skb->next;
+   if (sk->rqueue == skb)
+     {
+       sk->rqueue = NULL;
+     }
+   else
+     {
+       sk->rqueue->prev=skb->prev;
+       sk->rqueue->prev->next = sk->rqueue;
+     }
+   return (skb);
+}
+
+static  long
+diff (unsigned long seq1, unsigned long seq2)
+{
+   long d;
+   d=seq1-seq2;
+   if (d > 0) return (d);
+   /* I hope this returns what I want. */
+   return (~d+1);
+}
+
+ /* enter the time wait state. */
+static  void
+tcp_time_wait (volatile struct sock *sk)
+{
+   sk->state = TCP_TIME_WAIT;
+   sk->time_wait.len = TCP_TIMEWAIT_LEN;
+   sk->timeout = TIME_CLOSE;
+   reset_timer ((struct timer *)&sk->time_wait);
+}
+
+static void
+tcp_retransmit (volatile struct sock *sk, int all)
+{
+   if (all) 
+     {
+       ip_retransmit (sk, all);
+       return;
+     }
+   sk->rtt *= 2; /* exponential back off. */
+   if (sk->cong_window > 1)
+     sk->cong_window = sk->cong_window / 2;
+   sk->exp_growth = 0;
+
+   /* do the actuall retransmit. */
+   ip_retransmit (sk, all);
+   
+}
+
+/* this routine is called by the icmp module when it gets some
+   sort of error condition.  If err < 0 then the socket should
+   be closed and the error returned to the user.  If err > 0
+   it's just the icmp type << 8 | icmp code.  
+   header points to the first 8 bytes of the tcp header.  We need
+   to find the appropriate port. */
+
+void
+tcp_err (int err, unsigned char *header, unsigned long daddr,
+        unsigned long saddr, struct ip_protocol *protocol)
+{
+   struct tcp_header *th;
+   volatile struct sock *sk;
+   
+   th = (struct tcp_header *)header;
+   sk = get_sock (&tcp_prot, net16(th->dest), saddr, th->source, daddr);
+
+   if (sk == NULL) return;
+
+   if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8))
+     {
+       /* for now we will just trigger a linear backoff. The slow start
+          code should cause a real backoff here. */
+
+       if (sk->cong_window > 1)
+         sk->cong_window --;
+
+       return;
+     }
+
+   sk->err = icmp_err_convert[err & 0xff].errno;
+   if (icmp_err_convert[err & 0xff].fatal)
+     {
+       if (sk->state != TCP_ESTABLISHED)
+         sk->state = TCP_CLOSE;
+       sk->prot->close(sk, 0);
+     }
+
+   return;
+   
+}
+
+static int
+tcp_select (volatile struct sock *sk, int sel_type, select_table *wait)
+{
+  switch (sel_type)
+    {
+    case SEL_IN:
+       select_wait (sk->sleep, wait);
+       if (sk->rqueue != NULL &&
+          (between (sk->copied_seq, sk->rqueue->next->h.th->seq - 1,
+                    sk->rqueue->next->h.th->seq + sk->rqueue->next->len) ||
+           sk->state == TCP_LISTEN))
+        {
+           return (1);
+        }
+
+      switch (sk->state)
+       {
+       case TCP_LISTEN:
+       case TCP_ESTABLISHED:
+       case TCP_SYN_SENT:
+       case TCP_SYN_RECV:
+          return (0);
+       default:
+          return (1);
+       }
+
+    case SEL_OUT:
+       select_wait (sk->sleep, wait);
+       if (sk->state != TCP_ESTABLISHED) return (1);
+      /* hack so it will probably be able to write something
+        if it says it's ok to write. */
+      if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) return (1);
+      return (0);
+
+    case SEL_EX:
+      select_wait(sk->sleep,wait);
+      if (sk->err) return (1);
+      if (sk->state == TCP_FIN_WAIT1 ||
+         sk->state == TCP_FIN_WAIT2 ||
+         sk->state == TCP_TIME_WAIT ||
+         sk->state == TCP_LAST_ACK)
+       return (1);
+      return (0);
+    }
+  return (0);
+}
+
+static int
+tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
+{
+   switch (cmd)
+     {
+       default:
+       return (-EINVAL);
+#if 0
+       case SIOCATMARK:
+       /* try to figure out if we need to read some urgent data. */
+       if (sk->rqueue && sk->rqueue->next->th.urg)
+         {
+            int offset;
+            struct sk_buff *skb;
+            skb = sk->rqueue->next;
+            offset = sk->copied_seq +1 - skb->th.seq - skb->th.syn;
+            /* now we know we are at the urgent data. */
+            if (offset >= skb->len)
+              {
+                 verify_area ((void *)arg, sizeof (int));
+                 put_fs_long(1, (unsigned long *)arg);
+                 return (0);
+              }
+         }
+       verify_area ((void *)arg, sizeof (int));
+       put_fs_long(0, (unsigned long *)arg);
+       return (0);
+#endif
+     }
+}
+
+
+/* this routine computes a tcp checksum */
+static  unsigned short
+tcp_check (struct tcp_header *th, int len, unsigned long saddr,
+          unsigned long daddr)
+{     
+   unsigned long sum;
+   
+   if (saddr == 0) saddr = MY_IP_ADDR;
+   print_th (th);
+   __asm__("\t addl %%ecx,%%ebx\n"
+          "\t adcl %%edx,%%ebx\n"
+          "\t adcl $0, %%ebx\n"
+          : "=b" (sum)
+          : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IP_TCP*256)
+          : "cx","bx","dx" );
+   
+   if (len > 3)
+     {
+       __asm__(
+               "\tclc\n"
+               "1:\n"
+               "\t lodsl\n"
+               "\t adcl %%eax, %%ebx\n"
+               "\t loop 1b\n"
+               "\t adcl $0, %%ebx\n"
+               : "=b" (sum) , "=S" (th)
+               : "0" (sum), "c" (len/4) ,"1" (th)
+               : "ax", "cx", "bx", "si" );
+     }
+   
+   /* convert from 32 bits to 16 bits. */
+   __asm__(
+          "\t movl %%ebx, %%ecx\n"
+          "\t shrl $16,%%ecx\n"
+          "\t addw %%cx, %%bx\n"
+          "\t adcw $0, %%bx\n"
+          : "=b" (sum)
+          : "0" (sum)
+          : "bx", "cx");
+   
+   /* check for an extra word. */
+   if ((len & 2) != 0)
+     {
+       __asm__("\t lodsw\n"
+               "\t addw %%ax,%%bx\n"
+               "\t adcw $0, %%bx\n"
+               : "=b" (sum), "=S" (th)
+               : "0" (sum) ,"1" (th)
+               : "si", "ax", "bx");
+     }
+   
+   /* now check for the extra byte. */
+   if ((len & 1) != 0)
+     {
+       __asm__("\t lodsb\n"
+               "\t movb $0,%%ah\n"
+               "\t addw %%ax,%%bx\n"
+               "\t adcw $0, %%bx\n"
+               : "=b" (sum)
+               : "0" (sum) ,"S" (th)
+               : "si", "ax", "bx");
+     }
+   
+   /* we only want the bottom 16 bits, but we never cleared
+      the top 16. */
+   return ((~sum) & 0xffff);
+}
+
+
+static  void
+tcp_send_check (struct tcp_header *th, unsigned long saddr, 
+                unsigned long daddr, int len, volatile struct sock *sk)
+ {
+
+   th->check = 0;
+   if (sk && sk->no_check) return;
+   th->check = tcp_check (th, len, saddr, daddr);
+   return;
+}
+
+ /* This routine sends an ack and also updates the window. */
+ static  void
+ tcp_send_ack (unsigned long sequence, unsigned long ack,
+              volatile struct sock *sk,
+              struct tcp_header *th, unsigned long daddr)
+ {
+   struct sk_buff *buff;
+   struct tcp_header *t1;
+   struct device *dev=NULL;
+   int tmp;
+
+   /* we need to grab some memory, and put together an ack, and then
+      put it into the queue to be sent. */
+
+   buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1);
+   if (buff == NULL) 
+     {
+       /* force it to send an ack. */
+       sk->ack_backlog++;
+       if (sk->timeout != TIME_WRITE && sk->state < TCP_CLOSING)
+         {
+            sk->timeout = TIME_WRITE;
+            sk->time_wait.len = 10; /* got to do it quickly. */
+            reset_timer ((struct timer *)&sk->time_wait);
+         }
+       return;
+     }
+
+   buff->mem_addr = buff;
+   buff->mem_len = MAX_ACK_SIZE;
+   buff->len=sizeof (struct tcp_header);
+   buff->sk = sk;
+   t1 = (struct tcp_header *)(buff + 1);
+   /* put in the ip_header and routing stuff. */
+   tmp = sk->prot->build_header (buff, sk->saddr, daddr, &dev,
+                                IP_TCP, sk->opt, MAX_ACK_SIZE);
+   if (tmp < 0)
+     {
+       sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+       return;
+     }
+   buff->len += tmp;
+   t1 = (struct tcp_header *)((char *)t1 +tmp);
+
+   memcpy (t1, th, sizeof (*t1)); /* this should probably be removed. */
+
+   /* swap the send and the receive. */
+   t1->dest = th->source;
+   t1->source = th->dest;
+   t1->seq = net32(sequence);
+   t1->ack = 1;
+   sk->window = sk->prot->rspace(sk);
+   t1->window = net16(sk->window);
+   t1->res1=0;
+   t1->res2=0;
+   t1->rst = 0;
+   t1->urg = 0;
+   t1->syn = 0;
+   t1->psh = 0;
+   t1->fin = 0;
+   if (ack == sk->acked_seq)
+     {
+       sk->ack_backlog = 0;
+       sk->bytes_rcv = 0;
+       sk->ack_timed = 0;
+       if (sk->send_head == NULL &&
+           sk->wfront == NULL)
+         {
+            delete_timer((struct timer *)&sk->time_wait);
+            sk->timeout = 0;
+         }
+       
+     }
+   t1->ack_seq = net32(ack);
+   t1->doff = sizeof (*t1)/4;
+   tcp_send_check (t1, sk->saddr, daddr, sizeof (*t1), sk);
+   sk->prot->queue_xmit(sk, dev, buff, 1);
+}
+
+/* this routine builds a generic tcp header. */
+static  int
+tcp_build_header(struct tcp_header *th, volatile struct sock *sk, int push)
+{
+
+ /* want to get rid of this. */
+  memcpy (th,(void *) &(sk->dummy_th), sizeof (*th));
+  th->seq = net32(sk->send_seq);
+  th->psh = (push == 0) ? 1 : 0;
+  th->doff = sizeof (*th)/4;
+  th->ack = 1;
+  th->fin = 0;
+  sk->ack_backlog = 0;
+  sk->bytes_rcv = 0;
+  sk->ack_timed = 0;
+  th->ack_seq = net32(sk->acked_seq);
+  sk->window = sk->prot->rspace(sk);
+  th->window = net16(sk->window);
+
+  return (sizeof (*th));
+}
+
+/* This routine copies from a user buffer into a socket, and starts
+   the transmit system. */
+
+static int
+tcp_write(volatile struct sock *sk, unsigned char *from,
+         int len, int nonblock, unsigned flags)
+{
+  int copied=0;
+  int copy;
+  int tmp;
+  struct sk_buff *skb;
+  unsigned char *buff;
+  struct proto *prot;
+  struct device *dev=NULL;
+
+  PRINTK ("in TCP_WRITE sk = %X:\n",sk);
+  print_sk (sk);
+
+  sk->inuse = 1; /* no one else will use this socket. */
+  prot = sk->prot;
+  while (len > 0)
+    {
+       /* first thing we do is make sure that we are established. */    
+
+      while (sk->state != TCP_ESTABLISHED)
+       {
+         if (sk->state != TCP_SYN_SENT &&
+             sk->state != TCP_SYN_RECV)
+          {
+             release_sock (sk);
+             if (sk->keepopen)
+               {
+                   send_sig (SIGPIPE, current, 0);
+                   return (-EINTR);
+               }
+             if (copied) return (copied);
+             if (sk->err) return (-sk->err);
+             return (-ENOTCONN);
+           }
+
+         if (nonblock)
+           {
+             release_sock (sk);
+             return (-EAGAIN);
+           }
+
+         /* now here is a race condition.
+            release_sock could cause the connection to
+            enter the established mode, if that is the
+            case, then we will block here for ever, because
+            we will have gotten our wakeup call before we
+            go to sleep. */
+         release_sock (sk);
+         cli();
+         if (sk->state != TCP_ESTABLISHED)
+           {
+             interruptible_sleep_on (sk->sleep);
+             if (current->signal & ~current->blocked)
+               {
+                  sti();
+                  if (copied) return (copied);
+                  return (-ERESTARTSYS);
+               }
+           }
+         sti();
+         sk->inuse = 1;
+       }
+      /* we also need to worry about the window.  The smallest we
+        will send is about 200 bytes. */
+
+      copy = min (sk->mtu, diff(sk->window_seq, sk->send_seq));
+      if (copy < 200) copy = sk->mtu;
+      copy = min (copy, len);
+
+      skb=prot->wmalloc (sk, copy + prot->max_header+sizeof (*skb),0);
+
+      /* if we didn't get any memory, we need to sleep. */
+      if (skb == NULL)
+       {
+         if (nonblock ||  copied)
+           {
+             break;
+           }
+         /* here is another race condition. */
+         tmp = sk->wmem_alloc;
+         release_sock (sk);
+         /* again we will try to avoid it. */
+         cli ();
+         if (tmp <= sk->wmem_alloc)
+           {
+             interruptible_sleep_on (sk->sleep);
+             if (current->signal & ~current->blocked)
+               {
+                  sti();
+                  if (copied) return (copied);
+                  return (-ERESTARTSYS);
+               }
+           }
+         sti();
+         sk->inuse = 1;
+         continue;
+       }
+      skb->mem_addr = skb;
+      skb->mem_len = copy+prot->max_header+sizeof (*skb);
+      skb->len = 0;
+      skb->sk = sk;
+      buff =(unsigned char *)( skb+1);
+       /* we need to optimize this.  Perhaps some hints here
+         would be good. */
+
+      tmp = prot->build_header (skb, sk->saddr, sk->daddr, &dev,
+                               IP_TCP, sk->opt, skb->mem_len);
+      if (tmp < 0 )
+       {
+         prot->wfree (sk, skb->mem_addr, skb->mem_len);
+         release_sock (sk);
+         return (tmp);
+       }
+      skb->len += tmp;
+      skb->dev = dev;
+      buff+=tmp;
+      tmp = tcp_build_header((struct tcp_header *)buff, sk, len-copy);
+      if (tmp < 0)
+       {
+         prot->wfree (sk, skb->mem_addr, skb->mem_len);
+         release_sock (sk);
+         return (tmp);
+       }
+
+      if (flags & MSG_OOB)
+       {
+               ((struct tcp_header *)buff)->urg = 1;
+               ((struct tcp_header *)buff)->urg_ptr = copy;
+       }
+      skb->len += tmp;
+      memcpy_fromfs (buff+tmp, from, copy);
+
+      tcp_send_check ((struct tcp_header *)buff, sk->saddr, sk->daddr,
+                      copy +sizeof (struct tcp_header), sk);
+
+      from += copy;
+      copied += copy;
+      len -= copy;
+      skb->len += copy;
+      skb->free = 0;
+      sk->send_seq += copy;
+      skb->h.seq = sk->send_seq;
+      if (after (sk->send_seq , sk->window_seq) ||
+         sk->packets_out >= sk->cong_window)
+       {
+         PRINTK ("sk->cong_window = %d, sk->packets_out = %d\n",
+                 sk->cong_window, sk->packets_out);
+         PRINTK ("sk->send_seq = %d, sk->window_seq = %d\n",
+                 sk->send_seq, sk->window_seq);
+         skb->next = NULL;
+         if (sk->wback == NULL)
+           {
+             sk->wfront=skb;
+           }
+         else
+           {
+             sk->wback->next = skb;
+           }
+         sk->wback = skb;
+       }
+      else
+       {
+         prot->queue_xmit (sk, dev, skb,0);
+       }
+    }
+  sk->err = 0;
+  release_sock (sk);
+  return (copied);
+}
+
+
+static  void
+tcp_read_wakeup(volatile struct sock *sk)
+{
+  int tmp;
+  struct device *dev = NULL;
+  struct tcp_header *t1;
+  struct sk_buff *buff;
+
+  if (!sk->ack_backlog ) return;
+  PRINTK ("in tcp read wakeup\n");
+  /* we need to put code here to prevent this routine from being called. */
+  /* being called once in a while is ok, so only check if this is the
+     second time in a row. */
+
+  /* we need to grab some memory, and put together an ack, and then
+     put it into the queue to be sent. */
+
+  buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1);
+  if (buff == NULL) 
+    {
+       /* try again real soon. */
+       sk->timeout = TIME_WRITE;
+       sk->time_wait.len = 10;
+       reset_timer((struct timer *) &sk->time_wait);
+       return;
+    }
+
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_ACK_SIZE;
+  buff->len=sizeof (struct tcp_header);
+  buff->sk = sk;
+
+  /* put in the ip_header and routing stuff. */
+  tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
+                               IP_TCP, sk->opt, MAX_ACK_SIZE);
+  if (tmp < 0)
+    {
+      sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+      return;
+    }
+
+  buff->len += tmp;
+  t1 = (struct tcp_header *)((char *)(buff+1) +tmp);
+
+  memcpy (t1,(void *) &sk->dummy_th, sizeof (*t1));
+  t1->seq = net32(sk->send_seq);
+  t1->ack = 1;
+  t1->res1=0;
+  t1->res2=0;
+  t1->rst = 0;
+  t1->urg = 0;
+  t1->syn = 0;
+  t1->psh = 0;
+  sk->ack_backlog = 0;
+  sk->bytes_rcv = 0;
+  sk->window = sk->prot->rspace(sk);
+  t1->window = net16(sk->window);
+  t1->ack_seq = net32(sk->acked_seq);
+  t1->doff = sizeof (*t1)/4;
+  tcp_send_check (t1, sk->saddr, sk->daddr, sizeof (*t1), sk);
+  sk->prot->queue_xmit(sk, dev, buff, 1);
+}
+
+
+/* This routine frees used buffers. */
+/* It should consider sending an ack to let the
+   other end know we now have a bigger window. */
+
+static  void
+cleanup_rbuf (volatile struct sock *sk)
+{
+   PRINTK ("cleaning rbuf for sk=%X\n",sk);
+  /* we have to loop through all the buffer headers, and 
+     try to free up all the space we can. */
+  while (sk->rqueue != NULL )
+    {
+      struct sk_buff *skb;
+      skb=sk->rqueue->next;
+      if (!skb->used) break;
+      if (sk->rqueue == skb)
+       {
+         sk->rqueue = NULL;
+       }
+      else
+       {
+         skb->next->prev = skb->prev;
+         skb->prev->next = skb->next;
+       }
+      skb->sk = sk;
+      free_skb (skb, FREE_READ);
+    }
+   /* at this point we should send an ack if the difference in
+      the window, and the amount of space is bigger than
+      TCP_WINDOW_DIFF */
+   PRINTK ("sk->window left = %d, sk->prot->rspace(sk)=%d\n",
+          sk->window - sk->bytes_rcv, sk->prot->rspace(sk));
+
+   if ((sk->prot->rspace(sk) >
+       (sk->window - sk->bytes_rcv + TCP_WINDOW_DIFF)) ||
+       (sk->window - sk->bytes_rcv < 2*sk->mtu)) 
+     {
+       /* force it to send an ack. */
+       sk->ack_backlog++;
+       if (sk->timeout != TIME_WRITE && sk->state == TCP_ESTABLISHED)
+         {
+            sk->time_wait.len = TCP_ACK_TIME;
+            sk->timeout=TIME_WRITE;
+            reset_timer ((struct timer *)&sk->time_wait);
+         }
+     }
+
+}
+
+/* handle reading urgent data. */
+static  int
+tcp_read_urg(volatile struct sock * sk,
+            unsigned char *to, int len, unsigned flags)
+{
+    int copied = 0;
+    struct sk_buff *skb;
+    PRINTK ("tcp_read_urg(sk=%X, to=%X, len=%d, flags=%X)\n",
+           sk, to, len, flags);
+    print_sk(sk);
+    while (len > 0)
+      {
+        sk->inuse = 1;
+         while (sk->urg==0 || sk->rqueue == NULL)
+           {
+              /* now at this point, we may have gotten some data. */
+              release_sock (sk);
+               if (sk->state > TCP_CLOSING)
+                 {
+                     if (copied) return (copied);
+                     return (-ENOTCONN);
+                 }
+              cli();
+              if (sk->urg == 0 || sk->rqueue == NULL)
+                {
+                   interruptible_sleep_on (sk->sleep);
+                   if (current->signal & ~current->blocked)
+                     {
+                        sti();
+                        if (copied) return (copied);
+                        return (-ERESTARTSYS);
+                     }
+                }
+              sti();
+              sk->inuse = 1;
+           }
+         /* now we have some urgent data, we must find it.*/
+         for (skb = sk->rqueue->next; skb->next != sk->rqueue;
+              skb = skb->next)
+           {
+               int offset;
+               int amt;
+               if (!skb->h.th->urg) continue;
+               offset = 0;
+               amt = min(skb->h.th->urg_ptr,len);
+               verify_area (to, amt);
+               memcpy_tofs (to, (unsigned char *)(skb->h.th) +
+                            skb->h.th->doff*4
+                            + offset, amt);
+
+               if (!(flags & MSG_PEEK))
+                 {
+                    skb->urg_used = 1;
+                    sk->urg --;
+                 }
+               release_sock (sk);
+               copied += amt;
+               return (copied);
+           }
+      }
+    return (0);
+}
+
+/* This routine copies from a sock struct into the user buffer. */
+static  int
+tcp_read(volatile struct sock *sk, unsigned char *to,
+        int len, int nonblock, unsigned flags)
+{
+    int copied=0; /* will be used to say how much has been copied. */
+    struct sk_buff *skb;
+    unsigned long offset;
+    unsigned long used;
+
+    if (len == 0) return (0);
+    if (len < 0) 
+      {
+        return (-EINVAL);
+      }
+    
+    /* this error should be checked. */
+    if (sk->state == TCP_LISTEN) return (-ENOTCONN);
+
+    /* urgent data needs to be handled specially. */
+    if ((flags & MSG_OOB))
+      return (tcp_read_urg (sk, to, len, flags));
+
+    /* so no-one else will use this socket. */
+    sk->inuse = 1;
+    if (sk->rqueue != NULL)
+      skb=sk->rqueue->next;
+    else
+      skb = NULL;
+
+    while ( len > 0)
+      {
+        PRINTK("tcp_read (sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n",
+               sk, to, len, nonblock, flags);
+         while ( skb == NULL || before (sk->copied_seq+1, skb->h.th->seq) ||
+                skb->used) /* skb->used just checks to see if we've
+                              gone all the way around. */
+           {
+
+              PRINTK("skb = %X:\n",skb);
+              print_skb(skb);
+              print_sk (sk);
+
+              cleanup_rbuf(sk);
+
+               if (nonblock || ((flags & MSG_PEEK) && copied))
+                 {
+                    release_sock (sk);
+                    if (copied) return (copied);
+                    return (-EAGAIN);
+                 }
+
+               release_sock (sk); /* now we may have some data waiting. */
+
+
+              PRINTK ("tcp_read about to sleep. state = %d\n",sk->state);
+              cli();
+
+              if (sk->state == TCP_CLOSE || sk->state == TCP_TIME_WAIT)
+                  {
+                    sti();
+                    if (copied) return (copied);
+                    if (sk->err) return (-sk->err);
+                    if (!sk->done)
+                      {
+                         sk->done = 1;
+                         return (0);
+                      }
+                    return (-ENOTCONN);
+                 }
+                       
+               if ( sk->rqueue == NULL ||
+                   before (sk->copied_seq+1, sk->rqueue->next->h.th->seq))
+                 {
+                    interruptible_sleep_on (sk->sleep);
+                    if (current->signal & ~current->blocked)
+                      {
+                         sti ();
+                         if (copied) return (copied);
+                         return (-ERESTARTSYS);
+                      }
+                 }
+               sti();
+               PRINTK ("tcp_read woke up. \n");
+
+               sk->inuse = 1;
+
+               if (sk->rqueue != NULL)
+                 skb=sk->rqueue->next;
+               else
+                 skb = NULL;
+
+           }
+
+         /* Copy anything from the current block that needs to go
+            into the user buffer. */
+
+        offset = sk->copied_seq+1 - skb->h.th->seq;
+
+        if (skb->h.th->syn) offset --;
+        if (offset < skb->len )
+          {
+             /* if there is urgent data we must either return or skip
+                over it. */
+             if (skb->h.th->urg)
+               {
+                  if (skb->urg_used)
+                    {
+                       if (flags & MSG_PEEK) break;
+                       sk->copied_seq += skb->h.th->urg_ptr;
+                       offset += skb->h.th->urg_ptr;
+                       if (offset > skb->len)
+                         {
+                            skb->used = 1;
+                            skb=skb->next;
+                            continue;
+                         }
+                    }
+                  else
+                    {
+                       break;
+                    }
+               }
+             used = min(skb->len - offset, len);
+
+             verify_area (to, used);
+             memcpy_tofs(to, ((unsigned char *)skb->h.th) +
+                         skb->h.th->doff*4 +
+                         offset,
+                         used);
+             copied += used;
+             len -= used;
+             to += used;
+             if (!(flags & MSG_PEEK))
+               sk->copied_seq += used;
+             
+             /* mark this data used if we are really reading it, and if
+                it doesn't contain any urgent data. And we have used all
+                the data. */
+             if (!(flags & MSG_PEEK) &&
+                 (!skb->h.th->urg || skb->urg_used) &&
+                 (used + offset >= skb->len) )
+               skb->used = 1;
+             
+             /* see if this is the end of a message or if the remaining data
+                is urgent. */
+             if ( skb->h.th->psh || skb->h.th->urg)
+               {
+                  break;
+               }
+          }
+        else /* already used this data, must be a retransmit. */
+          {
+             skb->used = 1;
+          }
+        skb=skb->next;
+      }
+    cleanup_rbuf (sk);
+    release_sock (sk);
+    if (copied == 0 && nonblock) return (-EAGAIN);
+    return (copied);
+}
+
+/* this routine will send a reset to the other tcp. */
+static  void
+tcp_reset(unsigned long saddr, unsigned long daddr, struct tcp_header *th,
+          struct proto *prot, struct options *opt, struct device *dev)
+{
+  /* we need to grab some memory, and put together a reset, and then
+     put it into the queue to be sent. */
+  struct sk_buff *buff;
+  struct tcp_header *t1;
+  int tmp;
+  buff=prot->wmalloc(NULL, MAX_RESET_SIZE,1);
+  if (buff == NULL) return;
+
+  PRINTK("tcp_reset buff = %X\n", buff);
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_RESET_SIZE;
+  buff->len = sizeof (*t1);
+  buff->sk = NULL;
+  buff->dev = dev;
+
+  t1=(struct tcp_header *)(buff + 1);
+  /* put in the ip_header and routing stuff. */
+  tmp = prot->build_header (buff, saddr, daddr, &dev, IP_TCP, opt,
+                           sizeof(struct tcp_header));
+  if (tmp < 0)
+    {
+      prot->wfree (NULL,buff->mem_addr, buff->mem_len);
+      return;
+    }
+  t1 = (struct tcp_header *)((char *)t1 +tmp);
+  buff->len += tmp;
+  memcpy (t1, th, sizeof (*t1));
+  /* swap the send and the receive. */
+  t1->dest = th->source;
+  t1->source = th->dest;
+  t1->seq = th->ack_seq; /* add one so it will be in
+                           the right range.*/
+  t1->rst = 1;
+  t1->ack = 0;
+  t1->syn = 0;
+  t1->urg = 0;
+  t1->fin = 0;
+  t1->psh = 0;
+  t1->doff = sizeof (*t1)/4;
+  tcp_send_check (t1, saddr, daddr, sizeof (*t1), NULL);
+  prot->queue_xmit(NULL, dev, buff, 1);
+  
+}
+
+
+/* This routine handles a connection request.  This should make sure
+   we haven't already responded. */
+/* Because of the way BSD works, we have to send a syn/ack now. This also
+ means it will be harder to close a socket which is listening. */
+
+static  void
+tcp_conn_request(volatile struct sock *sk, struct sk_buff *skb,
+                unsigned long daddr,
+                unsigned long saddr, struct options *opt, struct device *dev)
+{
+  struct sk_buff *buff;
+  struct tcp_header *t1;
+  unsigned char *ptr;
+  volatile struct sock *newsk;
+  struct tcp_header *th;
+  int tmp;
+  th = skb->h.th;
+
+  PRINTK ("tcp_conn_request (sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
+         "                  opt = %X, dev = %X)\n",
+         sk, skb, daddr, saddr, opt, dev);
+  
+  /* if the socket is dead, don't accept the connection. */
+  if (!sk->dead)
+    {
+       wake_up(sk->sleep);
+    }
+  else
+    {
+       PRINTK ("tcp_conn_request on dead socket\n");
+       tcp_reset (daddr, saddr, th, sk->prot, opt, dev);
+       free_skb (skb, FREE_READ);
+       return;
+    }
+
+
+  /* we need to build a new sock struct. */
+  /* It is sort of bad to have a socket without an inode attached to
+     it, but the wake_up's will just wake up the listening socket,
+     and if the listening socket is destroyed before this is taken
+     off of the queue, this will take care of it. */
+
+  newsk = malloc(sizeof (struct sock));
+  if (newsk == NULL) 
+    {
+       /* just ignore the syn.  It will get retransmitted. */
+       free_skb (skb, FREE_READ);
+       return;
+    }
+
+
+  PRINTK ("newsk = %X\n", newsk);
+  memcpy ((void *)newsk, (void *)sk, sizeof (*newsk));
+  newsk->wback = NULL;
+  newsk->wfront = NULL;
+  newsk->rqueue = NULL;
+  newsk->send_head = NULL;
+  newsk->send_tail = NULL;
+  newsk->back_log = NULL;
+  newsk->blog = 0;
+  newsk->intr = 0;
+  newsk->proc = 0;
+  newsk->done = 0;
+
+  newsk->pair = NULL;
+  newsk->wmem_alloc = 0;
+  newsk->rmem_alloc = 0;
+
+  newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
+
+  newsk->err = 0;
+  newsk->shutdown = 0;
+  newsk->ack_backlog = 0;
+  newsk->acked_seq = skb->h.th->seq+1;
+  newsk->fin_seq = skb->h.th->seq;
+  newsk->copied_seq = skb->h.th->seq;
+  newsk->state = TCP_SYN_RECV;
+  newsk->timeout = 0;
+  newsk->send_seq = timer_seq*SEQ_TICK-seq_offset;
+  newsk->rcv_ack_seq = newsk->send_seq;
+  newsk->urg =0;
+  newsk->retransmits = 0;
+  newsk->destroy = 0;
+  newsk->time_wait.sk = newsk;
+  newsk->time_wait.next = NULL;
+  newsk->dummy_th.source = skb->h.th->dest;
+  newsk->dummy_th.dest = skb->h.th->source;
+  /* swap these two, they are from our point of view. */
+  newsk->daddr=saddr;
+  newsk->saddr=daddr;
+
+  put_sock (newsk->num,newsk);
+  newsk->dummy_th.res1=0;
+  newsk->dummy_th.doff=6;
+  newsk->dummy_th.fin=0;
+  newsk->dummy_th.syn=0;
+  newsk->dummy_th.rst=0;
+  newsk->dummy_th.psh=0;
+  newsk->dummy_th.ack=0;
+  newsk->dummy_th.urg=0;
+  newsk->dummy_th.res2=0;
+  newsk->acked_seq = skb->h.th->seq+1;
+  newsk->copied_seq = skb->h.th->seq;
+
+  if (skb->h.th->doff == 5)
+    {
+      newsk->mtu=dev->mtu-HEADER_SIZE;
+    }
+  else
+    {
+      ptr = (unsigned char *)(skb+1);
+      if (ptr[0] != 2 || ptr[1] != 4)
+       {
+          newsk->mtu=576-HEADER_SIZE;
+       }
+      else
+       {
+         newsk->mtu = min (ptr[2]*256+ptr[3]-HEADER_SIZE,
+                           dev->mtu-HEADER_SIZE);
+       }
+    }
+
+  print_sk (newsk);
+  buff=newsk->prot->wmalloc(newsk,MAX_SYN_SIZE,1);
+  if (buff == NULL)
+    {
+       sk->err = -ENOMEM;
+       newsk->dead = 1;
+       release_sock (newsk);
+       free_skb (skb, FREE_READ);
+       return;
+    }
+  
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_SYN_SIZE;
+  buff->len=sizeof (struct tcp_header)+4;
+  buff->sk = newsk;
+  
+  t1=(struct tcp_header *)(buff + 1);
+  /* put in the ip_header and routing stuff. */
+
+  tmp = sk->prot->build_header (buff, newsk->saddr, newsk->daddr, &dev,
+                               IP_TCP, NULL, MAX_SYN_SIZE);
+
+  /* something went wrong. */
+  if (tmp < 0)
+    {
+       sk->err = tmp;
+       sk->prot->wfree(newsk, buff->mem_addr, buff->mem_len);
+       newsk->dead = 1;
+       release_sock (newsk);
+       free_skb (skb, FREE_READ);
+       return;
+    }
+
+  buff->len += tmp;
+  t1 = (struct tcp_header *)((char *)t1 +tmp);
+  
+  memcpy (t1, skb->h.th, sizeof (*t1));
+  buff->h.seq = newsk->send_seq;
+  /* swap the send and the receive. */
+  t1->dest = skb->h.th->source;
+  t1->source = newsk->dummy_th.source;
+  t1->seq = net32(newsk->send_seq++);
+  t1->ack = 1;
+  newsk->window = sk->prot->rspace(newsk);
+  t1->window = net16(newsk->window);
+  t1->res1=0;
+  t1->res2=0;
+  t1->rst = 0;
+  t1->urg = 0;
+  t1->psh = 0;
+  t1->syn = 1;
+  t1->ack_seq = net32(skb->h.th->seq+1);
+  t1->doff = sizeof (*t1)/4+1;
+
+  ptr = (unsigned char *)(t1+1);
+  ptr[0]=2;
+  ptr[1]=4;
+  ptr[2]=((dev->mtu - HEADER_SIZE) >> 8) & 0xff;
+  ptr[3]=(dev->mtu - HEADER_SIZE) & 0xff;
+
+  tcp_send_check (t1, daddr, saddr, sizeof (*t1)+4, newsk);
+  newsk->prot->queue_xmit(newsk, dev, buff, 0);
+
+  newsk->time_wait.len = TCP_CONNECT_TIME;
+  PRINTK ("newsk->time_wait.sk = %X\n", newsk->time_wait.sk);
+  reset_timer ((struct timer *)&newsk->time_wait);
+  skb->sk = newsk;
+  /* charge the sock_buff to newsk. */
+  sk->rmem_alloc -= skb->mem_len;
+  newsk->rmem_alloc += skb->mem_len;
+
+  if (sk->rqueue == NULL)
+    {
+      skb->next = skb;
+      skb->prev = skb;
+      sk->rqueue = skb;
+    }
+  else
+    {
+      skb->next = sk->rqueue;
+      skb->prev = sk->rqueue->prev;
+      sk->rqueue->prev = skb;
+      skb->prev->next = skb;
+    }
+  release_sock (newsk);
+}
+
+static  void
+tcp_close (volatile struct sock *sk, int timeout)
+{
+  /* we need to grab some memory, and put together a fin, and then
+     put it into the queue to be sent. */
+  struct sk_buff *buff;
+  int need_reset = 0;
+  struct tcp_header *t1,*th;
+  struct proto *prot;
+  struct device *dev=NULL;
+  int tmp;
+  PRINTK ("tcp_close ((struct sock *)%X, %d)\n",sk, timeout);
+  sk->inuse = 1;
+  sk->keepopen = 0;
+  sk->shutdown = SHUTDOWN_MASK;
+
+  if (!sk->dead)
+    wake_up (sk->sleep);
+
+
+  /* we need to flush the recv. buffs. */
+  
+  if (sk->rqueue != NULL)
+    {
+       struct sk_buff *skb;
+       struct sk_buff *skb2;
+       skb = sk->rqueue;
+       do {
+         skb2=skb->next;
+         free_skb (skb, FREE_READ);
+         skb=skb2;
+       } while (skb != sk->rqueue);
+       need_reset = 1;
+    }
+  sk->rqueue = NULL;
+
+
+  switch (sk->state)
+    {
+      case TCP_FIN_WAIT1:
+      case TCP_FIN_WAIT2:
+      case TCP_LAST_ACK:
+       if (timeout)
+        tcp_time_wait(sk);
+       release_sock (sk);
+       if (!need_reset)
+        return;
+       break;
+
+      case TCP_TIME_WAIT:
+       if (timeout)
+        sk->state = TCP_CLOSE;
+       release_sock (sk);
+       return;
+
+      case TCP_LISTEN:
+       sk->state = TCP_CLOSE;
+       release_sock(sk);
+       return;
+
+      case TCP_CLOSE:
+
+       release_sock(sk);
+       return;
+       
+
+    case TCP_ESTABLISHED:
+    case TCP_SYN_SENT:
+    case TCP_SYN_RECV:
+
+      prot = (struct proto *)sk->prot;
+      th=(struct tcp_header *)&sk->dummy_th;
+
+       buff=prot->wmalloc(sk, MAX_FIN_SIZE,1);
+       if (buff == NULL)
+        {
+           /* this will force it to try again later. */
+           sk->state = TCP_ESTABLISHED;
+           sk->timeout = TIME_CLOSE;
+           sk->time_wait.len = 100; /* wait a second. */
+           reset_timer ((struct timer *)&sk->time_wait);
+           return;
+        }
+
+      buff->mem_addr = buff;
+      buff->mem_len = MAX_FIN_SIZE;
+      buff->sk = sk;
+      buff->len = sizeof (*t1);
+      t1=(struct tcp_header *)(buff + 1);
+      /* put in the ip_header and routing stuff. */
+      tmp = prot->build_header (buff,sk->saddr, sk->daddr, &dev,
+                               IP_TCP, sk->opt,
+                               sizeof(struct tcp_header));
+      if (tmp < 0)
+       {
+         prot->wfree (sk,buff->mem_addr, buff->mem_len);
+         PRINTK ("Unable to build header for fin.\n");
+         release_sock(sk);
+         return;
+       }
+      t1 = (struct tcp_header *)((char *)t1 +tmp);
+      buff ->len += tmp;
+      buff->dev = dev;
+      memcpy (t1, th, sizeof (*t1));
+      t1->seq = net32(sk->send_seq);
+      sk->send_seq++;
+      buff->h.seq = sk->send_seq;
+      t1->ack = 1;
+       /* ack everything immediately from now on. */
+      sk->delay_acks = 0;
+      t1->ack_seq = net32(sk->acked_seq);
+      t1->window = net16(sk->prot->rspace(sk));
+      t1->fin = 1;
+      t1->rst = need_reset;
+      t1->doff = sizeof (*t1)/4;
+      tcp_send_check (t1, sk->saddr, sk->daddr, sizeof (*t1), sk);
+
+      if (sk->wfront == NULL)
+       {
+         prot->queue_xmit(sk, dev, buff, 0);
+       }
+      else
+       {
+          sk->time_wait.len = sk->rtt;
+          sk->timeout = TIME_WRITE;
+          reset_timer ((struct timer *)&sk->time_wait);
+          buff->next = NULL;
+          if (sk->wback == NULL)
+            {
+               sk->wfront=buff;
+            }
+          else
+            {
+               sk->wback->next = buff;
+            }
+          sk->wback = buff;
+          
+       }
+      sk->state = TCP_FIN_WAIT1;
+    }
+  release_sock (sk);
+}
+
+
+/* This routine takes stuff off of the write queue, and puts it in the
+   xmit queue. */
+static  void
+tcp_write_xmit (volatile struct sock *sk)
+{
+  struct sk_buff *skb;
+  while (sk->wfront != NULL && before (sk->wfront->h.seq, sk->window_seq) &&
+        sk->packets_out < sk->cong_window)
+    {
+      skb = sk->wfront;
+      sk->wfront = skb->next;
+      if (sk->wfront == NULL)
+       sk->wback = NULL;
+      sk->prot->queue_xmit (sk, skb->dev, skb, skb->free);
+    }
+}
+
+
+
+/* This routine deals with incoming acks, but not outgoing ones. */
+
+static  int
+tcp_ack (volatile struct sock *sk, struct tcp_header *th, unsigned long saddr)
+{
+  unsigned long ack;
+  ack = net32(th->ack_seq);
+
+  if (!between (ack , sk->rcv_ack_seq, sk->send_seq)) 
+    {
+      if (after (ack, sk->send_seq) || sk->state != TCP_ESTABLISHED) 
+       {
+         return (0);
+       }
+      if (sk->keepopen)
+       reset_timer ((struct timer *)&sk->time_wait);
+      sk->retransmits = 0;
+      return (1);
+    }
+
+  sk->window_seq = ack + net16(th->window);
+
+
+  /* we don't want too many packets out there. */
+  if (sk->cong_window < 2048 && ack != sk->rcv_ack_seq)
+    {
+       if (sk->exp_growth)
+        sk->cong_window *= 2;
+       else
+        sk->cong_window++;
+    }
+
+  sk->rcv_ack_seq = ack;
+
+  /* see if we can take anything off of the retransmit queue. */
+  while (sk->send_head != NULL)
+    {
+      if (before (sk->send_head->h.seq, ack+1))
+       {
+         struct sk_buff *oskb;
+         /* we have one less packet out there. */
+         sk->packets_out --;
+         cli();
+         oskb = sk->send_head;
+         /* estimate the rtt. */
+         sk->rtt += ((jiffies - oskb->when) - sk->rtt)/2;
+         if (sk->rtt < 30) sk->rtt = 30;
+         sk->send_head = oskb->link3;
+         if (sk->send_head == NULL) 
+           {
+             sk->send_tail = NULL;
+           }
+         /* we may need to remove this from the dev send list. */
+         if (oskb->next != NULL)
+           {
+              if (oskb->next != oskb)
+                {
+                   oskb->next->prev = oskb->prev;
+                   oskb->prev->next = oskb->next;
+                }
+              else
+                {
+                   int i;
+                   for (i = 0; i < DEV_NUMBUFFS; i++)
+                     {
+                        if (oskb->dev->buffs[i] = oskb)
+                          {
+                             oskb->dev->buffs[i]= NULL;
+                             break;
+                          }
+                     }
+                }
+           }
+         free_skb  (oskb, FREE_WRITE); /* write. */
+         sti();
+         if (!sk->dead)
+           wake_up(sk->sleep);
+       }
+      else
+       {
+         break;
+       }
+
+    }
+
+
+  /* at this point we need to check to see if we have anything
+     which needs to be retransmiteed.  If we have failed to get
+     some acks i.e. had to retransmit something, and we succeded, we
+     should then attempt to retransmit everything right now. */
+
+  if (sk->retransmits && sk->send_head != NULL)
+    {
+      sk->prot->retransmit (sk,1);
+    }
+  sk->retransmits = 0;
+
+  /* maybe we can take some stuff off of the write queue, and put it onto
+     the xmit queue. */
+  if (sk->wfront != NULL && sk->packets_out < sk->cong_window)
+    {
+      if (after (sk->window_seq, sk->wfront->h.seq))
+       {
+         tcp_write_xmit (sk);
+       }
+    }
+  else
+    {
+       if (sk->send_head == NULL && sk->ack_backlog == 0 &&
+          sk->state != TCP_TIME_WAIT)
+        {
+           delete_timer((struct timer *)&sk->time_wait);
+           sk->timeout = 0;
+        }
+       else
+        {
+           if (sk->state == TCP_TIME_WAIT)
+             {
+                sk->time_wait.len = TCP_TIMEWAIT_LEN;
+                sk->timeout = TIME_CLOSE;
+             }
+           reset_timer ((struct timer *)&sk->time_wait);
+        }
+    }
+
+  /* see if we are done. */
+  if ( sk->state == TCP_TIME_WAIT)
+    {
+       if (sk->rcv_ack_seq == sk->send_seq &&   
+          sk->acked_seq == sk->fin_seq);
+       if (!sk->dead) wake_up (sk->sleep);
+       sk->state = TCP_CLOSE;
+    }
+
+  if (sk->state == TCP_FIN_WAIT1)
+    {
+      if (sk->rcv_ack_seq == sk->send_seq)
+       sk->state = TCP_FIN_WAIT2;
+    }
+  
+  if (sk->state == TCP_LAST_ACK)
+    {
+      if (sk->rcv_ack_seq == sk->send_seq)
+       {
+          if (sk->acked_seq != sk->fin_seq)
+            {
+               tcp_time_wait(sk);
+            }
+          else
+            {
+               sk->state = TCP_CLOSE;
+            }
+       }
+      if (!sk->dead) wake_up (sk->sleep);
+    }
+
+  return (1);
+}
+
+/* This routine handles the data.  If there is room in the buffer, it
+   will be have already been moved into it.  If there is no room,
+   then we will just have to discard the packet. */
+
+static  int
+tcp_data (struct sk_buff *skb, volatile struct sock *sk, 
+         unsigned long saddr, unsigned short len)
+{
+  struct sk_buff *skb1, *skb2;
+  struct tcp_header *th;
+
+  th = skb->h.th;
+  print_th (th);
+  skb->len = len - (th->doff*4);
+
+  PRINTK("tcp_data len = %d sk = %X:\n",skb->len, sk);
+  print_sk(sk);
+
+  sk->bytes_rcv += skb->len;
+
+  if (skb->len == 0 && !th->fin && !th->urg && !th->psh)
+    {
+      /* don't want to keep passing ack's back and fourth. */
+      if (!th->ack)
+       tcp_send_ack (sk->send_seq, sk->acked_seq,sk, th, saddr);
+      free_skb(skb, FREE_READ);
+      return (0);
+    }
+
+  if (sk->shutdown & RCV_SHUTDOWN)
+    {
+       /* just ack everything. */
+       sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
+       tcp_send_ack (sk->send_seq, sk->acked_seq, sk, skb->h.th, saddr);
+       free_skb (skb, FREE_READ);
+       if (sk->state == TCP_TIME_WAIT && sk->acked_seq == sk->fin_seq)
+        {
+           if (!sk->dead) wake_up (sk->sleep);
+           sk->state = TCP_CLOSE;
+        }
+       return (0);
+    }
+
+  /* now we have to walk the chain, and figure out where this one
+     goes into it.  This is set up so that the last packet we received
+     will be the first one we look at, that way if everything comes
+     in order, there will be no performance loss, and if they come
+     out of order we will be able to fit things in nicely. */
+  
+  if (sk->rqueue == NULL)
+    {
+       PRINTK ("tcp_data: skb = %X:\n",skb);
+       print_skb (skb);
+
+       sk->rqueue = skb;
+       skb->next = skb;
+       skb->prev = skb;
+       skb1= NULL;
+    }
+  else
+    {
+      PRINTK ("tcp_data adding to chain sk = %X:\n",sk);
+      print_sk (sk);
+
+      for (skb1=sk->rqueue; ; skb1=skb1->prev)
+       {
+         PRINTK ("skb1=%X\n",skb1);
+         print_skb(skb1);
+         PRINTK ("skb1->h.th->seq = %d\n", skb1->h.th->seq);
+         if (after ( th->seq+1, skb1->h.th->seq))
+           {
+             skb->prev = skb1;
+             skb->next = skb1->next;
+             skb->next->prev = skb;
+             skb1->next = skb;
+             if (skb1 == sk->rqueue)
+               sk->rqueue = skb;
+             break;
+           }
+         if  ( skb1->prev == sk->rqueue)
+           {
+              skb->next= skb1;
+              skb->prev = skb1->prev;
+              skb->prev->next = skb;
+              skb1->prev = skb;
+              skb1 = NULL; /* so we know we might be able to ack stuff. */
+              break;
+           }
+       }
+
+      PRINTK ("skb = %X:\n",skb);
+      print_skb (skb);
+      PRINTK ("sk now equals:\n");
+      print_sk (sk);
+
+    }
+
+  th->ack_seq = th->seq + skb->len;
+  if (th->syn) th->ack_seq ++;
+  if (th->fin) th->ack_seq ++;
+
+  if (before (sk->acked_seq, sk->copied_seq))
+    {
+       printk ("*** tcp.c:tcp_data bug acked < copied\n");
+       sk->acked_seq = sk->copied_seq;
+    }
+
+  /* now figure out if we can ack anything. */
+  if (skb1 == NULL || skb1->acked || before (th->seq, sk->acked_seq+1))
+    {
+      if (before (th->seq, sk->acked_seq+1))
+       {
+         sk->acked_seq = th->ack_seq;
+         skb->acked = 1;
+         
+         for (skb2=skb->next; skb2 != sk->rqueue->next; skb2=skb2->next)
+           {
+              if (before(skb2->h.th->seq, sk->acked_seq+1))
+                {
+                   sk->acked_seq = skb2->h.th->ack_seq;
+                   skb2->acked = 1;
+                   /* force an immediate ack. */
+                   sk->ack_backlog = sk->max_ack_backlog;
+                }
+              else
+                break;
+           }
+
+         /* this also takes care of updating the window. */
+         /* this if statement needs to be simplified. */
+
+         if (!sk->delay_acks || 
+             sk->ack_backlog >= sk->max_ack_backlog || 
+             sk->window < 2*sk->mtu + sk->bytes_rcv ||
+             sk->bytes_rcv > sk->max_unacked || 
+             th->fin)
+           {
+               tcp_send_ack (sk->send_seq, sk->acked_seq,sk,th, saddr);
+           }
+         else
+           {
+              sk->ack_backlog++;
+              sk->time_wait.len = TCP_ACK_TIME;
+              sk->timeout = TIME_WRITE;
+              reset_timer ((struct timer *)&sk->time_wait);
+           }
+       }
+   }
+  else
+    {
+       /* we missed a packet.  Send an ack to try to resync things. */
+       tcp_send_ack (sk->send_seq, sk->acked_seq, sk, th, saddr);
+    }
+
+  /* now tell the user we may have some data. */
+  if (!sk->dead)
+    {
+       wake_up (sk->sleep);
+    }
+  else
+    {
+       PRINTK ("data received on dead socket. \n");
+    }
+
+  if (sk->state > TCP_CLOSING && sk->acked_seq == sk->fin_seq)
+    {
+       sk->state = TCP_CLOSE;
+    }
+
+  return (0);
+}
+
+static  int
+tcp_urg (volatile struct sock *sk, struct tcp_header *th, unsigned long saddr)
+{
+    extern int kill_pg (int pg, int sig, int priv);
+    extern int kill_proc (int pid, int sig, int priv);
+    
+    if (!sk->dead)
+      wake_up(sk->sleep);
+    
+    if (sk->urginline)
+      {
+         th->urg = 0;
+         th->psh = 1;
+         return (0);
+      }
+
+    sk->urg++;
+
+    if (!sk->urg)
+      {
+         /* so if we get more urgent data, we don't 
+            signal the user again. */
+         if (sk->proc == 0) return (0);
+         if (sk->proc > 0)
+           {
+               kill_proc (sk->proc, SIGURG, 1);
+           }
+         else 
+           {
+               kill_pg (-sk->proc, SIGURG, 1);
+           }
+      }
+    return (0);
+}
+
+/* this deals with incoming fins. */
+static  int
+tcp_fin (volatile struct sock *sk, struct tcp_header *th, 
+        unsigned long saddr, struct device *dev)
+{
+  struct sk_buff *buff;
+  struct tcp_header *t1;
+  int tmp;
+  PRINTK ("tcp_fin (sk=%X, th=%X, saddr=%X, dev=%X)\n",
+         sk, th, saddr, dev);
+  
+  if (!sk->dead)
+    {
+      wake_up (sk->sleep);
+    }
+
+  /* after sending the fin, we aren't allowed to write anymore. */
+  sk->shutdown |= SEND_SHUTDOWN;
+
+  sk->err = 0;
+  switch (sk->state)
+    {
+    case TCP_SYN_RECV:
+    case TCP_SYN_SENT:
+    case TCP_ESTABLISHED:
+      sk->state = TCP_LAST_ACK;
+      break;
+
+     default:
+    case TCP_FIN_WAIT1:
+    case TCP_TIME_WAIT:
+      sk->state = TCP_LAST_ACK;
+      /* start the timers. */
+      sk->time_wait.len = TCP_TIMEWAIT_LEN;
+      sk->timeout = TIME_CLOSE;
+      reset_timer ((struct timer *)&sk->time_wait);
+      return (0);
+
+    case TCP_FIN_WAIT2:
+      sk->state = TCP_CLOSE;
+      return (0);
+    }
+
+  /* send an ack and our own fin. */
+  buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1);
+  if (buff == NULL)
+    {
+       /* we will ignore the fin.  That way it will be sent again. */
+       return (1);
+    }
+
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_ACK_SIZE;
+  buff->len=sizeof (struct tcp_header);
+  buff->sk = sk;
+
+  t1 = (struct tcp_header *)(buff + 1);
+  /* put in the ip_header and routing stuff. */
+  tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
+                               IP_TCP,  sk->opt, MAX_ACK_SIZE);
+  if (tmp < 0)
+    {
+      sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+      return (0);
+    }
+
+  buff->len += tmp;
+  t1 = (struct tcp_header *)((char *)t1 +tmp);
+  
+  memcpy (t1, th, sizeof (*t1));
+
+  /* swap the send and the receive. */
+  t1->dest = th->source;
+  t1->source = th->dest;
+
+
+  t1->seq = net32(sk->send_seq++);
+
+  /* contains the one that needs to be acked. */
+  sk->fin_seq = th->seq+1;
+
+  buff->h.seq = sk->send_seq;
+  t1->window = net16(sk->prot->rspace(sk));
+
+  t1->res1=0;
+  t1->res2=0;
+  t1->rst = 0;
+  t1->urg = 0;
+  t1->syn = 0;
+  t1->psh = 0;
+  t1->ack = 1; 
+  t1->fin = 1;
+  t1->ack_seq = net32(sk->acked_seq);
+
+  t1->doff = sizeof (*t1)/4;
+  tcp_send_check (t1, sk->saddr, sk->daddr, sizeof (*t1), sk);
+
+  /* can't just queue this up.  It should go at the end of
+     the write queue. */
+  if (sk->wback != NULL)
+    {
+      buff->next = NULL;
+      sk->wback->next = buff;
+      sk->wback = buff;
+    }
+  else
+    {
+      sk->prot->queue_xmit (sk, dev, buff,0);
+    }
+
+  return (0);
+}
+
+
+/* this will accept the next outstanding connection. */
+
+static volatile struct sock *
+tcp_accept (volatile struct sock *sk, int flags)
+{
+  volatile struct sock *newsk;
+  struct sk_buff *skb;
+  
+  PRINTK ("tcp_accept(sk=%X, flags=%X)\n", sk, flags);
+  print_sk(sk);
+  /* we need to make sure that this socket is listening, and that
+     it has something pending. */
+  
+  if (sk->state != TCP_LISTEN)
+    {
+      sk->err = EINVAL;
+      return (NULL); 
+    }
+  /* avoid the race. */
+
+  sk->inuse = 1;
+  cli();
+  while ( (skb = get_firstr(sk)) == NULL )
+    {
+      if (flags & O_NONBLOCK)
+       {
+         sti();
+         release_sock (sk);
+         sk->err = EAGAIN;
+         return (NULL);
+       }
+
+      release_sock (sk);
+      interruptible_sleep_on (sk->sleep);
+      if (current->signal & ~current->blocked)
+       {
+          sti();
+          sk->err = ERESTARTSYS;
+          return (NULL);
+       }
+
+      sk->inuse = 1;
+    }
+  sti();
+
+  /* now all we need to do is return skb->sk. */
+  newsk = skb->sk;
+  free_skb (skb, FREE_READ);
+  release_sock (sk);
+  return (newsk);
+}
+
+
+
+/* this will initiate an outgoing connection. */
+static int
+tcp_connect (volatile struct sock *sk, struct sockaddr_in *usin, int addr_len)
+{
+  struct sk_buff *buff;
+  struct sockaddr_in sin;
+  struct device *dev=NULL;
+  unsigned char *ptr;
+  int tmp;
+  struct tcp_header *t1;
+  if (sk->state != TCP_CLOSE) return (-EISCONN);
+  if (addr_len < 8) return (-EINVAL);
+
+  verify_area (usin, addr_len);
+  memcpy_fromfs (&sin,usin, min(sizeof (sin), addr_len));
+
+  if (sin.sin_family && sin.sin_family != AF_INET) return (-EAFNOSUPPORT);
+
+  sk->daddr = sin.sin_addr.s_addr;
+  sk->send_seq = timer_seq*SEQ_TICK-seq_offset;
+  sk->rcv_ack_seq = sk->send_seq -1;
+  sk->err = 0;
+  sk->dummy_th.dest = sin.sin_port;
+
+  buff=sk->prot->wmalloc(sk,MAX_SYN_SIZE,0);
+  if (buff == NULL) 
+    {
+      return (-ENOMEM);
+    }
+  sk->inuse = 1;
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_SYN_SIZE;
+  buff->len=24;
+  buff->sk = sk;
+  t1=(struct tcp_header *)(buff + 1);
+  /* put in the ip_header and routing stuff. */
+  /* We need to build the routing stuff fromt the things saved
+     in skb. */
+  tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
+                               IP_TCP, NULL, MAX_SYN_SIZE);
+  if (tmp < 0)
+    {
+      sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+      release_sock (sk);
+      return (-ENETUNREACH);
+    }
+  buff->len += tmp;
+  t1 = (struct tcp_header *)((char *)t1 +tmp);
+
+  memcpy (t1, (void *)&(sk->dummy_th), sizeof (*t1));
+  t1->seq = net32(sk->send_seq++);
+  buff->h.seq = sk->send_seq;
+  t1->ack = 0;
+  t1->window = 2;
+  t1->res1=0;
+  t1->res2=0;
+  t1->rst = 0;
+  t1->urg = 0;
+  t1->psh = 0;
+  t1->syn = 1;
+  t1->urg_ptr = 0;
+  t1->doff =6;
+  /* put in the tcp options to say mtu. */
+  ptr=(unsigned char *)(t1+1);
+  ptr[0]=2;
+  ptr[1]=4;
+  ptr[2]=(dev->mtu- HEADER_SIZE) >> 8;
+  ptr[3]=(dev->mtu- HEADER_SIZE) & 0xff;
+  sk->mtu = dev->mtu - HEADER_SIZE;
+  tcp_send_check (t1, sk->saddr, sk->daddr,
+                 sizeof (struct tcp_header) + 4, sk);
+  /* this must go first otherwise a really quick response will
+     get reset. */
+  sk->state = TCP_SYN_SENT;
+
+  sk->prot->queue_xmit(sk, dev, buff, 0);
+  
+  sk->time_wait.len = TCP_CONNECT_TIME;
+  reset_timer ((struct timer *)&sk->time_wait);
+  sk->retransmits = TCP_RETR1 - TCP_SYN_RETRIES;
+  release_sock (sk);
+  return (0);
+}
+
+
+/* this functions checks to see if the tcp header is actually
+   acceptible. */
+
+static  int
+tcp_sequence (volatile struct sock *sk, struct tcp_header *th, short len,
+             struct options *opt, unsigned long saddr)
+{
+   /* this isn't quite right.  sk->acked_seq could be more recent
+      than sk->window.  This is however close enough.  We will accept
+      slightly more packets than we should, but it should not cause
+      problems unless someone is trying to forge packets. */
+
+  if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)||
+      between(th->seq + len-sizeof (*th), sk->acked_seq+1, 
+             sk->acked_seq + sk->window))
+    {
+       return (1);
+    }
+
+  /* if it's too far ahead, send an ack to let the other end
+     know what we expect. */
+  if (after (th->seq, sk->acked_seq + sk->window))
+    {
+       tcp_send_ack (sk->send_seq, sk->acked_seq, sk, th, saddr);
+       return (0);
+    }
+
+  if (!th->rst)
+    {
+       if (len != th->doff*4 || th->fin || th->syn)
+        {
+           sk->delay_acks = 0;
+        }
+
+       /* try to resync things. */
+       tcp_send_ack (net32(th->ack_seq), sk->acked_seq, sk, th, saddr);
+    }
+
+  /* in case it's just a late ack, let it through */
+  if (th->ack && len == th->doff*4 && after (th->seq, sk->acked_seq - 4096) &&
+      !th->fin && !th->syn) return (1);
+
+  return (0);
+}
+
+/* This deals with the tcp option.  It isn't very general yet. */
+static void
+tcp_options (volatile struct sock *sk, struct tcp_header *th)
+{
+  unsigned char *ptr;
+  ptr = (unsigned char *)(th + 1);
+  if (ptr[0] != 2 || ptr[1] != 4)
+    {
+       sk->mtu = min (sk->mtu, 576-HEADER_SIZE);
+       return;
+    }
+  sk->mtu = min (sk->mtu, ptr[2]*256 + ptr[3] - HEADER_SIZE);
+}
+
+int
+tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+       unsigned long daddr, unsigned short len,
+       unsigned long saddr, int redo, struct ip_protocol * protocol)
+{
+  struct tcp_header *th;
+  volatile struct sock *sk;
+
+  th = skb->h.th;
+  /* find the socket. */
+  sk=get_sock(&tcp_prot, net16(th->dest), saddr, th->source, daddr);
+  PRINTK("<<\n");
+  PRINTK("len = %d, redo = %d, skb=%X\n", len, redo, skb);
+
+  if (sk)
+    {
+      PRINTK ("sk = %X:\n",sk);
+      print_sk (sk);
+    }
+
+  if (!redo)
+    {
+       if (th->check && tcp_check (th, len, saddr, daddr ))
+        {
+           skb->sk = NULL;
+           free_skb (skb, 0);
+           /* we don't release the socket because it was never
+              marked in use. */
+           return (0);
+        }
+
+       /*See if we know about the socket. */
+       if (sk == NULL)
+       {
+         if (!th->rst)
+           tcp_reset (daddr, saddr, th, &tcp_prot, opt,dev);
+         skb->sk = NULL;
+         free_skb (skb, 0);
+         return (0);
+       }
+
+       skb->len = len;
+       skb->sk = sk;
+       skb->acked = 0;
+       skb->used = 0;
+       skb->free = 0;
+       skb->urg_used = 0;
+       skb->saddr = daddr;
+       skb->daddr = saddr;
+
+       th->seq = net32(th->seq);
+
+       cli();
+
+       /* we may need to add it to the backlog here. */
+       if (sk->inuse)
+        {
+           if (sk->back_log == NULL)
+             {
+                sk->back_log = skb;
+                skb->next = skb;
+                skb->prev = skb;
+             }
+           else
+             {
+                skb->next = sk->back_log;
+                skb->prev = sk->back_log->prev;
+                skb->prev->next = skb;
+                skb->next->prev = skb;
+             }
+           sti();
+           return (0);
+        }
+       sk->inuse = 1;
+       sti();
+    }
+
+  /* charge the memory to the socket. */
+  if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
+    {
+       skb->sk = NULL;
+       free_skb (skb, 0);
+       release_sock (sk);
+       return (0);
+    }
+       
+  sk->rmem_alloc += skb->mem_len;
+
+  PRINTK ("About to do switch. \n");
+
+  /* now deal with it. */
+
+  switch (sk->state)
+    {
+       /* this should close the system down if it's waiting for an
+         ack that is never going to be sent. */
+    case TCP_LAST_ACK:
+      if (th->rst)
+       {
+         sk->err = ECONNRESET;
+         sk->state = TCP_CLOSE;
+         if (!sk->dead)
+           {
+             wake_up (sk->sleep);
+           }
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       }
+
+    case TCP_ESTABLISHED:
+    case TCP_FIN_WAIT1:
+    case TCP_FIN_WAIT2:
+    case TCP_TIME_WAIT:
+   
+      if (!tcp_sequence (sk, th, len, opt, saddr))
+       {
+          free_skb (skb, FREE_READ);
+          release_sock(sk);
+          return (0);
+       }
+      if (th->rst)
+       {
+         sk->err = ECONNRESET;
+         sk->state = TCP_CLOSE;
+         if (!sk->dead)
+           {
+             wake_up (sk->sleep);
+           }
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       }
+
+      if (opt->security != 0 || opt->compartment != 0 || th->syn)
+       {
+          sk->err = ECONNRESET;
+          sk->state = TCP_CLOSE;
+          tcp_reset (daddr, saddr,  th, sk->prot, opt,dev);
+          if (!sk->dead)
+            {
+               wake_up (sk->sleep);
+            }
+          free_skb (skb, FREE_READ);
+          release_sock(sk);
+          return (0);
+       }
+
+      if (th->ack)
+       {
+          if(!tcp_ack (sk, th, saddr))
+           {
+              free_skb (skb, FREE_READ);
+              release_sock(sk);
+              return (0);
+          }
+       }
+      if (th->urg)
+       {
+         if (tcp_urg (sk, th, saddr))
+           {
+              free_skb (skb, FREE_READ);
+              release_sock(sk);
+              return (0);
+           }
+       }
+
+      if ( tcp_data (skb, sk, saddr, len))
+       {
+          free_skb (skb, FREE_READ);
+          release_sock(sk);
+          return (0);
+       }
+
+      if (!th->fin)
+       {
+         release_sock(sk);
+         return (0);
+       }
+
+      tcp_fin (sk, th, saddr, dev);
+      release_sock(sk);
+      return (0);
+
+    case TCP_CLOSE:
+
+      if (sk->dead || sk->daddr)
+       {
+          PRINTK ("packet received for closed,dead socket\n");
+          free_skb (skb, FREE_READ);
+          release_sock (sk);
+          return (0);
+       }
+
+      if (!th->rst)
+       {
+         if (!th->ack)
+           th->ack_seq=0;
+         tcp_reset (daddr, saddr, th, sk->prot, opt,dev);
+       }
+      free_skb (skb, FREE_READ);
+      release_sock(sk);
+      return (0);
+
+    case TCP_LISTEN:
+      if (th->rst)
+       {
+          free_skb (skb, FREE_READ);
+          release_sock(sk);
+          return (0);
+       }
+      if (th->ack)
+       {
+         tcp_reset (daddr, saddr, th, sk->prot, opt,dev );
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       }
+
+      if (th->syn)
+       {
+/*       if (opt->security != 0 || opt->compartment != 0)
+           {
+             tcp_reset (daddr, saddr, th, prot, opt,dev);
+             release_sock(sk);
+             return (0);
+           } */
+
+         /* now we just put the whole thing including the header
+            and saddr, and protocol pointer into the buffer.
+            We can't respond until the user tells us to accept
+            the connection. */
+
+         tcp_conn_request (sk, skb, daddr, saddr, opt, dev);
+
+         release_sock(sk);
+         return (0);
+       }
+
+      free_skb (skb, FREE_READ);
+      release_sock(sk);
+      return (0);
+
+    default:
+      if (!tcp_sequence (sk, th, len, opt, saddr)) 
+       {
+          free_skb (skb, FREE_READ);
+          release_sock(sk);
+          return (0);
+       }
+
+    case TCP_SYN_SENT:
+      if (th->rst)
+       {
+         sk->err = ECONNREFUSED;
+         sk->state = TCP_CLOSE;
+         if (!sk->dead)
+           {
+             wake_up (sk->sleep);
+           }
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       }
+/*      if (opt->security != 0 || opt->compartment != 0 )
+       {
+         sk->err = ECONNRESET;
+         sk->state = TCP_CLOSE;
+         tcp_reset (daddr, saddr,  th, sk->prot, opt, dev);
+         if (!sk->dead)
+         {
+         wake_up (sk->sleep);
+         }
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       } */
+
+      if (!th->ack) 
+       {
+         if (th->syn)
+           {
+             sk->state = TCP_SYN_RECV;
+           }
+
+         free_skb (skb, FREE_READ);
+         release_sock(sk);
+         return (0);
+       }
+
+      switch (sk->state)
+       {
+       case TCP_SYN_SENT:
+         if (!tcp_ack(sk, th, saddr))
+           {
+             tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
+             free_skb (skb, FREE_READ);
+             release_sock(sk);
+             return (0);
+           }
+
+         /* if the syn bit is also set, switch to tcp_syn_recv,
+            and then to established. */
+             
+         if (!th->syn) 
+           {
+             free_skb (skb, FREE_READ);
+             release_sock (sk);
+             return (0);
+           }
+
+         /* ack the syn and fall through. */
+         sk->acked_seq = th->seq+1;
+         sk->fin_seq = th->seq;
+         tcp_send_ack (sk->send_seq, th->seq+1, sk, 
+                            th, sk->daddr);
+       
+       case TCP_SYN_RECV:
+         if (!tcp_ack(sk, th, saddr))
+           {
+             tcp_reset(daddr, saddr, th, sk->prot, opt, dev);
+             free_skb (skb, FREE_READ);
+             release_sock(sk);
+             return (0);
+           }
+
+         sk->state = TCP_ESTABLISHED;
+         /* now we need to finish filling out some of the tcp
+            header. */
+
+         /* we need to check for mtu info. */
+         tcp_options(sk, th);
+         sk->dummy_th.dest = th->source;
+         sk->copied_seq = sk->acked_seq-1;
+         if (!sk->dead)
+           {
+             wake_up (sk->sleep);
+           }
+
+         /* now process the rest like we were already in the established
+            state. */
+         if (th->urg)
+           if (tcp_urg (sk, th, saddr))
+             {
+                free_skb (skb, FREE_READ);
+                release_sock(sk);
+                return (0);
+             }
+         if (tcp_data (skb, sk, saddr, len))
+           free_skb (skb, FREE_READ);
+         
+         if (th->fin)
+           tcp_fin(sk, th, saddr, dev);
+
+         release_sock(sk);
+         return (0);
+       }
+
+      if (th->urg)
+       {
+         if (tcp_urg (sk, th, saddr))
+           {
+              free_skb (skb, FREE_READ);
+              release_sock (sk);
+              return (0);
+           }
+       }
+
+      if (tcp_data (skb, sk, saddr, len))
+       {
+          free_skb (skb, FREE_READ);
+          release_sock (sk);
+          return (0);
+       }
+
+      if (!th->fin)
+       {
+         release_sock(sk);
+         return (0);
+       }
+      tcp_fin (sk, th, saddr, dev);
+      release_sock(sk);
+      return (0);
+    }
+}
+
+
+/* this routine sends a packet with an out of date sequence number. It
+   assumes the other end will try to ack it. */
+
+static  void
+tcp_write_wakeup(volatile struct sock *sk)
+{
+  struct sk_buff *buff;
+  struct tcp_header *t1;
+  struct device *dev=NULL;
+  int tmp;
+  if (sk -> state != TCP_ESTABLISHED) return;
+
+  buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1);
+  /* no big loss. */
+  if (buff == NULL) return;
+
+  buff->mem_addr = buff;
+  buff->mem_len = MAX_ACK_SIZE;
+  buff->len=sizeof (struct tcp_header);
+  buff->free = 1;
+  buff->sk = sk;
+  PRINTK ("in tcp_write_wakeup\n");
+  t1=(struct tcp_header *)(buff + 1);
+
+  /* put in the ip_header and routing stuff. */
+  tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
+                               IP_TCP, sk->opt, MAX_ACK_SIZE);
+  if (tmp < 0)
+    {
+      sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+      return;
+    }
+
+  buff->len += tmp;
+  t1 = (struct tcp_header *)((char *)t1 +tmp);
+
+  memcpy (t1,(void *) &sk->dummy_th, sizeof (*t1));
+
+  /* use a previous sequence.  This should cause the other end
+     to send an ack. */
+  t1->seq = net32(sk->send_seq-1);
+  t1->ack = 1; 
+  t1->res1= 0;
+  t1->res2= 0;
+  t1->rst = 0;
+  t1->urg = 0;
+  t1->psh = 0;
+  t1->fin = 0;
+  t1->syn = 0;
+  t1->ack_seq = net32(sk->acked_seq);
+  t1->window = net16(sk->prot->rspace(sk));
+  t1->doff = sizeof (*t1)/4;
+  tcp_send_check (t1, sk->saddr, sk->daddr, sizeof (*t1), sk);
+  /* send it and free it.  This will prevent the timer from 
+     automatically being restarted. */
+  sk->prot->queue_xmit(sk, dev, buff, 1);
+
+}
+
+struct proto tcp_prot =
+{
+  sock_wmalloc,
+  sock_rmalloc,
+  sock_wfree,
+  sock_rfree,
+  sock_rspace,
+  sock_wspace,
+  tcp_close,
+  tcp_read,
+  tcp_write,
+  NULL,
+  NULL,
+  ip_build_header,
+  tcp_connect,
+  tcp_accept,
+  ip_queue_xmit,
+  tcp_retransmit,
+  tcp_write_wakeup,
+  tcp_read_wakeup,
+  tcp_rcv,
+  tcp_select,
+  tcp_ioctl,
+  NULL,
+  128,
+  0,
+  {NULL,}
+};
+
+
+
+
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
new file mode 100644 (file)
index 0000000..878ba03
--- /dev/null
@@ -0,0 +1,133 @@
+/* tcp.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_TCP_H
+#define _TCP_TCP_H
+
+struct tcp_header
+{
+  unsigned short source;
+  unsigned short dest;
+  unsigned long seq;
+  unsigned long ack_seq;
+  unsigned short res1:4, doff:4, fin:1, syn:1, rst:1, psh:1,
+                 ack:1, urg:1,res2:2;
+  unsigned short window;
+  unsigned short check;
+  unsigned short urg_ptr;
+};
+
+enum {
+  TCP_ESTABLISHED=1,
+  TCP_SYN_SENT,
+  TCP_SYN_RECV,
+  TCP_CLOSING, /* not a valid state, just a seperator so we can use
+                 < tcp_closing or > tcp_closing for checks. */
+  TCP_FIN_WAIT1,
+  TCP_FIN_WAIT2,
+  TCP_TIME_WAIT,
+  TCP_CLOSE,
+  TCP_LAST_ACK,
+  TCP_LISTEN
+};
+
+#define MAX_SYN_SIZE 44 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_FIN_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_ACK_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_RESET_SIZE 40 + sizeof (struct sk_buff) + MAX_HEADER
+#define MAX_WINDOW  12000
+#define MIN_WINDOW   2048
+#define MAX_ACK_BACKLOG 2
+#define MIN_WRITE_SPACE 2048
+#define TCP_WINDOW_DIFF 2048
+
+#define TCP_RETR1       7       /* this is howmany retries it does
+                                  before it tries to figure out
+                                  if the gateway is down. */
+
+#define TCP_RETR2      10      /* this should take between 3 and
+                                  ten minutes ( 1024 * rtt). */
+
+
+#define TCP_TIMEOUT_LEN 720000 /* should be about 2 hrs. */
+#define TCP_TIMEWAIT_LEN 6000 /* How long to wait to sucessfully 
+                                close the socket, about 60 seconds. */
+#define TCP_ACK_TIME 35 /* time to delay before sending an ack. */
+#define TCP_DONE_TIME 2500 /* maximum time to wait before actually destroying
+                            a socket. */
+#define TCP_WRITE_TIME 100 /* initial time to wait for an ack,
+                             after last transmit. */
+#define TCP_CONNECT_TIME 200 /* time to retransmit first syn. */
+#define TCP_SYN_RETRIES 30 /* number of times to retry openning a connection.
+                             */
+
+#define TCP_NO_CHECK 0 /* turn to one if you want the default to be no
+                         checksum . */
+
+void print_th (struct tcp_header *);
+#define HEADER_SIZE 64
+
+
+ /* this next routines deal with comparing 32 bit unsigned ints and
+    worry about wrap around. The general strategy is to do a normal
+    compare so long as neither of the numbers is within 4k of wrapping.
+    Otherwise we must check for the wrap. */
+
+ static inline int
+ before (unsigned long seq1, unsigned long seq2)
+ {
+   /* this inequality is strict. */
+   if (seq1 == seq2) return (0);
+   if (seq1 < seq2) 
+     {
+       if ((unsigned long)seq2-(unsigned long)seq1 < 32767UL) 
+        {
+          return (1);
+        }
+       else
+        {
+          return (0);
+        }
+     }
+   /* now we know seq1 > seq2.  So all we need to do is check to see
+      if seq1 has wrapped. */
+   if (seq2 < 4096UL && seq1 > (0xffffffUL - 4096UL))
+     {
+       return (1);
+     }
+   return (0);
+
+ }
+
+ static inline int
+ after (unsigned long seq1, unsigned long seq2)
+ {
+   return (before (seq2, seq1));
+ }
+
+ /* is s2<=s1<=s3 ? */
+ static inline int
+ between (unsigned long seq1, unsigned long seq2, unsigned long seq3)
+ {
+   return (after (seq1+1, seq2) && before (seq1, seq3+1));
+ }
+
+#endif
diff --git a/net/tcp/timer.c b/net/tcp/timer.c
new file mode 100644 (file)
index 0000000..fb0653f
--- /dev/null
@@ -0,0 +1,301 @@
+/* timer.c */
+/*
+  Copyright (C) 1992  Ross Biro
+  
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 1, or (at your option)
+  any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+  
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+  
+  The Author may be reached as bir7@leland.stanford.edu or
+  C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+  */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include "arp.h"
+#include "../kern_sock.h"
+
+static struct timer *timer_base=NULL;
+unsigned long seq_offset;
+
+void
+delete_timer (struct timer *t)
+{
+   struct timer *tm;
+   PRINTK ("delete_timer (t=%X)\n",t);
+   if (timer_base == NULL) return;
+   cli();
+   if (t == timer_base) 
+     {
+       timer_base = t->next;
+       if (timer_base != NULL)
+         {
+            timer_table[NET_TIMER].expires = timer_base->when;
+            timer_active |= 1 << NET_TIMER;
+         }
+       else
+         {
+            timer_active &= ~(1 << NET_TIMER);
+         }
+       sti();
+       return;
+     }
+   for (tm = timer_base;tm->next != NULL ;tm=tm->next)
+     {
+       if (tm->next == t)
+         {
+            tm->next = t->next;
+            sti();
+            return;
+         }
+     }
+   sti();
+}
+
+
+void
+reset_timer (struct timer *t)
+{
+   struct timer *tm;
+
+   delete_timer (t);
+   t->when = timer_seq + t->len;
+   PRINTK ("reset_timer (t=%X) when = %d jiffies = %d\n",t, t->when, jiffies);
+   if (t == NULL)
+     {
+       printk ("*** reset timer NULL timer\n");
+       __asm__ ("\t int $3\n"::);
+     }
+   /* first see if it goes at the beginning. */
+   cli();
+   if (timer_base == NULL) 
+     {
+       t->next = NULL;
+       timer_base = t;
+       timer_table[NET_TIMER].expires = t->when;
+       timer_active |= 1 << NET_TIMER;
+       sti();
+       return;
+     }
+   if (before (t->when, timer_base->when))
+     {
+       t->next = timer_base;
+       timer_base = t;
+       timer_table[NET_TIMER].expires = t->when;
+       timer_active |= 1 << NET_TIMER;
+       sti();
+       return;
+     }
+   for (tm = timer_base; ; tm=tm->next)
+     {
+       if (tm->next == NULL || before (t->when,tm->next->when))
+         {
+            t->next = tm->next;
+            tm->next = t;
+            sti();
+            return;
+         }
+     }
+}
+
+void
+net_timer (void)
+{
+   volatile struct sock *sk;
+
+   /* now we will only be called whenever we need to do something, but
+      we must be sure to process all of the sockets that need it. */
+
+   while (timer_base != NULL && after (timer_seq+1 ,timer_base->when))
+     {
+       int why;
+       sk = timer_base->sk;
+       cli();
+       if (sk->inuse)
+         {
+            sti();
+            break;
+         }
+       sk->inuse = 1;
+       sti();
+       PRINTK ("net_timer: found sk=%X\n",sk);
+       why = sk->timeout;
+
+       if (sk->keepopen)
+         {
+            sk->time_wait.len = TCP_TIMEOUT_LEN;
+            sk->timeout = TIME_KEEPOPEN;
+            reset_timer (timer_base);
+         }
+       else
+         {
+            sk->timeout = 0;
+            delete_timer(timer_base);
+         }
+       
+       /* always see if we need to send an ack. */
+       if (sk->ack_backlog)
+         {
+            sk->prot->read_wakeup(sk);
+            if (!sk->dead) wake_up (sk->sleep);
+         }
+       
+       /* now we need to figure out why the socket was on the timer. */
+       switch (why)
+         {
+
+           case TIME_DONE:
+            if (!sk->dead || sk->state != TCP_CLOSE)
+              {
+                 printk ("non dead socket in time_done\n");
+                 release_sock (sk);
+                 break;
+              }
+            destroy_sock (sk);
+            break;
+
+           case TIME_DESTROY: /* we've waited for a while for all
+                                 the memory assosiated with the
+                                 socket to be freed.  We need to
+                                 print an error message. */
+            PRINTK ("possible memory leak.  sk = %X\n", sk);
+            print_sk (sk);
+            reset_timer ((struct timer *)&sk->time_wait);
+            sk->inuse = 0;
+            break;
+            
+           case TIME_CLOSE: /* we've waited long enough, close the
+                               socket. */
+            
+            sk->state = TCP_CLOSE;
+            delete_timer ((struct timer *)&sk->time_wait);
+            /* kill the arp entry 
+               in case the hardware has changed. */
+            arp_destroy (sk->daddr);
+            if (!sk->dead)
+              wake_up (sk->sleep);
+            release_sock(sk);
+            break;
+            
+           case TIME_WRITE: /* try to retransmit. */
+            if (sk->send_head != NULL)
+              {
+                 sk->retransmits ++;
+                 if (sk->retransmits > TCP_RETR1)
+                   {
+                      arp_destroy (sk->daddr);
+                      ip_route_check (sk->daddr);
+                   }
+
+                 if (sk->retransmits > TCP_RETR2)
+                   {
+                      sk->err = ETIMEDOUT;
+                      arp_destroy (sk->daddr);
+                      if (sk->state == TCP_FIN_WAIT1 ||
+                          sk->state == TCP_FIN_WAIT2 ||
+                          sk->state == TCP_LAST_ACK)
+                        {
+                           sk->state = TCP_TIME_WAIT;
+                           sk->timeout = TIME_CLOSE;
+                           sk->time_wait.len = TCP_TIMEWAIT_LEN;
+                           reset_timer ((struct timer *)&sk->time_wait);
+                           release_sock(sk);
+                           break;
+                        }
+                      else /* sk->state == ... */
+                        {
+                           sk->prot->close (sk,1);
+                           break;
+                        }
+                   }
+                 else /* sk->retransmites .. */
+                   {
+                      sk->prot->retransmit (sk, 1);
+                      release_sock (sk);
+                   }
+                 break;
+              }
+            /* if we have stuff which hasn't been written because the
+               window is too small, fall throught to TIME_KEEPOPEN */
+            if (sk->wfront == NULL)
+              {
+                 release_sock (sk);
+                 break;
+              }
+
+            /* this basically assumes tcp here. */
+            /* exponential fall back. */
+            sk->rtt *= 2;
+            sk->time_wait.len = sk->rtt*2;
+            sk->timeout = TIME_WRITE;
+            reset_timer ((struct timer *)&sk->time_wait);
+
+           case TIME_KEEPOPEN: /* send something to keep the
+                                  connection open. */
+            sk->retransmits ++;
+            if (sk->retransmits > TCP_RETR1)
+              {
+                 arp_destroy (sk->daddr);
+                 ip_route_check (sk->daddr);
+                 
+              }
+            if (sk->retransmits > TCP_RETR2)
+              {
+                 arp_destroy (sk->daddr);
+                 sk->err = ETIMEDOUT;
+                 if (sk->state == TCP_FIN_WAIT1 ||
+                     sk->state == TCP_FIN_WAIT2)
+                   {
+                      sk->state = TCP_TIME_WAIT;
+                      if (!sk->dead)
+                        wake_up (sk->sleep);
+                      release_sock(sk);
+                   }
+                 else /* sk->state == */
+                   {
+                      sk->prot->close (sk, 1);
+                   }
+                 break;
+              }
+            else /* sk->retransmits. */
+              {
+                 if (sk->prot->write_wakeup != NULL)
+                   sk->prot->write_wakeup(sk);
+                 release_sock (sk);
+                 break;
+              }
+            
+           default:
+            release_sock(sk);
+            break;
+         } /* switch */
+     } /* while (timer_base != ...  */
+
+   /* Now we need to reset the timer. */
+   if (timer_base != NULL)
+     {
+       timer_table[NET_TIMER].expires = timer_base->when;
+       timer_active |= 1 << NET_TIMER;
+     }
+}
+
+
diff --git a/net/tcp/timer.h b/net/tcp/timer.h
new file mode 100644 (file)
index 0000000..c0bd9fe
--- /dev/null
@@ -0,0 +1,41 @@
+/* timer.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_TIMER_H
+#define _TCP_TIMER_H
+
+struct timer
+{
+  unsigned long len;
+  volatile struct sock *sk;
+  unsigned long when;
+  struct timer *next;
+};
+
+
+void delete_timer (struct timer *);
+void reset_timer (struct timer *);
+void net_timer (void);
+
+#define SEQ_TICK 3
+#define timer_seq jiffies
+extern unsigned long seq_offset;
+#endif
diff --git a/net/tcp/udp.c b/net/tcp/udp.c
new file mode 100644 (file)
index 0000000..23529e0
--- /dev/null
@@ -0,0 +1,635 @@
+/* udp.c */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <netinet/in.h>
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include "../kern_sock.h" /* for PRINTK */
+#include "udp.h"
+#include "icmp.h"
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+static void
+print_uh(struct udp_header *uh)
+{
+       PRINTK("source = %d, dest = %d\n", net16(uh->source), net16(uh->dest));
+       PRINTK("len = %d, check = %d\n", net16(uh->len), net16(uh->check));
+}
+
+
+int
+udp_select (volatile struct sock *sk, int sel_type, select_table *wait)
+{
+   select_wait(sk->sleep, wait);
+   switch (sel_type)
+     {
+       case SEL_IN:
+       if (sk->rqueue != NULL) 
+         {
+            return (1);
+         }
+       return (0);
+
+       case SEL_OUT:
+       if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
+         {
+            return (1);
+         }
+       return (0);
+       
+       case SEL_EX:
+       if (sk->err) return (1); /* can this ever happen? */
+       return (0);
+     }
+   return (0);
+}
+
+/* this routine is called by the icmp module when it gets some
+   sort of error condition.  If err < 0 then the socket should
+   be closed and the error returned to the user.  If err > 0
+   it's just the icmp type << 8 | icmp code.  
+   header points to the first 8 bytes of the tcp header.  We need
+   to find the appropriate port. */
+
+void
+udp_err (int err, unsigned char *header, unsigned long daddr,
+        unsigned long saddr, struct ip_protocol *protocol)
+{
+   struct tcp_header *th;
+   volatile struct sock *sk;
+   
+   th = (struct tcp_header *)header;
+   sk = get_sock (&udp_prot, net16(th->dest), saddr, th->source, daddr);
+
+   if (sk == NULL) return;
+   if (err & 0xff00 == (ICMP_SOURCE_QUENCH << 8))
+     {
+       if (sk->cong_window > 1)
+         sk->cong_window = sk->cong_window/2;
+       return;
+     }
+
+   sk->err = icmp_err_convert[err & 0xff].errno;
+   if (icmp_err_convert[err & 0xff].fatal)
+     {
+       sk->prot->close(sk, 0);
+     }
+
+   return;
+
+}
+
+static  unsigned short
+udp_check (struct udp_header *uh, int len,
+          unsigned long saddr, unsigned long daddr)
+{
+   unsigned long sum;
+   PRINTK ("udp_check (uh=%X, len = %d, saddr = %X, daddr = %X)\n",
+          uh, len, saddr, daddr);
+
+   print_uh (uh);
+
+  __asm__("\t addl %%ecx,%%ebx\n"
+         "\t adcl %%edx,%%ebx\n"
+         "\t adcl $0, %%ebx\n"
+         : "=b" (sum)
+         : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IP_UDP*256)
+         : "cx","bx","dx" );
+
+  if (len > 3)
+    {
+      __asm__(
+             "\tclc\n"
+             "1:\n"
+             "\t lodsl\n"
+             "\t adcl %%eax, %%ebx\n"
+             "\t loop 1b\n"
+             "\t adcl $0, %%ebx\n"
+             : "=b" (sum) , "=S" (uh)
+             : "0" (sum), "c" (len/4) ,"1" (uh)
+             : "ax", "cx", "bx", "si" );
+    }
+
+  /* convert from 32 bits to 16 bits. */
+  __asm__(
+         "\t movl %%ebx, %%ecx\n"
+         "\t shrl $16,%%ecx\n"
+         "\t addw %%cx, %%bx\n"
+         "\t adcw $0, %%bx\n"
+         : "=b" (sum)
+         : "0" (sum)
+         : "bx", "cx");
+
+
+  /* check for an extra word. */
+  if ((len & 2) != 0)
+    {
+      __asm__("\t lodsw\n"
+             "\t addw %%ax,%%bx\n"
+             "\t adcw $0, %%bx\n"
+             : "=b" (sum), "=S" (uh)
+             : "0" (sum) ,"1" (uh)
+             : "si", "ax", "bx");
+    }
+
+  /* now check for the extra byte. */
+  if ((len & 1) != 0)
+    {
+      __asm__("\t lodsb\n"
+             "\t movb $0,%%ah\n"
+             "\t addw %%ax,%%bx\n"
+             "\t adcw $0, %%bx\n"
+             : "=b" (sum)
+             : "0" (sum) ,"S" (uh)
+             : "si", "ax", "bx");
+    }
+  /* we only want the bottom 16 bits, but we never cleared
+     the top 16. */
+   return ((~sum) & 0xffff);
+}
+
+static  void
+udp_send_check (struct udp_header *uh, unsigned long saddr, 
+               unsigned long daddr, int len, volatile struct sock *sk)
+{
+  uh->check = 0;
+  if (sk && sk->no_check) return;
+  uh->check = udp_check (uh, len, saddr, daddr);
+}
+
+static  int
+udp_loopback (volatile struct sock *sk, unsigned short port,
+             unsigned char *from,
+             int len, unsigned long daddr, unsigned long saddr)
+{
+       struct udp_header *uh;
+       struct sk_buff *skb;
+       volatile struct sock *pair;
+       sk->inuse = 1;
+
+       PRINTK ("udp_loopback \n");
+
+       pair = get_sock (sk->prot, net16(port), saddr,
+                        sk->dummy_th.source, daddr);
+
+       if (pair == NULL) return (0);
+
+       skb = pair->prot->rmalloc (pair,
+                                  sizeof (*skb) + sizeof (*uh) + len + 4,
+                                  0);
+
+       /* if we didn't get the memory, just drop the packet. */
+       if (skb == NULL) return (len);
+
+       skb->mem_addr = skb;
+       skb->mem_len = sizeof (*skb) + len + sizeof (*uh) + 4;
+
+       skb->daddr = saddr;
+       skb->saddr = daddr;
+
+       skb->len = len;
+       skb->h.raw = (unsigned char *)(skb+1);
+
+       uh = skb->h.uh;
+       uh -> source = sk->dummy_th.source;
+       uh -> dest = port;
+       uh -> len = len + sizeof (*uh);
+       verify_area (from , len);
+       memcpy_fromfs(uh+1, from, len);
+       pair->inuse = 1;
+       if (pair->rqueue == NULL)
+         {
+                 pair->rqueue = skb;
+                 skb->next = skb;
+                 skb->prev = skb;
+         }
+       else
+         {
+                 skb->next = pair->rqueue;
+                 skb->prev = pair->rqueue->prev;
+                 skb->prev->next = skb;
+                 skb->next->prev = skb;
+         }
+       wake_up (pair->sleep);
+       release_sock (pair);
+       release_sock (sk);
+       return (len);
+
+}
+
+static int
+udp_sendto (volatile struct sock *sk, unsigned char *from, int len,
+           int noblock,
+           unsigned flags, struct sockaddr_in *usin, int addr_len)
+{
+       /* this should be easy, we just send the packet. */
+       struct sk_buff *skb;
+       struct udp_header *uh;
+       unsigned char *buff;
+       unsigned long saddr;
+       int copied=0;
+       int amt;
+       struct device *dev=NULL;
+       struct sockaddr_in sin;
+
+       /* check the flags. */
+       if (flags) return (-EINVAL);
+       if (len < 0) return (-EINVAL);
+       if (len == 0) return (0);
+
+       PRINTK ("sendto len = %d\n", len);
+
+       /* get and verify the address. */
+       if (usin)
+         {
+                 if (addr_len < sizeof (sin))
+                   return (-EINVAL);
+                 verify_area (usin, sizeof (sin));
+                 memcpy_fromfs (&sin, usin, sizeof(sin));
+                 if (sin.sin_family &&
+                     sin.sin_family != AF_INET)
+                   return (-EINVAL);
+                 if (sin.sin_port == 0)
+                   return (-EINVAL);
+         }
+       else
+         {
+                 if (sk->state != TCP_ESTABLISHED)
+                   return (-EINVAL);
+                 sin.sin_family = AF_INET;
+                 sin.sin_port = sk->dummy_th.dest;
+                 sin.sin_addr.s_addr = sk->daddr;
+         }
+
+       /* check for a valid saddr. */
+       saddr = sk->saddr;
+       if ((saddr &  0xff000000) == 0)
+         {
+            saddr = MY_IP_ADDR;
+         }
+
+       /* if it's a broadcast, make sure we get it. */
+       if ((sin.sin_addr.s_addr & 0xff000000) == 0)
+         {
+            int err;
+            err = udp_loopback (sk, sin.sin_port, from, len,
+                                sin.sin_addr.s_addr, saddr);
+            if (err < 0)
+              return (err);
+         }
+
+       sk->inuse = 1;
+
+       while (len > 0)
+         {
+                 int tmp;
+                 skb = sk->prot->wmalloc (sk, len + sizeof (*skb)
+                                              + sk->prot->max_header, 0);
+                 /* this should never happen, but it is possible. */
+
+                 if (skb == NULL)
+                   {
+                      printk ("udp_sendto: write buffer full?\n");
+                      print_sk(sk);
+                      release_sock (sk);
+                      if (copied || !noblock)
+                        return (copied);
+                      return (-EAGAIN);
+                   }
+
+                 skb->mem_addr = skb;
+                 skb->mem_len = len + sizeof (*skb) + sk->prot->max_header;
+                 skb->sk = sk;
+                 skb->free = 1;
+                 skb->arp = 0;
+
+                 /* now build the ip and dev header. */
+                 buff = (unsigned char *)(skb+1);
+                 tmp = sk->prot->build_header (skb, saddr,
+                                               sin.sin_addr.s_addr, &dev,
+                                               IP_UDP, sk->opt, skb->mem_len);
+                 if (tmp < 0 )
+                   {
+                           sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
+                           release_sock (sk);
+                           return (tmp);
+                   }
+                 buff += tmp;
+
+                 /* we shouldn't do this, instead we should just
+                    let the ip protocol fragment the packet. */
+                 amt = min (len + tmp + sizeof (*uh), dev->mtu);
+
+                 PRINTK ("amt = %d, dev = %X, dev->mtu = %d\n",
+                         amt, dev, dev->mtu);
+
+                 skb->len = amt;
+                 amt -= tmp; 
+
+                 uh = (struct udp_header *)buff;
+                 uh->len = net16(amt);
+                 uh->source = sk->dummy_th.source;
+                 uh->dest = sin.sin_port;
+
+                 amt -= sizeof (*uh);
+                 buff += sizeof (*uh);
+
+                 verify_area (from, amt);
+                 memcpy_fromfs( buff, from, amt);
+
+                 len -= amt;
+                 copied += amt;
+                 from += amt;
+                 udp_send_check (uh, saddr, sin.sin_addr.s_addr,
+                                 amt+sizeof (*uh), sk);
+                                 
+                 sk->prot->queue_xmit (sk, dev, skb, 1);
+         }
+       release_sock (sk);
+       return (copied);
+}
+
+static int
+udp_write (volatile struct sock *sk, unsigned char *buff, int len, int noblock,
+          unsigned flags)
+{
+       return (udp_sendto (sk, buff, len, noblock, flags, NULL, 0));
+}
+
+int
+udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
+             int noblock,
+             unsigned flags, struct sockaddr_in *sin, int *addr_len)
+{
+       /* this should be easy, if there is something there we
+          return it, otherwise we block. */
+       int copied=0;
+       struct sk_buff *skb;
+       if (len == 0) return (0);
+       if (len < 0) return (-EINVAL);
+       if (addr_len)
+         {
+                 verify_area (addr_len, sizeof(*addr_len));
+                 put_fs_long (sizeof (*sin), addr_len);
+         }
+       sk->inuse = 1;
+       while (sk->rqueue == NULL)
+         {
+            if (noblock)
+              {
+                 release_sock (sk);
+                 return (-EAGAIN);
+              }
+            release_sock (sk);
+            cli();
+            if (sk->rqueue == NULL)
+              {
+                 interruptible_sleep_on (sk->sleep);
+                 if (current->signal & ~current->blocked)
+                   {
+                      return (-ERESTARTSYS);
+                   }
+              }
+            sti();
+         }
+       skb = sk->rqueue;
+
+       if (!(flags & MSG_PEEK))
+         {
+                 if (skb->next == skb )
+                   {
+                           sk->rqueue = NULL;
+                   }
+                 else
+                   {
+                           sk->rqueue = sk->rqueue ->next;
+                           skb->prev->next = skb->next;
+                           skb->next->prev = skb->prev;
+                   }
+         }
+       copied = min (len, skb->len);
+       verify_area (to, copied);
+       memcpy_tofs (to, skb->h.raw + sizeof (struct udp_header), copied);
+       /* copy the address. */
+       if (sin)
+         {
+                 struct sockaddr_in addr;
+                 addr.sin_family = AF_INET;
+                 addr.sin_port = skb->h.uh->source;
+                 addr.sin_addr.s_addr = skb->daddr;
+                 verify_area (sin, sizeof (*sin));
+                 memcpy_tofs(sin, &addr, sizeof (*sin));
+         }
+
+       if (!(flags & MSG_PEEK))
+         {
+            free_skb (skb, FREE_READ);
+         }
+       release_sock (sk);
+       return (copied);
+
+}
+
+
+int
+udp_read (volatile struct sock *sk, unsigned char *buff, int len, int noblock,
+         unsigned flags)
+{
+       return (udp_recvfrom (sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+int
+udp_connect (volatile struct sock *sk, struct sockaddr_in *usin, int addr_len)
+{
+       struct sockaddr_in sin;
+       if (addr_len < sizeof (sin)) return (-EINVAL);
+       verify_area (usin, sizeof (sin));
+       memcpy_fromfs (&sin, usin, sizeof (sin));
+       if (sin.sin_family && sin.sin_family != AF_INET)
+         return (-EAFNOSUPPORT);
+       sk->daddr = sin.sin_addr.s_addr;
+       sk->dummy_th.dest = sin.sin_port;
+       sk->state = TCP_ESTABLISHED;
+       return(0);
+}
+
+static void
+udp_close(volatile struct sock *sk, int timeout)
+{
+       sk->inuse = 1;
+       sk->state = TCP_CLOSE;
+       if (sk->dead)
+         destroy_sock (sk);
+       else
+         release_sock (sk);
+}
+
+int
+udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+       unsigned long daddr, unsigned short len,
+       unsigned long saddr, int redo, struct ip_protocol *protocol)
+{
+       /* all we need to do is get the socket, and then do a checksum. */
+       struct proto *prot=&udp_prot;
+       volatile struct sock *sk;
+       struct udp_header *uh;
+
+       uh = (struct udp_header *) skb->h.uh;
+
+       if (dev->add_arp) dev->add_arp (saddr, skb, dev);
+
+       sk = get_sock (prot, net16(uh->dest), saddr, uh->source, daddr);
+
+       /* if we don't know about the socket, forget about it. */
+       if (sk == NULL &&
+           (daddr & 0xff000000 != 0) && (daddr & 0xff000000 != 0xff000000))
+         {
+            icmp_reply (skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+            skb->sk = NULL;
+            free_skb (skb, 0);
+            return (0);
+         }
+
+
+       if (!redo)
+         {
+            if (uh->check && udp_check (uh, len, saddr, daddr))
+              {
+                 PRINTK ("bad udp checksum\n");
+                 skb->sk = NULL;
+                 free_skb (skb, 0);
+                 return (0);
+              }
+
+            skb->sk = sk;
+            skb->dev = dev;
+            skb->len = len;
+
+            /* these are supposed to be switched. */
+            skb->daddr = saddr;
+            skb->saddr = daddr;
+
+            /* Now deal with the in use. */
+            cli();
+            if (sk->inuse)
+              {
+                 if (sk->back_log == NULL)
+                   {
+                      sk->back_log = skb;
+                      skb->next = skb;
+                      skb->prev = skb;
+                   }
+                 else
+                   {
+                      skb->next = sk->back_log;
+                      skb->prev = sk->back_log->prev;
+                      skb->prev->next = skb;
+                      skb->next->prev = skb;
+                   }
+                 sti();
+                 return (0);
+              }
+            sk->inuse = 1;
+            sti();
+         }
+
+       /* charge it too the socket. */
+       if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
+         {
+            skb->sk = NULL;
+            free_skb (skb, 0);
+            release_sock (sk);
+            return (0);
+         }
+            
+       sk->rmem_alloc += skb->mem_len;
+
+       /* At this point we should print the thing out. */
+       PRINTK ("<< \n");
+       print_sk (sk);
+
+       /* now add it to the data chain and wake things up. */
+       if (sk->rqueue == NULL)
+         {
+                 sk->rqueue = skb;
+                 skb->next = skb;
+                 skb->prev = skb;
+         }
+       else
+         {
+                 skb->next = sk->rqueue;
+                 skb->prev = sk->rqueue->prev;
+                 skb->prev->next = skb;
+                 skb->next->prev = skb;
+         }
+
+       skb->len = len - sizeof (*uh);
+
+       if (!sk->dead)
+         wake_up (sk->sleep);
+
+       release_sock (sk);
+       return (0);
+}
+
+
+
+struct proto udp_prot =
+{
+  sock_wmalloc,
+  sock_rmalloc,
+  sock_wfree,
+  sock_rfree,
+  sock_rspace,
+  sock_wspace,
+  udp_close,
+  udp_read,
+  udp_write,
+  udp_sendto,
+  udp_recvfrom,
+  ip_build_header,
+  udp_connect,
+  NULL,
+  ip_queue_xmit,
+  ip_retransmit,
+  NULL,
+  NULL,
+  udp_rcv,
+  udp_select,
+  NULL,
+  NULL,
+  128,
+  0,
+  {NULL,}
+};
+
diff --git a/net/tcp/udp.h b/net/tcp/udp.h
new file mode 100644 (file)
index 0000000..646926e
--- /dev/null
@@ -0,0 +1,36 @@
+/* udp.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+#ifndef _TCP_UDP_H
+#define _TCP_UDP_H
+
+struct udp_header
+{
+       unsigned short source;
+       unsigned short dest;
+       unsigned short len;
+       unsigned short check;
+};
+
+extern struct proto udp_prot;
+#define UDP_NO_CHECK 1
+
+#endif
diff --git a/net/tcp/we.c b/net/tcp/we.c
new file mode 100644 (file)
index 0000000..58b8208
--- /dev/null
@@ -0,0 +1,679 @@
+/* we.c an wd8003 and wd8013 ethernet driver for linux. */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+/* The bsd386 version was used as an example in order to write this
+   code */
+
+/*
+       The driver was significantly modified by Bob Harris to allow the
+       software to operate with either the wd8003 or wd8013 boards.  The
+       wd8013 boards will operate using full memory on board (as specified
+       by the user in Space.c) and the 16 bit wide interface.  The driver
+       will autodetect which board it is using on boot (i.e. "using 16 bit I/F").
+       In addition, the interrupts structure was significantly modified to 
+       respond to all the chips interrupts and to keep track of statistics.
+       The statistics are not currently used.  Debug messages can be toggled
+       by setting the wd_debug variable to a non-zero number.   The driver 
+       can detect an open or shorted cable - the wd8013 board functions after
+       the problem is corrected, but the wd8003 board does not always recover.
+       The driver is gradually being migrated toward the National Semiconductor
+       recommendations.  Constructive comments or suggestions can be sent to:
+
+               Bob Harris, rth@sparta.com
+               7926 Jones Branch Drive, Suite 900
+               McLean, Va. 22102
+*/
+/* Note:  My driver was full of bugs.  Basically if it works, credit
+   Bob Harris.  If it's broken blame me.  -RAB */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/tty.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/memory.h>
+#include <errno.h>
+#include <linux/fcntl.h>
+#include <netinet/in.h>
+
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "tcp.h"
+#include "sock.h"
+#include "arp.h"
+
+#include "wereg.h"
+
+static unsigned char interrupt_mask;
+/* format of status byte. 
+   bit 
+    0  start
+    1  open
+    2   transmitter in use */
+
+#define START 1
+#define OPEN  2
+#define TRS_BUSY 0x400
+#define IN_INT 8
+
+/* We need to get rid of all these statics and move them into the
+   device structure that way we can have more than one wd8003 board
+   in the system at once. */
+
+static volatile unsigned int status;
+
+static struct enet_statistics stats;   /* Statistics collection */
+static unsigned char max_pages;                /* Board memory/256 */
+static unsigned char wd_debug = 0;     /* turns on/off debug messages */
+static unsigned char dconfig = WD_DCONFIG;     /* default data configuration */
+static int tx_aborted = 0;                     /* Empties tx bit bucket */
+
+static  int
+max(int a, int b)
+{
+  if (a>b) return (a);
+  return (b);
+}
+
+static  void
+wd_start(struct device *dev)
+{
+  unsigned char cmd;
+  interrupt_mask=RECV_MASK|TRANS_MASK;
+  cli();
+  cmd = inb_p(WD_COMM);
+  cmd &= ~(CSTOP|CPAGE);
+  cmd |= CSTART;
+  outb_p(cmd, WD_COMM);
+  outb_p(interrupt_mask,WD_IMR);
+  sti();
+  status |= START;
+}
+
+int
+wd8003_open(struct device *dev)
+{
+  unsigned char cmd;
+  int i;
+  /* we probably don't want to be interrupted here. */
+  cli();
+  /* This section of code is mostly copied from the bsd driver which is
+     mostly copied from somewhere else. */
+  /* The somewhere else is probably the cmwymr(sp?) dos packet driver */
+
+  cmd=inb_p(WD_COMM);
+  cmd|=CSTOP;
+  cmd &= ~(CSTART|CPAGE);
+  outb_p(cmd, WD_COMM);
+  outb_p(0, WD_IMR);
+  sti();
+  outb_p( dconfig,WD_DCR);
+  /*Zero the remote byte count. */
+  outb_p(0, WD_RBY0);
+  outb_p(0, WD_RBY1);
+  outb_p(WD_MCONFIG,WD_RCC);
+  outb_p(WD_TCONFIG,WD_TRC);
+  outb_p(0,WD_TRPG);            /* Set the transmit page start = 0 */
+  outb_p( max_pages,WD_PSTOP); /* (read) page stop = top of board memory */
+  outb_p(WD_TXBS,WD_PSTRT);    /* (read) page start = cur = bnd = top of tx memory */
+  outb_p(WD_TXBS,WD_BNDR);
+  /* clear interrupt status. */
+  outb_p(0xff,WD_ISR);
+  /* we don't want no stinking interrupts. */
+  outb_p(0 ,WD_IMR);
+  cmd|=1<<CPAGE_SHIFT;
+  outb_p(cmd,WD_COMM);
+  /* set the ether address. */
+  for (i=0; i < ETHER_ADDR_LEN; i++)
+    {
+      outb_p(dev->dev_addr[i],WD_PAR0+i);
+    }
+  /* National recommends setting the boundry < current page register */
+  outb_p(WD_TXBS+1,WD_CUR);    /* Set the current page = page start + 1 */
+  /* set the multicast address. */
+  for (i=0; i < ETHER_ADDR_LEN; i++)
+    {
+      outb_p(dev->broadcast[i],WD_MAR0+i);
+    }
+
+  cmd&=~(CPAGE|CRDMA);
+  cmd|= 4<<CRDMA_SHIFT;
+  outb_p(cmd, WD_COMM);
+  outb_p(WD_RCONFIG,WD_RCC);
+  status = OPEN;
+  wd_start(dev); 
+  return (0);
+}
+
+/* This routine just calls the ether rcv_int. */
+static  int
+wdget(struct wd_ring *ring, struct device *dev)
+{
+  unsigned char *fptr;
+  unsigned long len;
+  fptr = (unsigned char *)(ring +1);
+  len = ring->count-4;
+  return (dev_rint(fptr, len, 0, dev));
+}
+
+int
+wd8003_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+  unsigned char cmd;
+  int len;
+  cli();
+  if (status & TRS_BUSY)
+    {
+       /* put in a time out. */
+       if (jiffies - dev->trans_start < 30)
+        return (1);
+       printk ("wd8003 transmit timed out. \n");
+    }
+  status |= TRS_BUSY;
+  sti();
+
+  if (!skb->arp)
+    {
+      if ( dev->rebuild_header (skb+1, dev)) 
+       {
+          skb->dev = dev;
+          arp_queue (skb);
+          status &= ~TRS_BUSY;
+          return (0);
+       }
+    }
+
+  memcpy ((unsigned char *)dev->mem_start, skb+1, skb->len);
+
+  len = skb->len;
+
+  /* now we need to set up the card info. */
+  dev->trans_start = jiffies;
+  len=max(len, ETHER_MIN_LEN); /* actually we should zero out
+                                 the extra memory. */
+/*  printk ("start_xmit len - %d\n", len);*/
+  cli();
+  cmd=inb_p(WD_COMM);
+  cmd &= ~CPAGE;
+  outb_p(cmd, WD_COMM);
+
+  interrupt_mask |= TRANS_MASK;
+  if (!(status & IN_INT))
+    outb (interrupt_mask, WD_IMR);
+
+  outb_p(len&0xff,WD_TB0);
+  outb_p(len>>8,WD_TB1);
+  cmd |= CTRANS;
+  outb_p(cmd,WD_COMM);
+  sti();
+  
+  if (skb->free)
+    {
+           free_skb (skb, FREE_WRITE);
+    }
+
+  return (0);
+}
+
+/* tell the card about the new boundary. */
+
+static  void
+wd_put_bnd(unsigned char bnd, struct device *dev )
+{
+
+       unsigned char cmd;
+
+       /* Ensure page 0 selected */
+       cmd = inb_p( CR );
+       if (cmd & 0x40) {
+               outb_p(cmd & (~CPAGE1), WD_COMM);       /* select page 0 */
+               outb_p(bnd, WD_BNDR);
+               outb_p(cmd | CPAGE1, WD_COMM);  /* reselect page 1 */
+       } else {
+               outb_p(bnd, WD_BNDR);
+       }
+}
+
+static  unsigned char
+wd_get_bnd( struct device *dev )
+{
+
+       unsigned char   cmd, bnd;
+
+       /* Ensure page 0 selected */
+       cmd = inb_p(WD_COMM);
+       if (cmd & 0x40) {
+               outb_p(cmd & (~CPAGE1), WD_COMM);       /* select page 0 */
+               bnd = inb_p(WD_BNDR);
+               outb_p(cmd | CPAGE1, WD_COMM);  /* reselect page 1 */
+               return (bnd);
+       } else {
+               return (inb_p(WD_BNDR));
+       }
+}
+
+static  unsigned char
+wd_get_cur( struct device *dev )
+{
+
+       unsigned char   cmd, cur;
+
+       /* Ensure page 1 selected */
+       cmd = inb_p(WD_COMM);
+       if (cmd & 0x40) {
+               return (inb_p(WD_CUR));
+       } else {
+               outb_p(cmd | CPAGE1, WD_COMM);  /* select page 1 */
+               cur = inb_p(WD_CUR);
+               outb_p(cmd & (~CPAGE1), WD_COMM);       /* reselect page 0 */
+               return (cur);
+       }
+}
+
+/* This routine handles the packet recieved interrupt. */
+/* Debug routines slow things down, but reveal bugs... */
+/* Modified Boundry Page Register to follow Current Page */
+
+static  void
+wd_rcv( struct device *dev )
+{
+   
+   unsigned char   pkt;        /* Next packet page start */
+   unsigned char   bnd;        /* Last packet page end */
+   unsigned char   cur;        /* Future packet page start */
+   unsigned char   cmd;        /* Command register save */
+   struct wd_ring *ring;
+   int            done=0;
+   
+   /* Calculate next packet location */
+   cur = wd_get_cur( dev );
+   bnd = wd_get_bnd( dev );
+   if( (pkt = bnd + 1) == max_pages )
+     pkt = WD_TXBS;
+   
+   while( done != 1)
+     {
+       if (pkt != cur)
+         {
+
+            /* Position pointer to packet in card ring buffer */
+            ring = (struct wd_ring *) (dev->mem_start + (pkt << 8));
+            
+            /* Ensure a valid packet */
+            if( ring->status & 1 )
+              { 
+                 /* Too small and too big packets are
+                    filtered by the board */
+                 if( wd_debug )
+                   printk("\nwd8013 - wdget: bnd = %d, pkt = %d, "
+                          "cur = %d, status = %d, len = %d, next = %d",
+                          bnd, pkt, cur, ring->status, ring->count,
+                          ring->next);
+                 
+                 stats.rx_packets++; /* count all receives */
+                 done = wdget( ring, dev ); /* get the packet */
+                 
+                 /* see if we need to process this packet again. */
+                 if (done == -1) continue;
+
+                 /* Calculate next packet location */
+                 pkt = ring->next;
+                 
+                 /* Compute new boundry - tell the chip */
+                 if( (bnd = pkt - 1) < WD_TXBS )
+                   bnd = max_pages - 1;
+                 wd_put_bnd(bnd, dev);
+                 
+                 /* update our copy of cur. */
+                 cur = wd_get_cur(dev);
+              }
+            else 
+              {        /* Bad packet in ring buffer -
+                          should not happen due to hardware filtering */
+                 printk("wd8013 - bad packet: len = %d, status = x%x, "
+                        "bnd = %d, pkt = %d, cur = %d\n"
+                        "trashing receive buffer!",
+                        ring->count, ring->status, bnd, pkt,
+                        cur);
+                 /* Reset bnd = cur-1 */
+                 if( ( bnd = wd_get_cur( dev ) - 1 ) < WD_TXBS )
+                   bnd = max_pages - 1;
+                 wd_put_bnd( bnd, dev );
+                 break; /* return */
+              }
+            
+         }
+       else
+         {
+            done = dev_rint(NULL, 0,0, dev);
+         }
+     }
+   
+   /* reset to page 0 */
+   cmd = inb_p(WD_COMM);
+   if (cmd & 0x40)
+     {
+       outb_p(cmd & ~(CPAGE1), WD_COMM);       /* select page 0 */
+     }
+}
+
+/* This routine handles the interrupt case of receiver overruns */
+
+static  void
+wd_rx_over( struct device *dev )
+{
+       unsigned char cmd, dummy;
+
+       /* Nothing actually has been overwritten */
+       /* the chip has stopped at the boundry */
+       /* but we must get it going again - according to National Semiconductor */
+       printk ("wd_rx_over\n");
+       cmd = inb_p( CR ); /* get current command register */
+       cmd = (cmd&~(STA|PS0|PS1))|STOP; /* toggle start and stop bits, select page 0 */
+       outb_p( cmd, CR );
+       dummy = inb_p( RBCR0 ); /* required to detect reset status */
+       dummy = inb_p( RBCR1 );
+       wd_rcv( dev );  /* clear out received packets */
+       
+       if( inb_p( ISR ) & PRX )
+               outb_p( PRX, ISR ); /* acknowledge RX interrupt */
+       while( ( inb_p( ISR ) & RST ) == 0 ); /* wait for reset to be completed */
+       outb_p( RST, ISR ); /* acknowledge RST interrupt */
+       outb_p( (cmd&~STOP)|STA, CR ); /* Start NIC */  
+       outb_p( WD_TCONFIG, TCR ); /* resume normal mode */
+}
+
+/*
+ * This get's the transmit interrupts. It assumes command page 0 is set, and
+ * returns with command page 0 set.
+ */
+
+static  void
+wd_trs( struct device *dev )
+{
+       unsigned char cmd, errors;
+       int len;
+
+       if( wd_debug )
+               printk("\nwd_trs() - TX complete, status = x%x", inb_p(TSR));
+
+       if( ( errors = inb_p( TSR ) & PTXOK  ) || tx_aborted ){
+               if( (errors&~0x02) == 0 ){
+                       stats.tx_packets++;
+                       tx_aborted = 0;
+               }
+               
+               /* attempt to start a new transmission. */
+               len = dev_tint( (unsigned char *)dev->mem_start, dev );
+               if( len != 0 ){
+                       len=max(len, ETHER_MIN_LEN);
+                       cmd=inb_p(WD_COMM);
+                       outb_p(len&0xff,WD_TB0);
+                       outb_p(len>>8,WD_TB1);
+                       cmd |= CTRANS;
+                       outb_p(cmd,WD_COMM);
+                       interrupt_mask |= TRANS_MASK;
+               }
+               else
+               {
+                       status &= ~TRS_BUSY;
+                       interrupt_mask &= ~TRANS_MASK;
+                       return;
+               }
+       }
+       else{ /* TX error occurred! - H/W will reschedule */
+               if( errors & CRS ){
+                       stats.tx_carrier_errors++;
+                       printk("\nwd8013 - network cable short!");
+               }
+               if (errors & COL )
+                       stats.collisions += inb_p( NCR );
+               if (errors & CDH )
+                       stats.tx_heartbeat_errors++;
+               if (errors & OWC )
+                       stats.tx_window_errors++;
+       }
+}
+
+void
+wd8003_interrupt(int reg_ptr)
+{
+       unsigned char   cmd;
+       unsigned char   errors;
+       unsigned char   isr;
+       struct device *dev;
+       struct pt_regs *ptr;
+       int irq;
+       int count = 0;
+
+       ptr = (struct pt_regs *)reg_ptr;
+       irq = -(ptr->orig_eax+2);
+       for (dev = dev_base; dev != NULL; dev = dev->next)
+         {
+            if (dev->irq == irq) break;
+         }
+       if (dev == NULL) 
+         {
+            printk ("we.c: irq %d for unknown device\n", irq);
+            return;
+         }
+       sti(); /* this could take a long time, we should have interrupts on. */
+
+       cmd = inb_p( CR );/* Select page 0 */  
+       if( cmd & (PS0|PS1 ) ){
+               cmd &= ~(PS0|PS1);
+               outb_p(cmd, CR );
+       }
+       
+       if (wd_debug)
+               printk("\nwd8013 - interrupt isr = x%x", inb_p( ISR ) );
+
+       status |= IN_INT;
+
+       do{ /* find out who called */ 
+
+               /* Check for overrunning receive buffer first */
+               if ( ( isr = inb_p( ISR ) ) & OVW ) {   /* Receiver overwrite warning */
+                       stats.rx_over_errors++;
+                       if( wd_debug )
+                               printk("\nwd8013 overrun bnd = %d, cur = %d", wd_get_bnd( dev ), wd_get_cur( dev ) );
+                       wd_rx_over( dev ); /* performs wd_rcv() as well */
+                       outb_p( OVW, ISR ); /* acknowledge interrupt */
+               } 
+               else if ( isr & PRX ) { /* got a packet. */
+                       wd_rcv( dev );
+                       outb_p( PRX, ISR ); /* acknowledge interrupt */
+               }
+               /* This completes rx processing... whats next */
+
+               if ( inb_p( ISR ) & PTX ) {     /* finished sending a packet. */
+                       wd_trs( dev );
+                       outb_p( PTX, ISR ); /* acknowledge interrupt */
+               }
+
+               if (inb_p( ISR ) & RXE ) {      /* recieve error */
+                       stats.rx_errors++; /* general errors */
+                       errors = inb_p( RSR ); /* detailed errors */
+                       if (errors & CRC )
+                               stats.rx_crc_errors++;
+                       if (errors & FAE )
+                               stats.rx_frame_errors++;
+                       if (errors & FO )
+                               stats.rx_fifo_errors++;
+                       if (errors & MPA )
+                               stats.rx_missed_errors++;
+                       outb_p( RXE, ISR ); /* acknowledge interrupt */
+               }
+
+               if (inb_p( ISR ) & TXE ) {      /* transmit aborted! */
+                       stats.tx_errors++; /* general errors */
+                       errors = inb_p( TSR ); /* get detailed errors */
+                       if (errors & ABT ){
+                               stats.tx_aborted_errors++;
+                               printk("\nwd8013 - network cable open!");
+                       }
+                       if (errors & FU )
+                               stats.tx_fifo_errors++;
+                               printk("\nwd8013 - TX FIFO underrun!");
+
+                       /* Cannot do anymore - empty the bit bucket */
+                       tx_aborted = 1;
+                       wd_trs( dev );
+                       tx_aborted = 0;
+
+                       outb_p( TXE, ISR ); /* acknowledge interrupt */
+               }
+
+               if( inb_p( ISR ) & CNTE ){ /* Tally counters overflowing */
+                       errors = inb_p( CNTR0 );
+                       errors = inb_p( CNTR1 );
+                       errors = inb_p( CNTR2 );
+                       outb_p( CNTE, ISR ); /* acknowledge interrupt */
+               }
+               if( inb_p( ISR ) & RST ) /* Reset has been performed */
+                       outb_p( RST, ISR ); /* acknowledge interrupt */
+
+               if( wd_debug ){
+                       if( ( isr = inb_p( ISR ) ) != 0 )
+                               printk("\nwd8013 - ISR not cleared = x%x", isr );
+               }
+               if( ++count > max_pages + 1 ){
+                       printk("\nwd8013_interrupt - infinite loop detected, isr = x%x, count = %d", isr, count );
+               }
+
+       } while( inb_p( ISR ) != 0 );
+
+       status &= ~IN_INT;
+}
+
+
+static struct sigaction wd8003_sigaction = 
+{
+   wd8003_interrupt,
+   0,
+   0,
+   NULL
+};
+
+void
+wd8003_init(struct device *dev)
+{
+  unsigned char csum;
+  int i;
+  csum = 0;
+  for (i = 0; i < 8; i++)
+    {
+      csum += inb_p(WD_ROM+i);
+    }
+  if (csum != WD_CHECK)
+    {
+      printk ("Warning WD8013 board not found at i/o = %X.\n",dev->base_addr);
+
+      /* make sure no one can attempt to open the device. */
+      status = OPEN;
+      return;
+    }
+  printk("wd8013");
+  /* initialize the rest of the device structure. */
+  dev->mtu = 1500; /* eth_mtu */
+  dev->hard_start_xmit = wd8003_start_xmit;
+  dev->open = wd8003_open;
+  dev->hard_header = eth_hard_header;
+  dev->add_arp = eth_add_arp;
+  dev->type_trans = eth_type_trans;
+  dev->hard_header_len = sizeof (struct enet_header);
+  dev->addr_len = ETHER_ADDR_LEN;
+  dev->type = ETHER_TYPE;
+  dev->queue_xmit = dev_queue_xmit;
+  dev->rebuild_header = eth_rebuild_header;
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+    dev->buffs[i] = NULL;
+
+#ifndef FORCE_8BIT
+  /* check for 16 bit board - it doesn't have register 0/8 aliasing */
+  for (i = 0; i < 8; i++) {
+       if( inb_p( EN_SAPROM+i ) != inb_p( EN_CMD+i) ){
+               csum = inb_p( EN_REG1 ); /* fiddle with 16bit bit */
+               outb( csum ^ BUS16, EN_REG1 ); /* attempt to clear 16bit bit */
+               if( (csum & BUS16) == (inb_p( EN_REG1 ) & BUS16) ) {
+                       printk(", using 16 bit I/F ");
+                       dconfig |= 1; /* use word mode of operation */
+                       outb_p( LAN16ENABLE|MEMMASK, EN_REG5);
+                       outb( csum , EN_REG1 );
+                       break; /* We have a 16bit board here! */
+               }
+               outb( csum , EN_REG1 );
+       }
+    }
+#endif /* FORCE_8BIT */
+
+  /* mapin the interface memory. */
+  outb_p(WD_IMEM,WD_CTL);
+
+  /* clear the interface memory */
+  for (i = dev->mem_start; i < dev->mem_end; i++)
+    {
+      *((unsigned char *)i) = 0;
+      if (*((unsigned char *)i) != 0) 
+       {
+         printk ("WD Memory error.\n");
+         if( (i - dev->mem_start) > 4096 )
+               break;
+         else
+               status = OPEN;
+       }
+    }
+  /* Calculate how many pages of memory on board */
+  max_pages = ( i - dev->mem_start )/256;
+
+  /* need to set up the dev->mem_end and dev->rmem_end */
+  dev->rmem_end = i;
+  dev->mem_end = i;
+
+  /* print the initialization message, and the
+     ethernet address. */
+  printk (", %d pages memory, ethernet Address: ", max_pages );
+  for (i = 0; i <ETHER_ADDR_LEN; i++)
+    {
+      dev->dev_addr[i]=inb_p(WD_ROM+i);
+      dev->broadcast[i]=0xff;
+      printk ("%2.2X ",dev->dev_addr[i]);
+    }
+
+  /* Clear the statistics */
+  for( i = 0; i < sizeof( struct enet_statistics ); i++ )
+       ((char *)&stats)[i] = 0;
+
+  printk ("\n");
+  status = 0;
+
+  if (irqaction (dev->irq, &wd8003_sigaction))
+    {
+       printk ("Unable to get IRQ%d for wd8013 board\n", dev->irq);
+    }
+}
diff --git a/net/tcp/wereg.h b/net/tcp/wereg.h
new file mode 100644 (file)
index 0000000..6a7df89
--- /dev/null
@@ -0,0 +1,478 @@
+/* wereg.h */
+/*
+    Copyright (C) 1992  Ross Biro
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+
+    The Author may be reached as bir7@leland.stanford.edu or
+    C/O Department of Mathematics; Stanford University; Stanford, CA 94305
+*/
+/* This is based on if_wereg.h from bsd386 */
+
+/* Uncomment the next line if you are having troubles with your 
+   8 bit card being recognized as 16 bit. */
+/* #define FORCE_8BIT */
+
+struct wd_ring
+{
+  unsigned char status; /* status */
+  /* format of status 
+     bit
+     0         packet ok
+     1         crc error
+     2         frame alignment error
+     3         fifo overrun
+     */
+#define STRECVD 0xf1
+  unsigned char next;   /* pointer to next packet. */
+  unsigned short count; /*packet lenght in bytes + 4 */
+};
+
+/* Format of command register. 
+   bits
+    0          stop
+    1          start
+    2          transmit packet
+   3-5         Remote DMA command              
+   6-7         Page Select */
+
+#define CSTOP 0x1
+#define CSTART  0x2
+#define CTRANS 0x4
+#define CRDMA 0x38
+#define CRDMA_SHIFT 3
+#define CPAGE 0xc0
+#define CPAGE_SHIFT 6
+
+#define CPAGE1 0x40
+
+
+#define CPAGE1 0x40
+
+/* interrupt status defenitions
+   bits
+     0         Recv.
+     1         Transmit
+     2         RcvErr
+     3         Transmit  Err
+     4         Overwrite warning
+     5         Counter overflow
+     6         Remote DMA complete
+     7         Reset Status */
+
+#define IRCV           0x1
+#define ITRS           0x2
+#define IRCE           0x4
+#define ITRE           0x8
+#define IOVER          0x10
+#define ICOUNTERS      0x20
+#define IDMA           0x40
+#define IRESET                 0x80
+#define IOVER          0x10
+#define ICOUNTERS      0x20
+#define IDMA           0x40
+#define IRESET                 0x80
+
+/* transmit status format
+   bits
+    0          Packet transmitted ok.
+    1          Non Deferred transmition
+    2          Transmit collied
+    3          Transmit aborted
+    4          Carrier Sense Lost
+    5          Fifo Underrun
+    6          CD Heartbeat
+    7          Out of Window Collision */
+
+#define TROK 0x1
+#define TRAB 0x4
+
+/* Some ID stuff */
+#define WD_ID1 0x03
+#define WD_ID2 0x05
+#define WD_CHECK 0xff
+#define WD_PAGE 256 /* page size in bytes. */
+#define WD_TXBS 6   /* size of transmit buffer in pages. */
+#define WD_MAX_PAGES 32 /* Number of pages off ram on card (8k) */
+#define WD_NIC 16   /* i/o base offset to NIC */
+
+/* Some configuration stuff. */
+/* where the memory is mapped in. */
+#define WD_MEM (dev->mem_start)
+#define WD_BUFFEND (dev->mem_end)
+#define WD_MEMSIZE (WD_BUFFEND-WD_MEM)
+#define WD_BASE (dev->base_addr)
+
+#define TRANS_MASK 0xa
+#define RECV_MASK  0x5
+#define WD_DCONFIG 0x48
+#define WD_RCONFIG 0x4
+#define WD_MCONFIG 0x20
+#define WD_TCONFIG 0x0
+#define WD_IMEM (((WD_MEM>>13) & 0x3f)|0x40)
+
+/* WD registers. */
+#define WD_ROM (WD_BASE+8)
+#define WD_CTL (WD_BASE+0)
+
+/* WD NIC register offsets */
+#define WD_COMM (WD_BASE+WD_NIC+0x00)          /* command register */
+#define WD_PSTRT (WD_BASE+WD_NIC+0x01)         /* page start register */
+#define WD_PSTOP (WD_BASE+WD_NIC+0x02)         /* page stop register */
+#define WD_BNDR  (WD_BASE+WD_NIC+0x03)         /* Boundary Pointer */
+#define WD_TRST (WD_BASE+WD_NIC+0x04)          /* Transmit Status */
+#define WD_TRPG (WD_BASE+WD_NIC+0x04)          /* Transmit Page */
+#define WD_TB0  (WD_BASE+WD_NIC+0x05)          /* Transmit byte count, low */
+#define WD_TB1 (WD_BASE+WD_NIC+0x06)           /* Transmit byte count, high */
+#define WD_ISR  (WD_BASE+WD_NIC+0x07)          /* Interrupt status register */
+#define WD_RBY0 (WD_BASE+WD_NIC+0x0a)            /* remote byte count low. */
+#define WD_RBY1 (WD_BASE+WD_NIC+0x0b)            /* remote byte count high. */
+#define WD_RCC (WD_BASE+WD_NIC+0x0c)           /* receive configuration */
+#define WD_TRC (WD_BASE+WD_NIC+0x0d)           /* transmit configuration */
+#define WD_DCR  (WD_BASE+WD_NIC+0x0e)            /* data configuration */
+#define WD_IMR  (WD_BASE+WD_NIC+0x0f)            /* Interrupt Mask register. */
+#define WD_PAR0 (WD_BASE+WD_NIC+0x01)
+#define WD_CUR  (WD_BASE+WD_NIC+0x07)
+#define WD_MAR0 (WD_BASE+WD_NIC+0x08)
+
+/* rth additions */
+
+#define EN_CMD (WD_BASE+0)
+#define EN_REG1 (WD_BASE+1)
+#define EN_REG5 (WD_BASE+5)
+#define EN_SAPROM (WD_BASE+8)
+#define EN_REGE (WD_BASE+0x0e)
+#define EN_OFFSET 16
+
+/* WD Commands for EN_CMD */
+#define EN_RESET
+#define EN_MEMEN 0x40
+#define EN_MEM_MASK 0x3f
+
+/* WD Bus Register bits */
+#define BUS16 1
+
+/* WD REG5 Commands */
+#define MEM16ENABLE 0x80
+#define LAN16ENABLE 0x40
+#define MEMMASK 0x1f
+#define BIT19 0x1
+
+/* Memory test pattern to use */
+#define TESTPATTERN 0x5a
+
+/* Western Digital Additional Registers */
+
+/* National Semiconductor Definitions */
+
+/* Page 0 */
+#define CR     (WD_BASE+WD_NIC+0)      /* RW - Command */
+#define CLDA0  (WD_BASE+WD_NIC+1)      /* R - Current Local DMA Address 0 */
+#define PSTART (WD_BASE+WD_NIC+1)      /* W - Page Start Register */
+#define CLDA1  (WD_BASE+WD_NIC+2)      /* R - Current Local DMA Address 1 */
+#define PSTOP  (WD_BASE+WD_NIC+2)      /* W - Page Stop Register */
+#define BNRY   (WD_BASE+WD_NIC+3)      /* RW - Boundry Pointer */
+#define TSR    (WD_BASE+WD_NIC+4)      /* R - Transmit Status Register */
+#define TPSR   (WD_BASE+WD_NIC+4)      /* W - Transmit Page Start */
+#define NCR    (WD_BASE+WD_NIC+5)      /* R - Number of Collisions */
+#define TBCR0  (WD_BASE+WD_NIC+5)      /* W - Transmit Byte Count 0 */
+#define FIFO   (WD_BASE+WD_NIC+6)      /* R - FIFO */
+#define TBCR1  (WD_BASE+WD_NIC+6)      /* W - Transmit Byte Count 1 */
+#define ISR    (WD_BASE+WD_NIC+7)      /* RW - Interrupt Status Register */
+#define CRDA0  (WD_BASE+WD_NIC+8)      /* R - Current Remote DMA Address 0 */
+#define RSAR0  (WD_BASE+WD_NIC+8)      /* W - Remote Start Address 0 */
+#define CRDA1  (WD_BASE+WD_NIC+9)      /* R - Current Remote DMA Address 1 */
+#define RSAR1  (WD_BASE+WD_NIC+9)      /* W - Remote Start Address 1 */
+               /* R - Reserved */
+#define RBCR0  (WD_BASE+WD_NIC+0x0a)   /* W - Remote Byte Count 0 */
+               /* R - Reserved */
+#define RBCR1  (WD_BASE+WD_NIC+0x0b)   /* W - Remote Byte Count 1 */
+#define RSR    (WD_BASE+WD_NIC+0x0c)   /* R - Receive Status Register */
+#define RCR    (WD_BASE+WD_NIC+0x0c)   /* W - Receive Configuration */
+#define CNTR0  (WD_BASE+WD_NIC+0x0d)   /* R - Frame Alignment Errors 0 */
+#define TCR    (WD_BASE+WD_NIC+0x0d)   /* W - Transmit Configuration */
+#define CNTR1  (WD_BASE+WD_NIC+0x0e)   /* R - Frame Alignment Errors 1 */ 
+#define DCR    (WD_BASE+WD_NIC+0x0e)   /* W - Data Configuration */
+#define CNTR2  (WD_BASE+WD_NIC+0x0f)   /* R - Missed Packet Errors */
+#define IMR    (WD_BASE+WD_NIC+0x0f)   /* W - Interrupt Mask Register */
+
+/* Page 1 */
+               /* RW - Command */
+#define PAR0   (WD_BASE+WD_NIC+0x01)   /* RW - Physical Address 0 */
+#define PAR1   (WD_BASE+WD_NIC+0x02)   /* RW - Physical Address 1 */
+#define PAR2   (WD_BASE+WD_NIC+0x03)   /* RW - Physical Address 2 */
+#define PAR3   (WD_BASE+WD_NIC+0x04)   /* RW - Physical Address 3 */
+#define PAR4   (WD_BASE+WD_NIC+0x04)   /* RW - Physical Address 4 */
+#define PAR5   (WD_BASE+WD_NIC+0x05)   /* RW - Physical Address 5 */
+#define PAR6   (WD_BASE+WD_NIC+0x06)   /* RW - Physical Address 6 */
+#define CURR   (WD_BASE+WD_NIC+0x07)   /* RW - Current Page */
+#define MAR0   (WD_BASE+WD_NIC+0x08)   /* RW - Multicast Address 0 */
+#define MAR1   (WD_BASE+WD_NIC+0x09)   /* RW - Multicast Address 1 */
+#define MAR2   (WD_BASE+WD_NIC+0x0a)   /* RW - Multicast Address 2 */
+#define MAR3   (WD_BASE+WD_NIC+0x0b)   /* RW - Multicast Address 3 */
+#define MAR4   (WD_BASE+WD_NIC+0x0c)   /* RW - Multicast Address 4 */
+#define MAR5   (WD_BASE+WD_NIC+0x0d)   /* RW - Multicast Address 5 */
+#define MAR6   (WD_BASE+WD_NIC+0x0e)   /* RW - Multicast Address 6 */
+#define MAR7   (WD_BASE+WD_NIC+0x0f)   /* RW - Multicast Address 7 */
+
+/* Page 2 */
+/* Page 2 Registers are RW opposite Page 0 */
+/* and should be used for diagnostic purposes only */
+
+/* Command Register bits */
+#define STOP   1       /* In progress jobs finished, software reset */
+#define STA    2       /* Activate the NIC */
+#define TXP    4       /* Initiate TX packet */
+#define RD0    8       /* Remote DMA commands */
+#define RD1    0x10    
+#define RD2    0x20
+#define PS0    0x40    /* Page Select */
+#define  PS1   0x80    /* 00 = 0, 01 = 1, 10 = 2, 11 = reserved */
+
+#define PAGE0  ~(PS0|PS1)      /* Remember to AND this */
+#define PAGE1  PS0             /* these can be OR'd */
+#define PAGE2  PS1
+#define NO_DMA RD2
+
+/* Interrupt Status Register bits */
+#define PRX    1       /* Packet received with no errors */
+#define PTX    2       /* Packet transmitted with no errors */
+#define RXE    4       /* Packet received with errors */
+#define TXE    8       /* Transmit aborted with errors */
+#define OVW    0x10    /* Overwrite warning */
+#define CNT    0x20    /* Counter overflow warning */
+#define RDC    0x40    /* Remote DMA complete */
+#define RST    0x80    /* Reset status - does not cause interrupts */
+
+/* Interrupt Mask Register - 1 = enabled */
+#define PRXE   1       /* Packet received */
+#define PTXE   2       /* Packet transmitted */
+#define RXEE   4       /* Receive error */
+#define TXEE   8       /* Transmit error */
+#define OVWE   0x10    /* Overwrite error */
+#define CNTE   0x20    /* Counter overflow */
+#define RDCE   0x40    /* Remote DMA complete */
+
+/* Data Configuration Register */
+#define WTS    1       /* Word Transfer 0 = byte, 1 = word */
+#define BOS    2       /* Byte Order 0 = 8086, 1 = 68000 */
+#define LAS    4       /* Long Address 0 = 16bit, 1 = 32 bit DMA */
+#define LS     8       /* Loopback = 0, 1 = Normal */
+#define AR     0x10    /* Autoinitialize = 1 DMA, 0 = software */
+#define FT0    0x20    /* FIFO Threshold (word mode /2 ) */
+#define FT1    0x40    /* 00 = 2, 01 = 4, 10 = 8, 11 = 12 bytes */
+
+/* Transmit Configuration Register */
+#define CRCI   1       /* CRC inhibit = 1, append = 0 */
+#define LB0    2       /* Loopback control 00 = normal loopback */
+#define LB1    4       /* 01 = internal, 10 = external1, 11 = external2 */
+#define ATD    8       /* Auto Transmit Enable = 1 tx inhibit enabled */
+#define OFST   0x10    /* Collision offset 1 = modify to low priority mode */
+
+/* Transmitter Status Register */
+#define PTXOK  1       /* Packet transmitted without error */
+               /* reserved */
+#define COL    4       /* Transmit collided, check NCR for count */
+#define ABT    8       /* Transmit aborted - 16 tries */
+#define CRS    0x10    /* Carrier Sense lost */
+#define FU     0x20    /* FIFO underrun */
+#define CDH    0x40    /* CD Heartbeat failed */
+#define OWC    0x80    /* Out of window collision */
+
+/* Receive configuration Register */
+#define SEP    1       /* Save error packets = 1 */
+#define ARUNT  2       /* Accept RUNT packets < 64 bytes */
+#define AB     4       /* Accept Broadcast packets */
+#define AM     8       /* Accept Multicast packets */
+#define PRO    0x10    /* Promiscuous mode */
+#define MON    0x20    /* Monitor mode */
+
+/* Receive Status Register */
+#define PRX    1       /* Packet received without error */
+#define CRC    2       /* CRC error */
+#define FAE    4       /* Frame Alignment error */
+#define FO     8       /* FIFO overrun error */
+#define MPA    0x10    /* Missed packet */
+#define PHY    0x20    /* Physical = 0, Multicast/Broadcast = 1 */
+#define DIS    0x40    /* Receiver disabled (monitor mode) */
+#define DFR    0x80    /* Deferring - jabber on line */
+
+
+/* rth additions */
+
+#define EN_CMD (WD_BASE+0)
+#define EN_REG1 (WD_BASE+1)
+#define EN_REG5 (WD_BASE+5)
+#define EN_SAPROM (WD_BASE+8)
+#define EN_REGE (WD_BASE+0x0e)
+#define EN_OFFSET 16
+
+/* WD Commands for EN_CMD */
+#define EN_RESET
+#define EN_MEMEN 0x40
+#define EN_MEM_MASK 0x3f
+
+/* WD Bus Register bits */
+#define BUS16 1
+
+/* WD REG5 Commands */
+#define MEM16ENABLE 0x80
+#define LAN16ENABLE 0x40
+#define MEMMASK 0x1f
+#define BIT19 0x1
+
+/* Memory test pattern to use */
+#define TESTPATTERN 0x5a
+
+/* Western Digital Additional Registers */
+
+/* National Semiconductor Definitions */
+
+/* Page 0 */
+#define CR     (WD_BASE+WD_NIC+0)      /* RW - Command */
+#define CLDA0  (WD_BASE+WD_NIC+1)      /* R - Current Local DMA Address 0 */
+#define PSTART (WD_BASE+WD_NIC+1)      /* W - Page Start Register */
+#define CLDA1  (WD_BASE+WD_NIC+2)      /* R - Current Local DMA Address 1 */
+#define PSTOP  (WD_BASE+WD_NIC+2)      /* W - Page Stop Register */
+#define BNRY   (WD_BASE+WD_NIC+3)      /* RW - Boundry Pointer */
+#define TSR    (WD_BASE+WD_NIC+4)      /* R - Transmit Status Register */
+#define TPSR   (WD_BASE+WD_NIC+4)      /* W - Transmit Page Start */
+#define NCR    (WD_BASE+WD_NIC+5)      /* R - Number of Collisions */
+#define TBCR0  (WD_BASE+WD_NIC+5)      /* W - Transmit Byte Count 0 */
+#define FIFO   (WD_BASE+WD_NIC+6)      /* R - FIFO */
+#define TBCR1  (WD_BASE+WD_NIC+6)      /* W - Transmit Byte Count 1 */
+#define ISR    (WD_BASE+WD_NIC+7)      /* RW - Interrupt Status Register */
+#define CRDA0  (WD_BASE+WD_NIC+8)      /* R - Current Remote DMA Address 0 */
+#define RSAR0  (WD_BASE+WD_NIC+8)      /* W - Remote Start Address 0 */
+#define CRDA1  (WD_BASE+WD_NIC+9)      /* R - Current Remote DMA Address 1 */
+#define RSAR1  (WD_BASE+WD_NIC+9)      /* W - Remote Start Address 1 */
+               /* R - Reserved */
+#define RBCR0  (WD_BASE+WD_NIC+0x0a)   /* W - Remote Byte Count 0 */
+               /* R - Reserved */
+#define RBCR1  (WD_BASE+WD_NIC+0x0b)   /* W - Remote Byte Count 1 */
+#define RSR    (WD_BASE+WD_NIC+0x0c)   /* R - Receive Status Register */
+#define RCR    (WD_BASE+WD_NIC+0x0c)   /* W - Receive Configuration */
+#define CNTR0  (WD_BASE+WD_NIC+0x0d)   /* R - Frame Alignment Errors 0 */
+#define TCR    (WD_BASE+WD_NIC+0x0d)   /* W - Transmit Configuration */
+#define CNTR1  (WD_BASE+WD_NIC+0x0e)   /* R - Frame Alignment Errors 1 */ 
+#define DCR    (WD_BASE+WD_NIC+0x0e)   /* W - Data Configuration */
+#define CNTR2  (WD_BASE+WD_NIC+0x0f)   /* R - Missed Packet Errors */
+#define IMR    (WD_BASE+WD_NIC+0x0f)   /* W - Interrupt Mask Register */
+
+/* Page 1 */
+               /* RW - Command */
+#define PAR0   (WD_BASE+WD_NIC+0x01)   /* RW - Physical Address 0 */
+#define PAR1   (WD_BASE+WD_NIC+0x02)   /* RW - Physical Address 1 */
+#define PAR2   (WD_BASE+WD_NIC+0x03)   /* RW - Physical Address 2 */
+#define PAR3   (WD_BASE+WD_NIC+0x04)   /* RW - Physical Address 3 */
+#define PAR4   (WD_BASE+WD_NIC+0x04)   /* RW - Physical Address 4 */
+#define PAR5   (WD_BASE+WD_NIC+0x05)   /* RW - Physical Address 5 */
+#define PAR6   (WD_BASE+WD_NIC+0x06)   /* RW - Physical Address 6 */
+#define CURR   (WD_BASE+WD_NIC+0x07)   /* RW - Current Page */
+#define MAR0   (WD_BASE+WD_NIC+0x08)   /* RW - Multicast Address 0 */
+#define MAR1   (WD_BASE+WD_NIC+0x09)   /* RW - Multicast Address 1 */
+#define MAR2   (WD_BASE+WD_NIC+0x0a)   /* RW - Multicast Address 2 */
+#define MAR3   (WD_BASE+WD_NIC+0x0b)   /* RW - Multicast Address 3 */
+#define MAR4   (WD_BASE+WD_NIC+0x0c)   /* RW - Multicast Address 4 */
+#define MAR5   (WD_BASE+WD_NIC+0x0d)   /* RW - Multicast Address 5 */
+#define MAR6   (WD_BASE+WD_NIC+0x0e)   /* RW - Multicast Address 6 */
+#define MAR7   (WD_BASE+WD_NIC+0x0f)   /* RW - Multicast Address 7 */
+
+/* Page 2 */
+/* Page 2 Registers are RW opposite Page 0 */
+/* and should be used for diagnostic purposes only */
+
+/* Command Register bits */
+#define STOP   1       /* In progress jobs finished, software reset */
+#define STA    2       /* Activate the NIC */
+#define TXP    4       /* Initiate TX packet */
+#define RD0    8       /* Remote DMA commands */
+#define RD1    0x10    
+#define RD2    0x20
+#define PS0    0x40    /* Page Select */
+#define  PS1   0x80    /* 00 = 0, 01 = 1, 10 = 2, 11 = reserved */
+
+#define PAGE0  ~(PS0|PS1)      /* Remember to AND this */
+#define PAGE1  PS0             /* these can be OR'd */
+#define PAGE2  PS1
+#define NO_DMA RD2
+
+/* Interrupt Status Register bits */
+#define PRX    1       /* Packet received with no errors */
+#define PTX    2       /* Packet transmitted with no errors */
+#define RXE    4       /* Packet received with errors */
+#define TXE    8       /* Transmit aborted with errors */
+#define OVW    0x10    /* Overwrite warning */
+#define CNT    0x20    /* Counter overflow warning */
+#define RDC    0x40    /* Remote DMA complete */
+#define RST    0x80    /* Reset status - does not cause interrupts */
+
+/* Interrupt Mask Register - 1 = enabled */
+#define PRXE   1       /* Packet received */
+#define PTXE   2       /* Packet transmitted */
+#define RXEE   4       /* Receive error */
+#define TXEE   8       /* Transmit error */
+#define OVWE   0x10    /* Overwrite error */
+#define CNTE   0x20    /* Counter overflow */
+#define RDCE   0x40    /* Remote DMA complete */
+
+/* Data Configuration Register */
+#define WTS    1       /* Word Transfer 0 = byte, 1 = word */
+#define BOS    2       /* Byte Order 0 = 8086, 1 = 68000 */
+#define LAS    4       /* Long Address 0 = 16bit, 1 = 32 bit DMA */
+#define LS     8       /* Loopback = 0, 1 = Normal */
+#define AR     0x10    /* Autoinitialize = 1 DMA, 0 = software */
+#define FT0    0x20    /* FIFO Threshold (word mode /2 ) */
+#define FT1    0x40    /* 00 = 2, 01 = 4, 10 = 8, 11 = 12 bytes */
+
+/* Transmit Configuration Register */
+#define CRCI   1       /* CRC inhibit = 1, append = 0 */
+#define LB0    2       /* Loopback control 00 = normal loopback */
+#define LB1    4       /* 01 = internal, 10 = external1, 11 = external2 */
+#define ATD    8       /* Auto Transmit Enable = 1 tx inhibit enabled */
+#define OFST   0x10    /* Collision offset 1 = modify to low priority mode */
+
+/* Transmitter Status Register */
+#define PTXOK  1       /* Packet transmitted without error */
+               /* reserved */
+#define COL    4       /* Transmit collided, check NCR for count */
+#define ABT    8       /* Transmit aborted - 16 tries */
+#define CRS    0x10    /* Carrier Sense lost */
+#define FU     0x20    /* FIFO underrun */
+#define CDH    0x40    /* CD Heartbeat failed */
+#define OWC    0x80    /* Out of window collision */
+
+/* Receive configuration Register */
+#define SEP    1       /* Save error packets = 1 */
+#define ARUNT  2       /* Accept RUNT packets < 64 bytes */
+#define AB     4       /* Accept Broadcast packets */
+#define AM     8       /* Accept Multicast packets */
+#define PRO    0x10    /* Promiscuous mode */
+#define MON    0x20    /* Monitor mode */
+
+/* Receive Status Register */
+#define PRX    1       /* Packet received without error */
+#define CRC    2       /* CRC error */
+#define FAE    4       /* Frame Alignment error */
+#define FO     8       /* FIFO overrun error */
+#define MPA    0x10    /* Missed packet */
+#define PHY    0x20    /* Physical = 0, Multicast/Broadcast = 1 */
+#define DIS    0x40    /* Receiver disabled (monitor mode) */
+#define DFR    0x80    /* Deferring - jabber on line */
+
+
+
+
+
index 3573df91abcf6ffd8b975d72bb39bc4333c096fc..d7005c682fb53c3a16fafafec54056f8be95175a 100644 (file)
@@ -32,7 +32,7 @@
 #define MINIX_HEADER 32
 #define GCC_HEADER 1024
 
-#define SYS_SIZE 0x5000
+#define SYS_SIZE 0x7000
 
 #define DEFAULT_MAJOR_ROOT 0
 #define DEFAULT_MINOR_ROOT 0
index 5af1c7cdbb5f2533d99d88fa45c41a5be5064a4c..97a80566df29a63c293cdf22fdb21bcc05e077be 100644 (file)
@@ -1,5 +1,5 @@
-#define UTS_RELEASE "0.97.pl5-15"
-#define UTS_VERSION "09/19/92"
-#define LINUX_COMPILE_TIME "13:29:53"
+#define UTS_RELEASE "0.98-21"
+#define UTS_VERSION "09/29/92"
+#define LINUX_COMPILE_TIME "20:36:17"
 #define LINUX_COMPILE_BY "root"
 #define LINUX_COMPILE_HOST "home"