Commit e25f5340 authored by Linus Torvalds's avatar Linus Torvalds

[PATCH] Linux-0.98 (September 29, 1992)

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
parent d9723f6c
...@@ -4,14 +4,14 @@ ...@@ -4,14 +4,14 @@
# default of FLOPPY is used by 'build'. # 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 # uncomment this if you want kernel profiling: the profile_shift is the
# granularity of the profiling (5 = 32-byte granularity) # granularity of the profiling (5 = 32-byte granularity)
# #
PROFILING = -DPROFILE_SHIFT=2 PROFILING =# -DPROFILE_SHIFT=2
# #
# uncomment the correct keyboard: # uncomment the correct keyboard:
...@@ -30,9 +30,9 @@ PROFILING = -DPROFILE_SHIFT=2 ...@@ -30,9 +30,9 @@ PROFILING = -DPROFILE_SHIFT=2
# 0x08 - tilde (~) # 0x08 - tilde (~)
# 0x10 - dieresis (umlaut) # 0x10 - dieresis (umlaut)
KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 # KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F # KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F
# KEYBOARD = -DKBD_US -DKBDFLAGS=0 KEYBOARD = -DKBD_US -DKBDFLAGS=0
# KEYBOARD = -DKBD_GR -DKBDFLAGS=0 # KEYBOARD = -DKBD_GR -DKBDFLAGS=0
# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F # KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F
# KEYBOARD = -DKBD_FR -DKBDFLAGS=0 # KEYBOARD = -DKBD_FR -DKBDFLAGS=0
...@@ -43,6 +43,8 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 ...@@ -43,6 +43,8 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
# KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0 # KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0
# KEYBOARD = -DKBD_SG -DKBDFLAGS=0 # KEYBOARD = -DKBD_SG -DKBDFLAGS=0
# KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x9F # KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x9F
# KEYBOARD = -DKBD_SF -DKBDFLAGS=0
# KEYBOARD = -DKBD_SF_LATIN1 -DKBDFLAGS=0x9F
# KEYBOARD = -DKDB_NO # KEYBOARD = -DKDB_NO
# #
...@@ -51,11 +53,24 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 ...@@ -51,11 +53,24 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
MATH_EMULATION = -DKERNEL_MATH_EMULATION 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 # 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 # if you want the ram-disk device, define this to be the
...@@ -80,7 +95,7 @@ LD =ld ...@@ -80,7 +95,7 @@ LD =ld
HOSTCC =gcc -static HOSTCC =gcc -static
CC =gcc -DKERNEL CC =gcc -DKERNEL
MAKE =make MAKE =make
CPP =$(CC) -E CPP =$(CC) -E -DMAX_MEGABYTES=$(MAX_MEGABYTES)
AR =ar AR =ar
ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o
...@@ -112,7 +127,7 @@ linuxsubdirs: dummy ...@@ -112,7 +127,7 @@ linuxsubdirs: dummy
Version: Version:
@./makever.sh @./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 UTS_VERSION \"`date +%D`\" >> tools/version.h
@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
...@@ -134,6 +149,9 @@ tools/build: tools/build.c ...@@ -134,6 +149,9 @@ tools/build: tools/build.c
boot/head.o: boot/head.s 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 tools/version.o: tools/version.c tools/version.h
init/main.o: init/main.c init/main.o: init/main.c
...@@ -173,7 +191,7 @@ kernel: dummy ...@@ -173,7 +191,7 @@ kernel: dummy
clean: clean:
rm -f Image System.map tmp_make core boot/bootsect boot/setup \ 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 rm -f init/*.o tools/system tools/build boot/*.o tools/*.o
for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
......
...@@ -72,10 +72,24 @@ want to include these in the kernel, do the following: ...@@ -72,10 +72,24 @@ want to include these in the kernel, do the following:
- remove the corresponding lines in the initialization of - remove the corresponding lines in the initialization of
file_systems in fs/super.c. file_systems in fs/super.c.
5. To configure more ptys do this: 5. The TCP/IP code is in the standard sources as of version 0.98, but
- change NR_PTYS in include/linux/tty.h to the number you want it is not compiled into the normal binary. To get TCP/IP working, you
- create the new files in /dev have to:
- recompile the kernel
- 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 * Running make
...@@ -105,7 +119,8 @@ copy. This requires dd. ...@@ -105,7 +119,8 @@ copy. This requires dd.
"make dep" updates all dependencies. This requires sed. It modifies "make dep" updates all dependencies. This requires sed. It modifies
the makefiles directly (the end of them, starting at the ###Dependencies 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 "make clean" will remove all object files and other files created by the
compilation. This requires basename. compilation. This requires basename.
...@@ -120,4 +135,4 @@ The tee part is so that you can check what is going on while the ...@@ -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 compilation runs. If you have GNU emacs and use M-x compile you don't
need this, of course. need this, of course.
Lars Wirzeniu Lars Wirzenius
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
* Copyright (C) 1991, 1992 Linus Torvalds * 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. * head.s contains the 32-bit startup code.
* *
...@@ -114,6 +118,8 @@ rp_sidt: ...@@ -114,6 +118,8 @@ rp_sidt:
* I put the kernel page tables right after the page directory, * I put the kernel page tables right after the page directory,
* using 4 of them to span 16 Mb of physical memory. People with * using 4 of them to span 16 Mb of physical memory. People with
* more than 16MB will have to expand this. * 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 .org 0x1000
pg0: pg0:
...@@ -128,6 +134,20 @@ pg2: ...@@ -128,6 +134,20 @@ pg2:
pg3: pg3:
.org 0x5000 .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, * 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 * so that a process isn't accidentally killed due to a page fault when
...@@ -136,7 +156,11 @@ pg3: ...@@ -136,7 +156,11 @@ pg3:
.globl _empty_bad_page .globl _empty_bad_page
_empty_bad_page: _empty_bad_page:
#if MAX_MEGABYTES == 32
.org 0xa000
#else
.org 0x6000 .org 0x6000
#endif
/* /*
* empty_bad_page_table is similar to the above, but is used when the * empty_bad_page_table is similar to the above, but is used when the
* system needs a bogus page-table * system needs a bogus page-table
...@@ -144,7 +168,11 @@ _empty_bad_page: ...@@ -144,7 +168,11 @@ _empty_bad_page:
.globl _empty_bad_page_table .globl _empty_bad_page_table
_empty_bad_page_table: _empty_bad_page_table:
#if MAX_MEGABYTES == 32
.org 0xb000
#else
.org 0x7000 .org 0x7000
#endif
/* /*
* tmp_floppy_area is used by the floppy-driver when DMA cannot * 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 * reach to a buffer-block. It needs to be aligned, so that it isn't
...@@ -232,10 +260,17 @@ ignore_int: ...@@ -232,10 +260,17 @@ ignore_int:
* I've tried to show which constants to change by having * I've tried to show which constants to change by having
* some kind of marker at them (search for "16Mb"), but I * some kind of marker at them (search for "16Mb"), but I
* won't guarantee that's all :-( ) * won't guarantee that's all :-( )
*
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
*/ */
.align 2 .align 2
setup_paging: 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 */ movl $1024*5,%ecx /* 5 pages - swapper_pg_dir+4 page tables */
#endif
xorl %eax,%eax xorl %eax,%eax
xorl %edi,%edi /* swapper_pg_dir is at 0x000 */ xorl %edi,%edi /* swapper_pg_dir is at 0x000 */
cld;rep;stosl cld;rep;stosl
...@@ -246,8 +281,18 @@ setup_paging: ...@@ -246,8 +281,18 @@ setup_paging:
movl $pg1+7,_swapper_pg_dir+3076 /* --------- " " --------- */ movl $pg1+7,_swapper_pg_dir+3076 /* --------- " " --------- */
movl $pg2+7,_swapper_pg_dir+3080 /* --------- " " --------- */ movl $pg2+7,_swapper_pg_dir+3080 /* --------- " " --------- */
movl $pg3+7,_swapper_pg_dir+3084 /* --------- " " --------- */ 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 $pg3+4092,%edi
movl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */ movl $0x0fff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */
#endif
std std
1: stosl /* fill pages backwards - more efficient :-) */ 1: stosl /* fill pages backwards - more efficient :-) */
subl $0x1000,%eax subl $0x1000,%eax
...@@ -282,10 +327,14 @@ gdt_descr: ...@@ -282,10 +327,14 @@ gdt_descr:
.word 256*8-1 .word 256*8-1
.long 0xc0000000+_gdt .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 .align 4
_gdt: _gdt:
.quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* NULL descriptor */
.quad 0xc0c09a0000000fff /* 16Mb at 0xC0000000 */ .quad 0xc0c39a000000ffff /* 1GB at 0xC0000000 */
.quad 0xc0c0920000000fff /* 16Mb */ .quad 0xc0c392000000ffff /* 1GB */
.quad 0x0000000000000000 /* TEMPORARY - don't use */ .quad 0x0000000000000000 /* TEMPORARY - don't use */
.fill 252,8,0 /* space for LDT's and TSS's etc */ .fill 252,8,0 /* space for LDT's and TSS's etc */
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.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); extern int check_cdrom_media_change(int, int);
#endif #endif
...@@ -133,7 +133,7 @@ void check_disk_change(int dev) ...@@ -133,7 +133,7 @@ void check_disk_change(int dev)
brelse(bh); brelse(bh);
break; break;
#ifdef CONFIG_BLK_DEV_SR #if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
case 11: /* CDROM */ case 11: /* CDROM */
i = check_cdrom_media_change(dev, 0); i = check_cdrom_media_change(dev, 0);
if (i) printk("Flushing buffers and inodes for CDROM\n"); if (i) printk("Flushing buffers and inodes for CDROM\n");
......
...@@ -101,13 +101,16 @@ int core_dump(long signr, struct pt_regs * regs) ...@@ -101,13 +101,16 @@ int core_dump(long signr, struct pt_regs * regs)
if (!file.f_op->write) if (!file.f_op->write)
goto close_coredump; goto close_coredump;
has_dumped = 1; has_dumped = 1;
/* write and seek example: from kernel space */ /* changed the size calculations - should hopefully work better. lbt */
__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
dump.magic = CMAGIC; dump.magic = CMAGIC;
dump.u_tsize = current->end_code / PAGE_SIZE; dump.start_code = 0;
dump.u_dsize = (current->brk - current->end_code) / PAGE_SIZE; dump.start_stack = regs->esp & ~(PAGE_SIZE - 1);
dump.u_ssize =((current->start_stack +(PAGE_SIZE-1)) / PAGE_SIZE) - dump.u_tsize = ((unsigned long) current->end_code) >> 12;
(regs->esp/ PAGE_SIZE); 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 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 we wrote the stack, but not the data area. */
if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE/1024 > if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE/1024 >
...@@ -121,8 +124,6 @@ int core_dump(long signr, struct pt_regs * regs) ...@@ -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.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump)));
dump.signal = signr; dump.signal = signr;
dump.regs = *regs; dump.regs = *regs;
dump.start_code = 0;
dump.start_stack = regs->esp & ~(PAGE_SIZE - 1);
/* Flag indicating the math stuff is valid. */ /* Flag indicating the math stuff is valid. */
if (dump.u_fpvalid = current->used_math) { if (dump.u_fpvalid = current->used_math) {
if (last_task_used_math == current) if (last_task_used_math == current)
...@@ -130,6 +131,7 @@ int core_dump(long signr, struct pt_regs * regs) ...@@ -130,6 +131,7 @@ int core_dump(long signr, struct pt_regs * regs)
else else
memcpy(&dump.i387,&current->tss.i387,sizeof(dump.i387)); memcpy(&dump.i387,&current->tss.i387,sizeof(dump.i387));
}; };
__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
DUMP_WRITE(&dump,sizeof(dump)); DUMP_WRITE(&dump,sizeof(dump));
DUMP_SEEK(sizeof(dump)); DUMP_SEEK(sizeof(dump));
/* Dump the task struct. Not be used by gdb, but could be useful */ /* 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) ...@@ -140,14 +142,14 @@ int core_dump(long signr, struct pt_regs * regs)
__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17)); __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17));
/* Dump the data area */ /* Dump the data area */
if (dump.u_dsize != 0) { if (dump.u_dsize != 0) {
dump_start = current->end_code; dump_start = dump.u_tsize << 12;
dump_size = current->brk - current->end_code; dump_size = dump.u_dsize << 12;
DUMP_WRITE(dump_start,dump_size); DUMP_WRITE(dump_start,dump_size);
}; };
/* Now prepare to dump the stack area */ /* Now prepare to dump the stack area */
if (dump.u_ssize != 0) { if (dump.u_ssize != 0) {
dump_start = regs->esp & ~(PAGE_SIZE - 1); dump_start = dump.start_stack;
dump_size = dump.u_ssize * PAGE_SIZE; dump_size = dump.u_ssize << 12;
DUMP_WRITE(dump_start,dump_size); DUMP_WRITE(dump_start,dump_size);
}; };
close_coredump: close_coredump:
......
...@@ -50,12 +50,12 @@ chrdev.o : chrdev.c /usr/include/linux/sched.h /usr/include/linux/head.h /usr/in ...@@ -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/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/termios.h /usr/include/asm/system.h /usr/include/linux/stat.h \
/usr/include/linux/fcntl.h /usr/include/linux/errno.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 \ dir.o : dir.c /usr/include/asm/segment.h /usr/include/linux/errno.h /usr/include/linux/kernel.h \
/usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.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/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.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/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_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/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
/usr/include/linux/ext_fs.h /usr/include/linux/stat.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 \ 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/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/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <asm/segment.h> #include <asm/segment.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ext_fs.h> #include <linux/ext_fs.h>
#include <linux/stat.h> #include <linux/stat.h>
...@@ -73,6 +74,13 @@ static int ext_readdir(struct inode * inode, struct file * filp, ...@@ -73,6 +74,13 @@ static int ext_readdir(struct inode * inode, struct file * filp,
while (offset < 1024 && filp->f_pos < inode->i_size) { while (offset < 1024 && filp->f_pos < inode->i_size) {
offset += de->rec_len; offset += de->rec_len;
filp->f_pos += 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) { if (de->inode) {
for (i = 0; i < de->name_len; i++) for (i = 0; i < de->name_len; i++)
if (c = de->name[i]) if (c = de->name[i])
......
...@@ -122,6 +122,14 @@ static struct buffer_head * ext_find_entry(struct inode * dir, ...@@ -122,6 +122,14 @@ static struct buffer_head * ext_find_entry(struct inode * dir,
if (prev_dir) if (prev_dir)
*prev_dir = NULL; *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)) { if (ext_match(namelen,name,de)) {
*res_dir = de; *res_dir = de;
if (next_dir) if (next_dir)
...@@ -253,6 +261,14 @@ printk ("ext_add_entry : creating next block\n"); ...@@ -253,6 +261,14 @@ printk ("ext_add_entry : creating next block\n");
dir->i_dirt = 1; dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME; 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->inode && de->rec_len >= rec_len) {
if (de->rec_len > rec_len if (de->rec_len > rec_len
&& de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) { && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) {
...@@ -471,6 +487,14 @@ static int empty_dir(struct inode * inode) ...@@ -471,6 +487,14 @@ static int empty_dir(struct inode * inode)
} }
de = (struct ext_dir_entry *) bh->b_data; 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) { if (de->inode) {
brelse(bh); brelse(bh);
return 0; return 0;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
/*--------- MICROSOFT BUSMOUSE ITEMS -------------*/ /*--------- MICROSOFT BUSMOUSE ITEMS -------------*/
#define MS_MSE_DATA_PORT 0x23d #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_CONTROL_PORT 0x23c
#define MS_MSE_CONFIG_PORT 0x23f #define MS_MSE_CONFIG_PORT 0x23f
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
#define DEF_INITSEG 0x9000 #define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000 #define DEF_SYSSEG 0x1000
#define DEF_SETUPSEG 0x9020 #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 * The root-device is no longer hard-coded. You can change the default
......
...@@ -48,6 +48,13 @@ struct sockaddr { ...@@ -48,6 +48,13 @@ struct sockaddr {
#define SO_KEEPALIVE 9 #define SO_KEEPALIVE 9
#define SO_OOBINLINE 10 #define SO_OOBINLINE 10
#define SO_NO_CHECK 11 #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 */ /* setsockoptions level */
#define SOL_SOCKET 1 #define SOL_SOCKET 1
......
#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
...@@ -162,8 +162,8 @@ void start_kernel(void) ...@@ -162,8 +162,8 @@ void start_kernel(void)
envp_init[1] = term; envp_init[1] = term;
memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000; memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024) if (memory_end > MAX_MEGABYTES*1024*1024)
memory_end = 16*1024*1024; memory_end = MAX_MEGABYTES*1024*1024;
memory_start = 1024*1024; memory_start = 1024*1024;
low_memory_start = (unsigned long) &end; low_memory_start = (unsigned long) &end;
low_memory_start += 0xfff; low_memory_start += 0xfff;
......
...@@ -63,7 +63,7 @@ static void extended_partition(struct gendisk *hd, int dev) ...@@ -63,7 +63,7 @@ static void extended_partition(struct gendisk *hd, int dev)
goto done; /* shouldn't happen */ goto done; /* shouldn't happen */
hd->part[current_minor].start_sect = this_sector + p->start_sect; hd->part[current_minor].start_sect = this_sector + p->start_sect;
printk(" Logical part %d start %d size %d end %d\n\r", 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].nr_sects,
hd->part[current_minor].start_sect + hd->part[current_minor].start_sect +
hd->part[current_minor].nr_sects - 1); hd->part[current_minor].nr_sects - 1);
...@@ -96,6 +96,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev) ...@@ -96,6 +96,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
struct buffer_head *bh; struct buffer_head *bh;
struct partition *p; struct partition *p;
unsigned long first_sector; unsigned long first_sector;
int mask = (1 << hd->minor_shift) - 1;
first_sector = hd->part[MINOR(dev)].start_sect; first_sector = hd->part[MINOR(dev)].start_sect;
...@@ -103,7 +104,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev) ...@@ -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); printk("Unable to read partition table of device %04x\n",dev);
return; 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 */ current_minor += 4; /* first "extra" minor */
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = 0x1BE + (void *)bh->b_data; p = 0x1BE + (void *)bh->b_data;
...@@ -127,14 +128,14 @@ static void check_partition(struct gendisk *hd, unsigned int dev) ...@@ -127,14 +128,14 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
p = 0x1BE + (void *)bh->b_data; p = 0x1BE + (void *)bh->b_data;
for (i = 4 ; i < 16 ; i++, current_minor++) { for (i = 4 ; i < 16 ; i++, current_minor++) {
p--; p--;
if ((current_minor & 0x3f) >= 60) if ((current_minor & mask) >= mask-2)
break; break;
if (!(p->start_sect && p->nr_sects)) if (!(p->start_sect && p->nr_sects))
continue; continue;
hd->part[current_minor].start_sect = p->start_sect; hd->part[current_minor].start_sect = p->start_sect;
hd->part[current_minor].nr_sects = p->nr_sects; hd->part[current_minor].nr_sects = p->nr_sects;
printk(" DM part %d start %d size %d end %d\n\r", 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].start_sect,
hd->part[current_minor].nr_sects, hd->part[current_minor].nr_sects,
hd->part[current_minor].start_sect + hd->part[current_minor].start_sect +
......
...@@ -251,23 +251,27 @@ static inline int wait_DRQ(void) ...@@ -251,23 +251,27 @@ static inline int wait_DRQ(void)
static void read_intr(void) static void read_intr(void)
{ {
int i; int i;
int retries = 100000;
i = (unsigned) inb_p(HD_STATUS); do {
if ((i & STAT_MASK) != STAT_OK) { i = (unsigned) inb_p(HD_STATUS);
printk("HD: read_intr: status = 0x%02x\n",i); if ((i & STAT_MASK) != STAT_OK)
goto bad_read; break;
} if (i & DRQ_STAT)
if (wait_DRQ()) { goto ok_to_read;
printk("HD: read_intr: no DRQ\n"); } while (--retries > 0);
goto bad_read; 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); 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->errors = 0;
CURRENT->buffer += 512; CURRENT->buffer += 512;
CURRENT->sector++; CURRENT->sector++;
...@@ -290,29 +294,31 @@ static void read_intr(void) ...@@ -290,29 +294,31 @@ static void read_intr(void)
#endif #endif
do_hd_request(); do_hd_request();
return; 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) static void write_intr(void)
{ {
int i; int i;
int retries = 100000;
i = (unsigned) inb_p(HD_STATUS); do {
if ((i & STAT_MASK) != STAT_OK) { i = (unsigned) inb_p(HD_STATUS);
printk("HD: write_intr: status = 0x%02x\n",i); if ((i & STAT_MASK) != STAT_OK)
goto bad_write; break;
} if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
if (CURRENT->nr_sectors > 1 && wait_DRQ()) { goto ok_to_write;
printk("HD: write_intr: no DRQ\n"); } while (--retries > 0);
goto bad_write; 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++; CURRENT->sector++;
i = --CURRENT->nr_sectors; i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors; --CURRENT->current_nr_sectors;
...@@ -330,16 +336,6 @@ static void write_intr(void) ...@@ -330,16 +336,6 @@ static void write_intr(void)
do_hd_request(); do_hd_request();
} }
return; 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) static void recal_intr(void)
...@@ -587,7 +583,7 @@ static void hd_interrupt(int unused) ...@@ -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 * means we run the IRQ-handler with interrupts disabled: this is bad for
* interrupt latency, but anything else has led to problems on some * interrupt latency, but anything else has led to problems on some
* machines... * machines...
......
/* /*
* ultrastor.c Copyright (C) 1991, 1992 David B. Gentzel * ultrastor.c Copyright (C) 1992 David B. Gentzel
* Low-level SCSI driver for UltraStor 14F * Low-level SCSI driver for UltraStor 14F
* by David B. Gentzel, Whitfield Software Services, Carnegie, PA * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
* (gentzel@nova.enet.dec.com) * (gentzel@nova.enet.dec.com)
...@@ -24,9 +24,8 @@ ...@@ -24,9 +24,8 @@
/* /*
* CAVEATS: ??? * CAVEATS: ???
* This driver is VERY stupid. It takes no advantage of much of the power * 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 * of the UltraStor controller. I hope to go back and beat it into shape,
* commands to complete. I hope to go back and beat it into shape, but * but PLEASE, anyone else who would like to, please make improvements!
* PLEASE, anyone else who would like to, please make improvements!
* *
* By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature * 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 * of the mid-level SCSI driver. Once I'm satisfied that the queueing
...@@ -155,7 +154,7 @@ static const unsigned short ultrastor_ports[] = { ...@@ -155,7 +154,7 @@ static const unsigned short ultrastor_ports[] = {
}; };
#endif #endif
void ultrastor_interrupt(void); static void ultrastor_interrupt(int cpl);
static void (*ultrastor_done)(int, int) = 0; static void (*ultrastor_done)(int, int) = 0;
...@@ -292,11 +291,19 @@ int ultrastor_14f_detect(int hostnum) ...@@ -292,11 +291,19 @@ int ultrastor_14f_detect(int hostnum)
host_number = hostnum; host_number = hostnum;
scsi_hosts[hostnum].this_id = config.ha_scsi_id; scsi_hosts[hostnum].this_id = config.ha_scsi_id;
#ifndef NO_QUEUEING #ifndef NO_QUEUEING
set_intr_gate(0x20 + config.interrupt, ultrastor_interrupt); {
/* gate to PIC 2 */ struct sigaction sa;
outb_p(inb_p(0x21) & ~BIT(2), 0x21);
/* enable the interrupt */ sa.sa_handler = ultrastor_interrupt;
outb(inb_p(0xA1) & ~BIT(config.interrupt - 8), 0xA1); 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 #endif
return TRUE; return TRUE;
} }
...@@ -309,7 +316,7 @@ const char *ultrastor_14f_info(void) ...@@ -309,7 +316,7 @@ const char *ultrastor_14f_info(void)
} }
static struct mscp mscp = { 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, int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd,
...@@ -437,15 +444,15 @@ int ultrastor_14f_reset(void) ...@@ -437,15 +444,15 @@ int ultrastor_14f_reset(void)
} }
#ifndef NO_QUEUEING #ifndef NO_QUEUEING
void ultrastor_interrupt_service(void) static void ultrastor_interrupt(int cpl)
{ {
#if (ULTRASTOR_DEBUG & UD_INTERRUPT) #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); (mscp.adapter_status << 16) | mscp.target_status);
#endif #endif
if (ultrastor_done == 0) if (ultrastor_done == 0)
panic("US14F: interrupt_service: unexpected interrupt!\n"); panic("US14F: interrupt: unexpected interrupt!\n");
else { else {
void (*done)(int, int); void (*done)(int, int);
...@@ -464,40 +471,9 @@ void ultrastor_interrupt_service(void) ...@@ -464,40 +471,9 @@ void ultrastor_interrupt_service(void)
} }
#if (ULTRASTOR_DEBUG & UD_INTERRUPT) #if (ULTRASTOR_DEBUG & UD_INTERRUPT)
printk("US14F: interrupt_service: returning\n"); printk("US14F: interrupt: returning\n");
#endif #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
#endif #endif
...@@ -31,7 +31,12 @@ ...@@ -31,7 +31,12 @@
* Added a couple of new functions to handle differences in using * Added a couple of new functions to handle differences in using
* MS vs. Logitech (where the int variable wasn't appropriate). * 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> #include <linux/kernel.h>
...@@ -254,9 +259,40 @@ long bus_mouse_init(long kmem_start) ...@@ -254,9 +259,40 @@ long bus_mouse_init(long kmem_start)
return kmem_start; return kmem_start;
} }
#define MS_DELAY 100000
long ms_bus_mouse_init(long kmem_start) 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(); MS_MSE_INT_OFF();
mouse.present = 1; mouse.present = 1;
......
...@@ -160,7 +160,7 @@ static void puts_queue(char *cp) ...@@ -160,7 +160,7 @@ static void puts_queue(char *cp)
struct tty_struct *tty = TTY_TABLE(0); struct tty_struct *tty = TTY_TABLE(0);
unsigned long new_head; unsigned long new_head;
char ch; char ch;
wake_up(&keypress_wait); wake_up(&keypress_wait);
if (!tty) if (!tty)
return; return;
...@@ -963,6 +963,95 @@ static unsigned char alt_map[] = { ...@@ -963,6 +963,95 @@ static unsigned char alt_map[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 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 #else
#error "KBD-type not defined" #error "KBD-type not defined"
#endif #endif
......
...@@ -328,7 +328,7 @@ static void init(struct async_struct * info) ...@@ -328,7 +328,7 @@ static void init(struct async_struct * info)
outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port); outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
status1 = inb_p(UART_MSR + port) & 0xF0; status1 = inb_p(UART_MSR + port) & 0xF0;
outb_p(scratch, UART_MCR + port); outb_p(scratch, UART_MCR + port);
outb_p(scratch, UART_MSR + port); outb_p(scratch2, UART_MSR + port);
if (status1 != 0x90) { if (status1 != 0x90) {
info->type = PORT_UNKNOWN; info->type = PORT_UNKNOWN;
return; return;
...@@ -379,9 +379,7 @@ static void init(struct async_struct * info) ...@@ -379,9 +379,7 @@ static void init(struct async_struct * info)
*/ */
void rs_write(struct tty_struct * tty) void rs_write(struct tty_struct * tty)
{ {
int line = tty->line - 64; do_rs_write(rs_table+DEV_TO_SL(tty->line));
do_rs_write(rs_table+line);
} }
/* /*
...@@ -571,6 +569,10 @@ static int set_serial_info(struct async_struct * info, ...@@ -571,6 +569,10 @@ static int set_serial_info(struct async_struct * info,
irq = ISR->irq; irq = ISR->irq;
if (irq == 2) if (irq == 2)
irq = 9; irq = 9;
if (!new_irq)
new_irq = irq;
if (!new_port)
new_port = info->port;
if (irq != new_irq) { if (irq != new_irq) {
/* /*
* We need to change the IRQ for this board. OK, if * We need to change the IRQ for this board. OK, if
...@@ -582,7 +584,7 @@ static int set_serial_info(struct async_struct * info, ...@@ -582,7 +584,7 @@ static int set_serial_info(struct async_struct * info,
sa.sa_flags = (SA_INTERRUPT); sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0; sa.sa_mask = 0;
sa.sa_restorer = NULL; sa.sa_restorer = NULL;
retval = irqaction(irq,&sa); retval = irqaction(new_irq,&sa);
if (retval) if (retval)
return retval; return retval;
} }
...@@ -608,6 +610,7 @@ static int set_serial_info(struct async_struct * info, ...@@ -608,6 +610,7 @@ static int set_serial_info(struct async_struct * info,
if (ISR->next_ISR) if (ISR->next_ISR)
ISR->next_ISR->prev_ISR = ISR; ISR->next_ISR->prev_ISR = ISR;
IRQ_ISR[new_irq] = ISR; IRQ_ISR[new_irq] = ISR;
ISR->irq = new_irq;
} }
cli(); cli();
if (new_port != info->port) { if (new_port != info->port) {
......
...@@ -155,7 +155,7 @@ static void mark_screen_rdonly(struct task_struct * tsk) ...@@ -155,7 +155,7 @@ static void mark_screen_rdonly(struct task_struct * tsk)
tmp = *(unsigned long *) tmp; tmp = *(unsigned long *) tmp;
if (tmp & PAGE_PRESENT) { if (tmp & PAGE_PRESENT) {
tmp &= 0xfffff000; tmp &= 0xfffff000;
pg_table = (0xA00000 >> PAGE_SHIFT) + (unsigned long *) tmp; pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp;
tmp = 32; tmp = 32;
while (tmp--) { while (tmp--) {
if (PAGE_PRESENT & *pg_table) if (PAGE_PRESENT & *pg_table)
......
...@@ -792,6 +792,10 @@ void do_page_fault(unsigned long *esp, unsigned long error_code) ...@@ -792,6 +792,10 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
/* get the address */ /* get the address */
__asm__("movl %%cr2,%0":"=r" (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) { if (esp[2] & VM_MASK) {
unsigned int bit; unsigned int bit;
......
...@@ -32,6 +32,7 @@ subdirs: dummy ...@@ -32,6 +32,7 @@ subdirs: dummy
clean: clean:
rm -f core *.o *.a tmp_make rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done 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: dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
......
#
# 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
/* 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;
/* 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();
}
/* 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
/* 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);
}
/* 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
/* 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);
}
/* 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
/* 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 */
}
/* 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);
/* 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
/* 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
/* 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;
}
/* 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;
/* 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,}
};
/* 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;
/* 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,}
};
/* 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);
}
}
/* 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
/* 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,}
};
/* 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
/* 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;
}
}
/* 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
/* 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,}
};
/* 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
/* 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);
}
}
/* 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 */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#define MINIX_HEADER 32 #define MINIX_HEADER 32
#define GCC_HEADER 1024 #define GCC_HEADER 1024
#define SYS_SIZE 0x5000 #define SYS_SIZE 0x7000
#define DEFAULT_MAJOR_ROOT 0 #define DEFAULT_MAJOR_ROOT 0
#define DEFAULT_MINOR_ROOT 0 #define DEFAULT_MINOR_ROOT 0
......
#define UTS_RELEASE "0.97.pl5-15" #define UTS_RELEASE "0.98-21"
#define UTS_VERSION "09/19/92" #define UTS_VERSION "09/29/92"
#define LINUX_COMPILE_TIME "13:29:53" #define LINUX_COMPILE_TIME "20:36:17"
#define LINUX_COMPILE_BY "root" #define LINUX_COMPILE_BY "root"
#define LINUX_COMPILE_HOST "home" #define LINUX_COMPILE_HOST "home"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment