aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@cc.helsinki.fi>1993-06-08 14:45:33 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:15 -0400
commitd3465417f56e99c858e0bc136e63aa67e6ef90bf (patch)
tree05c90924879d1385f09d578561f4220c7eb7bad3
parent87aa05879df067f26eb0120af913210ff94e32cf (diff)
downloadarchive-d3465417f56e99c858e0bc136e63aa67e6ef90bf.tar.gz
[ANNOUNCE] linux-0.99 patchlevel 10v0.99-pl10
I've finally released an official version of linux-0.99 patchlevel 10: there have been various alpha versions floating around which differ in details (notably networking code), which shouldn't be used any more. The new linux version is available only as full source code: the diffs would have been too big to be useful. You can find linux-0.99.10.tar.z (along with keytables.tar.z) on nic.funet.fi: pub/OS/Linux/PEOPLE/Linus and probably on tsx-11 and other linux archives within a day or two (so check there first if you are in the states). Linux-0.99 pl10 has a number of new features and changes in interface. The most notable of these are: - the networking code is reorganized (generally called "net-2", although unrelated to the BSD release). The new code implements a lot of standard features lacking in net-1, and also changes the user interface to be closer to the BSD standards. Notably, the old configuration binaries won't work, so to get the new networking to work you'll have to get the net-2 binaries as well. The networking binaries are available on tsx-11.mit.edu (and mirrors) under the directory pub/linux/packages/net/net-2 (and the setup syntax has changed somewhat..) The networking code has been mainly organized and rewritten by Fred van Kempen, with drivers by Donald Becker. - serial line setup has been changed: linux 0.99 pl10 does *not* try to autodetect serial ports very agressively. If you have other serial ports than the standard com1/com2, or nonstandard IRQ etc values, this means that it's less likely to work without any help. The solution is not to recompile the kernel - you should get the "setserial" program available from tsx-11.mit.edu in the directory pub/linux/sources/sbin/setserial-2.01.tar.z that allows you to dynamically configure your serial ports to suit your setup. The main organizer behind the serial line changes is tytso (Theodore Ts'o). - Keyboard setup has changed: it is no longer hardcoded at compile time, but instead you can use the new "loadkeys" program to load in a new keyboard map on the fly. The default keyboard map is the normal US keyboard (yes, I should have used the Finnish one by default, but after thinking of all the problems that would have resulted in I forgot about that idea). The loadkeys code can be found in the "keytables.tar.z" archive, which also contains keymaps for most normal keyboard types. To create a custom keyboard table is very easy - just take a 5 minute look at the existing map files (they resemble the ones used by xmodmap, so if you are familiar with those..) The loadable keymaps were mostly implemented by Risto Kankkunen. There are a lot of other internal kernel changes, but they should be mostly transparent, and noticeable only indirectly due to new features or (hopefully) better/faster/whatever operation. These include: - the SysV IPC patches are in by default: Krishna Balasubramanian. If you need these, you know what it's about (notably, dosemu 0.49 wants them). - inode handling is updated: inodes and files are now dynamically allocated within the kernel, and use a hash table for faster lookup (along with a NFU algorithm for the inode cache). Steven Tweedie. - Updated FPU emulation: mostly exception handling changes, making the emulator handle most exceptions the same way a 486 does. The emulator is written by Bill Metzenthen. - a few ext2-fs updates by Remy Card and Steven Tweedie. - support for the 'fsync()' function (Steven Tweedie) - various (minor) SCSI patches to catch some error conditions, add support for VLB adaptec controllers without DMA and so on (different people). - other changes - I forget. In addition to patches sent in by others, I've naturally made my own changes (often *to* the patches sent in by others :-). Among other things, the pl10 buffer cache code now also tries to share pages with executables, resulting in better cacheing especially of binaries (giving noticeable improvements in kernel recompilation speed on some machines). Also, I've changed a lot of low-level things around to help the iBCS2 project: this includes things like internal segment handling and the signal stack (which now looks the same as on SysV i386 unixes). All in all, pl10 has a disturbing amount of new code, but will hopefully work well despite (due to?) the number of changes. The new networking code in particular will change the network setup a lot - it now looks more standard, but if you were used to the old way of doing things.. On the other hand, most people actively using the networking features have hopefully gotten warnings about this on the NET channel for the last few weeks. Also, the networking code still isn't perfect: Fred is still working on it, but it seems to have reached a reasonably stable platform on which it will be easier to build. Look out for the new-and-improved networking manual, hopefully out soon(?). Standard request: please try it all out, give it a real shakedown, and send comments/bug-reports to the appropriate place (I'm always appropriate, but you may want to send the report to the mailing lists and/or the newsgroup as well). I apologize for the lateness of the release (forcing hlu to make interim gcc releases that relied on nonstandard kernels etc), and the changes are somewhat bigger than I'd prefer, so the more testerts that try it out, the faster we can try to fix any possible problems. The new kernel has gone through various stages of ALPHA-diffs and some late ALPHA-pl10's, so there shouldn't be any major surprises, but alpha releases tend not to get even close to the coverage a real release gets... Linus
-rw-r--r--Configure83
-rw-r--r--Makefile62
-rw-r--r--README99
-rw-r--r--boot/head.S79
-rw-r--r--boot/setup.S5
-rw-r--r--config.in19
-rw-r--r--fs/block_dev.c5
-rw-r--r--fs/buffer.c330
-rw-r--r--fs/exec.c27
-rw-r--r--fs/ext/Makefile2
-rw-r--r--fs/ext/dir.c2
-rw-r--r--fs/ext/file.c2
-rw-r--r--fs/ext/freelists.c1
-rw-r--r--fs/ext/fsync.c186
-rw-r--r--fs/ext/inode.c36
-rw-r--r--fs/ext2/Makefile2
-rw-r--r--fs/ext2/balloc.c7
-rw-r--r--fs/ext2/dcache.c6
-rw-r--r--fs/ext2/dir.c8
-rw-r--r--fs/ext2/file.c2
-rw-r--r--fs/ext2/fsync.c196
-rw-r--r--fs/ext2/ialloc.c7
-rw-r--r--fs/ext2/inode.c161
-rw-r--r--fs/ext2/namei.c129
-rw-r--r--fs/file_table.c72
-rw-r--r--fs/inode.c206
-rw-r--r--fs/isofs/inode.c6
-rw-r--r--fs/locks.c393
-rw-r--r--fs/minix/Makefile2
-rw-r--r--fs/minix/bitmap.c1
-rw-r--r--fs/minix/dir.c2
-rw-r--r--fs/minix/file.c2
-rw-r--r--fs/minix/fsync.c160
-rw-r--r--fs/minix/inode.c39
-rw-r--r--fs/msdos/dir.c2
-rw-r--r--fs/msdos/file.c2
-rw-r--r--fs/msdos/inode.c3
-rw-r--r--fs/msdos/misc.c2
-rw-r--r--fs/msdos/namei.c4
-rw-r--r--fs/nfs/inode.c3
-rw-r--r--fs/nfs/proc.c4
-rw-r--r--fs/nfs/sock.c6
-rw-r--r--fs/pipe.c2
-rw-r--r--fs/proc/Makefile2
-rw-r--r--fs/proc/array.c32
-rw-r--r--fs/proc/inode.c27
-rw-r--r--fs/proc/net.c211
-rw-r--r--fs/proc/root.c3
-rw-r--r--fs/super.c46
-rw-r--r--fs/xiafs/Makefile2
-rw-r--r--fs/xiafs/bitmap.c1
-rw-r--r--fs/xiafs/dir.c2
-rw-r--r--fs/xiafs/file.c2
-rw-r--r--fs/xiafs/fsync.c160
-rw-r--r--fs/xiafs/inode.c42
-rw-r--r--ibcs/Makefile43
-rw-r--r--ibcs/emulate.c26
-rw-r--r--include/asm/irq.h12
-rw-r--r--include/asm/segment.h6
-rw-r--r--include/asm/system.h37
-rw-r--r--include/linux/config.h4
-rw-r--r--include/linux/ddi.h79
-rw-r--r--include/linux/ext2_fs.h24
-rw-r--r--include/linux/ext2_fs_sb.h2
-rw-r--r--include/linux/ext_fs.h2
-rw-r--r--include/linux/fcntl.h2
-rw-r--r--include/linux/fdreg.h6
-rw-r--r--include/linux/fs.h24
-rw-r--r--include/linux/icmp.h81
-rw-r--r--include/linux/if.h147
-rw-r--r--include/linux/if_arp.h83
-rw-r--r--include/linux/if_ether.h88
-rw-r--r--include/linux/in.h171
-rw-r--r--include/linux/ip.h81
-rw-r--r--include/linux/ipc.h65
-rw-r--r--include/linux/kd.h2
-rw-r--r--include/linux/keyboard.h107
-rw-r--r--include/linux/minix_fs.h2
-rw-r--r--include/linux/mm.h6
-rw-r--r--include/linux/msdos_fs.h2
-rw-r--r--include/linux/msg.h73
-rw-r--r--include/linux/net.h133
-rw-r--r--include/linux/nfs.h4
-rw-r--r--include/linux/nfs_fs.h2
-rw-r--r--include/linux/proc_fs.h2
-rw-r--r--include/linux/ptrace.h12
-rw-r--r--include/linux/route.h45
-rw-r--r--include/linux/sched.h46
-rw-r--r--include/linux/segment.h10
-rw-r--r--include/linux/sem.h97
-rw-r--r--include/linux/shm.h88
-rw-r--r--include/linux/signal.h1
-rw-r--r--include/linux/sock_ioctl.h35
-rw-r--r--include/linux/socket.h67
-rw-r--r--include/linux/sockios.h72
-rw-r--r--include/linux/soundcard.h234
-rw-r--r--include/linux/string.h14
-rw-r--r--include/linux/sys.h22
-rw-r--r--include/linux/tcp.h61
-rw-r--r--include/linux/termios.h3
-rw-r--r--include/linux/timer.h23
-rw-r--r--include/linux/tty.h19
-rw-r--r--include/linux/udp.h29
-rw-r--r--include/linux/unistd.h29
-rw-r--r--include/linux/utsname.h11
-rw-r--r--include/linux/wait.h6
-rw-r--r--include/linux/xia_fs.h2
-rw-r--r--init/main.c32
-rw-r--r--ipc/Makefile46
-rw-r--r--ipc/msg.c412
-rw-r--r--ipc/sem.c488
-rw-r--r--ipc/shm.c730
-rw-r--r--ipc/util.c152
-rw-r--r--kernel/FPU-emu/README77
-rw-r--r--kernel/FPU-emu/control_w.h19
-rw-r--r--kernel/FPU-emu/errors.c205
-rw-r--r--kernel/FPU-emu/fpu_aux.c39
-rw-r--r--kernel/FPU-emu/fpu_emu.h23
-rw-r--r--kernel/FPU-emu/fpu_entry.c258
-rw-r--r--kernel/FPU-emu/fpu_etc.c20
-rw-r--r--kernel/FPU-emu/fpu_proto.h12
-rw-r--r--kernel/FPU-emu/fpu_trig.c540
-rw-r--r--kernel/FPU-emu/load_store.c11
-rw-r--r--kernel/FPU-emu/poly_2xm1.c4
-rw-r--r--kernel/FPU-emu/poly_atan.c3
-rw-r--r--kernel/FPU-emu/poly_l2.c6
-rw-r--r--kernel/FPU-emu/poly_tan.c3
-rw-r--r--kernel/FPU-emu/reg_add_sub.c83
-rw-r--r--kernel/FPU-emu/reg_compare.c193
-rw-r--r--kernel/FPU-emu/reg_div.S96
-rw-r--r--kernel/FPU-emu/reg_ld_str.c539
-rw-r--r--kernel/FPU-emu/reg_mul.c46
-rw-r--r--kernel/FPU-emu/reg_norm.S50
-rw-r--r--kernel/FPU-emu/reg_round.S440
-rw-r--r--kernel/FPU-emu/reg_u_add.S21
-rw-r--r--kernel/FPU-emu/reg_u_div.S21
-rw-r--r--kernel/FPU-emu/reg_u_mul.S23
-rw-r--r--kernel/FPU-emu/reg_u_sub.S20
-rw-r--r--kernel/FPU-emu/status_w.h39
-rw-r--r--kernel/FPU-emu/version.h2
-rw-r--r--kernel/blk_drv/blk.h12
-rw-r--r--kernel/blk_drv/floppy.c119
-rw-r--r--kernel/blk_drv/hd.c9
-rw-r--r--kernel/blk_drv/ll_rw_blk.c5
-rw-r--r--kernel/blk_drv/ramdisk.c135
-rw-r--r--kernel/blk_drv/scsi/aha1542.c41
-rw-r--r--kernel/blk_drv/scsi/fdomain.c393
-rw-r--r--kernel/blk_drv/scsi/scsi.c9
-rw-r--r--kernel/blk_drv/scsi/sd.c3
-rw-r--r--kernel/blk_drv/scsi/sr.c137
-rw-r--r--kernel/blk_drv/scsi/sr.h1
-rw-r--r--kernel/blk_drv/scsi/st.c3
-rw-r--r--kernel/blk_drv/scsi/wd7000.c2
-rw-r--r--kernel/blk_drv/xd.c2
-rw-r--r--kernel/chr_drv/Makefile6
-rw-r--r--kernel/chr_drv/console.c144
-rw-r--r--kernel/chr_drv/keyboard.c1526
-rw-r--r--kernel/chr_drv/mem.c3
-rw-r--r--kernel/chr_drv/pty.c17
-rw-r--r--kernel/chr_drv/serial.c619
-rw-r--r--kernel/chr_drv/tty_io.c163
-rw-r--r--kernel/chr_drv/vt.c64
-rw-r--r--kernel/exit.c97
-rw-r--r--kernel/fork.c57
-rw-r--r--kernel/ptrace.c62
-rw-r--r--kernel/sched.c146
-rw-r--r--kernel/signal.c196
-rw-r--r--kernel/sys.c55
-rw-r--r--kernel/sys_call.S72
-rw-r--r--kernel/traps.c112
-rw-r--r--lib/malloc.c184
-rw-r--r--mm/memory.c69
-rw-r--r--mm/mmap.c13
-rw-r--r--mm/swap.c13
-rw-r--r--net/Makefile32
-rw-r--r--net/Space.c83
-rw-r--r--net/ddi.c91
-rw-r--r--net/drv/Makefile47
-rw-r--r--net/drv/README9
-rw-r--r--net/drv/slip/Makefile39
-rw-r--r--net/drv/slip/slip.c661
-rw-r--r--net/drv/slip/slip.h64
-rw-r--r--net/drv/we8003/Makefile42
-rw-r--r--net/drv/we8003/dp8390.h202
-rw-r--r--net/drv/we8003/handler.c (renamed from net/tcp/we.c)309
-rw-r--r--net/drv/we8003/main.c179
-rw-r--r--net/drv/we8003/we8003.h33
-rw-r--r--net/inet/8390.c734
-rw-r--r--net/inet/8390.h150
-rw-r--r--net/inet/CONFIG43
-rw-r--r--net/inet/INTRO.839062
-rw-r--r--net/inet/LICENSE.839015
-rw-r--r--net/inet/Makefile85
-rw-r--r--net/inet/README.839073
-rw-r--r--net/inet/README.DLINK258
-rw-r--r--net/inet/README.DRIVERS425
-rw-r--r--net/inet/README.TODO32
-rw-r--r--net/inet/README1.PLIP113
-rw-r--r--net/inet/README2.PLIP78
-rw-r--r--net/inet/Space.c220
-rw-r--r--net/inet/arp.c861
-rw-r--r--net/inet/arp.h63
-rw-r--r--net/inet/auto_irq.c110
-rw-r--r--net/inet/d_link.c786
-rw-r--r--net/inet/dev.c902
-rw-r--r--net/inet/dev.h170
-rw-r--r--net/inet/el.c382
-rw-r--r--net/inet/elreg.h59
-rw-r--r--net/inet/eth.c142
-rw-r--r--net/inet/eth.h35
-rw-r--r--net/inet/hp.c321
-rw-r--r--net/inet/icmp.c440
-rw-r--r--net/inet/icmp.h36
-rw-r--r--net/inet/inet.h100
-rw-r--r--net/inet/ip.c787
-rw-r--r--net/inet/ip.h42
-rw-r--r--net/inet/loopback.c109
-rw-r--r--net/inet/ne.c393
-rw-r--r--net/inet/packet.c298
-rw-r--r--net/inet/plip.c392
-rw-r--r--net/inet/proc.c124
-rw-r--r--net/inet/protocol.c150
-rw-r--r--net/inet/protocol.h49
-rw-r--r--net/inet/raw.c407
-rw-r--r--net/inet/raw.h36
-rw-r--r--net/inet/route.c390
-rw-r--r--net/inet/route.h45
-rw-r--r--net/inet/skbuff.h67
-rw-r--r--net/inet/slip.c661
-rw-r--r--net/inet/slip.h58
-rw-r--r--net/inet/sock.c1734
-rw-r--r--net/inet/sock.h188
-rw-r--r--net/inet/tcp.c3124
-rw-r--r--net/inet/tcp.h140
-rw-r--r--net/inet/timer.c323
-rw-r--r--net/inet/timer.h43
-rw-r--r--net/inet/udp.c630
-rw-r--r--net/inet/udp.h47
-rw-r--r--net/inet/utils.c142
-rw-r--r--net/inet/wd.c416
-rw-r--r--net/kern_sock.h87
-rw-r--r--net/socket.c1466
-rw-r--r--net/socketcall.h20
-rw-r--r--net/tcp/Space.c106
-rw-r--r--net/tcp/arp.c624
-rw-r--r--net/tcp/arp.h98
-rw-r--r--net/tcp/dev.c488
-rw-r--r--net/tcp/dev.h127
-rw-r--r--net/tcp/eth.c138
-rw-r--r--net/tcp/eth.h103
-rw-r--r--net/tcp/icmp.c339
-rw-r--r--net/tcp/icmp.h76
-rw-r--r--net/tcp/ip.c1048
-rw-r--r--net/tcp/ip.h196
-rw-r--r--net/tcp/loopback.c134
-rw-r--r--net/tcp/pack_type.c71
-rw-r--r--net/tcp/packet.c350
-rw-r--r--net/tcp/protocols.c86
-rw-r--r--net/tcp/raw.c489
-rw-r--r--net/tcp/sock.c1885
-rw-r--r--net/tcp/sock.h220
-rw-r--r--net/tcp/tcp.c3392
-rw-r--r--net/tcp/tcp.h175
-rw-r--r--net/tcp/timer.c350
-rw-r--r--net/tcp/timer.h59
-rw-r--r--net/tcp/udp.c783
-rw-r--r--net/tcp/udp.h48
-rw-r--r--net/tcp/wereg.h490
-rw-r--r--net/unix.c773
-rw-r--r--net/unix/Makefile (renamed from net/tcp/Makefile)9
-rw-r--r--net/unix/proc.c75
-rw-r--r--net/unix/sock.c860
-rw-r--r--net/unix/unix.h66
-rw-r--r--tools/build.c6
-rw-r--r--tools/version.c6
-rw-r--r--zBoot/head.S23
-rw-r--r--zBoot/misc.c28
277 files changed, 31091 insertions, 17765 deletions
diff --git a/Configure b/Configure
index 62e4675..b362474 100644
--- a/Configure
+++ b/Configure
@@ -1,27 +1,55 @@
#! /bin/sh
+# Configure This script is used to configure the Linux kernel.
+#
+# Usage: Configure [-pro]
+#
+# Version; @(#)Configure 1.3 04/05/93
+#
+# Author: Linus Torvalds, <torvalds@helsinki.fi>
#
-# This script is used to configure the linux kernel.
-# It's a fast hack - feel free to do something better.
-CONFIG=.config~
-CONFIG_H=include/linux/autoconf.h
-> config.new
-echo "#" > $CONFIG
-echo "# Automatically generated make config: don't edit" >> $CONFIG
-echo "#" >> $CONFIG
-echo "/*" > $CONFIG_H
-echo " * Automatically generated C config: don't edit" >> $CONFIG_H
-echo " */" >> $CONFIG_H
+ # Set variables to initial state.
+ OPTS=""
+ CONFIG=.config~
+ CONFIG_H=include/linux/autoconf.h
+ next="y"
+ old="y"
-next="y"
-old="y"
+ # Check commandline arguments.
+ >config.new
+ while [ $# != 0 ]
+ do
+ case $1 in
+ -pro) OPTS="UTS_SYSNAME \"LINUX/Pro\""
+ ;;
+ *) echo "Usage: Configure [-pro]"
+ exit 1
+ ;;
+ esac
+ shift
+ done
+
+ echo "#" > $CONFIG
+ echo "# Automatically generated make config: don't edit" >> $CONFIG
+ echo "#" >> $CONFIG
-while read i
-do
+ echo "/*" > $CONFIG_H
+ echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+ echo " */" >> $CONFIG_H
+
+ # First of all, emit the "special" features to <linux/autoconf.h>.
+ if [ "${OPTS}" ]
+ then
+ echo "#define ${OPTS}" >> $CONFIG_H
+ fi
+
+ # Read our standard input (which is the CONFIG.IN file).
+ while read i
+ do
echo $i >> config.new
- echo >> $CONFIG
- echo >> $CONFIG_H
- echo
+ echo >> $CONFIG
+ echo >> $CONFIG_H
+ echo
echo "#" >> $CONFIG
echo "/*" >> $CONFIG_H
echo "**"
@@ -69,15 +97,14 @@ do
then
next="n"
fi
-done
-
-mv config.new config.in
+ done
+ mv config.new config.in
-echo
-echo "The linux kernel is now hopefully configured for your setup."
-echo "Check the top-level Makefile for additional configuration,"
-echo "and do a 'make dep ; make clean' if you want to be sure all"
-echo "the files are correctly re-made"
-echo
+ echo
+ echo "The linux kernel is now hopefully configured for your setup."
+ echo "Check the top-level Makefile for additional configuration,"
+ echo "and do a 'make dep ; make clean' if you want to be sure all"
+ echo "the files are correctly re-made"
+ echo
-exit 0
+ exit 0
diff --git a/Makefile b/Makefile
index c672ec8..e06a89c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-all: Version Image
+all: Version zImage
.EXPORT_ALL_VARIABLES:
@@ -18,6 +18,13 @@ else
CONFIGURATION = config
endif
+#
+# This probably won't help at all...
+#
+ifndef CONFIG_CLUEFUL
+CONFIGURATION = config
+endif
+
ifdef CONFIGURATION
CONFIGURE = dummy
endif
@@ -31,37 +38,6 @@ endif
ROOT_DEV = CURRENT
#
-# uncomment the correct keyboard:
-#
-# The value of KBDFLAGS should be or'ed together from the following
-# bits, depending on which features you want enabled.
-#
-# The least significant bits control if the following keys are "dead".
-# The key is dead by default if the bit is on.
-# 0x01 - backquote (`)
-# 0x02 - accent acute
-# 0x04 - circumflex (^)
-# 0x08 - tilde (~)
-# 0x10 - dieresis (umlaut)
-
-KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
-# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_US -DKBDFLAGS=0
-# KEYBOARD = -DKBD_GR -DKBDFLAGS=0
-# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_FR -DKBDFLAGS=0
-# KEYBOARD = -DKBD_FR_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_UK -DKBDFLAGS=0
-# KEYBOARD = -DKBD_DK -DKBDFLAGS=0
-# KEYBOARD = -DKBD_DK_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0
-# KEYBOARD = -DKBD_SG -DKBDFLAGS=0
-# KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_SF -DKBDFLAGS=0
-# KEYBOARD = -DKBD_SF_LATIN1 -DKBDFLAGS=0x1F
-# KEYBOARD = -DKBD_NO -DKBDFLAGS=0
-
-#
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
@@ -70,6 +46,9 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
SVGA_MODE= -DSVGA_MODE=3
+# Special options.
+#OPTS = -pro
+
#
# standard CFLAGS
#
@@ -93,19 +72,20 @@ LD86 =ld86 -0
AS =as
LD =ld
HOSTCC =gcc
-CC =gcc -DKERNEL
+CC =gcc -D__KERNEL__
MAKE =make
CPP =$(CC) -E
AR =ar
STRIP =strip
-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 ipc/ipc.o
FILESYSTEMS =fs/filesystems.a
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \
- kernel/blk_drv/scsi/scsi.a kernel/chr_drv/sound/sound.a
+ kernel/blk_drv/scsi/scsi.a kernel/chr_drv/sound/sound.a \
+ ibcs/ibcs.o
MATH =kernel/FPU-emu/math.a
LIBS =lib/lib.a
-SUBDIRS =kernel mm fs net lib
+SUBDIRS =kernel mm fs net ipc ibcs lib
KERNELHDRS =/usr/src/linux/include
@@ -125,7 +105,7 @@ lilo: $(CONFIGURE) Image
/etc/lilo/install
config:
- sh Configure < config.in
+ sh Configure $(OPTS) < config.in
mv .config~ .config
$(MAKE) soundconf
@@ -139,11 +119,12 @@ tools/./version.h: tools/version.h
tools/version.h: $(CONFIGURE) Makefile
@./makever.sh
- @echo \#define UTS_RELEASE \"0.99.pl9-`cat .version`\" > tools/version.h
- @echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h
+ @echo \#define UTS_RELEASE \"0.99.10\" > tools/version.h
+ @echo \#define UTS_VERSION \"\#`cat .version` `date`\" >> 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_HOST \"`hostname`\" >> tools/version.h
+ @echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h
Image: $(CONFIGURE) boot/bootsect boot/setup tools/system tools/build
tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
@@ -200,6 +181,7 @@ zdisk: zImage
dd bs=8192 if=zImage of=/dev/fd0
zlilo: $(CONFIGURE) zImage
+ if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi
cat zImage > /vmlinuz
/etc/lilo/install
@@ -249,7 +231,7 @@ depend dep:
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
- @echo "You have no" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
+ @echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
@echo
$(MAKE) $(CONFIGURATION)
@echo
diff --git a/README b/README
index 8543657..b7bef84 100644
--- a/README
+++ b/README
@@ -1,49 +1,53 @@
- [ NOTE! As of linux-0.97.pl5, the linux kernel include-files have
- finally been totally integrated with the normal headers. That means
- no more "-nostdinc -I$(KERNELHDRS)" in the Makefiles etc, but it
- also means that you /have/ to have the correct /usr/include/linux
- and ../asm symlinks. See "Basic configuration 2" ]
-
- VERY QUICK AND DIRTY README
- by Lars Wirzenius
+ DON'T PANIC
This is the README for the Linux kernel sources. It tells a few small
things about kernel configuration and other things that can perhaps be
useful if you want to compile the kernel from scratch. It leaves out a
-lot as well, probably because the person who wrote it doesn't understand
-very much about operating systems. Linus did his best to help, but all
-problems this causes are my fault.
+LOT as well, but it shouldn't really be that hard to compile the kernel.
+I hope.
-In order to compile this version of the kernel you need GCC 2.2.2 or
-newer. Some makefile targets require special commands which may not be
-available on all machines (see below). Normal utilities like ls etc are
-not explicitly listed, they are assumed to be available on all systems.
+In order to compile this version of the kernel you need GCC 2.3.3 or
+newer (older compiler versions may work, but I haven't tested it). Some
+makefile targets require special commands which may not be available on
+all machines (see below). Normal utilities like ls etc are not
+explicitly listed, they are assumed to be available on all systems.
Kernel sources are usually kept in /usr/src/linux. If you have them
elsewhere, you will have to change path names in a few places.
-Filenames that aren't absolute are supposed to be relative to the
-toplevel kernel source directory.
-
+Generally, if you aren't sure of what you are doing, make your life
+easier by using the standard /usr/src/linux source tree. Filenames that
+aren't absolute are supposed to be relative to the toplevel kernel
+source directory.
* Basic configuration
-1. Edit Makefile: Check the definitions of macros ROOTDEV, KEYBOARD,
-RAMDISK and SVGA_MODE before you run make. They are explained in the
-Makefile.
+ * SETUP
+
+1. make sure /usr/include/asm and /usr/include/linux are symlinks to
+the linux source tree include files. The output of
+
+ # ls -ld /usr/include/asm /usr/include/linux
-2. Create the symlinks:
+should look like this:
- ln -fs /usr/src/linux/include/linux /usr/include/linux
- ln -fs /usr/src/linux/include/asm /usr/include/asm
+ lrwxrwxrwx 1 root root 26 Apr 19 20:03 /usr/include/asm -> /usr/src/linux/include/asm
+ lrwxrwxrwx 1 root root 28 Apr 19 20:03 /usr/include/linux -> /usr/src/linux/include/linux
-This is required so that the linux sources will correctly find their
-header files - it is also used by the normal user-level header files to
-get some system-specific information.
+If it doesn't, create the appropriate symlinks with
-[ Linus' note2: This is automatically done by the gcc-2.2.2d and newer
- installation script, so if you have the new compiler, you should
- already have these links ]
+ # cd /usr/include
+ # rm -rf linux asm
+ # ln -s /usr/src/linux/include/linux .
+ # ln -s /usr/src/linux/include/asm .
+
+Also, if you are installing a new version of linux over the sources of
+an old one (or have user kernel patches to get a new version), you
+should probably do a "make mrproper" to remove any traces of old object
+files or incorrect dependency information.
+
+2. Edit Makefile: Check the definitions of macros ROOTDEV, RAMDISK and
+SVGA_MODE before you run make. They are explained in the Makefile.
3. Run "make config" in /usr/src/linux, and answer the questions that
the config script asks you. It should hopefully set up most of the rest
@@ -51,32 +55,37 @@ of the flags for your system.
4. Run "make dep" to set up all the dependencies correctly. The
default dependencies may not fit your system due to different compiler
-versions or similar.
+versions or similar. Also, you may wish to run "make clean" first to
+make sure you don't have any old object files that mess things up if you
+have changed or patched your kernel.
* Running make
-[ Linus' note3: if you have problems with make not working correctly,
- get a new copy of GNU make. The linux kernel makefiles are written
- for GNU make and will not work for anything else ]
-
Unless you know what you're doing, don't ever run the makefiles in
subdirectories by hand. There is a bit of interaction between the
-various makefiles, e.g. in the form of inherited macros and the like.
+various makefiles, e.g. in the form of inherited macros and the like.
The following targets all apply for the makefile at the root of the
-kernel source tree.
+kernel source tree.
-"make" or "make all" compiles everything.
+"make" or "make all" compiles the kernel and makes a compressed kernel
+image called "zImage". It also bumps compilation numbers to help you
+keep track of different kernels.
"make Image" is like "make all", but it doesn't bump the number in
.version, which tells how many times this version has been compiled
-(helps you differentiate between different configurations etc).
+(helps you differentiate between different configurations etc).
"make disk" is like "make Image", but it additionally writes out a copy
of the boot image to a floppy in your first floppy drive (/dev/fd0;
-change the filename if you want a different floppy). You need to have
-a formatted, overwritable floppy in that drive when it is time to do the
-copy. This requires dd.
+change the filename if you want a different floppy). You need to have a
+formatted, overwritable floppy in that drive when it is time to do the
+copy. This requires dd.
+
+"make zdisk" and "make zImage" are the same as their 'z-less'
+counterparts, but create a compressed kernel that autodecompresses on
+bootup. This is the preferred mode of operation, as it both allows for
+a larger kernel and makes the images smaller.
"make dep" updates all dependencies. This requires sed. It modifies
the makefiles directly (the end of them, starting at the ###Dependencies
@@ -84,7 +93,7 @@ the makefiles directly (the end of them, starting at the ###Dependencies
may not compile cleanly.
"make clean" will remove all object files and other files created by the
-compilation. This requires basename.
+compilation. This requires basename.
You may wish to redirect compiler error messages to a file so that you
can review them later and to ease problem fixing. You can do this with
@@ -94,6 +103,6 @@ Bash with:
The tee part is so that you can check what is going on while the
compilation runs. If you have GNU emacs and use M-x compile you don't
-need this, of course.
+need this, of course.
- Lars Wirzenius
+ Lars Wirzenius & Linus Torvalds
diff --git a/boot/head.S b/boot/head.S
index ab2f994..8b72530 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -1,16 +1,13 @@
/*
- * linux/boot/head.s
+ * linux/boot/head.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
- * head.s contains the 32-bit startup code.
- *
- * NOTE!!! Startup happens at absolute address 0x00000000, which is also where
- * the page directory will exist. The startup code will be overwritten by
- * the page directory.
+ * head.S contains the 32-bit startup code.
*/
+
.text
.globl _idt,_gdt,
.globl _swapper_pg_dir,_pg0
@@ -20,13 +17,19 @@
.globl _tmp_floppy_area,_floppy_track_buffer
#include <linux/tasks.h>
+#include <linux/segment.h>
+
+#define CL_MAGIC_ADDR 0x90020
+#define CL_MAGIC 0xA33F
+#define CL_BASE_ADDR 0x90000
+#define CL_OFFSET 0x90022
/*
* swapper_pg_dir is the main page directory, address 0x00001000
*/
startup_32:
cld
- movl $0x10,%eax
+ movl $KERNEL_DS,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
@@ -45,6 +48,40 @@ startup_32:
*/
pushl $0
popfl
+/*
+ * Copy bootup parameters out of the way. First 2kB of
+ * _empty_zero_page is for boot parameters, second 2kB
+ * is for the command line.
+ */
+ movl $0x90000,%esi
+ movl $_empty_zero_page,%edi
+ movl $512,%ecx
+ cld
+ rep
+ movsl
+ xorl %eax,%eax
+ movl $512,%ecx
+ rep
+ stosl
+ cmpw $CL_MAGIC,CL_MAGIC_ADDR
+ jne 1f
+ movl $_empty_zero_page+2048,%edi
+ movzwl CL_OFFSET,%esi
+ addl $CL_BASE_ADDR,%esi
+ movl $2048,%ecx
+ rep
+ movsb
+1:
+/*
+ * Clear BSS
+ */
+ xorl %eax,%eax
+ movl $__edata,%edi
+ movl $__end,%ecx
+ subl %edi,%ecx
+ cld
+ rep
+ stosb
/* check if it is 486 or 386. */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
@@ -90,8 +127,8 @@ startup_32:
call setup_paging
lgdt gdt_descr
lidt idt_descr
- ljmp $0x08,$1f
-1: movl $0x10,%eax # reload all the segment registers
+ ljmp $KERNEL_CS,$1f
+1: movl $KERNEL_DS,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
@@ -132,13 +169,12 @@ check_x87:
* idt - that can be done only after paging has been enabled
* and the kernel moved to 0xC0000000. Interrupts
* are enabled elsewhere, when we can be relatively
- * sure everything is ok. This routine will be over-
- * written by the page tables.
+ * sure everything is ok.
*/
setup_idt:
lea ignore_int,%edx
- movl $0x00080000,%eax
- movw %dx,%ax /* selector = 0x0008 = cs */
+ movl $(KERNEL_CS << 16),%eax
+ movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea _idt,%edi
@@ -238,7 +274,7 @@ ignore_int:
push %ds
push %es
push %fs
- movl $0x10,%eax
+ movl $KERNEL_DS,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
@@ -266,13 +302,10 @@ idt_descr:
_idt:
.fill 256,8,0 # idt is uninitialized
-/*
- * The real GDT is also 256 entries long - no real reason
- */
.align 4
.word 0
gdt_descr:
- .word (4+2*NR_TASKS)*8-1
+ .word (8+2*NR_TASKS)*8-1
.long 0xc0000000+_gdt
/*
@@ -282,7 +315,11 @@ gdt_descr:
.align 4
_gdt:
.quad 0x0000000000000000 /* NULL descriptor */
- .quad 0xc0c39a000000ffff /* 1GB at 0xC0000000 */
- .quad 0xc0c392000000ffff /* 1GB */
- .quad 0x0000000000000000 /* TEMPORARY - don't use */
+ .quad 0x0000000000000000 /* not used */
+ .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
+ .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
+ .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
+ .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */
+ .quad 0x0000000000000000 /* not used */
+ .quad 0x0000000000000000 /* not used */
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
diff --git a/boot/setup.S b/boot/setup.S
index 3dbd9cf..78c6095 100644
--- a/boot/setup.S
+++ b/boot/setup.S
@@ -20,6 +20,7 @@
! NOTE! These had better be the same as in bootsect.s!
#include <linux/config.h>
+#include <linux/segment.h>
#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
@@ -247,7 +248,7 @@ end_move:
lmsw ax ! This is it!
jmp flush_instr
flush_instr:
- jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
+ jmpi 0,KERNEL_CS ! jmp offset 0 of segment 0x10 (cs)
! This routine checks that the keyboard command queue is empty
! (after emptying the output buffers)
@@ -765,6 +766,8 @@ beep: mov al,#0x07
gdt:
.word 0,0,0,0 ! dummy
+ .word 0,0,0,0 ! unused
+
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
diff --git a/config.in b/config.in
index df3891e..08d2c6c 100644
--- a/config.in
+++ b/config.in
@@ -6,12 +6,14 @@ Normal harddisk support
CONFIG_BLK_DEV_HD y/n y
XT harddisk support
CONFIG_BLK_DEV_XD y/n n
-TCP/IP
-CONFIG_TCPIP y/n y
+TCP/IP Networking
+CONFIG_INET y/n y
Kernel profiling support
CONFIG_PROFILE y/n n
Limit memory to low 16MB
CONFIG_MAX_16M y/n n
+System V IPC
+CONFIG_SYSVIPC y/n y
Use -m486 flag for 486-specific optimizations
CONFIG_M486 y/n y
:
@@ -46,8 +48,6 @@ CONFIG_SCSI_7000FASST y/n n
.
Filesystems
.
-Mount root initially readonly
-CONFIG_ROOT_RDONLY y/n n
Standard (minix) fs support
CONFIG_MINIX_FS y/n y
Extended fs support
@@ -69,12 +69,6 @@ Various character device drivers..
.
Keyboard meta-key sends ESC-prefix
CONFIG_KBD_META y/n y
-Autoconfigure serial IRQ lines at bootup
-CONFIG_AUTO_IRQ y/n n
-AST Fourport serial driver support
-CONFIG_AST_FOURPORT y/n n
-Accent Async 4 serial support
-CONFIG_ACCENT_ASYNC y/n n
Logitech busmouse support
CONFIG_BUSMOUSE y/n n
PS/2 mouse (aka 'auxiliary device') support
@@ -85,3 +79,8 @@ ATIXL busmouse support
CONFIG_ATIXL_BUSMOUSE y/n n
Soundcard support (distributed separately)
CONFIG_SOUND y/n n
+.
+Final check to see if you are awake
+.
+Have you edited the main Makefile for root device etc
+CONFIG_CLUEFUL y/n n
diff --git a/fs/block_dev.c b/fs/block_dev.c
index def02f6..4731b57 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -176,3 +176,8 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
filp->f_reada = 1;
return read;
}
+
+int block_fsync(struct inode *inode, struct file *filp)
+{
+ return fsync_dev (inode->i_rdev);
+}
diff --git a/fs/buffer.c b/fs/buffer.c
index fce0733..cbe8c75 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -19,6 +19,7 @@
#include <stdarg.h>
#include <linux/config.h>
+#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -61,6 +62,7 @@ void __wait_on_buffer(struct buffer_head * bh)
{
struct wait_queue wait = { current, NULL };
+ bh->b_count++;
add_wait_queue(&bh->b_wait, &wait);
repeat:
current->state = TASK_UNINTERRUPTIBLE;
@@ -69,32 +71,86 @@ repeat:
goto repeat;
}
remove_wait_queue(&bh->b_wait, &wait);
+ bh->b_count--;
current->state = TASK_RUNNING;
}
-static void sync_buffers(dev_t dev)
+/* Call sync_buffers with wait!=0 to ensure that the call does not
+ return until all buffer writes have completed. Sync() may return
+ before the writes have finished; fsync() may not. */
+
+static int sync_buffers(dev_t dev, int wait)
{
- int i;
+ int i, retry, pass = 0, err = 0;
struct buffer_head * bh;
+ /* One pass for no-wait, three for wait:
+ 0) write out all dirty, unlocked buffers;
+ 1) write out all dirty buffers, waiting if locked;
+ 2) wait for completion by waiting for all buffers to unlock.
+ */
+repeat:
+ retry = 0;
bh = free_list;
for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) {
if (dev && bh->b_dev != dev)
continue;
+#ifdef 0 /* Disable bad-block debugging code */
+ if (bh->b_req && !bh->b_lock &&
+ !bh->b_dirt && !bh->b_uptodate)
+ printk ("Warning (IO error) - orphaned block %08x on %04x\n",
+ bh->b_blocknr, bh->b_dev);
+#endif
if (bh->b_lock)
+ {
+ /* Buffer is locked; skip it unless wait is
+ requested AND pass > 0. */
+ if (!wait || !pass) {
+ retry = 1;
+ continue;
+ }
+ wait_on_buffer (bh);
+ }
+ /* If an unlocked buffer is not uptodate, there has been
+ an IO error. Skip it. */
+ if (wait && bh->b_req && !bh->b_lock &&
+ !bh->b_dirt && !bh->b_uptodate)
+ {
+ err = 1;
continue;
- if (!bh->b_dirt)
+ }
+ /* Don't write clean buffers. Don't write ANY buffers
+ on the third pass. */
+ if (!bh->b_dirt || pass>=2)
continue;
+ bh->b_count++;
ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ retry = 1;
}
+ /* If we are waiting for the sync to succeed, and if any dirty
+ blocks were written, then repeat; on the second pass, only
+ wait for buffers being written (do not pass to write any
+ more buffers on the second pass). */
+ if (wait && retry && ++pass<=2)
+ goto repeat;
+ return err;
}
void sync_dev(dev_t dev)
{
- sync_buffers(dev);
+ sync_buffers(dev, 0);
+ sync_supers(dev);
+ sync_inodes(dev);
+ sync_buffers(dev, 0);
+}
+
+int fsync_dev(dev_t dev)
+{
+ sync_buffers(dev, 0);
sync_supers(dev);
sync_inodes(dev);
- sync_buffers(dev);
+ return sync_buffers(dev, 1);
}
int sys_sync(void)
@@ -103,9 +159,23 @@ int sys_sync(void)
return 0;
}
-int sys_fsync(int fd)
+int file_fsync (struct inode *inode, struct file *filp)
{
- return -ENOSYS;
+ return fsync_dev(inode->i_dev);
+}
+
+int sys_fsync(unsigned int fd)
+{
+ struct file * file;
+ struct inode * inode;
+
+ if (fd>=NR_OPEN || !(file=current->filp[fd]) || !(inode=file->f_inode))
+ return -EBADF;
+ if (!file->f_op || !file->f_op->fsync)
+ return -EINVAL;
+ if (file->f_op->fsync(inode,file))
+ return -EIO;
+ return 0;
}
void invalidate_buffers(dev_t dev)
@@ -119,7 +189,7 @@ void invalidate_buffers(dev_t dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev)
- bh->b_uptodate = bh->b_dirt = 0;
+ bh->b_uptodate = bh->b_dirt = bh->b_req = 0;
}
}
@@ -325,9 +395,7 @@ repeat:
return bh;
}
grow_size -= size;
- if (nr_free_pages > min_free_pages &&
- buffermem < 6*1024*1024 &&
- grow_size <= 0) {
+ if (nr_free_pages > min_free_pages && grow_size <= 0) {
grow_buffers(size);
grow_size = 4096;
}
@@ -345,8 +413,11 @@ repeat:
break;
}
#if 0
- if (tmp->b_dirt)
+ if (tmp->b_dirt) {
+ tmp->b_count++;
ll_rw_block(WRITEA, 1, &tmp);
+ tmp->b_count--;
+ }
#endif
}
@@ -364,7 +435,7 @@ repeat:
if (bh->b_count || bh->b_size != size)
goto repeat;
if (bh->b_dirt) {
- sync_buffers(0);
+ sync_buffers(0,0);
goto repeat;
}
/* NOTE!! While we slept waiting for this block, somebody else might */
@@ -376,6 +447,7 @@ repeat:
bh->b_count=1;
bh->b_dirt=0;
bh->b_uptodate=0;
+ bh->b_req=0;
remove_from_queues(bh);
bh->b_dev=dev;
bh->b_blocknr=block;
@@ -498,14 +570,187 @@ static struct buffer_head * get_unused_buffer_head(void)
bh->b_next_free = NULL;
bh->b_data = NULL;
bh->b_size = 0;
+ bh->b_req = 0;
return bh;
}
-static inline unsigned long try_to_share_buffers(unsigned long address, dev_t dev, int b[], int size)
+/*
+ * Create the appropriate buffers when given a page for data area and
+ * the size of each buffer.. Use the bh->b_this_page linked list to
+ * follow the buffers created. Return NULL if unable to create more
+ * buffers.
+ */
+static struct buffer_head * create_buffers(unsigned long page, unsigned long size)
+{
+ struct buffer_head *bh, *head;
+ unsigned long offset;
+
+ head = NULL;
+ offset = 4096;
+ while ((offset -= size) < 4096) {
+ bh = get_unused_buffer_head();
+ if (!bh)
+ goto no_grow;
+ bh->b_this_page = head;
+ head = bh;
+ bh->b_data = (char *) (page+offset);
+ bh->b_size = size;
+ }
+ return head;
+/*
+ * In case anything failed, we just free everything we got.
+ */
+no_grow:
+ bh = head;
+ while (bh) {
+ head = bh;
+ bh = bh->b_this_page;
+ put_unused_buffer_head(head);
+ }
+ return NULL;
+}
+
+static void read_buffers(struct buffer_head * bh[], int nrbuf)
{
+ int i;
+ int bhnum = 0;
+ struct buffer_head * bhr[8];
+
+ for (i = 0 ; i < nrbuf ; i++) {
+ if (bh[i] && !bh[i]->b_uptodate)
+ bhr[bhnum++] = bh[i];
+ }
+ if (bhnum)
+ ll_rw_block(READ, bhnum, bhr);
+ for (i = 0 ; i < nrbuf ; i++) {
+ if (bh[i]) {
+ wait_on_buffer(bh[i]);
+ }
+ }
+}
+
+static unsigned long check_aligned(struct buffer_head * first, unsigned long address,
+ dev_t dev, int *b, int size)
+{
+ struct buffer_head * bh[8];
+ unsigned long page;
+ unsigned long offset;
+ int block;
+ int nrbuf;
+
+ page = (unsigned long) first->b_data;
+ if (page & 0xfff) {
+ brelse(first);
+ return 0;
+ }
+ mem_map[MAP_NR(page)]++;
+ bh[0] = first;
+ nrbuf = 1;
+ for (offset = size ; offset < 4096 ; offset += size) {
+ block = *++b;
+ if (!block)
+ goto no_go;
+ first = get_hash_table(dev, block, size);
+ if (!first)
+ goto no_go;
+ bh[nrbuf++] = first;
+ if (page+offset != (unsigned long) first->b_data)
+ goto no_go;
+ }
+ read_buffers(bh,nrbuf); /* make sure they are actually read correctly */
+ while (nrbuf-- > 0)
+ brelse(bh[nrbuf]);
+ free_page(address);
+ ++current->min_flt;
+ return page;
+no_go:
+ while (nrbuf-- > 0)
+ brelse(bh[nrbuf]);
+ free_page(page);
+ return 0;
+}
+
+static unsigned long try_to_load_aligned(unsigned long address,
+ dev_t dev, int b[], int size)
+{
+ struct buffer_head * bh, * tmp, * arr[8];
+ unsigned long offset;
+ int * p;
+ int block;
+
+ bh = create_buffers(address, size);
+ if (!bh)
+ return 0;
+ p = b;
+ for (offset = 0 ; offset < 4096 ; offset += size) {
+ block = *(p++);
+ if (!block)
+ goto not_aligned;
+ tmp = get_hash_table(dev, block, size);
+ if (tmp) {
+ brelse(tmp);
+ goto not_aligned;
+ }
+ }
+ tmp = bh;
+ p = b;
+ block = 0;
+ while (1) {
+ arr[block++] = bh;
+ bh->b_count = 1;
+ bh->b_dirt = 0;
+ bh->b_uptodate = 0;
+ bh->b_dev = dev;
+ bh->b_blocknr = *(p++);
+ nr_buffers++;
+ insert_into_queues(bh);
+ if (bh->b_this_page)
+ bh = bh->b_this_page;
+ else
+ break;
+ }
+ buffermem += 4096;
+ bh->b_this_page = tmp;
+ mem_map[MAP_NR(address)]++;
+ read_buffers(arr,block);
+ while (block-- > 0)
+ brelse(arr[block]);
+ ++current->maj_flt;
+ return address;
+not_aligned:
+ while ((tmp = bh) != NULL) {
+ bh = bh->b_this_page;
+ put_unused_buffer_head(tmp);
+ }
return 0;
}
+/*
+ * Try-to-share-buffers tries to minimize memory use by trying to keep
+ * both code pages and the buffer area in the same page. This is done by
+ * (a) checking if the buffers are already aligned correctly in memory and
+ * (b) if none of the buffer heads are in memory at all, trying to load
+ * them into memory the way we want them.
+ *
+ * This doesn't guarantee that the memory is shared, but should under most
+ * circumstances work very well indeed (ie >90% sharing of code pages on
+ * demand-loadable executables).
+ */
+static inline unsigned long try_to_share_buffers(unsigned long address,
+ dev_t dev, int *b, int size)
+{
+ struct buffer_head * bh;
+ int block;
+
+ block = b[0];
+ if (!block)
+ return 0;
+ bh = get_hash_table(dev, block, size);
+ if (bh)
+ return check_aligned(bh, address, dev, b, size);
+ return try_to_load_aligned(address, dev, b, size);
+}
+
#define COPYBLK(from,to) \
__asm__ __volatile__("rep ; movsl" \
::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
@@ -516,14 +761,12 @@ __asm__ __volatile__("rep ; movsl" \
* a function of its own, as there is some speed to be got by reading them
* all at the same time, not waiting for one to be read, and then another
* etc. This also allows us to optimize memory usage by sharing code pages
- * and filesystem buffers.. This is not yet implemented.
+ * and filesystem buffers..
*/
unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, int prot)
{
- struct buffer_head * bh[4];
- struct buffer_head * bhr[4];
+ struct buffer_head * bh[8];
unsigned long where;
- int bhnum = 0;
int i;
if (!(prot & PAGE_RW)) {
@@ -531,20 +774,16 @@ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, in
if (where)
return where;
}
+ ++current->maj_flt;
for (i=0 ; i<4 ; i++) {
bh[i] = NULL;
- if (b[i]) {
+ if (b[i])
bh[i] = getblk(dev, b[i], size);
- if (bh[i] && !bh[i]->b_uptodate)
- bhr[bhnum++] = bh[i];
- }
}
- if (bhnum)
- ll_rw_block(READ, bhnum, bhr);
+ read_buffers(bh,4);
where = address;
for (i=0 ; i<4 ; i++,address += BLOCK_SIZE) {
if (bh[i]) {
- wait_on_buffer(bh[i]);
if (bh[i]->b_uptodate)
COPYBLK((unsigned long) bh[i]->b_data,address);
brelse(bh[i]);
@@ -562,7 +801,6 @@ unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, in
void grow_buffers(int size)
{
unsigned long page;
- int i;
struct buffer_head *bh, *tmp;
if ((size & 511) || (size > 4096)) {
@@ -572,16 +810,10 @@ void grow_buffers(int size)
page = get_free_page(GFP_BUFFER);
if (!page)
return;
- tmp = NULL;
- i = 0;
- for (i = 0 ; i+size <= 4096 ; i += size) {
- bh = get_unused_buffer_head();
- if (!bh)
- goto no_grow;
- bh->b_this_page = tmp;
- tmp = bh;
- bh->b_data = (char * ) (page+i);
- bh->b_size = size;
+ bh = create_buffers(page, size);
+ if (!bh) {
+ free_page(page);
+ return;
}
tmp = bh;
while (1) {
@@ -604,32 +836,20 @@ void grow_buffers(int size)
tmp->b_this_page = bh;
buffermem += 4096;
return;
-/*
- * In case anything failed, we just free everything we got.
- */
-no_grow:
- bh = tmp;
- while (bh) {
- tmp = bh;
- bh = bh->b_this_page;
- put_unused_buffer_head(tmp);
- }
- free_page(page);
}
/*
* try_to_free() checks if all the buffers on this particular page
* are unused, and free's the page if so.
*/
-static int try_to_free(struct buffer_head * bh)
+static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
{
unsigned long page;
struct buffer_head * tmp, * p;
+ *bhp = bh;
page = (unsigned long) bh->b_data;
page &= 0xfffff000;
- if (mem_map[MAP_NR(page)] != 1)
- return 0;
tmp = bh;
do {
if (!tmp)
@@ -643,12 +863,14 @@ static int try_to_free(struct buffer_head * bh)
p = tmp;
tmp = tmp->b_this_page;
nr_buffers--;
+ if (p == *bhp)
+ *bhp = p->b_prev_free;
remove_from_queues(p);
put_unused_buffer_head(p);
} while (tmp != bh);
buffermem -= 4096;
free_page(page);
- return 1;
+ return !mem_map[MAP_NR(page)];
}
/*
@@ -664,7 +886,7 @@ int shrink_buffers(unsigned int priority)
int i;
if (priority < 2)
- sync_buffers(0);
+ sync_buffers(0,0);
bh = free_list;
i = nr_buffers >> priority;
for ( ; i-- > 0 ; bh = bh->b_next_free) {
@@ -676,10 +898,12 @@ int shrink_buffers(unsigned int priority)
else
wait_on_buffer(bh);
if (bh->b_dirt) {
+ bh->b_count++;
ll_rw_block(WRITEA, 1, &bh);
+ bh->b_count--;
continue;
}
- if (try_to_free(bh))
+ if (try_to_free(bh, &bh))
return 1;
}
return 0;
diff --git a/fs/exec.c b/fs/exec.c
index 0aa24a3..721a752 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -29,11 +29,13 @@
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/user.h>
+#include <linux/segment.h>
#include <asm/segment.h>
extern int sys_exit(int exit_code);
extern int sys_close(int fd);
+extern void shm_exit (void);
/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
@@ -80,8 +82,8 @@ int core_dump(long signr, struct pt_regs * regs)
if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
return 0;
__asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
- if (open_namei("core",O_CREAT | O_WRONLY | O_TRUNC,0600,&inode,NULL)) {
+ __asm__("mov %w0,%%fs"::"r" (KERNEL_DS));
+ if (open_namei("core",O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
inode = NULL;
goto end_coredump;
}
@@ -139,7 +141,7 @@ int core_dump(long signr, struct pt_regs * regs)
convert it into standard 387 format first.. */
dump.u_fpvalid = 0;
}
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+ __asm__("mov %w0,%%fs"::"r" (KERNEL_DS));
/* struct user */
DUMP_WRITE(&dump,sizeof(dump));
/* name of the executable */
@@ -147,7 +149,7 @@ int core_dump(long signr, struct pt_regs * regs)
/* Now dump all of the user data. Include malloced stuff as well */
DUMP_SEEK(PAGE_SIZE);
/* now we start writing out the user space info */
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17));
+ __asm__("mov %w0,%%fs"::"r" (USER_DS));
/* Dump the data area */
if (dump.u_dsize != 0) {
dump_start = dump.u_tsize << 12;
@@ -161,13 +163,13 @@ int core_dump(long signr, struct pt_regs * regs)
DUMP_WRITE(dump_start,dump_size);
};
/* Finally dump the task struct. Not be used by gdb, but could be useful */
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+ __asm__("mov %w0,%%fs"::"r" (KERNEL_DS));
DUMP_WRITE(current,sizeof(*current));
close_coredump:
if (file.f_op->release)
file.f_op->release(inode,&file);
end_coredump:
- __asm__("mov %0,%%fs"::"r" (fs));
+ __asm__("mov %w0,%%fs"::"r" (fs));
iput(inode);
return has_dumped;
}
@@ -187,7 +189,7 @@ int sys_uselib(const char * library)
unsigned long offset;
int error;
- if (!library || get_limit(0x17) != TASK_SIZE)
+ if (!library || get_limit(USER_DS) != TASK_SIZE)
return -EINVAL;
if ((libnum >= MAX_SHARED_LIBS) || (libnum < 0))
return -EINVAL;
@@ -361,12 +363,6 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
data_limit = TASK_SIZE;
code_base = data_base = 0;
current->start_code = code_base;
- set_base(current->ldt[1],code_base);
- set_limit(current->ldt[1],code_limit);
- set_base(current->ldt[2],data_base);
- set_limit(current->ldt[2],data_limit);
-/* make sure fs points to the NEW data segment */
- __asm__("pushl $0x17\n\tpop %%fs"::);
data_base += data_limit;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
@@ -435,7 +431,7 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
int ch;
- if ((0xffff & eip[1]) != 0x000f)
+ if ((0xffff & eip[1]) != USER_CS)
panic("VFS: execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
@@ -591,6 +587,8 @@ restart_interp:
if (i < 15)
current->comm[i++] = ch;
current->comm[i] = '\0';
+ if (current->shm)
+ shm_exit();
if (current->executable) {
iput(current->executable);
current->executable = NULL;
@@ -604,7 +602,6 @@ restart_interp:
!permission(inode,MAY_READ))
current->dumpable = 0;
current->numlibraries = 0;
- current->signal = 0;
for (i=0 ; i<32 ; i++) {
current->sigaction[i].sa_mask = 0;
current->sigaction[i].sa_flags = 0;
diff --git a/fs/ext/Makefile b/fs/ext/Makefile
index 4d241db..2a9bf99 100644
--- a/fs/ext/Makefile
+++ b/fs/ext/Makefile
@@ -15,7 +15,7 @@
$(AS) -o $*.o $<
OBJS= freelists.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o
+ file.o dir.o symlink.o fsync.o
ext.o: $(OBJS)
$(LD) -r -o ext.o $(OBJS)
diff --git a/fs/ext/dir.c b/fs/ext/dir.c
index da09fb4..a7667a3 100644
--- a/fs/ext/dir.c
+++ b/fs/ext/dir.c
@@ -37,7 +37,7 @@ static struct file_operations ext_dir_operations = {
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* fsync */
+ file_fsync /* fsync */
};
/*
diff --git a/fs/ext/file.c b/fs/ext/file.c
index 804e6fb..6c5019c 100644
--- a/fs/ext/file.c
+++ b/fs/ext/file.c
@@ -48,7 +48,7 @@ static struct file_operations ext_file_operations = {
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
- NULL /* fsync */
+ ext_sync_file /* fsync */
};
struct inode_operations ext_file_inode_operations = {
diff --git a/fs/ext/freelists.c b/fs/ext/freelists.c
index 53d6ab4..e50d9ff 100644
--- a/fs/ext/freelists.c
+++ b/fs/ext/freelists.c
@@ -289,6 +289,7 @@ printk("ext_free_inode: inode empty, skipping to %d\n", efi->next);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
inode->i_blocks = inode->i_blksize = 0;
+ insert_inode_hash(inode);
#ifdef EXTFS_DEBUG
printk("ext_new_inode : allocating inode %d\n", inode->i_ino);
#endif
diff --git a/fs/ext/fsync.c b/fs/ext/fsync.c
new file mode 100644
index 0000000..1978519
--- /dev/null
+++ b/fs/ext/fsync.c
@@ -0,0 +1,186 @@
+
+/*
+ * linux/fs/ext/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ * from
+ * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * extfs fsync primitive
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/ext_fs.h>
+
+
+#define blocksize BLOCK_SIZE
+#define addr_per_block 256
+
+static int sync_block (struct inode * inode, unsigned long * block, int wait)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = get_hash_table(inode->i_dev, *block, blocksize);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt)
+ {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+static int sync_iblock (struct inode * inode, unsigned long * iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc, tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 9; i++) {
+ rc = sync_block (inode, inode->u.ext_i.i_data + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned long *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, diblock, &dind_bh, wait);
+ if (rc || !inode)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned long *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+static int sync_tindirect(struct inode *inode, unsigned long *tiblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * tind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+ if (rc || !tind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_dindirect (inode,
+ ((unsigned long *) tind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
+int ext_sync_file(struct inode * inode, struct file *file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+ for (wait=0; wait<=1; wait++)
+ {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, inode->u.ext_i.i_data+9, wait);
+ err |= sync_dindirect(inode, inode->u.ext_i.i_data+10, wait);
+ err |= sync_tindirect(inode, inode->u.ext_i.i_data+11, wait);
+ }
+ err |= ext_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/ext/inode.c b/fs/ext/inode.c
index ff8efc1..fa01b33 100644
--- a/fs/ext/inode.c
+++ b/fs/ext/inode.c
@@ -50,7 +50,8 @@ static struct super_operations ext_sops = {
ext_put_inode,
ext_put_super,
ext_write_super,
- ext_statfs
+ ext_statfs,
+ NULL
};
struct super_block *ext_read_super(struct super_block *s,void *data,
@@ -383,7 +384,7 @@ void ext_read_inode(struct inode * inode)
init_fifo(inode);
}
-void ext_write_inode(struct inode * inode)
+static struct buffer_head * ext_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct ext_inode * raw_inode;
@@ -406,5 +407,36 @@ void ext_write_inode(struct inode * inode)
raw_inode->i_zone[block] = inode->u.ext_i.i_data[block];
bh->b_dirt=1;
inode->i_dirt=0;
+ return bh;
+}
+
+void ext_write_inode(struct inode * inode)
+{
+ struct buffer_head *bh;
+ bh = ext_update_inode (inode);
brelse(bh);
}
+
+int ext_sync_inode (struct inode *inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = ext_update_inode(inode);
+ if (bh && bh->b_dirt)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing ext inode [%04x:%08x]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
index b91eaf8..dd04ba7 100644
--- a/fs/ext2/Makefile
+++ b/fs/ext2/Makefile
@@ -14,7 +14,7 @@
.s.o:
$(AS) -o $*.o $<
-OBJS= acl.o balloc.o bitmap.o dcache.o dir.o file.o ialloc.o inode.o \
+OBJS= acl.o balloc.o bitmap.o dcache.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o symlink.o truncate.o
ext2.o: $(OBJS)
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 0fdd81b..32c78fb 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -38,7 +38,7 @@
:"a" (0), "c" (size/4), "D" ((long) (addr)) \
:"cx", "di")
-static inline int find_first_zero_bit (unsigned *addr, unsigned size)
+static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
{
int res;
if (!size)
@@ -63,9 +63,10 @@ static inline int find_first_zero_bit (unsigned *addr, unsigned size)
return res;
}
-static inline int find_next_zero_bit (unsigned * addr, int size, int offset)
+static inline int find_next_zero_bit (unsigned long * addr, int size,
+ int offset)
{
- unsigned *p = ((unsigned *) addr) + (offset >> 5);
+ unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
int set = 0, bit = offset & 31, res;
if (bit) {
diff --git a/fs/ext2/dcache.c b/fs/ext2/dcache.c
index e5fec8b..a74be03 100644
--- a/fs/ext2/dcache.c
+++ b/fs/ext2/dcache.c
@@ -191,8 +191,6 @@ void ext2_dcache_invalidate (unsigned short dev)
/*
* Lookup a directory entry in the cache
- *
- * Note: the name is in the caller's address space
*/
unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
const char *name, int len)
@@ -243,8 +241,6 @@ unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
*
* This function is called by ext2_lookup(), ext2_readdir()
* and the functions which create directory entries
- *
- * Note: the name is in the kernel address space
*/
void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
int len, int ino)
@@ -305,8 +301,6 @@ void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
* Remove a directory from the cache
*
* This function is called by the functions which remove directory entries
- *
- * Note: the name is in the kernel address space
*/
void ext2_dcache_remove (unsigned short dev, unsigned long dir,
const char *name, int len)
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 1a94986..52045c4 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -41,7 +41,7 @@ static struct file_operations ext2_dir_operations = {
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* fsync */
+ file_fsync /* fsync */
};
/*
@@ -77,13 +77,13 @@ int ext2_check_dir_entry (char * function, struct inode * dir,
error_msg = "rec_len % 4 != 0";
else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
error_msg = "rec_len is too small for name_len";
- else if (((char *) de - bh->b_data) + de->rec_len >
+ else if (dir && ((char *) de - bh->b_data) + de->rec_len >
dir->i_sb->s_blocksize)
- error_msg = "directory entry accross blocks";
+ error_msg = "directory entry across blocks";
if (error_msg != NULL) {
printk ("%s: bad directory entry (dev %04x, dir %d): %s\n",
- function, dir->i_dev, dir->i_ino, error_msg);
+ function, dir ? dir->i_dev : 0, dir->i_ino, error_msg);
printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
offset, de->inode, de->rec_len, de->name_len);
}
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 68279b2..c3a16db 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -48,7 +48,7 @@ static struct file_operations ext2_file_operations = {
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
- NULL /* fsync */
+ ext2_sync_file /* fsync */
};
struct inode_operations ext2_file_inode_operations = {
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
new file mode 100644
index 0000000..3c85c4e
--- /dev/null
+++ b/fs/ext2/fsync.c
@@ -0,0 +1,196 @@
+/*
+ * linux/fs/ext2/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ * from
+ * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2fs fsync primitive
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+
+#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
+#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
+
+static int sync_block (struct inode * inode, unsigned long * block, int wait)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = get_hash_table(inode->i_dev, *block, blocksize);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt)
+ {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+static int sync_iblock (struct inode * inode, unsigned long * iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc, tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
+ rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned long *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned long *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+static int sync_tindirect(struct inode *inode, unsigned long *tiblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * tind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+ if (rc || !tind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_dindirect (inode,
+ ((unsigned long *) tind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
+int ext2_sync_file(struct inode * inode, struct file *file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+ /* Don't sync fast links! */
+ if (S_ISLNK(inode->i_mode) && !(inode->i_blocks))
+ goto skip;
+
+ for (wait=0; wait<=1; wait++)
+ {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode,
+ inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
+ wait);
+ err |= sync_dindirect(inode,
+ inode->u.ext2_i.i_data+EXT2_DIND_BLOCK,
+ wait);
+ err |= sync_tindirect(inode,
+ inode->u.ext2_i.i_data+EXT2_TIND_BLOCK,
+ wait);
+ }
+skip:
+ err |= ext2_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 39fcb6c..0719e9b 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -32,7 +32,7 @@
#include <asm/bitops.h>
-static inline int find_first_zero_bit(unsigned * addr, unsigned size)
+static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
{
int res;
if (!size)
@@ -358,7 +358,8 @@ repeat:
if (!gdp) {
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
tmp = get_group_desc(sb, j);
- if (tmp->bg_free_inodes_count >= avefreei) {
+ if (tmp->bg_free_inodes_count &&
+ tmp->bg_free_inodes_count >= avefreei) {
if (!gdp ||
(tmp->bg_free_inodes_count >
gdp->bg_free_inodes_count)) {
@@ -405,6 +406,7 @@ repeat:
if (!gdp) {
unlock_super (sb);
+ iput(inode);
return NULL;
}
bitmap_nr = load_inode_bitmap (sb, i);
@@ -456,6 +458,7 @@ repeat:
inode->u.ext2_i.i_dtime = 0;
inode->u.ext2_i.i_block_group = i;
inode->i_op = NULL;
+ insert_inode_hash(inode);
inc_inode_version (inode, gdp, mode);
#ifdef EXT2FS_DEBUG
printk ("ext2_new_inode : allocating inode %d\n", inode->i_ino);
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index a82ae1d..c9c135a 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -26,7 +26,8 @@
void ext2_put_inode (struct inode * inode)
{
- if (inode->i_nlink || inode->i_ino == EXT2_ACL_INO)
+ if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO ||
+ inode->i_ino == EXT2_ACL_DATA_INO)
return;
inode->i_size = 0;
if (inode->i_blocks)
@@ -67,7 +68,8 @@ static struct super_operations ext2_sops = {
ext2_put_inode,
ext2_put_super,
ext2_write_super,
- ext2_statfs
+ ext2_statfs,
+ ext2_remount
};
#ifdef EXT2FS_PRE_02B_COMPAT
@@ -217,6 +219,13 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
bh_count = (s->u.ext2_sb.s_groups_count +
EXT2_DESC_PER_BLOCK(s) - 1) /
EXT2_DESC_PER_BLOCK(s);
+ if (bh_count >= EXT2_MAX_GROUP_DESC) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: Too big file system\n");
+ return NULL;
+ }
for (i = 0; i < bh_count; i++) {
s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2, s->s_blocksize);
if (!s->u.ext2_sb.s_group_desc[i]) {
@@ -263,9 +272,9 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
s->s_dirt = 1;
}
#endif
- printk ("[EXT II FS %s, bs=%d, fs=%d, gc=%d, bpg=%d, ipg=%d]\n",
- EXT2FS_VERSION, s->s_blocksize, s->u.ext2_sb.s_frag_size,
- s->u.ext2_sb.s_groups_count,
+ printk ("[EXT II FS %s, %s, bs=%d, fs=%d, gc=%d, bpg=%d, ipg=%d]\n",
+ EXT2FS_VERSION, EXT2FS_DATE, s->s_blocksize,
+ s->u.ext2_sb.s_frag_size, s->u.ext2_sb.s_groups_count,
EXT2_BLOCKS_PER_GROUP(s), EXT2_INODES_PER_GROUP(s));
return s;
}
@@ -283,56 +292,91 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
* Note that this function also writes backups of the super block and
* of the group descriptors in each group.
*/
-void ext2_write_super (struct super_block * sb)
+
+static void ext2_commit_super (struct super_block *sb,
+ struct ext2_super_block *es)
{
- struct ext2_super_block * es;
struct buffer_head * bh;
unsigned long block;
unsigned long bh_count;
int i, j;
- if ((sb->s_flags & MS_RDONLY) == 0) {
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es->s_wtime = CURRENT_TIME;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ bh_count = (sb->u.ext2_sb.s_groups_count +
+ EXT2_DESC_PER_BLOCK(sb) - 1) /
+ EXT2_DESC_PER_BLOCK(sb);
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ block = sb->u.ext2_sb.s_first_data_block +
+ i * sb->u.ext2_sb.s_blocks_per_group;
+ if (!(bh = bread (sb->s_dev, block, BLOCK_SIZE)))
+ printk ("ext2_commit_super: Unable to read backup super block for group %d\n", i);
+ else {
#ifdef EXT2FS_DEBUG
- printk ("ext2_write_super: setting valid to 0\n");
-#endif
- es->s_valid = 0;
- es->s_wtime = CURRENT_TIME;
- sb->u.ext2_sb.s_sbh->b_dirt = 1;
- bh_count = (sb->u.ext2_sb.s_groups_count +
- EXT2_DESC_PER_BLOCK(sb) - 1) /
- EXT2_DESC_PER_BLOCK(sb);
- for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
- block = sb->u.ext2_sb.s_first_data_block +
- i * sb->u.ext2_sb.s_blocks_per_group;
- if (!(bh = bread (sb->s_dev, block, BLOCK_SIZE)))
- printk ("ext2_write_super: Unable to read backup super block for group %d\n", i);
- else {
+ printk ("ext2_commit_super: writing super block backup in group %d at block %d\n", i, block);
+#endif
+ memcpy (bh->b_data, es, BLOCK_SIZE);
+ bh ->b_dirt = 1;
+ brelse (bh);
+ }
+ for (j = 0; j < bh_count; j++) {
+ block ++;
#ifdef EXT2FS_DEBUG
- printk ("ext2_write_super: writing super block backup in group %d at block %d\n", i, block);
+ printk ("ext2_commit_super: writing descriptors (block %d) backup in group %d at block %d\n", j, i, block);
#endif
- memcpy (bh->b_data, es, BLOCK_SIZE);
+ if (!(bh = bread (sb->s_dev, block, sb->s_blocksize)))
+ printk ("ext2_commit_super: Unable to read backup descriptor for group %d\n", i);
+ else {
+ memcpy (bh->b_data, sb->u.ext2_sb.s_group_desc[j]->b_data, sb->s_blocksize);
bh ->b_dirt = 1;
brelse (bh);
}
- for (j = 0; j < bh_count; j++) {
- block ++;
-#ifdef EXT2FS_DEBUG
- printk ("ext2_write_super: writing descriptors (block %d) backup in group %d at block %d\n", j, i, block);
-#endif
- if (!(bh = bread (sb->s_dev, block, sb->s_blocksize)))
- printk ("ext2_write_super: Unable to read backup descriptor for group %d\n", i);
- else {
- memcpy (bh->b_data, sb->u.ext2_sb.s_group_desc[j]->b_data, sb->s_blocksize);
- bh ->b_dirt = 1;
- brelse (bh);
- }
- }
}
}
sb->s_dirt = 0;
}
+void ext2_write_super (struct super_block * sb)
+{
+ struct ext2_super_block * es;
+
+ if ((sb->s_flags & MS_RDONLY) == 0) {
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_write_super: setting valid to 0\n");
+#endif
+ es->s_valid = 0;
+ ext2_commit_super (sb, es);
+ }
+ sb->s_dirt = 0;
+}
+
+int ext2_remount(struct super_block *sb, int *flags)
+{
+ struct ext2_super_block * es;
+
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*flags & MS_RDONLY) {
+ if (es->s_valid || !sb->u.ext2_sb.s_was_mounted_valid)
+ return 0;
+ /* OK, we are remounting a valid rw partition rdonly, so set
+ the rdonly flag and then mark the partition as valid
+ again. */
+ sb->s_flags |= MS_RDONLY;
+ es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
+ ext2_commit_super(sb, es);
+ }
+ else {
+ /* Mounting a RDONLY partition read-write, so reread and
+ store the current valid flag. (It may have been changed
+ by ext2fs since we originally mounted the partition.) */
+ sb->u.ext2_sb.s_was_mounted_valid = es->s_valid;
+ }
+ return 0;
+}
+
void ext2_statfs (struct super_block * sb, struct statfs * buf)
{
long tmp;
@@ -647,8 +691,8 @@ void ext2_read_inode (struct inode * inode)
unsigned long block;
struct ext2_group_desc * gdp;
- if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_INO &&
- inode->i_ino < EXT2_FIRST_INO) ||
+ if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
+ inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
printk ("ext2_read_inode: bad inode number of dev %0x04: %d\n",
inode->i_dev, inode->i_ino);
@@ -697,7 +741,8 @@ void ext2_read_inode (struct inode * inode)
inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
brelse (bh);
inode->i_op = NULL;
- if (inode->i_ino == EXT2_ACL_INO)
+ if (inode->i_ino == EXT2_ACL_IDX_INO ||
+ inode->i_ino == EXT2_ACL_DATA_INO)
/* Nothing to do */ ;
else if (S_ISREG(inode->i_mode))
inode->i_op = &ext2_file_inode_operations;
@@ -713,7 +758,7 @@ void ext2_read_inode (struct inode * inode)
init_fifo(inode);
}
-void ext2_write_inode (struct inode * inode)
+static struct buffer_head * ext2_update_inode (struct inode * inode)
{
struct buffer_head * bh;
struct ext2_inode * raw_inode;
@@ -727,7 +772,7 @@ void ext2_write_inode (struct inode * inode)
inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n",
inode->i_dev, inode->i_ino);
- return;
+ return 0;
}
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
@@ -768,5 +813,35 @@ void ext2_write_inode (struct inode * inode)
raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
bh->b_dirt = 1;
inode->i_dirt = 0;
+ return bh;
+}
+
+void ext2_write_inode (struct inode * inode)
+{
+ struct buffer_head * bh;
+ bh = ext2_update_inode(inode);
+ brelse (bh);
+}
+
+int ext2_sync_inode (struct inode *inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = ext2_update_inode(inode);
+ if (bh && bh->b_dirt)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing ext2 inode [%04x:%08x]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
brelse (bh);
+ return err;
}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index d000efc..9e6082e 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -63,14 +63,10 @@ static int ext2_match (int len, const char * const name,
* itself (as a parameter - res_dir). It does NOT read the inode of the
* entry - you'll have to do that yourself if you want to.
*
- * In the ext2 file system, this function also returns a pointer on the
- * previous directory entry because functions which remove directory
- * entries need it
*/
static struct buffer_head * ext2_find_entry (struct inode * dir,
const char * const name, int namelen,
- struct ext2_dir_entry ** res_dir,
- struct ext2_dir_entry ** prev_dir)
+ struct ext2_dir_entry ** res_dir)
{
long offset;
struct buffer_head * bh;
@@ -92,8 +88,6 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
bh = ext2_bread (dir, 0, 0, &err);
if (!bh)
return NULL;
- if (prev_dir)
- *prev_dir = NULL;
offset = 0;
de = (struct ext2_dir_entry *) bh->b_data;
while (offset < dir->i_size) {
@@ -105,8 +99,6 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
continue;
}
de = (struct ext2_dir_entry *) bh->b_data;
- if (prev_dir)
- *prev_dir = NULL;
}
if (! ext2_check_dir_entry ("ext2_find_entry", dir, de, bh,
offset)) {
@@ -118,8 +110,6 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
return bh;
}
offset += de->rec_len;
- if (prev_dir)
- *prev_dir = de;
de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
}
brelse (bh);
@@ -143,7 +133,7 @@ int ext2_lookup (struct inode * dir, const char * name, int len,
#ifndef DONT_USE_DCACHE
if (!(ino = ext2_dcache_lookup (dir->i_dev, dir->i_ino, name, len))) {
#endif
- if (!(bh = ext2_find_entry (dir, name, len, &de, NULL))) {
+ if (!(bh = ext2_find_entry (dir, name, len, &de))) {
iput (dir);
return -ENOENT;
}
@@ -200,6 +190,13 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
#endif
if (!namelen)
return NULL;
+ /* Is this a busy deleted directory? Can't create new files
+ if so */
+ if (dir->i_size == 0)
+ {
+ *err = -ENOENT;
+ return NULL;
+ }
bh = ext2_bread (dir, 0, 0, err);
if (!bh)
return NULL;
@@ -215,6 +212,10 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
if (!bh)
return NULL;
if (dir->i_size <= offset) {
+ if (dir->i_size == 0) {
+ *err = -ENOENT;
+ return NULL;
+ }
#ifdef EXT2FS_DEBUG
printk ("ext2_add_entry: creating next block\n");
#endif
@@ -262,6 +263,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
bh->b_dirt = 1;
*res_dir = de;
+ *err = 0;
return bh;
}
offset += de->rec_len;
@@ -275,13 +277,31 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry
*/
-static void ext2_delete_entry (struct ext2_dir_entry * dir,
- struct ext2_dir_entry * prev_dir)
+static int ext2_delete_entry (struct ext2_dir_entry * dir,
+ struct buffer_head *bh)
{
- if (prev_dir != NULL)
- prev_dir->rec_len += dir->rec_len;
- else
- dir->inode = 0;
+ struct ext2_dir_entry * de, * pde;
+ int i;
+
+ i = 0;
+ pde = NULL;
+ de = (struct ext2_dir_entry *) bh->b_data;
+ while (i < bh->b_size) {
+ if (! ext2_check_dir_entry ("ext2_delete_entry", NULL,
+ de, bh, i))
+ return -EIO;
+ if (de == dir) {
+ if (pde)
+ pde->rec_len += dir->rec_len;
+ else
+ dir->inode = 0;
+ return 0;
+ }
+ i += de->rec_len;
+ pde = de;
+ de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ }
+ return -ENOENT;
}
int ext2_create (struct inode * dir,const char * name, int len, int mode,
@@ -333,7 +353,7 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
if (!dir)
return -ENOENT;
- bh = ext2_find_entry (dir, name, len, &de, NULL);
+ bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
@@ -395,7 +415,7 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
if (!dir)
return -ENOENT;
- bh = ext2_find_entry (dir, name, len, &de, NULL);
+ bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
@@ -519,18 +539,26 @@ int ext2_rmdir (struct inode * dir, const char * name, int len)
int retval;
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de, * pde;
+ struct ext2_dir_entry * de;
+repeat:
if (!dir)
return -ENOENT;
inode = NULL;
- bh = ext2_find_entry (dir, name, len, &de, &pde);
+ bh = ext2_find_entry (dir, name, len, &de);
retval = -ENOENT;
if (!bh)
goto end_rmdir;
retval = -EPERM;
if (!(inode = iget (dir->i_sb, de->inode)))
goto end_rmdir;
+ if (de->inode != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
if ((dir->i_mode & S_ISVTX) && current->euid &&
inode->i_uid != current->euid)
goto end_rmdir;
@@ -546,23 +574,31 @@ int ext2_rmdir (struct inode * dir, const char * name, int len)
retval = -ENOTEMPTY;
goto end_rmdir;
}
- if (inode->i_count > 1) {
- retval = -EBUSY;
- goto end_rmdir;
+ if (inode->i_count > 1 && inode->i_nlink <= 2) {
+ /* Are we deleting the last instance of a busy directory?
+ Better clean up if so. */
+ /* Make directory empty (it will be truncated when finally
+ dereferenced). This also inhibits ext2_add_entry. */
+ inode->i_size = 0;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_remove(inode->i_dev, inode->i_ino, ".", 1);
+ ext2_dcache_remove(inode->i_dev, inode->i_ino, "..", 2);
+#endif
}
if (inode->i_nlink != 2)
printk ("empty directory has nlink!=2 (%d)\n", inode->i_nlink);
#ifndef DONT_USE_DCACHE
ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
#endif
- ext2_delete_entry (de, pde);
+ retval = ext2_delete_entry (de, bh);
+ if (retval)
+ goto end_rmdir;
bh->b_dirt = 1;
inode->i_nlink = 0;
inode->i_dirt = 1;
dir->i_nlink --;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
- retval = 0;
end_rmdir:
iput (dir);
iput (inode);
@@ -575,17 +611,25 @@ int ext2_unlink (struct inode * dir, const char * name, int len)
int retval;
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de, * pde;
+ struct ext2_dir_entry * de;
+repeat:
if (!dir)
return -ENOENT;
retval = -ENOENT;
inode = NULL;
- bh = ext2_find_entry (dir, name, len, &de, &pde);
+ bh = ext2_find_entry (dir, name, len, &de);
if (!bh)
goto end_unlink;
if (!(inode = iget (dir->i_sb, de->inode)))
goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
retval = -EPERM;
if ((dir->i_mode & S_ISVTX) && !suser() &&
current->euid != inode->i_uid &&
@@ -601,7 +645,9 @@ int ext2_unlink (struct inode * dir, const char * name, int len)
#ifndef DONT_USE_DCACHE
ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
#endif
- ext2_delete_entry (de, pde);
+ retval = ext2_delete_entry (de, bh);
+ if (retval)
+ goto end_unlink;
bh->b_dirt = 1;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
@@ -665,7 +711,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
}
inode->i_size = i;
inode->i_dirt = 1;
- bh = ext2_find_entry (dir, name, len, &de, NULL);
+ bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
inode->i_nlink --;
inode->i_dirt = 1;
@@ -711,7 +757,7 @@ int ext2_link (struct inode * oldinode, struct inode * dir,
iput (dir);
return -EMLINK;
}
- bh = ext2_find_entry (dir, name, len, &de, NULL);
+ bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
@@ -788,11 +834,13 @@ static int do_ext2_rename (struct inode * old_dir, const char * old_name,
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
- struct ext2_dir_entry * old_de, * new_de, * pde;
+ struct ext2_dir_entry * old_de, * new_de;
int retval;
goto start_up;
try_again:
+ if (new_bh && new_de)
+ ext2_delete_entry(new_de, new_bh);
brelse (old_bh);
brelse (new_bh);
brelse (dir_bh);
@@ -803,7 +851,8 @@ try_again:
start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
- old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de, &pde);
+ new_de = NULL;
+ old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de);
retval = -ENOENT;
if (!old_bh)
goto end_rename;
@@ -815,7 +864,7 @@ start_up:
current->euid != old_inode->i_uid &&
current->euid != old_dir->i_uid && !suser())
goto end_rename;
- new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de, NULL);
+ new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de);
if (new_bh) {
new_inode = iget (new_dir->i_sb, new_de->inode);
if (!new_inode) {
@@ -882,11 +931,11 @@ start_up:
ext2_dcache_add (new_dir->i_dev, new_dir->i_ino, new_de->name,
new_de->name_len, new_de->inode);
#endif
- if (old_bh->b_blocknr == new_bh->b_blocknr &&
- ((char *) new_de) + new_de->rec_len == (char *) old_de)
- new_de->rec_len += old_de->rec_len;
- else
- ext2_delete_entry (old_de, pde);
+ retval = ext2_delete_entry (old_de, old_bh);
+ if (retval == -ENOENT)
+ goto try_again;
+ if (retval)
+ goto end_rename;
if (new_inode) {
new_inode->i_nlink --;
new_inode->i_dirt = 1;
diff --git a/fs/file_table.c b/fs/file_table.c
index fb55105..7e5c713 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -6,25 +6,89 @@
#include <linux/fs.h>
#include <linux/string.h>
+#include <linux/mm.h>
-struct file file_table[NR_FILE];
+struct file * first_file;
+int nr_files = 0;
+
+static void insert_file_free(struct file *file)
+{
+ file->f_next = first_file;
+ file->f_prev = first_file->f_prev;
+ file->f_next->f_prev = file;
+ file->f_prev->f_next = file;
+ first_file = file;
+}
+
+static void remove_file_free(struct file *file)
+{
+ if (first_file == file)
+ first_file = first_file->f_next;
+ if (file->f_next)
+ file->f_next->f_prev = file->f_prev;
+ if (file->f_prev)
+ file->f_prev->f_next = file->f_next;
+ file->f_next = file->f_prev = NULL;
+}
+
+static void put_last_free(struct file *file)
+{
+ remove_file_free(file);
+ file->f_prev = first_file->f_prev;
+ file->f_prev->f_next = file;
+ file->f_next = first_file;
+ file->f_next->f_prev = file;
+}
+
+void grow_files(void)
+{
+ unsigned long page;
+ struct file * file;
+ int i;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return;
+ file = (struct file *) page;
+ for (i=0; i < (PAGE_SIZE / sizeof(struct file)); i++, file++)
+ {
+ if (!first_file)
+ {
+ file->f_next = file;
+ file->f_prev = file;
+ first_file = file;
+ }
+ else
+ insert_file_free(file);
+ }
+ nr_files += i;
+}
unsigned long file_table_init(unsigned long start, unsigned long end)
{
- memset(file_table,0,sizeof(file_table));
+ first_file = NULL;
return start;
}
struct file * get_empty_filp(void)
{
int i;
- struct file * f = file_table+0;
+ struct file * f;
- for (i = 0; i++ < NR_FILE; f++)
+ if (!first_file)
+ grow_files();
+repeat:
+ for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
if (!f->f_count) {
+ remove_file_free(f);
memset(f,0,sizeof(*f));
+ put_last_free(f);
f->f_count = 1;
return f;
}
+ if (nr_files < NR_FILE) {
+ grow_files();
+ goto repeat;
+ }
return NULL;
}
diff --git a/fs/inode.c b/fs/inode.c
index 9bcab7c..75883cf 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -12,18 +12,105 @@
#include <asm/system.h>
-static struct inode * inode_table;
-static struct inode * last_inode;
+static struct inode * hash_table[NR_IHASH];
+static struct inode * first_inode;
static struct wait_queue * inode_wait = NULL;
+static int nr_inodes = 0, nr_free_inodes = 0;
+
+static inline int const hashfn(dev_t dev, int i)
+{
+ return (dev ^ i) % NR_IHASH;
+}
+
+static inline struct inode ** const hash(dev_t dev, int i)
+{
+ return hash_table + hashfn(dev, i);
+}
+
+static void insert_inode_free(struct inode *inode)
+{
+ inode->i_next = first_inode;
+ inode->i_prev = first_inode->i_prev;
+ inode->i_next->i_prev = inode;
+ inode->i_prev->i_next = inode;
+ first_inode = inode;
+}
+
+static void remove_inode_free(struct inode *inode)
+{
+ if (first_inode == inode)
+ first_inode = first_inode->i_next;
+ if (inode->i_next)
+ inode->i_next->i_prev = inode->i_prev;
+ if (inode->i_prev)
+ inode->i_prev->i_next = inode->i_next;
+ inode->i_next = inode->i_prev = NULL;
+}
+
+void insert_inode_hash(struct inode *inode)
+{
+ struct inode **h;
+ h = hash(inode->i_dev, inode->i_ino);
+
+ inode->i_hash_next = *h;
+ inode->i_hash_prev = NULL;
+ if (inode->i_hash_next)
+ inode->i_hash_next->i_hash_prev = inode;
+ *h = inode;
+}
+
+static void remove_inode_hash(struct inode *inode)
+{
+ struct inode **h;
+ h = hash(inode->i_dev, inode->i_ino);
+
+ if (*h == inode)
+ *h = inode->i_hash_next;
+ if (inode->i_hash_next)
+ inode->i_hash_next->i_hash_prev = inode->i_hash_prev;
+ if (inode->i_hash_prev)
+ inode->i_hash_prev->i_hash_next = inode->i_hash_next;
+ inode->i_hash_prev = inode->i_hash_next = NULL;
+}
+
+static void put_last_free(struct inode *inode)
+{
+ remove_inode_free(inode);
+ inode->i_prev = first_inode->i_prev;
+ inode->i_prev->i_next = inode;
+ inode->i_next = first_inode;
+ inode->i_next->i_prev = inode;
+}
+
+void grow_inodes(void)
+{
+ unsigned long page;
+ struct inode * inode;
+ int i;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return;
+ inode = (struct inode *) page;
+ for (i=0; i < (PAGE_SIZE / sizeof(struct inode)); i++, inode++)
+ {
+ if (!first_inode)
+ {
+ inode->i_next = inode;
+ inode->i_prev = inode;
+ first_inode = inode;
+ }
+ else
+ insert_inode_free(inode);
+ }
+ nr_inodes += i;
+ nr_free_inodes += i;
+}
unsigned long inode_init(unsigned long start, unsigned long end)
{
- start += 0x0000000f;
- start &= 0xfffffff0;
- inode_table = (struct inode *) start;
- last_inode = inode_table;
- start = (unsigned long) (inode_table + NR_INODE);
- memset(inode_table,0,NR_INODE*sizeof(struct inode));
+ memset(hash_table, 0, sizeof(hash_table));
+ first_inode = NULL;
return start;
}
@@ -65,15 +152,21 @@ void clear_inode(struct inode * inode)
wait_on_inode(inode);
wait = ((volatile struct inode *) inode)->i_wait;
+ remove_inode_hash(inode);
+ remove_inode_free(inode);
+ if (inode->i_count)
+ nr_free_inodes++;
memset(inode,0,sizeof(*inode));
((volatile struct inode *) inode)->i_wait = wait;
+ insert_inode_free(inode);
}
int fs_may_mount(dev_t dev)
{
struct inode * inode;
+ int i;
- for (inode = inode_table+0 ; inode < inode_table+NR_INODE ; inode++) {
+ for (inode = first_inode, i=0; i<nr_inodes; i++, inode=inode->i_next) {
if (inode->i_dev != dev)
continue;
if (inode->i_count || inode->i_dirt || inode->i_lock)
@@ -86,8 +179,9 @@ int fs_may_mount(dev_t dev)
int fs_may_umount(dev_t dev, struct inode * mount_root)
{
struct inode * inode;
+ int i;
- for (inode = inode_table+0 ; inode < inode_table+NR_INODE ; inode++) {
+ for (inode = first_inode, i=0; i<nr_inodes; i++, inode=inode->i_next) {
if (inode->i_dev==dev && inode->i_count)
if (inode == mount_root && inode->i_count == 1)
continue;
@@ -97,6 +191,22 @@ int fs_may_umount(dev_t dev, struct inode * mount_root)
return 1;
}
+int fs_may_remount_ro(dev_t dev)
+{
+ struct file * file;
+ int i;
+
+ /* Check that no files are currently opened for writing. */
+ for (file = first_file, i=0; i<nr_files; i++, file=file->f_next) {
+ if (!file->f_count || !file->f_inode ||
+ file->f_inode->i_dev != dev)
+ continue;
+ if (S_ISREG(file->f_inode->i_mode) && (file->f_mode & 2))
+ return 0;
+ }
+ return 1;
+}
+
static void write_inode(struct inode * inode)
{
if (!inode->i_dirt)
@@ -159,8 +269,8 @@ void invalidate_inodes(dev_t dev)
int i;
struct inode * inode;
- inode = 0+inode_table;
- for(i=0 ; i<NR_INODE ; i++,inode++) {
+ inode = first_inode;
+ for(i = 0; i < nr_inodes*2; i++, inode = inode->i_next) {
wait_on_inode(inode);
if (inode->i_dev == dev) {
if (inode->i_count) {
@@ -175,9 +285,11 @@ void invalidate_inodes(dev_t dev)
void sync_inodes(dev_t dev)
{
+ int i;
struct inode * inode;
- for(inode = 0+inode_table ; inode < NR_INODE+inode_table ; inode++) {
+ inode = first_inode;
+ for(i = 0; i < nr_inodes*2; i++, inode = inode->i_next) {
if (dev && inode->i_dev != dev)
continue;
wait_on_inode(inode);
@@ -224,25 +336,36 @@ repeat:
goto repeat;
}
inode->i_count--;
+ nr_free_inodes++;
return;
}
struct inode * get_empty_inode(void)
{
- struct inode * inode;
+ struct inode * inode, * best;
int i;
+ if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 2))
+ grow_inodes();
repeat:
- inode = NULL;
- for (i = NR_INODE; i ; i--) {
- if (++last_inode >= inode_table + NR_INODE)
- last_inode = inode_table;
- if (!last_inode->i_count) {
- inode = last_inode;
- if (!inode->i_dirt && !inode->i_lock)
+ inode = first_inode;
+ best = NULL;
+ for (i = 0; i<nr_inodes; inode = inode->i_next, i++) {
+ if (!inode->i_count) {
+ if (!best)
+ best = inode;
+ if (!inode->i_dirt && !inode->i_lock) {
+ best = inode;
break;
+ }
}
}
+ if (!best || best->i_dirt || best->i_lock)
+ if (nr_inodes < NR_INODE) {
+ grow_inodes();
+ goto repeat;
+ }
+ inode = best;
if (!inode) {
printk("VFS: No free inodes - contact Linus\n");
sleep_on(&inode_wait);
@@ -261,6 +384,12 @@ repeat:
clear_inode(inode);
inode->i_count = 1;
inode->i_nlink = 1;
+ nr_free_inodes--;
+ if (nr_free_inodes < 0)
+ {
+ printk ("VFS: get_empty_inode: bad free inode count.\n");
+ nr_free_inodes = 0;
+ }
return inode;
}
@@ -293,18 +422,19 @@ struct inode * iget(struct super_block * sb,int nr)
if (!sb)
panic("VFS: iget with sb==NULL");
+repeat:
empty = get_empty_inode();
- inode = inode_table;
- while (inode < NR_INODE+inode_table) {
+ inode = *(hash(sb->s_dev,nr));
+ while (inode) {
if (inode->i_dev != sb->s_dev || inode->i_ino != nr) {
- inode++;
+ inode = inode->i_hash_next;
continue;
}
wait_on_inode(inode);
- if (inode->i_dev != sb->s_dev || inode->i_ino != nr) {
- inode = inode_table;
- continue;
- }
+ if (inode->i_dev != sb->s_dev || inode->i_ino != nr)
+ goto repeat;
+ if (!inode->i_count)
+ nr_free_inodes--;
inode->i_count++;
if (inode->i_mount) {
int i;
@@ -314,14 +444,8 @@ struct inode * iget(struct super_block * sb,int nr)
break;
if (i >= NR_SUPER) {
printk("VFS: Mounted inode hasn't got sb\n");
- if (empty) {
- if (last_inode > inode_table)
- --last_inode;
- else
- last_inode
- = inode_table + NR_INODE;
+ if (empty)
iput(empty);
- }
return inode;
}
iput(inode);
@@ -329,18 +453,14 @@ struct inode * iget(struct super_block * sb,int nr)
printk("VFS: Mounted device %d/%d has no rootinode\n",
MAJOR(inode->i_dev), MINOR(inode->i_dev));
else {
+ if (!inode->i_count)
+ nr_free_inodes--;
inode->i_count++;
wait_on_inode(inode);
}
}
- if (empty) {
- if (last_inode > inode_table)
- --last_inode;
- else
- last_inode
- = inode_table + NR_INODE;
+ if (empty)
iput(empty);
- }
return inode;
}
if (!empty)
@@ -350,6 +470,8 @@ struct inode * iget(struct super_block * sb,int nr)
inode->i_dev = sb->s_dev;
inode->i_ino = nr;
inode->i_flags = sb->s_flags;
+ put_last_free(inode);
+ insert_inode_hash(inode);
read_inode(inode);
return inode;
}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 4a087f9..8baa2fb 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -45,7 +45,8 @@ static struct super_operations isofs_sops = {
NULL, /* put_inode */
isofs_put_super,
NULL, /* write_super */
- isofs_statfs
+ isofs_statfs,
+ NULL
};
@@ -317,7 +318,8 @@ void isofs_read_inode(struct inode * inode)
/* There are defective discs out there - we do this to protect
ourselves. A cdrom will never contain more than 700Mb */
- if(inode->i_size < 0 || inode->i_size > 700000000) {
+ if((inode->i_size < 0 || inode->i_size > 700000000) &&
+ inode->i_sb->u.isofs_sb.s_cruft == 'n') {
printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
};
diff --git a/fs/locks.c b/fs/locks.c
index fc588a0..7aed2cc 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -8,6 +8,8 @@
* - deadlock detection/avoidance (of dubious merit, but since it's in
* the definition, I guess it should be provided eventually)
* - mandatory locks (requires lots of changes elsewhere)
+ *
+ * Edited by Kai Petzke, wpp@marie.physik.tu-berlin.de
*/
#include <asm/segment.h>
@@ -24,9 +26,8 @@ static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l);
static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);
static int overlap(struct file_lock *fl1, struct file_lock *fl2);
static int lock_it(struct file *filp, struct file_lock *caller);
-static int unlock_it(struct file *filp, struct file_lock *caller);
-static struct file_lock *alloc_lock(struct file *filp, struct file_lock *template);
-static void free_lock(struct file *filp, struct file_lock *fl);
+static struct file_lock *alloc_lock(struct file_lock **pos, struct file_lock *template);
+static void free_lock(struct file_lock **fl);
static struct file_lock file_lock_table[NR_FILE_LOCKS];
static struct file_lock *file_lock_free_list;
@@ -130,36 +131,31 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
break;
}
- /*
- * F_UNLCK needs to be handled differently ...
- */
-
- if (file_lock.fl_type == F_UNLCK)
- return unlock_it(filp, &file_lock);
-
- /*
- * Scan for a conflicting lock ...
- */
-
+ /*
+ * Scan for a conflicting lock ...
+ */
+
+ if (file_lock.fl_type != F_UNLCK) {
repeat:
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (!conflict(&file_lock, fl))
- continue;
- /*
- * File is locked by another process. If this is F_SETLKW
- * wait for the lock to be released.
- * FIXME: We need to check for deadlocks here.
- */
- if (cmd == F_SETLKW) {
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- interruptible_sleep_on(&fl->fl_wait);
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- goto repeat;
- }
- return -EAGAIN;
- }
+ for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!conflict(&file_lock, fl))
+ continue;
+ /*
+ * File is locked by another process. If this is
+ * F_SETLKW wait for the lock to be released.
+ * FIXME: We need to check for deadlocks here.
+ */
+ if (cmd == F_SETLKW) {
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ interruptible_sleep_on(&fl->fl_wait);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ goto repeat;
+ }
+ return -EAGAIN;
+ }
+ }
/*
* Lock doesn't conflict with any other lock ...
@@ -174,18 +170,19 @@ repeat:
void fcntl_remove_locks(struct task_struct *task, struct file *filp)
{
- struct file_lock *fl,*next;
+ struct file_lock *fl;
+ struct file_lock **before;
- for (fl = filp->f_inode->i_flock; fl != NULL; ) {
- /*
- * If this one is freed, {fl_next} gets clobbered when the
- * entry is moved to the free list, so grab it now ...
- */
- next = fl->fl_next;
- if (fl->fl_owner == task)
- free_lock(filp, fl);
- fl = next;
- }
+ /* Find first lock owned by caller ... */
+
+ before = &filp->f_inode->i_flock;
+ while ((fl = *before) && task != fl->fl_owner)
+ before = &fl->fl_next;
+
+ /* The list is sorted by owner ... */
+
+ while ((fl = *before) && task == fl->fl_owner)
+ free_lock(before);
}
/*
@@ -243,213 +240,181 @@ static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
static int overlap(struct file_lock *fl1, struct file_lock *fl2)
{
- if (fl1->fl_start <= fl2->fl_start) {
- return fl1->fl_end >= fl2->fl_start;
- } else {
- return fl2->fl_end >= fl1->fl_start;
- }
+ return fl1->fl_end >= fl2->fl_start && fl2->fl_end >= fl1->fl_start;
}
/*
* Add a lock to a file ...
* Result is 0 for success or -ENOLCK.
*
- * We try to be real clever here and always minimize the number of table
- * entries we use. For example we merge adjacent locks whenever possible. This
- * consumes a bit of cpu and code space, is it really worth it? Beats me.
- *
- * I've tried to keep the following as small and simple as possible. If you can
- * make it smaller or simpler, please do. /dje 92Aug11
+ * We merge adjacent locks whenever possible.
*
* WARNING: We assume the lock doesn't conflict with any other lock.
*/
+
+/*
+ * Rewritten by Kai Petzke:
+ * We sort the lock list first by owner, then by the starting address.
+ *
+ * To make freeing a lock much faster, we keep a pointer to the lock before the
+ * actual one. But the real gain of the new coding was, that lock_it() and
+ * unlock_it() became one function.
+ *
+ * To all purists: Yes, I use a few goto's. Just pass on to the next function.
+ */
static int lock_it(struct file *filp, struct file_lock *caller)
{
- struct file_lock *fl,*new;
+ struct file_lock *fl;
+ struct file_lock *left = 0;
+ struct file_lock *right = 0;
+ struct file_lock **before;
+ int added = 0;
/*
- * It's easier if we allocate a slot for the lock first, and then
- * release it later if we have to (IE: if it can be merged with
- * another). This way the for() loop always knows that {caller} is an
- * existing entry. This will cause the routine to fail unnecessarily
- * in rare cases, but perfection can be pushed too far. :-)
+ * Find the first old lock with the same owner as the new lock.
*/
- if ((caller = alloc_lock(filp, caller)) == NULL)
- return -ENOLCK;
+ before = &filp->f_inode->i_flock;
+ while ((fl = *before) && caller->fl_owner != fl->fl_owner)
+ before = &fl->fl_next;
/*
- * First scan to see if we are changing/augmenting an existing lock ...
+ * Look up all locks of this owner.
*/
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (caller->fl_owner != fl->fl_owner)
- continue;
- if (caller == fl)
- continue;
- if (!overlap(caller, fl)) {
+ while ((fl = *before) && caller->fl_owner == fl->fl_owner) {
+ /*
+ * Detect adjacent or overlapping regions (if same lock type)
+ */
+ if (caller->fl_type == fl->fl_type) {
+ if (fl->fl_end < caller->fl_start - 1)
+ goto next_lock;
/*
- * Detect adjacent regions (if same lock type) ...
+ * If the next lock in the list has entirely bigger
+ * addresses than the new one, insert the lock here.
*/
- if (caller->fl_type != fl->fl_type)
- continue;
- if (caller->fl_end + 1 == fl->fl_start) {
+ if (fl->fl_start > caller->fl_end + 1)
+ break;
+
+ /*
+ * If we come here, the new and old lock are of the
+ * same type and adjacent or overlapping. Make one
+ * lock yielding from the lower start address of both
+ * locks to the higher end address.
+ */
+ if (fl->fl_start > caller->fl_start)
fl->fl_start = caller->fl_start;
- free_lock(filp, caller);
- caller = fl;
- /* must continue, may overlap others now */
- } else if (caller->fl_start - 1 == fl->fl_end) {
+ else
+ caller->fl_start = fl->fl_start;
+ if (fl->fl_end < caller->fl_end)
fl->fl_end = caller->fl_end;
- free_lock(filp, caller);
- caller = fl;
- /* must continue, may overlap others now */
+ else
+ caller->fl_end = fl->fl_end;
+ if (added) {
+ free_lock(before);
+ continue;
}
- continue;
+ caller = fl;
+ added = 1;
+ goto next_lock;
}
/*
- * We've found an overlapping region. Is it a change of lock
- * type, or are we changing the size of the locked space?
+ * Processing for different lock types is a bit more complex.
*/
- if (caller->fl_type != fl->fl_type) {
- if (caller->fl_start > fl->fl_start && caller->fl_end < fl->fl_end) {
- /*
- * The new lock splits the old one in two ...
- * {fl} is the bottom piece, {caller} is the
- * new lock, and {new} is the top piece.
- */
- if ((new = alloc_lock(filp, fl)) == NULL) {
- free_lock(filp, caller);
- return -ENOLCK;
- }
- fl->fl_end = caller->fl_start - 1;
- new->fl_start = caller->fl_end + 1;
- return 0;
- }
- if (caller->fl_start <= fl->fl_start && caller->fl_end >= fl->fl_end) {
- /*
- * The new lock completely replaces old one ...
- */
- free_lock(filp, fl);
- return 0;
- }
- if (caller->fl_end < fl->fl_end) {
- fl->fl_start = caller->fl_end + 1;
- /* must continue, may be more overlaps */
- } else if (caller->fl_start > fl->fl_start) {
- fl->fl_end = caller->fl_start - 1;
- /* must continue, may be more overlaps */
- } else {
- printk("VFS: lock_it: program bug: unanticipated overlap\n");
- free_lock(filp, caller);
- return -ENOLCK;
- }
- } else { /* The new lock augments an existing lock ... */
- int grew = 0;
-
- if (caller->fl_start < fl->fl_start) {
- fl->fl_start = caller->fl_start;
- grew = 1;
- }
- if (caller->fl_end > fl->fl_end) {
- fl->fl_end = caller->fl_end;
- grew = 1;
+ if (fl->fl_end < caller->fl_start)
+ goto next_lock;
+ if (fl->fl_start > caller->fl_end)
+ break;
+ if (caller->fl_type == F_UNLCK)
+ added = 1;
+ if (fl->fl_start < caller->fl_start)
+ left = fl;
+ /*
+ * If the next lock in the list has a higher end address than
+ * the new one, insert the new one here.
+ */
+ if (fl->fl_end > caller->fl_end) {
+ right = fl;
+ break;
+ }
+ if (fl->fl_start >= caller->fl_start) {
+ /*
+ * The new lock completely replaces an old one (This may
+ * happen several times).
+ */
+ if (added) {
+ free_lock(before);
+ continue;
}
- free_lock(filp, caller);
+ /*
+ * Replace the old lock with the new one. Wake up
+ * anybody waiting for the old one, as the change in
+ * lock type migth satisfy his needs.
+ */
+ wake_up(&fl->fl_wait);
+ fl->fl_start = caller->fl_start;
+ fl->fl_end = caller->fl_end;
+ fl->fl_type = caller->fl_type;
+ fl->fl_wait = 0;
caller = fl;
- if (!grew)
- return 0;
- /* must continue, may be more overlaps */
+ added = 1;
}
+ /*
+ * Go on to next lock.
+ */
+next_lock:
+ before = &(*before)->fl_next;
}
- /*
- * New lock doesn't overlap any regions ...
- * alloc_lock() has already been called, so we're done!
- */
-
- return 0;
-}
-
-/*
- * Handle F_UNLCK ...
- * Result is 0 for success, or -EINVAL or -ENOLCK.
- * ENOLCK can happen when a lock is split into two.
- */
-
-static int unlock_it(struct file *filp, struct file_lock *caller)
-{
- int one_unlocked = 0;
- struct file_lock *fl,*next;
-
- for (fl = filp->f_inode->i_flock; fl != NULL; ) {
- if (caller->fl_owner != fl->fl_owner || !overlap(caller, fl)) {
- fl = fl->fl_next;
- continue;
- }
- one_unlocked = 1;
- if (caller->fl_start > fl->fl_start && caller->fl_end < fl->fl_end) {
+ if (! added) {
+ if (caller->fl_type == F_UNLCK)
+ return -EINVAL;
+ if (! (caller = alloc_lock(before, caller)))
+ return -ENOLCK;
+ }
+ if (right) {
+ if (left == right) {
/*
- * Lock is split in two ...
- * {fl} is the bottom piece, {next} is the top piece.
+ * The new lock breaks the old one in two pieces, so we
+ * have to allocate one more lock (in this case, even
+ * F_UNLCK may fail!).
*/
- if ((next = alloc_lock(filp, fl)) == NULL)
+ if (! (left = alloc_lock(before, right))) {
+ if (! added)
+ free_lock(before);
return -ENOLCK;
- fl->fl_end = caller->fl_start - 1;
- next->fl_start = caller->fl_end + 1;
- return 0;
- }
- /*
- * At this point we know there is an overlap and we know the
- * lock isn't split into two ...
- *
- * Unless the lock table is broken, entries will not overlap.
- * IE: User X won't have an entry locking bytes 1-3 and another
- * entry locking bytes 3-5. Therefore, if the area being
- * unlocked is a subset of the total area, we don't need to
- * traverse any more of the list. The code is a tad more
- * complicated by this optimization. Perhaps it's not worth it.
- *
- * WARNING: We assume free_lock() does not alter
- * {fl_start, fl_end}.
- *
- * {fl_next} gets clobbered when the entry is moved to
- * the free list, so grab it now ...
- */
- next = fl->fl_next;
- if (caller->fl_start <= fl->fl_start && caller->fl_end >= fl->fl_end) {
- free_lock(filp, fl);
- } else if (caller->fl_start > fl->fl_start) {
- fl->fl_end = caller->fl_start - 1;
- } else {
- /* caller->fl_end < fl->fl_end */
- fl->fl_start = caller->fl_end + 1;
+ }
}
- if (caller->fl_start >= fl->fl_start && caller->fl_end <= fl->fl_end)
- return 0; /* no more to be found */
- fl = next;
- /* must continue, there may be more to unlock */
+ right->fl_start = caller->fl_end + 1;
}
-
- return one_unlocked ? 0 : -EINVAL;
+ if (left)
+ left->fl_end = caller->fl_start - 1;
+ return 0;
}
-static struct file_lock *alloc_lock(struct file *filp, struct file_lock *template)
+/*
+ * File_lock() inserts a lock at the position pos of the linked list.
+ */
+
+static struct file_lock *alloc_lock(struct file_lock **pos,
+ struct file_lock *template)
{
struct file_lock *new;
- if (file_lock_free_list == NULL)
+ new = file_lock_free_list;
+ if (new == NULL)
return NULL; /* no available entry */
- if (file_lock_free_list->fl_owner != NULL)
- panic("VFS: alloc_lock: broken free list\n");
+ if (new->fl_owner != NULL)
+ panic("alloc_lock: broken free list\n");
- new = file_lock_free_list; /* remove from free list */
- file_lock_free_list = file_lock_free_list->fl_next;
+ /* remove from free list */
+ file_lock_free_list = new->fl_next;
*new = *template;
- new->fl_next = filp->f_inode->i_flock; /* insert into file's list */
- filp->f_inode->i_flock = new;
+ new->fl_next = *pos; /* insert into file's list */
+ *pos = new;
new->fl_owner = current; /* FIXME: needed? */
new->fl_wait = NULL;
@@ -458,31 +423,17 @@ static struct file_lock *alloc_lock(struct file *filp, struct file_lock *templat
/*
* Add a lock to the free list ...
- *
- * WARNING: We must not alter {fl_start, fl_end}. See unlock_it().
*/
-static void free_lock(struct file *filp, struct file_lock *fl)
+static void free_lock(struct file_lock **fl_p)
{
- struct file_lock **fl_p;
+ struct file_lock *fl;
+ fl = *fl_p;
if (fl->fl_owner == NULL) /* sanity check */
- panic("VFS: free_lock: broken lock list\n");
+ panic("free_lock: broken lock list\n");
- /*
- * We only use a singly linked list to save some memory space
- * (the only place we'd use a doubly linked list is here).
- */
-
- for (fl_p = &filp->f_inode->i_flock; *fl_p != NULL; fl_p = &(*fl_p)->fl_next) {
- if (*fl_p == fl)
- break;
- }
- if (*fl_p == NULL) {
- printk("VFS: free_lock: lock is not in file's lock list\n");
- } else {
- *fl_p = (*fl_p)->fl_next;
- }
+ *fl_p = (*fl_p)->fl_next;
fl->fl_next = file_lock_free_list; /* add to free list */
file_lock_free_list = fl;
diff --git a/fs/minix/Makefile b/fs/minix/Makefile
index 80109a0..7f7fa3e 100644
--- a/fs/minix/Makefile
+++ b/fs/minix/Makefile
@@ -15,7 +15,7 @@
$(AS) -o $*.o $<
OBJS= bitmap.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o
+ file.o dir.o symlink.o fsync.o
minix.o: $(OBJS)
$(LD) -r -o minix.o $(OBJS)
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 66e1aed..b445288 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -217,6 +217,7 @@ struct inode * minix_new_inode(const struct inode * dir)
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = NULL;
inode->i_blocks = inode->i_blksize = 0;
+ insert_inode_hash(inode);
return inode;
}
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index 8c678e9..f32a683 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -30,7 +30,7 @@ static struct file_operations minix_dir_operations = {
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* default fsync */
+ file_fsync /* default fsync */
};
/*
diff --git a/fs/minix/file.c b/fs/minix/file.c
index e78e89e..75d6b4c 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -42,7 +42,7 @@ static struct file_operations minix_file_operations = {
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
- NULL /* fsync */
+ minix_sync_file /* fsync */
};
struct inode_operations minix_file_inode_operations = {
diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c
new file mode 100644
index 0000000..3f2f831
--- /dev/null
+++ b/fs/minix/fsync.c
@@ -0,0 +1,160 @@
+/*
+ * linux/fs/minix/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * minix fsync primitive
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+
+#define blocksize BLOCK_SIZE
+#define addr_per_block 512
+
+static int sync_block (struct inode * inode, unsigned short * block, int wait)
+{
+ struct buffer_head * bh;
+ unsigned short tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = get_hash_table(inode->i_dev, *block, blocksize);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt)
+ {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+static int sync_iblock (struct inode * inode, unsigned short * iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc;
+ unsigned short tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 7; i++) {
+ rc = sync_block (inode, inode->u.minix_i.i_data + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned short *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned short *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned short *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+int minix_sync_file(struct inode * inode, struct file * file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+
+ for (wait=0; wait<=1; wait++)
+ {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, inode->u.minix_i.i_data+7, wait);
+ err |= sync_dindirect(inode, inode->u.minix_i.i_data+8, wait);
+ }
+ err |= minix_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 1f17383..da3b75b 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -45,7 +45,8 @@ static struct super_operations minix_sops = {
minix_put_inode,
minix_put_super,
NULL,
- minix_statfs
+ minix_statfs,
+ NULL
};
struct super_block *minix_read_super(struct super_block *s,void *data,
@@ -362,7 +363,7 @@ void minix_read_inode(struct inode * inode)
init_fifo(inode);
}
-void minix_write_inode(struct inode * inode)
+static struct buffer_head * minix_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct minix_inode * raw_inode;
@@ -373,14 +374,14 @@ void minix_write_inode(struct inode * inode)
printk("Bad inode number on dev 0x%04x: %d is out of range\n",
inode->i_dev, ino);
inode->i_dirt = 0;
- return;
+ return 0;
}
block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks +
(ino-1)/MINIX_INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) {
printk("unable to read i-node block\n");
inode->i_dirt = 0;
- return;
+ return 0;
}
raw_inode = ((struct minix_inode *)bh->b_data) +
(ino-1)%MINIX_INODES_PER_BLOCK;
@@ -396,5 +397,35 @@ void minix_write_inode(struct inode * inode)
raw_inode->i_zone[block] = inode->u.minix_i.i_data[block];
inode->i_dirt=0;
bh->b_dirt=1;
+ return bh;
+}
+
+void minix_write_inode(struct inode * inode)
+{
+ struct buffer_head *bh;
+ bh = minix_update_inode(inode);
brelse(bh);
}
+
+int minix_sync_inode(struct inode * inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = minix_update_inode(inode);
+ if (bh && bh->b_dirt)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing minix inode [%04x:%08x]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c
index 90415ce..e42cc5c 100644
--- a/fs/msdos/dir.c
+++ b/fs/msdos/dir.c
@@ -33,7 +33,7 @@ static struct file_operations msdos_dir_operations = {
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* fsync */
+ file_fsync /* fsync */
};
struct inode_operations msdos_dir_inode_operations = {
diff --git a/fs/msdos/file.c b/fs/msdos/file.c
index afddf81..f450937 100644
--- a/fs/msdos/file.c
+++ b/fs/msdos/file.c
@@ -35,7 +35,7 @@ static struct file_operations msdos_file_operations = {
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
- NULL /* fsync */
+ file_fsync /* fsync */
};
struct inode_operations msdos_file_inode_operations = {
diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c
index 1d690a7..0ef6829 100644
--- a/fs/msdos/inode.c
+++ b/fs/msdos/inode.c
@@ -61,7 +61,8 @@ static struct super_operations msdos_sops = {
msdos_put_inode,
msdos_put_super,
NULL, /* added in 0.96c */
- msdos_statfs
+ msdos_statfs,
+ NULL
};
diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c
index 6f1aef5..697e3be 100644
--- a/fs/msdos/misc.c
+++ b/fs/msdos/misc.c
@@ -264,7 +264,7 @@ void date_unix2dos(int unix_date,unsigned short *time,
non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
returned in bh. */
-int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh,
+int msdos_get_entry(struct inode *dir, off_t *pos,struct buffer_head **bh,
struct msdos_dir_entry **de)
{
int sector,offset;
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 082c558..009aed2 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -34,7 +34,7 @@ static int msdos_format_name(char conv,const char *name,int len,char *res,
int dot_dirs)
{
char *walk,**reserved;
- char c;
+ unsigned char c;
int space;
if (IS_FREE(name)) return -EINVAL;
@@ -295,7 +295,7 @@ mkdir_error:
static int msdos_empty(struct inode *dir)
{
- int pos;
+ off_t pos;
struct buffer_head *bh;
struct msdos_dir_entry *de;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index cf72f40..68e173f 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -32,7 +32,8 @@ static struct super_operations nfs_sops = {
nfs_put_inode, /* put inode */
nfs_put_super, /* put superblock */
NULL, /* write superblock */
- nfs_statfs /* stat filesystem */
+ nfs_statfs, /* stat filesystem */
+ NULL
};
static void nfs_put_inode(struct inode * inode)
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index e62739e..7e58c0d 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -16,6 +16,7 @@
#define NFS_PROC_DEBUG
#endif
+#include <linux/config.h>
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -23,8 +24,7 @@
#include <linux/utsname.h>
#include <linux/errno.h>
#include <linux/string.h>
-
-#include <netinet/in.h>
+#include <linux/in.h>
#ifdef NFS_PROC_DEBUG
static int proc_debug = 0;
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index 910719f..2f97999 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -6,16 +6,16 @@
* low-level nfs remote procedure call interface
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <asm/segment.h>
+#include <linux/in.h>
+#include <linux/net.h>
-#include <netinet/in.h>
-
-#include "../../net/kern_sock.h"
extern struct socket *socki_lookup(struct inode *inode);
diff --git a/fs/pipe.c b/fs/pipe.c
index 0bd2d3f..1c74c94 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -69,7 +69,7 @@ static int pipe_write(struct inode * inode, struct file * filp, char * buf, int
if (current->signal & ~current->blocked)
return written?written:-ERESTARTSYS;
if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
+ return written?written:-EAGAIN;
else
interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
}
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index cc16676..e786446 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -14,7 +14,7 @@
.s.o:
$(AS) -o $*.o $<
-OBJS= inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o
+OBJS= inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o
proc.o: $(OBJS)
$(LD) -r -o proc.o $(OBJS)
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 460e64b..9e4f2c2 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -19,15 +19,6 @@
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
-#define KSTK_EIP(stack) (((unsigned long *)stack)[1019])
-#define KSTK_ESP(stack) (((unsigned long *)stack)[1022])
-
-#define _SSIZE(stack) (TASK_SIZE - KSTK_ESP(stack))
-#define SSIZE(stack) (KSTK_ESP(stack) ? _SSIZE(stack) : 0)
-
-#define VSIZE(task,stack) ((task)->brk + 1023 + SSIZE(stack))
-
-
static int get_loadavg(char * buffer)
{
int a, b, c;
@@ -163,8 +154,10 @@ static unsigned long get_wchan(struct task_struct *p)
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
- ebp = p->tss.ebp;
stack_page = p->kernel_stack_page;
+ if (!stack_page)
+ return 0;
+ ebp = p->tss.ebp;
do {
if (ebp < stack_page || ebp >= 4092+stack_page)
return 0;
@@ -177,10 +170,14 @@ static unsigned long get_wchan(struct task_struct *p)
return 0;
}
+#define KSTK_EIP(stack) (((unsigned long *)stack)[1019])
+#define KSTK_ESP(stack) (((unsigned long *)stack)[1022])
+
static int get_stat(int pid, char * buffer)
{
struct task_struct ** p = get_task(pid);
unsigned long sigignore=0, sigcatch=0, bit=1, wchan;
+ unsigned long vsize, eip, esp;
int i,tty_pgrp;
char state;
@@ -190,6 +187,15 @@ static int get_stat(int pid, char * buffer)
state = '.';
else
state = "RSDZTD"[(*p)->state];
+ eip = esp = 0;
+ vsize = (*p)->kernel_stack_page;
+ if (vsize) {
+ eip = KSTK_EIP(vsize);
+ esp = KSTK_ESP(vsize);
+ vsize = (*p)->brk + 4095;
+ if (esp)
+ vsize += TASK_SIZE - esp;
+ }
wchan = get_wchan(*p);
for(i=0; i<32; ++i) {
switch((int) (*p)->sigaction[i].sa_handler) {
@@ -230,14 +236,14 @@ static int get_stat(int pid, char * buffer)
(*p)->timeout,
(*p)->it_real_value,
(*p)->start_time,
- VSIZE((*p),(*p)->kernel_stack_page),
+ vsize,
(*p)->rss, /* you might want to shift this left 3 */
(*p)->rlim[RLIMIT_RSS].rlim_cur,
(*p)->start_code,
(*p)->end_code,
(*p)->start_stack,
- KSTK_ESP((*p)->kernel_stack_page),
- KSTK_EIP((*p)->kernel_stack_page),
+ esp,
+ eip,
(*p)->signal,
(*p)->blocked,
sigignore,
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 22ef31b..d6c4411 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -37,7 +37,8 @@ static struct super_operations proc_sops = {
proc_put_inode,
proc_put_super,
NULL,
- proc_statfs
+ proc_statfs,
+ NULL
};
struct super_block *proc_read_super(struct super_block *s,void *data,
@@ -100,12 +101,26 @@ void proc_read_inode(struct inode * inode)
inode->i_op = &proc_root_inode_operations;
return;
}
- if (!pid) {
+ if ((ino >= 128) && (ino <= 160)) { /* files within /proc/net */
inode->i_mode = S_IFREG | 0444;
- inode->i_op = &proc_array_inode_operations;
- if (ino == 5) {
- inode->i_mode = S_IFREG | 0400;
- inode->i_op = &proc_kmsg_inode_operations;
+ inode->i_op = &proc_net_inode_operations;
+ return;
+ }
+ if (!pid) {
+ switch (ino) {
+ case 5:
+ inode->i_mode = S_IFREG | 0444;
+ inode->i_op = &proc_kmsg_inode_operations;
+ break;
+ case 8: /* for the net directory */
+ inode->i_mode = S_IFDIR | 0555;
+ inode->i_nlink = 2;
+ inode->i_op = &proc_net_inode_operations;
+ break;
+ default:
+ inode->i_mode = S_IFREG | 0444;
+ inode->i_op = &proc_array_inode_operations;
+ break;
}
return;
}
diff --git a/fs/proc/net.c b/fs/proc/net.c
new file mode 100644
index 0000000..466163d
--- /dev/null
+++ b/fs/proc/net.c
@@ -0,0 +1,211 @@
+/*
+ * linux/fs/proc/net.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * gjh 3/'93 heim@peanuts.informatik.uni-tuebingen.de (Gerald J. Heim)
+ * most of this file is stolen from base.c
+ * it works, but you shouldn't use it as a guideline
+ * for new proc-fs entries. once i'll make it better.
+ * fvk 3/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
+ * cleaned up the whole thing, moved "net" specific code to
+ * the NET kernel layer (where it belonged in the first place).
+ * Michael K. Johnson (johnsonm@stolaf.edu) 3/93
+ * Added support from my previous inet.c. Cleaned things up
+ * quite a bit, modularized the code.
+ * fvk 4/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen)
+ * Renamed "route_get_info()" to "rt_get_info()" for consistency.
+ *
+ * proc net directory handling functions
+ */
+#include <linux/autoconf.h>
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+/* forward references */
+static int proc_readnet(struct inode * inode, struct file * file,
+ char * buf, int count);
+static int proc_readnetdir(struct inode *, struct file *,
+ struct dirent *, int);
+static int proc_lookupnet(struct inode *,const char *,int,struct inode **);
+
+/* the get_*_info() functions are in the net code, and are configured
+ in via the standard mechanism... */
+#ifdef CONFIG_INET
+extern int unix_get_info(char *);
+extern int tcp_get_info(char *);
+extern int udp_get_info(char *);
+extern int raw_get_info(char *);
+extern int arp_get_info(char *);
+extern int dev_get_info(char *);
+extern int rt_get_info(char *);
+#endif /* CONFIG_INET */
+
+
+static struct file_operations proc_net_operations = {
+ NULL, /* lseek - default */
+ proc_readnet, /* read - bad */
+ NULL, /* write - bad */
+ proc_readnetdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_net_inode_operations = {
+ &proc_net_operations, /* default net directory file-ops */
+ NULL, /* create */
+ proc_lookupnet, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static struct proc_dir_entry net_dir[] = {
+ { 1,2,".." },
+ { 8,1,"." }
+#ifdef CONFIG_INET
+ ,{ 128,4,"unix" },
+ { 129,3,"arp" },
+ { 130,5,"route" },
+ { 131,3,"dev" },
+ { 132,3,"raw" },
+ { 133,3,"tcp" },
+ { 134,3,"udp" }
+#endif /* CONFIG_INET */
+};
+
+#define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])))
+
+
+static int proc_lookupnet(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ unsigned int ino;
+ int i;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ i = NR_NET_DIRENTRY;
+ while (i-- > 0 && !proc_match(len,name,net_dir+i))
+ /* nothing */;
+ if (i < 0) {
+ iput(dir);
+ return -ENOENT;
+ }
+ ino = net_dir[i].low_ino;
+ if (!(*result = iget(dir->i_sb,ino))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ iput(dir);
+ return 0;
+}
+
+static int proc_readnetdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ struct proc_dir_entry * de;
+ unsigned int ino;
+ int i,j;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ ino = inode->i_ino;
+ if (((unsigned) filp->f_pos) < NR_NET_DIRENTRY) {
+ de = net_dir + filp->f_pos;
+ filp->f_pos++;
+ i = de->namelen;
+ ino = de->low_ino;
+ put_fs_long(ino, &dirent->d_ino);
+ put_fs_word(i,&dirent->d_reclen);
+ put_fs_byte(0,i+dirent->d_name);
+ j = i;
+ while (i--)
+ put_fs_byte(de->name[i], i+dirent->d_name);
+ return j;
+ }
+ return 0;
+}
+
+
+static int proc_readnet(struct inode * inode, struct file * file,
+ char * buf, int count)
+{
+ char * page;
+ int length;
+ int end;
+ unsigned int ino;
+
+ if (count < 0)
+ return -EINVAL;
+ page = (char *) get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ ino = inode->i_ino;
+ switch (ino) {
+#ifdef CONFIG_INET
+ case 128:
+ length = unix_get_info(page);
+ break;
+ case 129:
+ length = arp_get_info(page);
+ break;
+ case 130:
+ length = rt_get_info(page);
+ break;
+ case 131:
+ length = dev_get_info(page);
+ break;
+ case 132:
+ length = raw_get_info(page);
+ break;
+ case 133:
+ length = tcp_get_info(page);
+ break;
+ case 134:
+ length = udp_get_info(page);
+ break;
+#endif /* CONFIG_INET */
+ default:
+ free_page((unsigned long) page);
+ return -EBADF;
+ }
+ if (file->f_pos >= length) {
+ free_page((unsigned long) page);
+ return 0;
+ }
+ if (count + file->f_pos > length)
+ count = length - file->f_pos;
+ end = count + file->f_pos;
+ memcpy_tofs(buf, page + file->f_pos, count);
+ free_page((unsigned long) page);
+ file->f_pos = end;
+ return count;
+
+}
diff --git a/fs/proc/root.c b/fs/proc/root.c
index af77c16..f1b9e7d 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -58,7 +58,8 @@ static struct proc_dir_entry root_dir[] = {
{ 4,7,"meminfo" },
{ 5,4,"kmsg" },
{ 6,7,"version" },
- { 7,4,"self" } /* will change inode # */
+ { 7,4,"self" }, /* will change inode # */
+ { 8,3,"net" }
};
#define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
diff --git a/fs/super.c b/fs/super.c
index ac7509d..294d512 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -34,6 +34,8 @@ extern int root_mountflags;
struct super_block super_block[NR_SUPER];
+static int do_remount_sb(struct super_block *sb, int flags);
+
/* this is initialized in init/main.c */
dev_t ROOT_DEV = 0;
@@ -198,9 +200,20 @@ static void put_unnamed_dev(dev_t dev)
static int do_umount(dev_t dev)
{
struct super_block * sb;
-
- if (dev==ROOT_DEV)
- return -EBUSY;
+ int retval;
+
+ if (dev==ROOT_DEV) {
+ /* Special case for "unmounting" root. We just try to remount
+ it readonly, and sync() the device. */
+ if (!(sb=get_super(dev)))
+ return -ENOENT;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ retval = do_remount_sb(sb, MS_RDONLY);
+ if (retval)
+ return retval;
+ }
+ return 0;
+ }
if (!(sb=get_super(dev)) || !(sb->s_covered))
return -ENOENT;
if (!sb->s_covered->i_mount)
@@ -267,7 +280,7 @@ int sys_umount(char * name)
iput(inode);
return -ENXIO;
}
- if (!(retval = do_umount(dev))) {
+ if (!(retval = do_umount(dev)) && dev != ROOT_DEV) {
fops = blkdev_fops[MAJOR(dev)];
if (fops && fops->release)
fops->release(inode,NULL);
@@ -278,7 +291,7 @@ int sys_umount(char * name)
iput(inode);
if (retval)
return retval;
- sync_dev(dev);
+ fsync_dev(dev);
return 0;
}
@@ -329,6 +342,24 @@ static int do_mount(dev_t dev, const char * dir, char * type, int flags, void *
* FS-specific mount options can't be altered by remounting.
*/
+static int do_remount_sb(struct super_block *sb, int flags)
+{
+ int retval;
+
+ /* If we are remounting RDONLY, make sure there are no rw files open */
+ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
+ if (!fs_may_remount_ro(sb->s_dev))
+ return -EBUSY;
+ if (sb->s_op && sb->s_op->remount_fs) {
+ retval = sb->s_op->remount_fs(sb, &flags);
+ if (retval)
+ return retval;
+ }
+ sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) |
+ (flags & MS_RMT_MASK);
+ return 0;
+}
+
static int do_remount(const char *dir,int flags)
{
struct inode *dir_i;
@@ -341,10 +372,9 @@ static int do_remount(const char *dir,int flags)
iput(dir_i);
return -EINVAL;
}
- dir_i->i_sb->s_flags = (dir_i->i_sb->s_flags & ~MS_RMT_MASK) |
- (flags & MS_RMT_MASK);
+ retval = do_remount_sb(dir_i->i_sb, flags);
iput(dir_i);
- return 0;
+ return retval;
}
diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile
index 90a8b75..4cc1cb0 100644
--- a/fs/xiafs/Makefile
+++ b/fs/xiafs/Makefile
@@ -15,7 +15,7 @@
$(AS) -o $*.o $<
OBJS= bitmap.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o
+ file.o dir.o symlink.o fsync.o
xiafs.o: $(OBJS)
$(LD) -r -o xiafs.o $(OBJS)
diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c
index 03660d3..2b5f990 100644
--- a/fs/xiafs/bitmap.c
+++ b/fs/xiafs/bitmap.c
@@ -332,6 +332,7 @@ struct inode * xiafs_new_inode(struct inode * dir)
inode->i_op = NULL;
inode->i_blocks = 0;
inode->i_blksize = XIAFS_ZSIZE(inode->i_sb);
+ insert_inode_hash(inode);
return inode;
}
diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c
index ebd41f9..fca3c2b 100644
--- a/fs/xiafs/dir.c
+++ b/fs/xiafs/dir.c
@@ -32,7 +32,7 @@ static struct file_operations xiafs_dir_operations = {
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* default fsync */
+ file_fsync /* default fsync */
};
/*
diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c
index 4a0f34d..af74628 100644
--- a/fs/xiafs/file.c
+++ b/fs/xiafs/file.c
@@ -45,7 +45,7 @@ static struct file_operations xiafs_file_operations = {
NULL, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
- NULL /* fsync */
+ xiafs_sync_file /* fsync */
};
struct inode_operations xiafs_file_inode_operations = {
diff --git a/fs/xiafs/fsync.c b/fs/xiafs/fsync.c
new file mode 100644
index 0000000..0e84f54
--- /dev/null
+++ b/fs/xiafs/fsync.c
@@ -0,0 +1,160 @@
+/*
+ * linux/fs/xiafs/fsync.c
+ *
+ * Changes Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * xiafs fsync primitive
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/xia_fs.h>
+
+#include "xiafs_mac.h"
+
+
+#define blocksize (XIAFS_ZSIZE(inode->i_sb))
+#define addr_per_block (XIAFS_ADDRS_PER_Z(inode->i_sb))
+
+static int sync_block (struct inode * inode, int * block, int wait)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = get_hash_table(inode->i_dev, *block, blocksize);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt)
+ {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+static int sync_iblock (struct inode * inode, int * iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc, tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 8; i++) {
+ rc = sync_block (inode, inode->u.ext_i.i_data + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block (inode,
+ ((daddr_t *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((daddr_t *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+int xiafs_sync_file(struct inode * inode, struct file * file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+ for (wait=0; wait<=1; wait++)
+ {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, &inode->u.xiafs_i.i_ind_zone, wait);
+ err |= sync_dindirect(inode, &inode->u.xiafs_i.i_dind_zone, wait);
+ }
+ err |= xiafs_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c
index d93e971..fb3491b 100644
--- a/fs/xiafs/inode.c
+++ b/fs/xiafs/inode.c
@@ -52,7 +52,8 @@ static struct super_operations xiafs_sops = {
xiafs_put_inode,
xiafs_put_super,
NULL,
- xiafs_statfs
+ xiafs_statfs,
+ NULL
};
struct super_block *xiafs_read_super(struct super_block *s, void *data,
@@ -402,7 +403,7 @@ void xiafs_read_inode(struct inode * inode)
init_fifo(inode);
}
-void xiafs_write_inode(struct inode * inode)
+static struct buffer_head * xiafs_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct xiafs_inode * raw_inode;
@@ -412,14 +413,14 @@ void xiafs_write_inode(struct inode * inode)
if (IS_RDONLY (inode)) {
printk("XIA-FS: write_inode on a read-only filesystem (%s %d)\n", WHERE_ERR);
inode->i_dirt = 0;
- return;
+ return 0;
}
ino = inode->i_ino;
if (!ino || ino > inode->i_sb->u.xiafs_sb.s_ninodes) {
printk("XIA-FS: bad inode number (%s %d)\n", WHERE_ERR);
inode->i_dirt=0;
- return;
+ return 0;
}
zone = 1 + inode->i_sb->u.xiafs_sb.s_imap_zones +
inode->i_sb->u.xiafs_sb.s_zmap_zones +
@@ -427,7 +428,7 @@ void xiafs_write_inode(struct inode * inode)
if (!(bh=bread(inode->i_dev, zone, XIAFS_ZSIZE(inode->i_sb)))) {
printk("XIA-FS: read i-node zone failed (%s %d)\n", WHERE_ERR);
inode->i_dirt=0;
- return;
+ return 0;
}
raw_inode = ((struct xiafs_inode *)bh->b_data) +
((ino-1) & (XIAFS_INODES_PER_Z(inode->i_sb) -1));
@@ -453,7 +454,36 @@ void xiafs_write_inode(struct inode * inode)
}
inode->i_dirt=0;
bh->b_dirt=1;
- brelse(bh);
+ return bh;
+}
+
+
+void xiafs_write_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ bh = xiafs_update_inode(inode);
+ brelse (bh);
}
+int xiafs_sync_inode (struct inode *inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+ bh = xiafs_update_inode(inode);
+ if (bh && bh->b_dirt)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing xiafs inode [%04x:%08x]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
diff --git a/ibcs/Makefile b/ibcs/Makefile
new file mode 100644
index 0000000..6b646a9
--- /dev/null
+++ b/ibcs/Makefile
@@ -0,0 +1,43 @@
+#
+# Makefile for the iBCS emulator files
+#
+# 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 definitions are now in the main makefile...
+
+.S.s:
+ $(CPP) -traditional $< -o $*.s
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+SUBDIRS =
+
+OBJS = emulate.o
+
+ibcs.o: $(OBJS)
+ $(LD) -r -o ibcs.o $(OBJS)
+ sync
+
+clean:
+ rm -f core *.o *.a *.s
+ for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
+
+dep:
+ $(CPP) -M *.c > .depend
+ for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff --git a/ibcs/emulate.c b/ibcs/emulate.c
new file mode 100644
index 0000000..f8c4b1e
--- /dev/null
+++ b/ibcs/emulate.c
@@ -0,0 +1,26 @@
+/*
+ * linux/abi/emulate.c
+ *
+ * Copyright (C) 1993 Linus Torvalds
+ */
+
+/*
+ * Emulate.c contains the entry point for the 'lcall 7,xxx' handler.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/segment.h>
+#include <linux/ptrace.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+void iABI_emulate(struct pt_regs * regs)
+{
+ printk("lcall 7,xxx: eax = %08x\n",regs->eax);
+}
diff --git a/include/asm/irq.h b/include/asm/irq.h
index 4c696b1..f5cb9d0 100644
--- a/include/asm/irq.h
+++ b/include/asm/irq.h
@@ -4,8 +4,12 @@
/*
* linux/include/asm/irq.h
*
- * (C) 1992 Linus Torvalds
+ * (C) 1992, 1993 Linus Torvalds
*/
+
+#include <linux/segment.h>
+#define __STR(x) #x
+#define STR(x) __STR(x)
#define SAVE_ALL \
"cld\n\t" \
@@ -20,10 +24,10 @@
"pushl %edx\n\t" \
"pushl %ecx\n\t" \
"pushl %ebx\n\t" \
- "movl $0x10,%edx\n\t" \
+ "movl $" STR(KERNEL_DS) ",%edx\n\t" \
"mov %dx,%ds\n\t" \
"mov %dx,%es\n\t" \
- "movl $0x17,%edx\n\t" \
+ "movl $" STR(USER_DS) ",%edx\n\t" \
"mov %dx,%fs\n\t"
/*
@@ -47,7 +51,7 @@
"pushl %eax\n\t" \
"pushl %edx\n\t" \
"pushl %ecx\n\t" \
- "movl $0x10,%edx\n\t" \
+ "movl $" STR(KERNEL_DS) ",%edx\n\t" \
"mov %dx,%ds\n\t" \
"mov %dx,%es\n\t"
diff --git a/include/asm/segment.h b/include/asm/segment.h
index b5fc088..cefbc35 100644
--- a/include/asm/segment.h
+++ b/include/asm/segment.h
@@ -24,17 +24,17 @@ static inline unsigned long get_fs_long(const unsigned long *addr)
static inline void put_fs_byte(char val,char *addr)
{
-__asm__ ("movb %0,%%fs:%1"::"q" (val),"m" (*addr));
+__asm__ ("movb %0,%%fs:%1"::"iq" (val),"m" (*addr));
}
static inline void put_fs_word(short val,short * addr)
{
-__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr));
+__asm__ ("movw %0,%%fs:%1"::"ir" (val),"m" (*addr));
}
static inline void put_fs_long(unsigned long val,unsigned long * addr)
{
-__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
+__asm__ ("movl %0,%%fs:%1"::"ir" (val),"m" (*addr));
}
static inline void memcpy_tofs(void * to, const void * from, unsigned long n)
diff --git a/include/asm/system.h b/include/asm/system.h
index d9d694d..53eacc9 100644
--- a/include/asm/system.h
+++ b/include/asm/system.h
@@ -1,23 +1,25 @@
#ifndef __ASM_SYSTEM_H
#define __ASM_SYSTEM_H
+#include <linux/segment.h>
+
#define move_to_user_mode() \
__asm__ __volatile__ ("movl %%esp,%%eax\n\t" \
- "pushl $0x17\n\t" \
+ "pushl %0\n\t" \
"pushl %%eax\n\t" \
"pushfl\n\t" \
- "pushl $0x0f\n\t" \
+ "pushl %1\n\t" \
"pushl $1f\n\t" \
"iret\n" \
- "1:\tmovl $0x17,%%eax\n\t" \
+ "1:\tmovl %0,%%eax\n\t" \
"mov %%ax,%%ds\n\t" \
"mov %%ax,%%es\n\t" \
"mov %%ax,%%fs\n\t" \
"mov %%ax,%%gs" \
- :::"ax")
+ ::"i" (USER_DS), "i" (USER_CS):"ax")
-#define sti() __asm__ __volatile__ ("sti"::)
-#define cli() __asm__ __volatile__ ("cli"::)
+#define sti() __asm__ __volatile__ ("sti":::"memory")
+#define cli() __asm__ __volatile__ ("cli":::"memory")
#define nop() __asm__ __volatile__ ("nop"::)
extern inline int tas(char * m)
@@ -29,22 +31,22 @@ extern inline int tas(char * m)
}
#define save_flags(x) \
-__asm__ __volatile__("pushfl ; popl %0":"=r" (x))
+__asm__ __volatile__("pushfl ; popl %0":"=r" (x)::"memory")
#define restore_flags(x) \
-__asm__ __volatile__("pushl %0 ; popfl"::"r" (x))
+__asm__ __volatile__("pushl %0 ; popfl"::"r" (x):"memory")
-#define iret() __asm__ __volatile__ ("iret"::)
+#define iret() __asm__ __volatile__ ("iret":::"memory")
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
- "movw %0,%%dx\n\t" \
- "movl %%eax,%1\n\t" \
- "movl %%edx,%2" \
- :: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
- "m" (*((char *) (gate_addr))), \
- "m" (*(4+(char *) (gate_addr))), \
- "d" ((char *) (addr)),"a" (0x00080000) \
+ "movw %2,%%dx\n\t" \
+ "movl %%eax,%0\n\t" \
+ "movl %%edx,%1" \
+ :"=m" (*((long *) (gate_addr))), \
+ "=m" (*(1+(long *) (gate_addr))) \
+ :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
+ "d" ((char *) (addr)),"a" (KERNEL_CS << 16) \
:"ax","dx")
#define set_intr_gate(n,addr) \
@@ -56,6 +58,9 @@ __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
+#define set_call_gate(a,addr) \
+ _set_gate(a,12,3,addr)
+
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
*(gate_addr) = ((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
diff --git a/include/linux/config.h b/include/linux/config.h
index 3d967ff..b21ca68 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -19,6 +19,10 @@
#define UTS_MACHINE "i386" /* hardware type */
#endif
+#ifndef UTS_DOMAINNAME
+#define UTS_DOMAINNAME "(none)" /* set by setdomainname() */
+#endif
+
/*
* The definitions for UTS_RELEASE and UTS_VERSION are now defined
* in linux/version.h, and should only be used by linux/version.c
diff --git a/include/linux/ddi.h b/include/linux/ddi.h
new file mode 100644
index 0000000..a136b2a
--- /dev/null
+++ b/include/linux/ddi.h
@@ -0,0 +1,79 @@
+/*
+ * ddi.h Define the structure for linking in I/O drivers into the
+ * operating system kernel. This method is currently only
+ * used by NET layer drivers, but it will be expanded into
+ * a link methos for ALL kernel-resident device drivers.
+ *
+ * Version: @(#)ddi.h 1.0.2 04/22/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_DDI_H
+#define _LINUX_DDI_H
+
+
+/* DDI control block flags. */
+#define DDI_FREADY 0x10000000 /* device is initialized */
+#define DDI_FPRESENT 0x20000000 /* device hardware is present */
+#define DDI_FBLKDEV 0x00000001 /* device has a BLK spec. file */
+#define DDI_FCHRDEV 0x00000002 /* device has a CHR spec. file */
+
+/* Various constants. */
+#define DDI_MAXNAME 16 /* length of a DDI ID string */
+
+
+/* This structure is used to set up a DDI driver. */
+struct ddconf {
+ int ioaddr; /* main I/O (port) address */
+ int ioaux; /* auxiliary I/O (HD, AST) */
+ int irq; /* IRQ channel */
+ int dma; /* DMA channel to use */
+ unsigned long memsize; /* size of onboard memory */
+ unsigned long memaddr; /* base address of memory */
+};
+
+
+/* The DDI device control block. */
+struct ddi_device {
+ char *title; /* title of the driver */
+ char name[DDI_MAXNAME]; /* unit name of the I/O driver */
+ short int unit; /* unit number of this driver */
+ short int nunits; /* number of units in driver */
+ int (*init)(struct ddi_device *); /* initialization func */
+ int (*handler)(int, ...); /* command handler */
+ short int major; /* driver major dev number */
+ short int minor; /* driver minor dev number */
+ unsigned long flags; /* various flags */
+ struct ddconf config; /* driver HW setup */
+};
+
+
+/* This structure is used to set up networking protocols. */
+struct ddi_proto {
+ char *name; /* protocol name */
+ void (*init)(struct ddi_proto *); /* initialization func */
+};
+
+
+/* This structure is used to link a STREAMS interface. */
+struct iflink {
+ char id[DDI_MAXNAME]; /* DDI ID string */
+ char stream[DDI_MAXNAME]; /* STREAMS interface name */
+ int family; /* address (protocol) family */
+ unsigned int flags; /* any flags needed (unused) */
+};
+
+
+/* DDI control requests. */
+#define DDIOCSDBG 0x9000 /* set DDI debug level */
+#define DDIOCGNAME 0x9001 /* get DDI ID name */
+#define DDIOCGCONF 0x9002 /* get DDI HW config */
+#define DDIOCSCONF 0x9003 /* set DDI HW config */
+
+
+/* DDI global functions. */
+extern void ddi_init(void);
+extern struct ddi_device *ddi_map(const char *id);
+
+
+#endif /* _LINUX_DDI_H */
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 13a4d47..4f30d57 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -28,14 +28,16 @@
/*
* The second extended file system version
*/
-#define EXT2FS_VERSION "0.3, 93/04/22"
+#define EXT2FS_DATE "93/06/06"
+#define EXT2FS_VERSION "0.3a"
/*
* Special inodes numbers
*/
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
-#define EXT2_ACL_INO 3 /* ACL inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_FIRST_INO 11 /* First non reserved inode */
/*
@@ -55,14 +57,14 @@
#define EXT2_MIN_BLOCK_SIZE 1024
#define EXT2_MAX_BLOCK_SIZE 4096
#define EXT2_MIN_BLOCK_LOG_SIZE 10
-#ifdef KERNEL
+#ifdef __KERNEL__
# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
#else
# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#endif
#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (unsigned long))
-#ifdef KERNEL
+#ifdef __KERNEL__
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_log_block_size + 10)
#else
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
@@ -75,7 +77,7 @@
#define EXT2_MIN_FRAG_SIZE 1024
#define EXT2_MAX_FRAG_SIZE 1024
#define EXT2_MIN_FRAG_LOG_SIZE 10
-#ifdef KERNEL
+#ifdef __KERNEL__
# define EXT2_FRAG_SIZE(s) ((s)->u.ext2_sb.s_frag_size)
# define EXT2_FRAGS_PER_BLOCK(s) ((s)->u.ext2_sb.s_frags_per_block)
#else
@@ -86,22 +88,21 @@
/*
* ACL structures
*/
-
struct ext2_acl_header /* Header of Access Control Lists */
{
+ unsigned long aclh_size;
unsigned long aclh_file_count;
unsigned long aclh_acle_count;
unsigned long aclh_first_acle;
- unsigned long aclh_reserved;
};
struct ext2_acl_entry /* Access Control List Entry */
{
+ unsigned long acle_size;
unsigned short acle_perms; /* Access permissions */
unsigned short acle_type; /* Type of entry */
unsigned short acle_tag; /* User or group identity */
unsigned short acle_pad1;
- unsigned long acle_reserved;
unsigned long acle_next; /* Pointer on next entry for the */
/* same inode or on next free entry */
};
@@ -133,7 +134,7 @@ struct ext2_group_desc
/*
* Macro-instructions used to manage group descriptors
*/
-#ifdef KERNEL
+#ifdef __KERNEL__
# define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group)
# define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block)
# define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group)
@@ -259,6 +260,9 @@ extern int ext2_check_dir_entry (char *, struct inode *,
extern int ext2_read (struct inode *, struct file *, char *, int);
extern int ext2_write (struct inode *, struct file *, char *, int);
+/* fsync.c */
+extern int ext2_sync_file(struct inode *, struct file *);
+
/* ialloc.c */
extern struct inode * ext2_new_inode (const struct inode *, int);
extern void ext2_free_inode (struct inode *);
@@ -272,11 +276,13 @@ extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
extern void ext2_put_super (struct super_block *);
extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *);
extern struct super_block * ext2_read_super (struct super_block *,void *,int);
extern void ext2_read_inode (struct inode *);
extern void ext2_write_inode (struct inode *);
extern void ext2_put_inode (struct inode *);
extern void ext2_statfs (struct super_block *, struct statfs *);
+extern int ext2_sync_inode(struct inode *);
/* ioctl.c */
extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index 81c5249..59870b1 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -1,7 +1,7 @@
#ifndef _EXT2_FS_SB
#define _EXT2_FS_SB
-#define EXT2_MAX_GROUP_DESC 4
+#define EXT2_MAX_GROUP_DESC 8
#define EXT2_MAX_GROUP_LOADED 8
/*
diff --git a/include/linux/ext_fs.h b/include/linux/ext_fs.h
index 4f5c6ad..b288492 100644
--- a/include/linux/ext_fs.h
+++ b/include/linux/ext_fs.h
@@ -94,6 +94,8 @@ extern void ext_read_inode(struct inode *);
extern void ext_write_inode(struct inode *);
extern void ext_put_inode(struct inode *);
extern void ext_statfs(struct super_block *, struct statfs *);
+extern int ext_sync_inode(struct inode *);
+extern int ext_sync_file(struct inode *, struct file *);
extern int ext_lseek(struct inode *, struct file *, off_t, int);
extern int ext_read(struct inode *, struct file *, char *, int);
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index e7ec6c0..c390062 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -20,7 +20,7 @@
#define F_SETFD 2 /* set f_flags */
#define F_GETFL 3 /* more flags (cloexec) */
#define F_SETFL 4
-#define F_GETLK 5 /* not implemented */
+#define F_GETLK 5
#define F_SETLK 6
#define F_SETLKW 7
diff --git a/include/linux/fdreg.h b/include/linux/fdreg.h
index 6d172b6..de1e9f6 100644
--- a/include/linux/fdreg.h
+++ b/include/linux/fdreg.h
@@ -6,12 +6,6 @@
* Handbook", Sanches and Canton.
*/
-extern int ticks_to_floppy_on(unsigned int nr);
-extern void floppy_on(unsigned int nr);
-extern void floppy_off(unsigned int nr);
-extern void floppy_select(unsigned int nr);
-extern void floppy_deselect(unsigned int nr);
-
/* Fd controller regs. S&C, about page 340 */
#define FD_STATUS 0x3f4
#define FD_DATA 0x3f5
diff --git a/include/linux/fs.h b/include/linux/fs.h
index add94da..6a3d930 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -21,12 +21,13 @@
* recompiled to take full advantage of the new limits..
*/
#undef NR_OPEN
-#define NR_OPEN 256 /* don't change - fd_set etc depend on this */
+#define NR_OPEN 256
-#define NR_INODE 256 /* this should be bigger than NR_FILE */
-#define NR_FILE 128 /* this can well be larger on a larger system */
-#define NR_SUPER 16
+#define NR_INODE 2048 /* this should be bigger than NR_FILE */
+#define NR_FILE 1024 /* this can well be larger on a larger system */
+#define NR_SUPER 32
#define NR_HASH 997
+#define NR_IHASH 131
#define NR_FILE_LOCKS 32
#define BLOCK_SIZE 1024
#define BLOCK_SIZE_BITS 10
@@ -146,6 +147,7 @@ struct buffer_head {
unsigned char b_uptodate;
unsigned char b_dirt; /* 0-clean,1-dirty */
unsigned char b_lock; /* 0 - ok, 1 -locked */
+ unsigned char b_req; /* 0 if the buffer has been invalidated */
struct wait_queue * b_wait;
struct buffer_head * b_prev; /* doubly linked list of hash-queue */
struct buffer_head * b_next;
@@ -213,6 +215,7 @@ struct file {
unsigned short f_flags;
unsigned short f_count;
unsigned short f_reada;
+ struct file *f_next, *f_prev;
struct inode * f_inode;
struct file_operations * f_op;
};
@@ -298,6 +301,7 @@ struct super_operations {
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
void (*statfs) (struct super_block *, struct statfs *);
+ int (*remount_fs) (struct super_block *, int *);
};
struct file_system_type {
@@ -325,8 +329,10 @@ extern struct file_system_type *get_fs_type(char *name);
extern int fs_may_mount(dev_t dev);
extern int fs_may_umount(dev_t dev, struct inode * mount_root);
+extern int fs_may_remount_ro(dev_t dev);
-extern struct file file_table[NR_FILE];
+extern struct file *first_file;
+extern int nr_files;
extern struct super_block super_block[NR_SUPER];
extern void grow_buffers(int size);
@@ -340,11 +346,9 @@ extern void check_disk_change(dev_t dev);
extern void invalidate_inodes(dev_t dev);
extern void invalidate_buffers(dev_t dev);
extern int floppy_change(struct buffer_head * first_block);
-extern int ticks_to_floppy_on(unsigned int dev);
-extern void floppy_on(unsigned int dev);
-extern void floppy_off(unsigned int dev);
extern void sync_inodes(dev_t dev);
extern void sync_dev(dev_t dev);
+extern int fsync_dev(dev_t dev);
extern void sync_supers(dev_t dev);
extern int bmap(struct inode * inode,int block);
extern int notify_change(int flags, struct inode * inode);
@@ -357,6 +361,7 @@ extern int do_mknod(const char * filename, int mode, dev_t dev);
extern void iput(struct inode * inode);
extern struct inode * iget(struct super_block * sb,int nr);
extern struct inode * get_empty_inode(void);
+extern void insert_inode_hash(struct inode *);
extern void clear_inode(struct inode *);
extern struct inode * get_pipe_inode(void);
extern struct file * get_empty_filp(void);
@@ -381,4 +386,7 @@ extern int read_ahead[];
extern int char_write(struct inode *, struct file *, char *, int);
extern int block_write(struct inode *, struct file *, char *, int);
+extern int block_fsync(struct inode *, struct file *);
+extern int file_fsync(struct inode *, struct file *);
+
#endif
diff --git a/include/linux/icmp.h b/include/linux/icmp.h
new file mode 100644
index 0000000..ca1e8c6
--- /dev/null
+++ b/include/linux/icmp.h
@@ -0,0 +1,81 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP protocol.
+ *
+ * Version: @(#)icmp.h 1.0.3 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_ICMP_H
+#define _LINUX_ICMP_H
+
+#define ICMP_ECHOREPLY 0 /* Echo Reply */
+#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
+#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
+#define ICMP_REDIRECT 5 /* Redirect (change route) */
+#define ICMP_ECHO 8 /* Echo Request */
+#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
+#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
+#define ICMP_TIMESTAMP 13 /* Timestamp Request */
+#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
+#define ICMP_INFO_REQUEST 15 /* Information Request */
+#define ICMP_INFO_REPLY 16 /* Information Reply */
+#define ICMP_ADDRESS 17 /* Address Mask Request */
+#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
+
+
+/* Codes for UNREACH. */
+#define ICMP_NET_UNREACH 0 /* Network Unreachable */
+#define ICMP_HOST_UNREACH 1 /* Host Unreachable */
+#define ICMP_PROT_UNREACH 2 /* Protocol Unreachable */
+#define ICMP_PORT_UNREACH 3 /* Port Unreachable */
+#define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
+#define ICMP_SR_FAILED 5 /* Source Route failed */
+#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
+
+/* Codes for REDIRECT. */
+#define ICMP_REDIR_NET 0 /* Redirect Net */
+#define ICMP_REDIR_HOST 1 /* Redirect Host */
+#define ICMP_REDIR_NETTOS 2 /* Redirect Net for TOS */
+#define ICMP_REDIR_HOSTTOS 3 /* Redirect Host for TOS */
+
+/* Codes for TIME_EXCEEDED. */
+#define ICMP_EXC_TTL 0 /* TTL count exceeded */
+#define ICMP_EXC_FRAGTIME 1 /* Fragment Reass time exceeded */
+
+
+struct icmphdr {
+ 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;
+};
+
+
+#endif /* _LINUX_ICMP_H */
diff --git a/include/linux/if.h b/include/linux/if.h
new file mode 100644
index 0000000..909bd02
--- /dev/null
+++ b/include/linux/if.h
@@ -0,0 +1,147 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the INET interface module.
+ *
+ * Version: @(#)if.h 1.0.2 04/18/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_H
+#define _LINUX_IF_H
+
+#include <linux/types.h> /* for "caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+
+
+/* Structure defining a queue for a network interface. */
+struct ifnet {
+ char *if_name; /* name, e.g. ``en'' or ``lo'' */
+ short if_unit; /* sub-unit for device driver */
+ short if_mtu; /* maximum transmission unit */
+ short if_flags; /* up/down, broadcast, etc. */
+ short if_timer; /* time 'til if_watchdog called */
+ int if_metric; /* routing metric (not used) */
+ struct ifaddr *if_addrlist; /* linked list of addrs per if */
+ struct ifqueue {
+#ifdef not_yet_in_linux
+ struct mbuf *ifq_head;
+ struct mbuf *ifq_tail;
+ int ifq_len;
+ int ifq_maxlen;
+ int ifq_drops;
+#endif
+ } if_snd; /* output queue */
+
+ /* Procedure handles. */
+ int (*if_init)(); /* init routine */
+ int (*if_output)(); /* output routine */
+ int (*if_ioctl)(); /* ioctl routine */
+ int (*if_reset)(); /* bus reset routine */
+ int (*if_watchdog)(); /* timer routine */
+
+ /* Generic interface statistics. */
+ int if_ipackets; /* packets recv'd on interface */
+ int if_ierrors; /* input errors on interface */
+ int if_opackets; /* packets sent on interface */
+ int if_oerrors; /* output errors on interface */
+ int if_collisions; /* collisions on CSMA i'faces */
+
+ /* Linked list: pointer to next interface. */
+ struct ifnet *if_next;
+};
+
+/* Standard interface flags. */
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_LOOPBACK 0x8 /* is a loopback net */
+#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
+#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
+#define IFF_RUNNING 0x40 /* resources allocated */
+#define IFF_NOARP 0x80 /* no ARP protocol */
+
+/* These are not yet used: */
+#define IFF_PROMISC 0x100 /* recve all packets */
+#define IFF_ALLMULTI 0x200 /* recve all multicast packets */
+
+
+/*
+ * The ifaddr structure contains information about one address
+ * of an interface. They are maintained by the different address
+ * families, are allocated and attached when an address is set,
+ * and are linked together so all addresses for an interface can
+ * be located.
+ */
+struct ifaddr {
+ struct sockaddr ifa_addr; /* address of interface */
+ union {
+ struct sockaddr ifu_broadaddr;
+ struct sockaddr ifu_dstaddr;
+ } ifa_ifu;
+ struct iface *ifa_ifp; /* back-pointer to interface */
+ struct ifaddr *ifa_next; /* next address for interface */
+};
+#define ifa_broadaddr ifa_ifu.ifu_broadaddr /* broadcast address */
+#define ifa_dstaddr ifa_ifu.ifu_dstaddr /* other end of link */
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+struct ifreq {
+#define IFNAMSIZ 16
+ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ short ifru_flags;
+ int ifru_metric;
+ int ifru_mtu;
+ caddr_t ifru_data;
+ } ifr_ifru;
+};
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * must know all networks accessible).
+ */
+struct ifconf {
+ int ifc_len; /* size of buffer */
+ union {
+ caddr_t ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures */
+
+
+/* BSD UNIX expects to find these here, so here we go: */
+#include <linux/if_arp.h>
+#include <linux/route.h>
+
+#endif /* _NET_IF_H */
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
new file mode 100644
index 0000000..1fcbed9
--- /dev/null
+++ b/include/linux/if_arp.h
@@ -0,0 +1,83 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the ARP (RFC 826) protocol.
+ *
+ * Version: @(#)if_arp.h 1.0.1 04/16/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source.
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_ARP_H
+#define _LINUX_IF_ARP_H
+
+/* ARP protocol HARDWARE identifiers. */
+#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
+#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+#define ARPHRD_EETHER 2 /* Experimental Ethernet */
+#define ARPHRD_AX25 3 /* AX.25 Level 2 */
+#define ARPHRD_PRONET 4 /* PROnet token ring */
+#define ARPHRD_CHAOS 5 /* Chaosnet */
+#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet- huh? */
+#define ARPHRD_ARCNET 7 /* ARCnet */
+#define ARPHRD_APPLETLK 8 /* APPLEtalk */
+
+/* ARP protocol opcodes. */
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+#define ARPOP_RREQUEST 3 /* RARP request */
+#define ARPOP_RREPLY 4 /* RARP reply */
+
+
+/*
+ * Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. ARP packets are variable
+ * in size; the arphdr structure defines the fixed-length portion.
+ * Protocol type values are the same as those for 10 Mb/s Ethernet.
+ * It is followed by the variable-sized fields ar_sha, arp_spa,
+ * arp_tha and arp_tpa in that order, according to the lengths
+ * specified. Field names used correspond to RFC 826.
+ */
+struct arphdr {
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /* The rest is variable in size, according to the sizes above. */
+#if 0
+ unsigned char ar_sha[]; /* sender hardware address */
+ unsigned char ar_spa[]; /* sender protocol address */
+ unsigned char ar_tha[]; /* target hardware address */
+ unsigned char ar_tpa[]; /* target protocol address */
+#endif /* not actually included! */
+};
+
+
+/* ARP ioctl request. */
+struct arpreq {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+};
+
+/* ARP Flag values. */
+#define ATF_INUSE 0x01 /* entry in use */
+#define ATF_COM 0x02 /* completed entry (ha valid) */
+#define ATF_PERM 0x04 /* permanent entry */
+#define ATF_PUBL 0x08 /* publish entry */
+#define ATF_USETRAILERS 0x10 /* has requested trailers */
+
+
+#endif /* _LINUX_IF_ARP_H */
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
new file mode 100644
index 0000000..2efcfee
--- /dev/null
+++ b/include/linux/if_ether.h
@@ -0,0 +1,88 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Ethernet IEE 802.3 interface.
+ *
+ * Version: @(#)if_ether.h 1.0.1 03/15/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+
+/* IEEE 802.3 Ethernet magic constants. */
+#define ETH_ALEN 6 /* #bytes in eth addr */
+#define ETH_HLEN 14 /* #bytes in eth header */
+#define ETH_ZLEN 64 /* min #bytes in frame */
+#define ETH_FLEN 1536 /* max #bytes in frame */
+#define ETH_DLEN (ETH_FLEN - ETH_HLEN) /* max #bytes of data */
+
+/* These are the defined Ethernet Protocol ID's. */
+#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
+#define ETH_P_ECHO 0x0200 /* Ethernet Echo packet */
+#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_RARP 0x0835 /* Reverse Addr Res packet */
+
+/* Define the Ethernet Broadcast Address (48 bits set to "1"). */
+#define ETH_A_BCAST "\377\377\377\377\377\377"
+
+/* This is an Ethernet frame header. */
+struct ethhdr {
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+/* This is the complete Ethernet frame. */
+struct ethframe {
+ struct ethhdr f_hdr; /* frame header */
+ char f_data[ETH_DLEN]; /* frame data (variable)*/
+};
+
+
+/* Receiver modes */
+#define ETH_MODE_MONITOR 1 /* Monitor mode - no receive */
+#define ETH_MODE_PHYS 2 /* Physical address receive only */
+#define ETH_MODE_BCAST 3 /* Broadcast receive + mode 2 */
+#define ETH_MODE_MCAST 4 /* Multicast receive + mode 3 */
+#define ETH_MODE_PROMISC 5 /* Promiscuous mode - receive all */
+
+
+/* Ethernet statistics collection data. */
+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 multicast; /* multicast packets received */
+ int collisions;
+
+ /* detailed rx_errors: */
+ int rx_length_errors;
+ int rx_over_errors; /* receiver ring buff overflow */
+ int rx_crc_errors; /* recved pkt with crc error */
+ int rx_frame_errors; /* recv'd frame alignment error */
+ int rx_fifo_errors; /* recv'r 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;
+};
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/include/linux/in.h b/include/linux/in.h
new file mode 100644
index 0000000..12e3865
--- /dev/null
+++ b/include/linux/in.h
@@ -0,0 +1,171 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the Internet Protocol.
+ *
+ * Version: @(#)in.h 1.0.1 04/21/93
+ *
+ * Authors: Original taken from the GNU Project <netinet/in.h> file.
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IN_H
+#define _LINUX_IN_H
+
+
+/* Standard well-defined IP protocols. */
+enum {
+ IPPROTO_IP = 0, /* Dummy protocol for TCP */
+ IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
+ IPPROTO_GGP = 2, /* Gateway Protocol (deprecated) */
+ IPPROTO_TCP = 6, /* Transmission Control Protocol */
+ IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
+ IPPROTO_PUP = 12, /* PUP protocol */
+ IPPROTO_UDP = 17, /* User Datagram Protocol */
+ IPPROTO_IDP = 22, /* XNS IDP protocol */
+
+ IPPROTO_RAW = 255, /* Raw IP packets */
+ IPPROTO_MAX
+};
+
+
+/* Internet address. */
+struct in_addr {
+ unsigned long int s_addr;
+};
+
+
+/* Structure describing an Internet (IP) socket address. */
+#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
+struct sockaddr_in {
+ short int sin_family; /* Address family */
+ unsigned short int sin_port; /* Port number */
+ struct in_addr sin_addr; /* Internet address */
+
+ /* Pad to size of `struct sockaddr'. */
+ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
+ sizeof(unsigned short int) - sizeof(struct in_addr)];
+};
+#define sin_zero __pad /* for BSD UNIX comp. -FvK */
+
+
+/*
+ * Definitions of the bits in an Internet address integer.
+ * On subnets, host and network parts are found according
+ * to the subnet mask, not these masks.
+ */
+#define IN_CLASSA(a) ((((long int) (a)) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET)
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(a) ((((long int) (a)) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET)
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(a) ((((long int) (a)) & 0xc0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET)
+
+#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) = 0xe0000000)
+#define IN_MULTICAST(a) IN_CLASSD(a)
+
+#define IN_EXPERIMENTAL(a) ((((long int) (a)) & 0xe0000000) = 0xe0000000)
+#define IN_BADCLASS(a) ((((long int) (a)) & 0xf0000000) = 0xf0000000)
+
+/* Address to accept any incoming messages. */
+#define INADDR_ANY ((unsigned long int) 0x00000000)
+
+/* Address to send to all hosts. */
+#define INADDR_BROADCAST ((unsigned long int) 0xffffffff)
+
+/* Address indicating an error return. */
+#define INADDR_NONE 0xffffffff
+
+/* Network number for local host loopback. */
+#define IN_LOOPBACKNET 127
+
+/* Address to loopback in software to local host. */
+#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
+
+
+/*
+ * Options for use with `getsockopt' and `setsockopt' at
+ * the IP level. LINUX does not yet have the IP_OPTIONS
+ * option (grin), so we undefine it for now.- HJ && FvK
+ */
+#if 0
+# define IP_OPTIONS 1 /* IP per-packet options */
+#endif
+#define IP_HDRINCL 2 /* raw packet header option */
+
+
+/* Linux Internet number representation function declarations. */
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+extern unsigned long int ntohl(unsigned long int);
+extern unsigned short int ntohs(unsigned short int);
+extern unsigned long int htonl(unsigned long int);
+extern unsigned short int htons(unsigned short int);
+
+static __inline__ unsigned long int
+__ntohl(unsigned long int x)
+{
+ register unsigned long int tmp __asm__ ("ax") = x;
+ __asm__ __volatile__ ("xchgb %%al,%%ah\n\t" /* swap lower bytes */
+ "rorl $16,%%eax\n\t" /* swap words */
+ "xchgb %%al,%%ah\n\t" /* swap higher bytes */
+ : "=a" (tmp) : "a" (tmp) );
+ return(tmp);
+}
+
+static __inline__ unsigned short int
+__ntohs(unsigned short int x)
+{
+ register unsigned short int tmp __asm__ ("ax") = x;
+ __asm__ __volatile__ ("xchgb %%al,%%ah\n\t" /* swap bytes */
+ : "=a" (tmp) : "a" (tmp));
+ return(tmp);
+}
+
+static __inline__ unsigned long int
+__htonl(unsigned long int x)
+{
+ register unsigned long int tmp __asm__ ("ax") = x;
+ __asm__ __volatile__ ("xchgb %%al,%%ah\n\t" /* swap lower bytes */
+ "rorl $16,%%eax\n\t" /* swap words */
+ "xchgb %%al,%%ah\n\t" /* swap higher bytes */
+ : "=a" (tmp) : "a" (tmp));
+ return(tmp);
+}
+
+static __inline__ unsigned short int
+__htons(unsigned short int x)
+{
+ register unsigned short int tmp __asm__ ("ax") = x;
+ __asm__ __volatile__ ("xchgb %%al,%%ah\n\t" /* swap bytes */
+ : "=a" (tmp) : "a" (tmp));
+ return(tmp);
+}
+
+#ifdef __OPTIMIZE__
+# define ntohl(x) __ntohl((x))
+# define ntohs(x) __ntohs((x))
+# define htonl(x) __htonl((x))
+# define htons(x) __htons((x))
+#endif
+
+#endif /* _LINUX_IN_H */
diff --git a/include/linux/ip.h b/include/linux/ip.h
new file mode 100644
index 0000000..26d4a59
--- /dev/null
+++ b/include/linux/ip.h
@@ -0,0 +1,81 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP protocol.
+ *
+ * Version: @(#)ip.h 1.0.2 04/28/93
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IP_H
+#define _LINUX_IP_H
+
+
+#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
+
+
+struct timestamp {
+ unsigned char len;
+ unsigned char ptr;
+ union {
+ unsigned char flags:4,
+ overflow:4;
+ unsigned char full_char;
+ } x;
+ unsigned long data[9];
+};
+
+
+#define MAX_ROUTE 16
+
+struct route {
+ char route_size;
+ char pointer;
+ unsigned long route[MAX_ROUTE];
+};
+
+
+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 iphdr {
+ 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. */
+};
+
+
+#endif /* _LINUX_IP_H */
diff --git a/include/linux/ipc.h b/include/linux/ipc.h
new file mode 100644
index 0000000..bf98798
--- /dev/null
+++ b/include/linux/ipc.h
@@ -0,0 +1,65 @@
+#ifndef _LINUX_IPC_H
+#define _LINUX_IPC_H
+#include <linux/types.h>
+
+typedef int key_t; /* should go in <types.h> type for IPC key */
+#define IPC_PRIVATE ((key_t) 0)
+
+struct ipc_perm
+{
+ key_t key;
+ ushort uid; /* owner euid and egid */
+ ushort gid;
+ ushort cuid; /* creator euid and egid */
+ ushort cgid;
+ ushort mode; /* access modes see mode flags below */
+ ushort seq; /* sequence number */
+};
+
+
+/* resource get request flags */
+#define IPC_CREAT 00001000 /* create if key is nonexistent */
+#define IPC_EXCL 00002000 /* fail if key exists */
+#define IPC_NOWAIT 00004000 /* return error on wait */
+
+
+/*
+ * Control commands used with semctl, msgctl and shmctl
+ * see also specific commands in sem.h, msg.h and shm.h
+ */
+#define IPC_RMID 0 /* remove resource */
+#define IPC_SET 1 /* set ipc_perm options */
+#define IPC_STAT 2 /* get ipc_perm options */
+#define IPC_INFO 3 /* see ipcs */
+
+#ifdef __KERNEL__
+
+/* special shmsegs[id], msgque[id] or semary[id] values */
+#define IPC_UNUSED ((void *) -1)
+#define IPC_NOID ((void *) -2) /* being allocated/destroyed */
+
+/*
+ * These are used to wrap system calls. See ipc/util.c, libipc.c
+ */
+struct ipc_kludge {
+ struct msgbuf *msgp;
+ long msgtyp;
+};
+
+#define SEMOP 1
+#define SEMGET 2
+#define SEMCTL 3
+#define MSGSND 11
+#define MSGRCV 12
+#define MSGGET 13
+#define MSGCTL 14
+#define SHMAT 21
+#define SHMDT 22
+#define SHMGET 23
+#define SHMCTL 24
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_IPC_H */
+
+
diff --git a/include/linux/kd.h b/include/linux/kd.h
index c114f77..9ef695a 100644
--- a/include/linux/kd.h
+++ b/include/linux/kd.h
@@ -166,7 +166,7 @@ typedef char scrnmap_t;
struct kbentry {
u_char kb_table;
u_char kb_index;
- u_char kb_value;
+ u_short kb_value;
};
#define K_NORMTAB 0x00
#define K_SHIFTTAB 0x01
diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h
index da2a785..01d6de9 100644
--- a/include/linux/keyboard.h
+++ b/include/linux/keyboard.h
@@ -23,9 +23,11 @@ extern unsigned long kbd_flags;
#define KG_RSHIFT 1
#define KG_LCTRL 2
#define KG_RCTRL 3
-#define KG_ALT 4
-#define KG_ALTGR 5
-#define KG_CAPSLOCK 6
+#define KG_LALT 4
+#define KG_RALT 5 /* doesn't exist, but.. */
+#define KG_LALTGR 6 /* doesn't exist, but.. */
+#define KG_RALTGR 7
+#define KG_CAPSLOCK 8
/*
* "dead" keys - prefix key values that are valid only for the next
@@ -47,7 +49,6 @@ extern unsigned long kbd_prev_dead_keys;
struct kbd_struct {
unsigned long flags;
unsigned long default_flags;
- unsigned char kbd_flags;
};
extern struct kbd_struct kbd_table[];
@@ -131,4 +132,102 @@ extern inline void chg_vc_kbd_flag(struct kbd_struct * kbd, int flag)
kbd->flags ^= 1 << flag;
}
+#define NR_KEYS 112
+#define NR_KEYMAPS 3
+extern const int NR_TYPES;
+extern const unsigned char max_vals[];
+extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
+
+#define KT_LATIN 0 /* we depend on this being zero */
+#define KT_FN 1
+#define KT_SPEC 2
+#define KT_PAD 3
+#define KT_DEAD 4
+#define KT_CONS 5
+#define KT_CUR 6
+#define KT_SHIFT 7
+
+#define K(t,v) (((t)<<8)|(v))
+#define KTYP(x) ((x) >> 8)
+#define KVAL(x) ((x) & 0xff)
+
+#define K_F1 K(KT_FN,0)
+#define K_F2 K(KT_FN,1)
+#define K_F3 K(KT_FN,2)
+#define K_F4 K(KT_FN,3)
+#define K_F5 K(KT_FN,4)
+#define K_F6 K(KT_FN,5)
+#define K_F7 K(KT_FN,6)
+#define K_F8 K(KT_FN,7)
+#define K_F9 K(KT_FN,8)
+#define K_F10 K(KT_FN,9)
+#define K_F11 K(KT_FN,10)
+#define K_F12 K(KT_FN,11)
+#define K_F13 K(KT_FN,12)
+#define K_F14 K(KT_FN,13)
+#define K_F15 K(KT_FN,14)
+#define K_F16 K(KT_FN,15)
+#define K_F17 K(KT_FN,16)
+#define K_F18 K(KT_FN,17)
+#define K_F19 K(KT_FN,18)
+#define K_F20 K(KT_FN,19)
+#define K_FIND K(KT_FN,20)
+#define K_INSERT K(KT_FN,21)
+#define K_REMOVE K(KT_FN,22)
+#define K_SELECT K(KT_FN,23)
+#define K_PGUP K(KT_FN,24)
+#define K_PGDN K(KT_FN,25)
+
+#define K_HOLE K(KT_SPEC,0)
+#define K_ENTER K(KT_SPEC,1)
+#define K_SH_REGS K(KT_SPEC,2)
+#define K_SH_MEM K(KT_SPEC,3)
+#define K_SH_STAT K(KT_SPEC,4)
+#define K_BREAK K(KT_SPEC,5)
+#define K_CONS K(KT_SPEC,6)
+#define K_CAPS K(KT_SPEC,7)
+#define K_NUM K(KT_SPEC,8)
+#define K_HOLD K(KT_SPEC,9)
+
+#define K_P0 K(KT_PAD,0)
+#define K_P1 K(KT_PAD,1)
+#define K_P2 K(KT_PAD,2)
+#define K_P3 K(KT_PAD,3)
+#define K_P4 K(KT_PAD,4)
+#define K_P5 K(KT_PAD,5)
+#define K_P6 K(KT_PAD,6)
+#define K_P7 K(KT_PAD,7)
+#define K_P8 K(KT_PAD,8)
+#define K_P9 K(KT_PAD,9)
+#define K_PPLUS K(KT_PAD,10) /* key-pad plus */
+#define K_PMINUS K(KT_PAD,11) /* key-pad minus */
+#define K_PSTAR K(KT_PAD,12) /* key-pad asterisk (star) */
+#define K_PSLASH K(KT_PAD,13) /* key-pad slash */
+#define K_PENTER K(KT_PAD,14) /* key-pad enter */
+#define K_PCOMMA K(KT_PAD,15) /* key-pad comma: kludge... */
+#define K_PDOT K(KT_PAD,16) /* key-pad dot (period): kludge... */
+
+#define K_DGRAVE K(KT_DEAD,0)
+#define K_DACUTE K(KT_DEAD,1)
+#define K_DCIRCM K(KT_DEAD,2)
+#define K_DTILDE K(KT_DEAD,3)
+#define K_DDIERE K(KT_DEAD,4)
+
+#define K_DOWN K(KT_CUR,0)
+#define K_LEFT K(KT_CUR,1)
+#define K_RIGHT K(KT_CUR,2)
+#define K_UP K(KT_CUR,3)
+
+#define K_LSHIFT K(KT_SHIFT,KG_LSHIFT)
+#define K_RSHIFT K(KT_SHIFT,KG_RSHIFT)
+#define K_LCTRL K(KT_SHIFT,KG_LCTRL)
+#define K_RCTRL K(KT_SHIFT,KG_RCTRL)
+#define K_LALT K(KT_SHIFT,KG_LALT)
+#define K_RALT K(KT_SHIFT,KG_RALT)
+#define K_LALTGR K(KT_SHIFT,KG_LALTGR)
+#define K_RALTGR K(KT_SHIFT,KG_RALTGR)
+
+#define K_ALT K_LALT
+#define K_ALTGR K_RALTGR
+
#endif
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index a48badb..9bc56b1 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -104,6 +104,8 @@ extern void minix_read_inode(struct inode *);
extern void minix_write_inode(struct inode *);
extern void minix_put_inode(struct inode *);
extern void minix_statfs(struct super_block *, struct statfs *);
+extern int minix_sync_inode(struct inode *);
+extern int minix_sync_file(struct inode *, struct file *);
extern struct inode_operations minix_file_inode_operations;
extern struct inode_operations minix_dir_inode_operations;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index f207a6c..17923bd 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -106,7 +106,6 @@ extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
extern void mem_init(unsigned long low_start_mem,
unsigned long start_mem, unsigned long end_mem);
extern void show_mem(void);
-extern void do_page_fault(unsigned long *esp, unsigned long error_code);
extern void oom(struct task_struct * task);
extern void si_meminfo(struct sysinfo * val);
@@ -153,4 +152,9 @@ extern unsigned short * mem_map;
#define GFP_USER 0x02
#define GFP_KERNEL 0x03
+
+/* vm_ops not present page codes */
+#define SHM_SWP_TYPE 0x41
+extern void shm_no_page (ulong *);
+
#endif
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index d32841c..8531024 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -133,7 +133,7 @@ extern int msdos_add_cluster(struct inode *inode);
extern int date_dos2unix(unsigned short time,unsigned short date);
extern void date_unix2dos(int unix_date,unsigned short *time,
unsigned short *date);
-extern int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh,
+extern int msdos_get_entry(struct inode *dir,off_t *pos,struct buffer_head **bh,
struct msdos_dir_entry **de);
extern int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh,
struct msdos_dir_entry **res_de,int *ino);
diff --git a/include/linux/msg.h b/include/linux/msg.h
new file mode 100644
index 0000000..29c4676
--- /dev/null
+++ b/include/linux/msg.h
@@ -0,0 +1,73 @@
+#ifndef _LINUX_MSG_H
+#define _LINUX_MSG_H
+#include <linux/ipc.h>
+
+/* msgrcv options */
+#define MSG_NOERROR 010000 /* no error if message is too big */
+#define MSG_EXCEPT 020000 /* recv any msg except of specified type.*/
+
+
+/* one msg structure for each message */
+struct msg {
+ struct msg *msg_next; /* next message on queue */
+ long msg_type;
+ char *msg_spot; /* message text address */
+ short msg_ts; /* message text size */
+};
+
+/* one msqid structure for each queue on the system */
+struct msqid_ds {
+ struct ipc_perm msg_perm;
+ struct msg *msg_first; /* first message on queue */
+ struct msg *msg_last; /* last message in queue */
+ time_t msg_stime; /* last msgsnd time */
+ time_t msg_rtime; /* last msgrcv time */
+ time_t msg_ctime; /* last change time */
+ struct wait_queue *wwait;
+ struct wait_queue *rwait;
+ ushort msg_cbytes; /* current number of bytes on queue */
+ ushort msg_qnum; /* number of messages in queue */
+ ushort msg_qbytes; /* max number of bytes on queue */
+ ushort msg_lspid; /* pid of last msgsnd */
+ ushort msg_lrpid; /* last receive pid */
+};
+
+
+/* message buffer for msgsnd and msgrcv calls */
+struct msgbuf {
+ long mtype; /* type of message */
+ char mtext[1]; /* message text */
+};
+
+
+struct msginfo {
+ int msgpool;
+ int msgmap;
+ int msgmax;
+ int msgmnb;
+ int msgmni;
+ int msgssz;
+ int msgtql;
+ ushort msgseg;
+};
+
+#define MSGMNI 128 /* <= 1K */ /* max # of msg queue identifiers */
+#define MSGMAX 4080 /* <= 4080 */ /* max size of message (bytes) */
+#define MSGMNB 16384 /* ? */ /* default max size of a message queue */
+
+/* unused */
+#define MSGPOOL (MSGMNI*MSGMNB/1024) /* size in kilobytes of message pool */
+#define MSGTQL MSGMNB /* number of system message headers */
+#define MSGMAP MSGMNB /* number of entries in message map */
+#define MSGSSZ 16 /* message segment size */
+#define MSGSEG ((MSGPOOL*1024)/ MSGSSZ) /* max no. of segments */
+
+#ifdef __KERNEL__
+
+/* ipcs ctl commands */
+#define MSG_STAT 11
+#define MSG_INFO 12
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_MSG_H */
diff --git a/include/linux/net.h b/include/linux/net.h
new file mode 100644
index 0000000..c861208
--- /dev/null
+++ b/include/linux/net.h
@@ -0,0 +1,133 @@
+/*
+ * NET An implementation of the SOCKET network access protocol.
+ * This is the master header file for the Linux NET layer,
+ * or, in plain English: the networking handling part of the
+ * kernel.
+ *
+ * Version: @(#)net.h 1.0.3 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_NET_H
+#define _LINUX_NET_H
+
+
+#include <linux/wait.h>
+
+
+#define NSOCKETS 128 /* should be dynamic, later... */
+#define NPROTO 16 /* should be enough for now.. */
+#define SOCKET_MAJOR 16 /* Linux VFS major dev number */
+
+
+#define SYS_SOCKET 1 /* sys_socket(2) */
+#define SYS_BIND 2 /* sys_bind(2) */
+#define SYS_CONNECT 3 /* sys_connect(2) */
+#define SYS_LISTEN 4 /* sys_listen(2) */
+#define SYS_ACCEPT 5 /* sys_accept(2) */
+#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
+#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
+#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
+#define SYS_SEND 9 /* sys_send(2) */
+#define SYS_RECV 10 /* sys_recv(2) */
+#define SYS_SENDTO 11 /* sys_sendto(2) */
+#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
+#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
+#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
+#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
+
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING, /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+
+
+/*
+ * Internel representation of a socket. not all the fields are used by
+ * all configurations:
+ *
+ * server client
+ * conn client connected to server connected to
+ * iconn list of clients -unused-
+ * awaiting connections
+ * wait sleep for clients, sleep for connection,
+ * sleep for i/o sleep for i/o
+ */
+struct socket {
+ short type; /* SOCK_STREAM, ... */
+ socket_state state;
+ long flags;
+ struct proto_ops *ops; /* protocols do most everything */
+ void *data; /* protocol data */
+ struct socket *conn; /* server socket connected to */
+ struct socket *iconn; /* incomplete client conn.s */
+ struct socket *next;
+ struct wait_queue **wait; /* ptr to place to wait on */
+ void *dummy;
+};
+
+#define SOCK_INODE(S) ((struct inode *)(S)->dummy)
+extern struct socket sockets[NSOCKETS];
+#define last_socket (sockets + NSOCKETS - 1)
+
+
+struct proto_ops {
+ int family;
+
+ int (*create) (struct socket *sock, int protocol);
+ int (*dup) (struct socket *newsock, struct socket *oldsock);
+ int (*release) (struct socket *sock, struct socket *peer);
+ int (*bind) (struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+ int (*connect) (struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags);
+ int (*socketpair) (struct socket *sock1, struct socket *sock2);
+ int (*accept) (struct socket *sock, struct socket *newsock,
+ int flags);
+ int (*getname) (struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer);
+ int (*read) (struct socket *sock, char *ubuf, int size,
+ int nonblock);
+ int (*write) (struct socket *sock, char *ubuf, int size,
+ int nonblock);
+ int (*select) (struct socket *sock, int sel_type,
+ select_table *wait);
+ int (*ioctl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ int (*listen) (struct socket *sock, int len);
+ int (*send) (struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags);
+ int (*recv) (struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags);
+ int (*sendto) (struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags, struct sockaddr *, int addr_len);
+ int (*recvfrom) (struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags, struct sockaddr *, int *addr_len);
+ int (*shutdown) (struct socket *sock, int flags);
+ int (*setsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+ int (*getsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+ int (*fcntl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+};
+
+
+extern int sock_awaitconn(struct socket *mysock, struct socket *servsock);
+extern int sock_register(int family, struct proto_ops *ops);
+
+
+#endif /* _LINUX_NET_H */
diff --git a/include/linux/nfs.h b/include/linux/nfs.h
index 4974b1a..60bc293 100644
--- a/include/linux/nfs.h
+++ b/include/linux/nfs.h
@@ -18,7 +18,7 @@
#define NFSMODE_SOCK 0140000
#define NFSMODE_FIFO 0010000
-#ifdef KERNEL /* user programs should get these from the rpc header files */
+#ifdef __KERNEL__ /* user programs should get these from the rpc header files */
#define RPC_VERSION 2
@@ -59,7 +59,7 @@ enum rpc_auth_stat {
RPC_AUTH_TOOWEAK = 5,
};
-#endif /* KERNEL */
+#endif /* __KERNEL__ */
enum nfs_stat {
NFS_OK = 0,
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 23f8c67..1c5db08 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -11,7 +11,7 @@
#include <linux/nfs.h>
-#include <netinet/in.h>
+#include <linux/in.h>
#include <linux/nfs_mount.h>
/*
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index cf40473..14f1a3b 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -25,10 +25,12 @@ extern int proc_match(int, const char *, struct proc_dir_entry *);
extern struct inode_operations proc_root_inode_operations;
extern struct inode_operations proc_base_inode_operations;
+extern struct inode_operations proc_net_inode_operations;
extern struct inode_operations proc_mem_inode_operations;
extern struct inode_operations proc_array_inode_operations;
extern struct inode_operations proc_kmsg_inode_operations;
extern struct inode_operations proc_link_inode_operations;
extern struct inode_operations proc_fd_inode_operations;
+extern struct inode_operations proc_net_inode_operations;
#endif
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index dc4fb38..c20f6e1 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -54,16 +54,16 @@ struct pt_regs {
long edi;
long ebp;
long eax;
- long ds;
- long es;
- long fs;
- long gs;
+ unsigned short ds, __dsu;
+ unsigned short es, __esu;
+ unsigned short fs, __fsu;
+ unsigned short gs, __gsu;
long orig_eax;
long eip;
- long cs;
+ unsigned short cs, __csu;
long eflags;
long esp;
- long ss;
+ unsigned short ss, __ssu;
};
#endif
diff --git a/include/linux/route.h b/include/linux/route.h
new file mode 100644
index 0000000..6ab7e94
--- /dev/null
+++ b/include/linux/route.h
@@ -0,0 +1,45 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the IP router interface.
+ *
+ * Version: @(#)route.h 1.0.3 05/27/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_ROUTE_H
+#define _LINUX_ROUTE_H
+
+#include <linux/if.h>
+
+
+/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
+struct rtentry {
+ unsigned long rt_hash; /* hash key for lookups */
+ struct sockaddr rt_dst;
+ struct sockaddr rt_gateway;
+ short rt_flags;
+ short rt_refcnt;
+ unsigned long rt_use;
+#ifdef BSD_COMPATIBLE
+ struct ifnet *rt_ifp;
+#else
+ void *rt_dev;
+#endif
+};
+#define RTF_UP 0x0001 /* route useable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* re-instate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+
+#endif /* _LINUX_ROUTE_H */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 575e32a..04f5c8a 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -9,7 +9,6 @@
* #define DEBUG
*/
-
#define HZ 100
#include <linux/tasks.h>
@@ -154,7 +153,7 @@ struct task_struct {
struct sigaction sigaction[32];
unsigned long saved_kernel_stack;
unsigned long kernel_stack_page;
- int exit_code;
+ int exit_code, exit_signal;
int dumpable:1;
int swappable:1;
unsigned long start_code,end_code,end_data,brk,start_stack;
@@ -167,6 +166,7 @@ struct task_struct {
* p->p_pptr->pid)
*/
struct task_struct *p_opptr,*p_pptr, *p_cptr, *p_ysptr, *p_osptr;
+ struct wait_queue *wait_chldexit; /* for wait4() */
/*
* For ease of programming... Normal sleeps don't need to
* keep track of a wait-queue: every task has an entry of it's own
@@ -193,6 +193,8 @@ struct task_struct {
struct inode * root;
struct inode * executable;
struct vm_area_struct * mmap;
+ struct shm_desc *shm;
+ struct sem_undo *semun;
struct {
struct inode * library;
unsigned long start;
@@ -202,7 +204,7 @@ struct task_struct {
int numlibraries;
struct file * filp[NR_OPEN];
fd_set close_on_exec;
-/* ldt for this task 0 - zero 1 - cs 2 - ds&ss, rest unused */
+/* ldt for this task - not currently used */
struct desc_struct ldt[32];
/* tss for this task */
struct tss_struct tss;
@@ -217,6 +219,13 @@ struct task_struct {
#define PF_TRACESYS 0x00000020 /* tracing system calls */
/*
+ * cloning flags:
+ */
+#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
+#define COPYVM 0x00000100 /* set if VM copy desired (like normal fork()) */
+#define COPYFD 0x00000200 /* set if fd's should be copied, not shared (NI) */
+
+/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
*/
@@ -224,11 +233,11 @@ struct task_struct {
/* state etc */ { 0,15,15,0,0,0,0, \
/* signals */ {{ 0, },}, \
/* stack */ 0,0, \
-/* ec,brk... */ 0,0,0,0,0,0,0,0, \
+/* ec,brk... */ 0,0,0,0,0,0,0,0,0, \
/* argv.. */ 0,0,0,0, \
/* pid etc.. */ 0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
-/* proc links*/ &init_task,&init_task,NULL,NULL,NULL, \
+/* proc links*/ &init_task,&init_task,NULL,NULL,NULL,NULL, \
/* uid etc */ 0,0,0,0,0,0, \
/* timeout */ 0,0,0,0,0,0,0,0,0,0,0,0, \
/* min_flt */ 0,0,0,0, \
@@ -240,18 +249,17 @@ struct task_struct {
/* comm */ "swapper", \
/* vm86_info */ NULL, 0, \
/* fs info */ 0,-1,0022,NULL,NULL,NULL,NULL, \
+/* ipc */ NULL, NULL, \
/* libraries */ { { NULL, 0, 0, 0}, }, 0, \
/* filp */ {NULL,}, \
/* cloe */ {{ 0, }}, \
{ \
- {0,0}, \
-/* ldt */ {0x1ff,0xc0c0fa00}, \
- {0x1ff,0xc0c0f200}, \
+/* ldt */ {0,0}, \
}, \
/*tss*/ {0,sizeof(init_kernel_stack) + (long) &init_kernel_stack, \
- 0x10,0,0,0,0,(long) &swapper_pg_dir,\
+ KERNEL_DS,0,0,0,0,(long) &swapper_pg_dir,\
0,0,0,0,0,0,0,0, \
- 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
+ 0,0,USER_DS,USER_DS,USER_DS,USER_DS,USER_DS,USER_DS, \
_LDT(0),0x80000000,{0xffffffff}, \
{ { 0, }, } \
} \
@@ -269,13 +277,12 @@ extern int ignore_irq13;
#define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)
-extern void add_timer(long jiffies, void (*fn)(void));
-
extern void sleep_on(struct wait_queue ** p);
extern void interruptible_sleep_on(struct wait_queue ** p);
extern void wake_up(struct wait_queue ** p);
extern void wake_up_interruptible(struct wait_queue ** p);
+extern void notify_parent(struct task_struct * tsk);
extern int send_sig(unsigned long sig,struct task_struct * p,int priv);
extern int in_group_p(gid_t grp);
@@ -284,10 +291,19 @@ extern void free_irq(unsigned int irq);
extern int irqaction(unsigned int irq,struct sigaction * new);
/*
- * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
- * 4-TSS0, 5-LDT0, 6-TSS1 etc ...
+ * Entry into gdt where to find first TSS. GDT layout:
+ * 0 - nul
+ * 1 - kernel code segment
+ * 2 - kernel data segment
+ * 3 - user code segment
+ * 4 - user data segment
+ * ...
+ * 8 - TSS #0
+ * 9 - LDT #0
+ * 10 - TSS #1
+ * 11 - LDT #1
*/
-#define FIRST_TSS_ENTRY 4
+#define FIRST_TSS_ENTRY 8
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
diff --git a/include/linux/segment.h b/include/linux/segment.h
new file mode 100644
index 0000000..aa2a98a
--- /dev/null
+++ b/include/linux/segment.h
@@ -0,0 +1,10 @@
+#ifndef _LINUX_SEGMENT_H
+#define _LINUX_SEGMENT_H
+
+#define KERNEL_CS 0x10
+#define KERNEL_DS 0x18
+
+#define USER_CS 0x23
+#define USER_DS 0x2B
+
+#endif
diff --git a/include/linux/sem.h b/include/linux/sem.h
new file mode 100644
index 0000000..a996c3f
--- /dev/null
+++ b/include/linux/sem.h
@@ -0,0 +1,97 @@
+#ifndef _LINUX_SEM_H
+#define _LINUX_SEM_H
+#include <linux/ipc.h>
+
+/* semop flags */
+#define SEM_UNDO 010000 /* undo the operation on exit */
+
+/* semctl Command Definitions. */
+#define GETPID 11 /* get sempid */
+#define GETVAL 12 /* get semval */
+#define GETALL 13 /* get all semval's */
+#define GETNCNT 14 /* get semncnt */
+#define GETZCNT 15 /* get semzcnt */
+#define SETVAL 16 /* set semval */
+#define SETALL 17 /* set all semval's */
+
+/* One semid data structure for each set of semaphores in the system. */
+struct semid_ds {
+ struct ipc_perm sem_perm; /* permissions .. see ipc.h */
+ time_t sem_otime; /* last semop time */
+ time_t sem_ctime; /* last change time */
+ struct sem *sem_base; /* ptr to first semaphore in array */
+ struct wait_queue *eventn;
+ struct wait_queue *eventz;
+ struct sem_undo *undo; /* undo requests on this array */
+ ushort sem_nsems; /* no. of semaphores in array */
+};
+
+
+/* One semaphore structure for each semaphore in the system. */
+struct sem {
+ short sempid; /* pid of last operation */
+ ushort semval; /* current value */
+ ushort semncnt; /* num procs awaiting increase in semval */
+ ushort semzcnt; /* num procs awaiting semval = 0 */
+};
+
+/* semop system calls takes an array of these.*/
+struct sembuf {
+ ushort sem_num; /* semaphore index in array */
+ short sem_op; /* semaphore operation */
+ short sem_flg; /* operation flags */
+};
+
+/* arg for semctl system calls. */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+ ushort *array; /* array for GETALL & SETALL */
+};
+
+
+struct seminfo {
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
+};
+
+#define SEMMNI 128 /* ? max # of semaphore identifiers */
+#define SEMMSL 32 /* <= 512 max num of semaphores per id */
+#define SEMMNS (SEMMNI*SEMMSL) /* ? max # of semaphores in system */
+#define SEMOPM 32 /* ~ 100 max num of ops per semop call */
+#define SEMVMX 32767 /* semaphore maximum value */
+
+/* unused */
+#define SEMUME SEMOPM /* max num of undo entries per process */
+#define SEMMNU SEMMNS /* num of undo structures system wide */
+#define SEMAEM (SEMVMX >> 1) /* adjust on exit max value */
+#define SEMMAP SEMMNS /* # of entries in semaphore map */
+#define SEMUSZ 20 /* sizeof struct sem_undo */
+
+#ifdef __KERNEL__
+/* ipcs ctl cmds */
+#define SEM_STAT 18
+#define SEM_INFO 19
+
+/* per process undo requests */
+/* this gets linked into the task_struct */
+struct sem_undo {
+ struct sem_undo *proc_next;
+ struct sem_undo *id_next;
+ struct sem_undo *id_prev;
+ int semid;
+ short semadj; /* semval adjusted by exit */
+ ushort sem_num; /* semaphore index in array semid */
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SEM_H */
diff --git a/include/linux/shm.h b/include/linux/shm.h
new file mode 100644
index 0000000..cd6c767
--- /dev/null
+++ b/include/linux/shm.h
@@ -0,0 +1,88 @@
+#ifndef _LINUX_SHM_H_
+#define _LINUX_SHM_H_
+#include <linux/ipc.h>
+
+struct shmid_ds {
+ struct ipc_perm shm_perm; /* operation perms */
+ int shm_segsz; /* size of segment (bytes) */
+ time_t shm_atime; /* last attach time */
+ time_t shm_dtime; /* last detach time */
+ time_t shm_ctime; /* last change time */
+ unsigned short shm_cpid; /* pid of creator */
+ unsigned short shm_lpid; /* pid of last operator */
+ short shm_nattch; /* no. of current attaches */
+ /* the following are private */
+ unsigned short shm_npages; /* size of segment (pages) */
+ unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
+ struct shm_desc *attaches; /* descriptors for attaches */
+};
+
+/* mode for attach */
+#define SHM_RDONLY 010000 /* read-only access */
+#define SHM_RND 020000 /* round attach address to SHMLBA boundary */
+#define SHM_REMAP 040000 /* take-over region on attach */
+
+/* super user shmctl commands */
+#define SHM_LOCK 11
+#define SHM_UNLOCK 12
+
+struct shminfo {
+ int shmmax;
+ int shmmin;
+ int shmmni;
+ int shmseg;
+ int shmall;
+};
+
+#define SHMMAX 0x400000 /* <= 4M */ /* max shared seg size (bytes) */
+#define SHMMIN 1 /* really PAGE_SIZE */ /* min shared seg size (bytes)*/
+#define SHMMNI 128 /* <= 4096 */ /* max num of segs system wide */
+#define SHMALL 0x10000 /* <= SHMMAX*SHMMNI/PAGE_SIZE */ /* max shm system wide (pages) */
+#define SHMLBA 0x1000 /* = PAGE_SIZE */ /* attach addr multiple */
+#define SHMSEG SHMMNI /* <= SHMMNI */ /* max shared segs per process */
+
+
+#ifdef __KERNEL__
+
+/* shm_mode upper byte flags */
+#define SHM_DEST 01000 /* segment will be destroyed on last detach */
+#define SHM_LOCKED 02000 /* segment will not be swapped */
+
+/* ipcs ctl commands */
+#define SHM_STAT 13
+#define SHM_INFO 14
+struct shm_info {
+ int used_ids;
+ ulong shm_tot; /* total allocated shm */
+ ulong shm_rss; /* total resident shm */
+ ulong shm_swp; /* total swapped shm */
+ ulong swap_attempts;
+ ulong swap_successes;
+};
+
+
+/*
+ * Per process internal structure for managing segments.
+ * A shmat will add to and shmdt will remove from the list.
+ */
+struct shm_desc {
+ struct task_struct *task; /* attacher */
+ unsigned long shm_sgn; /* signature for this attach */
+ unsigned long start; /* virt addr of attach, multiple of SHMLBA */
+ unsigned long end; /* multiple of SHMLBA */
+ struct shm_desc *task_next; /* next attach for task */
+ struct shm_desc *seg_next; /* next attach for segment */
+};
+
+/* not present page table entry format bit 0 is 0, high byte defined in mm.h */
+#define SHM_IDX_SHIFT 20
+#define SHM_IDX_MASK 0x3FF
+#define SHM_ID_SHIFT 8
+#define SHM_ID_MASK 0xFFF
+#define SHM_READ_ONLY 0x80000000
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SHM_H_ */
+
+
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 0f3a16a..040a721 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -51,6 +51,7 @@ typedef unsigned int sigset_t; /* 32 bits */
/*
#define SIGLOST 29
*/
+#define SIGPWR 30
/*
* sa_flags values: SA_STACK is not currently supported, but will allow the
diff --git a/include/linux/sock_ioctl.h b/include/linux/sock_ioctl.h
deleted file mode 100644
index cd824e1..0000000
--- a/include/linux/sock_ioctl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef _LINUX_SOCK_IOCTL_H
-#define _LINUX_SOCK_IOCTL_H
-
-#define MAX_IP_NAME 20
-/* some ioctl. Their values are not special. */
-#define IP_SET_DEV 0x2401
-
-struct ip_config
-{
- char name[MAX_IP_NAME];
- unsigned long paddr;
- unsigned long router;
- unsigned long net;
- unsigned long up:1,destroy:1;
-};
-
-#define SIOCSARP 0x2501
-#define SIOCGARP 0x2502
-#define SIOCDARP 0x2503
-
-/*
- * ARP ioctl request
- */
-struct arpreq {
- struct sockaddr arp_pa; /* protocol address */
- struct sockaddr arp_ha; /* hardware address */
- int arp_flags; /* flags */
-};
-
-#define ATF_COM 0x02
-#define ATF_PERM 0x04
-#define ATF_PUBL 0x08
-#define ATF_USETRAILERS 0x10
-
-#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 2ae6906..9415bce 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -1,51 +1,43 @@
#ifndef _LINUX_SOCKET_H
#define _LINUX_SOCKET_H
+#include <linux/sockios.h> /* the SIOCxxx I/O controls */
+
+
struct sockaddr {
- unsigned short sa_family; /* address family, AF_xxx */
- char sa_data[14]; /* 14 bytes of protocol address */
+ unsigned short sa_family; /* address family, AF_xxx */
+ char sa_data[14]; /* 14 bytes of protocol address */
};
-/*
- * socket types
- */
-#define SOCK_STREAM 1 /* stream (connection) socket */
-#define SOCK_DGRAM 2 /* datagram (connectionless) socket */
-#define SOCK_RAW 3 /* raw socket */
-#define SOCK_RDM 4 /* reliably-delivered message */
-#define SOCK_SEQPACKET 5 /* sequential packet socket */
-#define SOCK_PACKET 10 /* linux specific way of getting
- packets at the dev level. For
- writing rarp and other similiar
- things on the user level. */
-
-/*
- * supported address families
- */
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similiar things on the */
+ /* user level. */
+
+/* Supported address families. */
#define AF_UNSPEC 0
#define AF_UNIX 1
#define AF_INET 2
-/*
- * protocol families, same as address families
- */
+/* Protocol families, same as address families. */
#define PF_UNIX AF_UNIX
#define PF_INET AF_INET
-/* flags we can use with send/ and recv. */
+/* Flags we can use with send/ and recv. */
#define MSG_OOB 1
#define MSG_PEEK 2
-/* ioctl's */
-#define FIOSETOWN 0x8901 /* the 89 is for uniqueness.
- This should be somewhere else. */
-#define SIOCSPGRP 0x8902
-#define FIOGETOWN 0x8903 /* this too. */
-#define SIOCGPGRP 0x8904
-#define SIOCATMARK 0x8905
-
+/* Setsockoptions(2) level. */
+#define SOL_SOCKET 1
-/* for setsockoptions */
+/* For setsockoptions(2) */
#define SO_DEBUG 1
#define SO_REUSEADDR 2
#define SO_TYPE 3
@@ -55,17 +47,14 @@ struct sockaddr {
#define SO_SNDBUF 7
#define SO_RCVBUF 8
#define SO_KEEPALIVE 9
-#define SO_OOBINLINE 10
-#define SO_NO_CHECK 11
-#define SO_PRIORITY 12
-#define SO_LINGER 13
+#define SO_OOBINLINE 10
+#define SO_NO_CHECK 11
+#define SO_PRIORITY 12
+#define SO_LINGER 13
-/* the different priorities */
+/* The various priorities. */
#define SOPRI_INTERACTIVE 0
#define SOPRI_NORMAL 1
#define SOPRI_BACKGROUND 2
-/* setsockoptions level */
-#define SOL_SOCKET 1
-
#endif /* _LINUX_SOCKET_H */
diff --git a/include/linux/sockios.h b/include/linux/sockios.h
new file mode 100644
index 0000000..fe5591e
--- /dev/null
+++ b/include/linux/sockios.h
@@ -0,0 +1,72 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the socket-level I/O control calls.
+ *
+ * Version: @(#)sockios.h 1.0.2 03/09/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_SOCKIOS_H
+#define _LINUX_SOCKIOS_H
+
+/* This section will go away soon! */
+#if 1 /* FIXME: */
+#define MAX_IP_NAME 20
+#define IP_SET_DEV 0x2401
+
+struct ip_config {
+ char name[MAX_IP_NAME];
+ unsigned long paddr;
+ unsigned long router;
+ unsigned long net;
+ unsigned long up:1,destroy:1;
+};
+#endif /* FIXME: */
+
+/* Socket-level I/O control calls. */
+#define FIOSETOWN 0x8901
+#define SIOCSPGRP 0x8902
+#define FIOGETOWN 0x8903
+#define SIOCGPGRP 0x8904
+#define SIOCATMARK 0x8905
+
+/* Socket configuration controls. */
+#define SIOCGIFNAME 0x8910 /* get iface name */
+#define SIOCSIFLINK 0x8911 /* set iface channel */
+#define SIOCGIFCONF 0x8912 /* get iface list */
+#define SIOCGIFFLAGS 0x8913 /* get flags */
+#define SIOCSIFFLAGS 0x8914 /* set flags */
+#define SIOCGIFADDR 0x8915 /* get PA address */
+#define SIOCSIFADDR 0x8916 /* set PA address */
+#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */
+#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */
+#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
+#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
+#define SIOCGIFNETMASK 0x891b /* get network PA mask */
+#define SIOCSIFNETMASK 0x891c /* set network PA mask */
+#define SIOCGIFMETRIC 0x891d /* get metric */
+#define SIOCSIFMETRIC 0x891e /* set metric */
+#define SIOCGIFMEM 0x891f /* get memory address (BSD) */
+#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
+#define SIOCGIFMTU 0x8921 /* get MTU size */
+#define SIOCSIFMTU 0x8922 /* set MTU size */
+
+/* Routing table calls. */
+#define SIOCADDRT 0x8940 /* add routing table entry */
+#define SIOCDELRT 0x8941 /* delete routing table entry */
+
+/* ARP cache control calls. */
+#define SIOCDARP 0x8950 /* delete ARP table entry */
+#define SIOCGARP 0x8951 /* get ARP table entry */
+#define SIOCSARP 0x8952 /* set ARP table entry */
+
+#endif /* _LINUX_SOCKIOS_H */
diff --git a/include/linux/soundcard.h b/include/linux/soundcard.h
deleted file mode 100644
index 60cfe07..0000000
--- a/include/linux/soundcard.h
+++ /dev/null
@@ -1,234 +0,0 @@
-#ifndef SOUNDCARD_H
-#define SOUNDCARD_H
-/*
- * linux/soundcard.h
- *
- * Sound Card driver for Linux
- *
- * (C) Hannu Savolainen 1992
- *
- */
-
-/*
- * Supported card ID numbers
- */
-
-#define SNDCARD_ADLIB 1
-#define SNDCARD_SB 2
-#define SNDCARD_PAS 3
-
-/*
- * IOCTL Commands. Suffix 0x4252 is string "SB".
- */
-
-#define SNDCTL_CONFIGURE 0x00014252
-
-#define SNDCTL_FM_LOAD_INSTR 0x01014252
-/* #define SNDCTL_FM_RETURN_INSTR 0x01034252 */
-#define SNDCTL_SEQ_SYNC 0x01044252
-#define SNDCTL_SEQ_RESET 0x01054252
-#define SNDCTL_SYNTH_INFO 0x01064252
-#define SNDCTL_SEQ_TESTMIDI 0x01074252
-#define SNDCTL_SEQ_PERCMODE 0x01084252
-
-#define SNDCTL_DSP_SPEED 0x01114252
-#define SNDCTL_DSP_STEREO 0x01124252
-#define SNDCTL_DSP_GETBLKSIZE 0x01134252
-#define SNDCTL_DSP_SYNC 0x01144252
-#define SNDCTL_DSP_RESET 0x01154252
-#define SNDCTL_DSP_SAMPLESIZE 0x01164252
-/* 8, 12 or 16) */
-
-#define SEQ_FMNOTEOFF 0
-#define SEQ_FMNOTEON 1
-#define SEQ_WAIT 2
-#define SEQ_FMPGMCHANGE 3
-#define SEQ_SYNCTIMER 4
-#define SEQ_MIDIPUTC 5
-#define SEQ_DRUMON 6 /* Play percussive instrument */
-#define SEQ_DRUMOFF 7
-
-typedef unsigned char sbi_instr_data[16];
-
-struct sbi_instrument {
- int channel; /* Channel to be programmed */
- sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */
- };
-
-struct synth_info { /* Read only */
- int synth_type;
-#define SYNTH_TYPE_FM 0
-
- int synth_subtype;
-#define FM_TYPE_ADLIB 0
-#define FM_TYPE_OPL3 1
-
- int perc_mode; /* 0=off 1=off */
- int nr_voices;
- int nr_drums;
- int instr_bank_size;
- int dummies[20]; /* Reserve space */
- };
-/*
- Definitions for a "universal" sound driver
- by Craig metz (cmetz@thor.tjhsst.edu)
-*/
-
-/*
- IOCTL requests take the general form of a base address plus
- a device type plus a request type. The base address fills the
- top three bytes of the request longword and is formed from the
- letters 'SND'. The top nybble of the remaining byte indicates
- the device type to be interacted with, and the request type
- indicates what the device needs to do, and bit 3 of the lower
- nybble distinguishes read and write.
-
- IOCTL Calling Form:
-
- ioctl(fh, SOUND_call, &parameter);
-*/
-
-#define SOUND_BASE 0x534E4400
-
-#define SOUND_READ 0x00000000
-#define SOUND_WRITE 0x00000008
-
-/*
- Mixer control - device type 0
-
- All parameters are of type "unsigned short int" - The LSB is the
- left value, the MSB is the right value. Both are in percents, 0
- being mute and 100 being full power. In the event that the card
- only supports mono mixer control, the LSB will be the value used.
-*/
-
-#define SOUND_MIXER 0x00
-#define SOUND_MIXER_TYPE unsigned short int
-
-#define SOUND_MIXER_VOLUME 0x0
-#define SOUND_MIXER_BASS 0x1
-#define SOUND_MIXER_TREBLE 0x2
-#define SOUND_MIXER_MIDI 0x3
-#define SOUND_MIXER_PCM 0x4
-#define SOUND_MIXER_SPEAKER 0x5
-#define SOUND_MIXER_LINE 0x6
-#define SOUND_MIXER_MIC 0x7
-
-#define SOUND_MIXER_READ_VOLUME (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_VOLUME )
-#define SOUND_MIXER_READ_BASS (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_BASS )
-#define SOUND_MIXER_READ_TREBLE (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_TREBLE )
-#define SOUND_MIXER_READ_MIDI (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_MIDI )
-#define SOUND_MIXER_READ_PCM (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_PCM )
-#define SOUND_MIXER_READ_SPEAKER (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_SPEAKER)
-#define SOUND_MIXER_READ_LINE (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_LINE )
-#define SOUND_MIXER_READ_MIC (SOUND_BASE | SOUND_MIXER | SOUND_READ | SOUND_MIXER_MIC )
-
-#define SOUND_MIXER_WRITE_VOLUME (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_VOLUME )
-#define SOUND_MIXER_WRITE_BASS (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_BASS )
-#define SOUND_MIXER_WRITE_TREBLE (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_TREBLE )
-#define SOUND_MIXER_WRITE_MIDI (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_MIDI )
-#define SOUND_MIXER_WRITE_PCM (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_PCM )
-#define SOUND_MIXER_WRITE_SPEAKER (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_SPEAKER)
-#define SOUND_MIXER_WRITE_LINE (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_LINE )
-#define SOUND_MIXER_WRITE_MIC (SOUND_BASE | SOUND_MIXER | SOUND_WRITE | SOUND_MIXER_MIC )
-
-/*
- PCM control - device type 1
-
- All parameters are of type "unsigned short int".
-
- RATE = sampling rate in Hz
- CHANNELS = number of channels (1 = mono, 2 = stereo)
- BITS = number of bits/sample/channel (8 = 8 bit,
- 12 = 12 bit, 16 = 16 bit)
- FILTER = flag (0 = don't filter, 1 = use best filter)
-*/
-
-#define SOUND_PCM 0x10
-#define SOUND_PCM_TYPE unsigned short int
-
-#define SOUND_RATE 0x0
-#define SOUND_CHANNELS 0x1
-#define SOUND_BITS 0x2
-#define SOUND_FILTER 0x3
-
-#define SOUND_PCM_READ_RATE (SOUND_BASE | SOUND_PCM | SOUND_READ | SOUND_RATE )
-#define SOUND_PCM_READ_CHANNELS (SOUND_BASE | SOUND_PCM | SOUND_READ | SOUND_CHANNELS)
-#define SOUND_PCM_READ_BITS (SOUND_BASE | SOUND_PCM | SOUND_READ | SOUND_BITS )
-#define SOUND_PCM_READ_FILTER (SOUND_BASE | SOUND_PCM | SOUND_READ | SOUND_FILTER )
-
-#define SOUND_PCM_WRITE_RATE (SOUND_BASE | SOUND_PCM | SOUND_WRITE | SOUND_RATE )
-#define SOUND_PCM_WRITE_CHANNELS (SOUND_BASE | SOUND_PCM | SOUND_WRITE | SOUND_CHANNELS)
-#define SOUND_PCM_WRITE_BITS (SOUND_BASE | SOUND_PCM | SOUND_WRITE | SOUND_BITS )
-#define SOUND_PCM_WRITE_FILTER (SOUND_BASE | SOUND_PCM | SOUND_WRITE | SOUND_FILTER )
-
-/*
- * The Mixer ioctl calls are compatible with mach386 driver by
- * Steve Haehnichen <shaehnic@ucsd.edu>
- */
-
-typedef unsigned char BYTE;
-typedef unsigned char FLAG;
-struct stereo_vol
-{
- BYTE l; /* Left volume */
- BYTE r; /* Right volume */
-};
-#define MIXER_IOCTL_SET_LEVELS 0x02014252
-#define MIXER_IOCTL_SET_PARAMS 0x02024252
-#define MIXER_IOCTL_READ_LEVELS 0x02034252
-#define MIXER_IOCTL_READ_PARAMS 0x02044252
-#define MIXER_IOCTL_RESET 0x02054252
-
-/*
- * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
- */
-struct sb_mixer_levels
-{
- struct stereo_vol master; /* Master volume */
- struct stereo_vol voc; /* DSP Voice volume */
- struct stereo_vol fm; /* FM volume */
- struct stereo_vol line; /* Line-in volume */
- struct stereo_vol cd; /* CD audio */
- BYTE mic; /* Microphone level */
-};
-
-/*
- * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
- */
-struct sb_mixer_params
-{
- BYTE record_source; /* Recording source (See SRC_xxx below) */
- FLAG hifreq_filter; /* Filter frequency (hi/low) */
- FLAG filter_input; /* ANFI input filter */
- FLAG filter_output; /* DNFI output filter */
- FLAG dsp_stereo; /* 1 if DSP is in Stereo mode */
-};
-
-#define SRC_MIC 1 /* Select Microphone recording source */
-#define SRC_CD 3 /* Select CD recording source */
-#define SRC_LINE 7 /* Use Line-in for recording source */
-
-
-/*
- * Dynamic configuration mechanism.
- * (for soundload program)
- */
-
-
-struct soundcard_config
-{
- int config_command;
-#define SNDCONF_RESET 0
-#define SNDCONF_START 1
-#define SNDCONF_SETCARD 2
-
- int cardtype; /* SNDCARD_ADLIB etc. */
- int card_subtype; /* Card dependent number */
-
- int config_parms[100]; /* Card dependent parameters */
-};
-
-extern long soundcard_init(long mem_start);
-
-#endif
diff --git a/include/linux/string.h b/include/linux/string.h
index ebe39df..8d5523b 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -26,7 +26,7 @@ __asm__("cld\n"
"stosb\n\t"
"testb %%al,%%al\n\t"
"jne 1b"
- ::"S" (src),"D" (dest):"si","di","ax");
+ ::"S" (src),"D" (dest):"si","di","ax","memory");
return dest;
}
@@ -42,7 +42,7 @@ __asm__("cld\n"
"rep\n\t"
"stosb\n"
"2:"
- ::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx");
+ ::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
return dest;
}
@@ -76,7 +76,7 @@ __asm__("cld\n\t"
"2:\txorl %2,%2\n\t"
"stosb"
::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
- :"si","di","ax","cx");
+ :"si","di","ax","cx","memory");
return dest;
}
@@ -324,7 +324,7 @@ __asm__("testl %1,%1\n\t"
"8:"
:"=b" (__res),"=S" (___strtok)
:"0" (___strtok),"1" (s),"g" (ct)
- :"ax","cx","dx","di");
+ :"ax","cx","dx","di","memory");
return __res;
}
@@ -342,7 +342,7 @@ __asm__("cld\n\t"
"movsw\n"
"2:\n"
::"d" (n),"D" ((long) to),"S" ((long) from)
- : "cx","di","si");
+ : "cx","di","si","memory");
return (to);
}
@@ -360,7 +360,7 @@ __asm__("std\n\t"
"movsb\n\t"
"cld"
::"c" (n),"S" (src+n-1),"D" (dest+n-1)
- :"cx","si","di");
+ :"cx","si","di","memory");
return dest;
}
@@ -402,7 +402,7 @@ __asm__("cld\n\t"
"rep\n\t"
"stosb"
::"a" (c),"D" (s),"c" (count)
- :"cx","di");
+ :"cx","di","memory");
return s;
}
diff --git a/include/linux/sys.h b/include/linux/sys.h
index d98e680..09fa4df 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -124,6 +124,22 @@ extern int sys_sysinfo();
extern int sys_ipc();
extern int sys_fsync();
extern int sys_sigreturn();
+extern int sys_setdomainname();
+extern int sys_olduname();
+extern int sys_old_syscall();
+
+/*
+ * These are system calls that will be removed at some time
+ * due to newer versions existing..
+ */
+#ifdef notdef
+#define sys_waitpid sys_old_syscall /* sys_wait4 */
+#define sys_olduname sys_old_syscall /* sys_newuname */
+#define sys_stat sys_old_syscall /* sys_newstat */
+#define sys_fstat sys_old_syscall /* sys_newfstat */
+#define sys_lstat sys_old_syscall /* sys_newlstat */
+#define sys_signal sys_old_syscall /* sys_sigaction */
+#endif
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
@@ -135,7 +151,7 @@ sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
-sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
+sys_olduname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending,
sys_sethostname, sys_setrlimit, sys_getrlimit, sys_getrusage,
@@ -145,9 +161,9 @@ sys_swapon, sys_reboot, sys_readdir, sys_mmap, sys_munmap, sys_truncate,
sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, sys_setpriority,
sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall,
sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat,
-sys_newfstat, sys_newuname, sys_iopl, sys_vhangup, sys_idle, sys_vm86,
+sys_newfstat, sys_uname, sys_iopl, sys_vhangup, sys_idle, sys_vm86,
sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn,
-sys_clone };
+sys_clone, sys_setdomainname, sys_newuname};
/* So we don't have to do any more manual updating.... */
int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
new file mode 100644
index 0000000..e666f51
--- /dev/null
+++ b/include/linux/tcp.h
@@ -0,0 +1,61 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP protocol.
+ *
+ * Version: @(#)tcp.h 1.0.2 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_TCP_H
+#define _LINUX_TCP_H
+
+
+#define HEADER_SIZE 64 /* maximum header size */
+
+
+struct tcphdr {
+ 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,
+#if 0
+ TCP_CLOSING, /* not a valid state, just a seperator so we can use
+ < tcp_closing or > tcp_closing for checks. */
+#endif
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN
+};
+
+#endif /* _LINUX_TCP_H */
diff --git a/include/linux/termios.h b/include/linux/termios.h
index 59b89c5..5dcc243 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -44,6 +44,9 @@
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define FIOASYNC 0x5452
+#define TIOCSERCONFIG 0x5453
+#define TIOCSERGWILD 0x5454
+#define TIOCSERSWILD 0x5455
/* Used for packet mode */
#define TIOCPKT_FLUSHREAD 1
diff --git a/include/linux/timer.h b/include/linux/timer.h
index eb6d6cc..fd3aea9 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -45,4 +45,27 @@ struct timer_struct {
extern unsigned long timer_active;
extern struct timer_struct timer_table[32];
+/*
+ * This is completely separate from the above, and is the
+ * "new and improved" way of handling timers more dynamically.
+ * Hopefully efficient and general enough for most things.
+ *
+ * The "hardcoded" timers above are still useful for well-
+ * defined problems, but the timer-list is probably better
+ * when you need multiple outstanding timers or similar.
+ *
+ * The "data" field is in case you want to use the same
+ * timeout function for several timeouts. You can use this
+ * to distinguish between the different invocations.
+ */
+struct timer_list {
+ struct timer_list *next;
+ unsigned long expires;
+ unsigned long data;
+ void (*function)(unsigned long);
+};
+
+extern void add_timer(struct timer_list * timer);
+extern void del_timer(struct timer_list * timer);
+
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 95c8ee8..5a0c5bf 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -99,19 +99,22 @@ struct serial_struct {
#define ASYNC_HUP_NOTIFY 0x0001 /* Notify blocked open on hangups */
#define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ASYNC_SKIP_TEST 0x0008 /* Skip UART test on bootup */
#define ASYNC_SPD_MASK 0x0030
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
#define ASYNC_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
#define ASYNC_SPD_CUST 0x0030 /* Use user-specified divisor */
-#define ASYNC_FLAGS 0x0037 /* Possible legal async flags */
+#define ASYNC_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ASYNC_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+
+#define ASYNC_FLAGS 0x00F7 /* Possible legal async flags */
/* Internal flags used only by kernel/chr_drv/serial.c */
#define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */
#define ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
#define ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ASYNC_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40)
@@ -174,10 +177,12 @@ extern int get_tty_queue(struct tty_queue * queue);
#define I_STRP(tty) _I_FLAG((tty),ISTRIP)
#define O_POST(tty) _O_FLAG((tty),OPOST)
+#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_NOCR(tty) _O_FLAG((tty),ONOCR)
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
-#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
+#define O_TABDLY(tty) _O_FLAG((tty),TABDLY)
#define C_LOCAL(tty) _C_FLAG((tty),CLOCAL)
#define C_RTSCTS(tty) _C_FLAG((tty),CRTSCTS)
@@ -209,6 +214,7 @@ struct tty_struct {
int disc;
int flags;
int count;
+ int column;
struct winsize winsize;
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
@@ -226,7 +232,7 @@ struct tty_struct {
struct tty_queue read_q;
struct tty_queue write_q;
struct tty_queue secondary;
- };
+};
struct tty_ldisc {
int flags;
@@ -294,6 +300,7 @@ struct tty_ldisc {
#define TTY_SQ_THROTTLED 3
#define TTY_RQ_THROTTLED 4
#define TTY_IO_ERROR 5
+#define TTY_SLAVE_OPENED 6
/*
* When a break, frame error, or parity error happens, these codes are
@@ -340,6 +347,10 @@ extern void flush_output(struct tty_struct * tty);
extern void wait_until_sent(struct tty_struct * tty);
extern void copy_to_cooked(struct tty_struct * tty);
extern int tty_register_ldisc(int disc, struct tty_ldisc *new);
+extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
+ int buflen);
+extern int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
+ void (*callback)(void * data), void * callarg);
extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
extern int is_orphaned_pgrp(int pgrp);
diff --git a/include/linux/udp.h b/include/linux/udp.h
new file mode 100644
index 0000000..471301a
--- /dev/null
+++ b/include/linux/udp.h
@@ -0,0 +1,29 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP protocol.
+ *
+ * Version: @(#)udp.h 1.0.2 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_UDP_H
+#define _LINUX_UDP_H
+
+
+struct udphdr {
+ unsigned short source;
+ unsigned short dest;
+ unsigned short len;
+ unsigned short check;
+};
+
+
+#endif /* _LINUX_UDP_H */
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
index c6aab9a..1056a21 100644
--- a/include/linux/unistd.h
+++ b/include/linux/unistd.h
@@ -65,7 +65,7 @@
#define __NR_mpx 56
#define __NR_setpgid 57
#define __NR_ulimit 58
-#define __NR_olduname 59
+#define __NR_oldolduname 59
#define __NR_umask 60
#define __NR_chroot 61
#define __NR_ustat 62
@@ -115,7 +115,7 @@
#define __NR_stat 106
#define __NR_lstat 107
#define __NR_fstat 108
-#define __NR_uname 109
+#define __NR_olduname 109
#define __NR_iopl 110
#define __NR_vhangup 111
#define __NR_idle 112
@@ -124,9 +124,11 @@
#define __NR_swapoff 115
#define __NR_sysinfo 116
#define __NR_ipc 117 /* not implemented yet */
-#define __NR_fsync 118 /* not implemented yet */
+#define __NR_fsync 118
#define __NR_sigreturn 119
#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
extern int errno;
@@ -148,10 +150,9 @@ return -1; \
type name(atype a) \
{ \
long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
+__asm__ volatile ("int $0x80" \
: "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)):"bx"); \
+ : "0" (__NR_##name),"b" ((long)(a))); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
@@ -162,10 +163,9 @@ return -1; \
type name(atype a,btype b) \
{ \
long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
+__asm__ volatile ("int $0x80" \
: "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)):"bx"); \
+ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
@@ -176,10 +176,9 @@ return -1; \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
+__asm__ volatile ("int $0x80" \
: "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)),"d" ((long)(c)):"bx"); \
+ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
@@ -190,8 +189,7 @@ return -1; \
type name (atype a, btype b, ctype c, dtype d) \
{ \
long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
+__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
"d" ((long)(c)),"S" ((long)(d))); \
@@ -205,8 +203,7 @@ return -1; \
type name (atype a,btype b,ctype c,dtype d,etype e) \
{ \
long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
+__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
"d" ((long)(c)),"S" ((long)(d)),"D" ((long)(e))); \
diff --git a/include/linux/utsname.h b/include/linux/utsname.h
index 64de0df..7aef28f 100644
--- a/include/linux/utsname.h
+++ b/include/linux/utsname.h
@@ -3,7 +3,7 @@
#define __OLD_UTS_LEN 8
-struct old_utsname {
+struct oldold_utsname {
char sysname[9];
char nodename[9];
char release[9];
@@ -13,12 +13,21 @@ struct old_utsname {
#define __NEW_UTS_LEN 64
+struct old_utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+};
+
struct new_utsname {
char sysname[65];
char nodename[65];
char release[65];
char version[65];
char machine[65];
+ char domainname[65];
};
extern struct new_utsname system_utsname;
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 58dd088..b0642ec 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -1,8 +1,10 @@
#ifndef _LINUX_WAIT_H
#define _LINUX_WAIT_H
-#define WNOHANG 1
-#define WUNTRACED 2
+#define WNOHANG 0x00000001
+#define WUNTRACED 0x00000002
+
+#define __WCLONE 0x80000000
struct wait_queue {
struct task_struct * task;
diff --git a/include/linux/xia_fs.h b/include/linux/xia_fs.h
index 2d4abff..ab0e2a4 100644
--- a/include/linux/xia_fs.h
+++ b/include/linux/xia_fs.h
@@ -97,6 +97,8 @@ extern void xiafs_read_inode(struct inode *);
extern void xiafs_write_inode(struct inode *);
extern void xiafs_put_inode(struct inode *);
extern void xiafs_statfs(struct super_block *, struct statfs *);
+extern int xiafs_sync_inode(struct inode *);
+extern int xiafs_sync_file(struct inode *, struct file *);
extern struct inode_operations xiafs_file_inode_operations;
extern struct inode_operations xiafs_dir_inode_operations;
diff --git a/init/main.c b/init/main.c
index a393cc5..a183127 100644
--- a/init/main.c
+++ b/init/main.c
@@ -23,7 +23,7 @@
extern unsigned long * prof_buffer;
extern unsigned long prof_len;
-extern int end;
+extern char edata, end;
extern char *linux_banner;
/*
@@ -58,6 +58,7 @@ static inline pid_t wait(int * wait_stat)
static char printbuf[1024];
+extern char empty_zero_page[4096];
extern int vsprintf(char *,const char *,va_list);
extern void init(void);
extern void init_IRQ(void);
@@ -70,6 +71,9 @@ extern long kernel_mktime(struct mktime * time);
extern unsigned long simple_strtoul(const char *cp,char **endp,unsigned int
base);
+#ifdef CONFIG_SYSVIPC
+extern void ipc_init(void);
+#endif
#ifdef CONFIG_SCSI
extern unsigned long scsi_dev_init(unsigned long, unsigned long);
#endif
@@ -77,23 +81,21 @@ extern unsigned long scsi_dev_init(unsigned long, unsigned long);
/*
* This is set up by the setup-routine at boot-time
*/
-#define EXT_MEM_K (*(unsigned short *)0x90002)
-#define DRIVE_INFO (*(struct drive_info *)0x90080)
-#define SCREEN_INFO (*(struct screen_info *)0x90000)
-#define MOUNT_ROOT_RDONLY (*(unsigned short *)0x901F2)
-#define RAMDISK_SIZE (*(unsigned short *)0x901F8)
-#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
-#define AUX_DEVICE_INFO (*(unsigned char *)0x901FF)
+#define PARAM empty_zero_page
+#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#define DRIVE_INFO (*(struct drive_info *) (PARAM+0x80))
+#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
+#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
+#define RAMDISK_SIZE (*(unsigned short *) (PARAM+0x1F8))
+#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
+#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
/*
* Boot command-line arguments
*/
#define MAX_INIT_ARGS 8
#define MAX_INIT_ENVS 8
-#define CL_MAGIC_ADDR (*(unsigned short *) 0x90020)
-#define CL_MAGIC 0xa33f
-#define CL_BASE_ADDR ((char *) 0x90000)
-#define CL_OFFSET (*(unsigned short *) 0x90022)
+#define COMMAND_LINE ((char *) (PARAM+2048))
/*
* Yeah, yeah, it's ugly, but I cannot find how to do this correctly
@@ -249,6 +251,7 @@ void start_kernel(void)
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
ramdisk_size = RAMDISK_SIZE;
+ strcpy(command_line,COMMAND_LINE);
#ifdef CONFIG_MAX_16M
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
@@ -265,8 +268,6 @@ void start_kernel(void)
low_memory_start += 0xfff;
low_memory_start &= 0xfffff000;
memory_start = paging_init(memory_start,memory_end);
- if (CL_MAGIC_ADDR == CL_MAGIC)
- strcpy(command_line,CL_BASE_ADDR+CL_OFFSET);
trap_init();
init_IRQ();
sched_init();
@@ -289,6 +290,9 @@ void start_kernel(void)
time_init();
floppy_init();
sock_init();
+#ifdef CONFIG_SYSVIPC
+ ipc_init();
+#endif
sti();
/*
* check if exception 16 works correctly.. This is truly evil
diff --git a/ipc/Makefile b/ipc/Makefile
new file mode 100644
index 0000000..5180a80
--- /dev/null
+++ b/ipc/Makefile
@@ -0,0 +1,46 @@
+#
+# Makefile for the linux ipc.
+#
+# 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...
+
+
+SUBDIRS =
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+
+OBJS = util.o
+
+ifdef CONFIG_SYSVIPC
+OBJS := $(OBJS) msg.o sem.o shm.o
+endif
+
+ipc.o: $(OBJS)
+ $(LD) -r -o ipc.o $(OBJS)
+
+subdirs: dummy
+ for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
+
+clean:
+ rm -f core *.o *.a *.s
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) clean) || exit; done
+
+dep:
+ $(CPP) -M *.c > .depend
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/ipc/msg.c b/ipc/msg.c
new file mode 100644
index 0000000..60b5dfb
--- /dev/null
+++ b/ipc/msg.c
@@ -0,0 +1,412 @@
+/*
+ * linux/ipc/msg.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ */
+
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/msg.h>
+
+extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
+
+static void freeque (int id);
+static int newque (key_t key, int msgflg);
+static int findkey (key_t key);
+
+static struct msqid_ds *msgque[MSGMNI];
+static int msgbytes = 0;
+static int msghdrs = 0;
+static int msg_seq = 0;
+static int used_queues = 0;
+static int max_msqid = 0;
+static struct wait_queue *msg_lock = NULL;
+
+void msg_init (void)
+{
+ int id;
+
+ for (id=0; id < MSGMNI; id++)
+ msgque[id] = IPC_UNUSED;
+ msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
+ msg_lock = NULL;
+ return;
+}
+
+int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
+{
+ int id;
+ struct msqid_ds *msq;
+ struct ipc_perm *ipcp;
+ struct msg *msgh;
+ long mtype;
+
+ if (msgsz > MSGMAX || msgsz < 0 || msqid < 0)
+ return -EINVAL;
+ if (!msgp)
+ return -EFAULT;
+ if ((mtype = get_fs_long (&msgp->mtype)) < 1)
+ return -EINVAL;
+ id = msqid % MSGMNI;
+ msq = msgque [id];
+ if (msq == IPC_UNUSED || msq == IPC_NOID)
+ return -EINVAL;
+ ipcp = &msq->msg_perm;
+
+ slept:
+ if (ipcp->seq != (msqid / MSGMNI))
+ return -EIDRM;
+ if (ipcperms(ipcp, 0222))
+ return -EACCES;
+
+ if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
+ /* no space in queue */
+ if (msgflg & IPC_NOWAIT)
+ return -EAGAIN;
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ interruptible_sleep_on (&msq->wwait);
+ goto slept;
+ }
+
+ /* allocate message header and text space*/
+ msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER);
+ if (!msgh)
+ return -ENOMEM;
+ msgh->msg_spot = (char *) (msgh + 1);
+ memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
+
+ if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
+ || ipcp->seq != msqid / MSGMNI) {
+ kfree (msgh);
+ return -EIDRM;
+ }
+
+ msgh->msg_next = NULL;
+ if (!msq->msg_first)
+ msq->msg_first = msq->msg_last = msgh;
+ else {
+ msq->msg_last->msg_next = msgh;
+ msq->msg_last = msgh;
+ }
+ msgh->msg_ts = msgsz;
+ msgh->msg_type = mtype;
+ msq->msg_cbytes += msgsz;
+ msgbytes += msgsz;
+ msghdrs++;
+ msq->msg_qnum++;
+ msq->msg_lspid = current->pid;
+ msq->msg_stime = CURRENT_TIME;
+ if (msq->rwait)
+ wake_up (&msq->rwait);
+ return msgsz;
+}
+
+int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
+ int msgflg)
+{
+ struct msqid_ds *msq;
+ struct ipc_perm *ipcp;
+ struct msg *tmsg, *leastp = NULL;
+ struct msg *nmsg = NULL;
+ int id;
+
+ if (msqid < 0 || msgsz < 0)
+ return -EINVAL;
+ id = msqid % MSGMNI;
+ msq = msgque [id];
+ if (msq == IPC_NOID || msq == IPC_UNUSED)
+ return -EINVAL;
+ ipcp = &msq->msg_perm;
+ if (!msgp || !msgp->mtext)
+ return -EFAULT;
+
+ /*
+ * find message of correct type.
+ * msgtyp = 0 => get first.
+ * msgtyp > 0 => get first message of matching type.
+ * msgtyp < 0 => get message with least type must be < abs(msgtype).
+ */
+ while (!nmsg) {
+ if(ipcp->seq != msqid / MSGMNI)
+ return -EIDRM;
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ if (msgtyp == 0)
+ nmsg = msq->msg_first;
+ else if (msgtyp > 0) {
+ if (msgflg & MSG_EXCEPT) {
+ for (tmsg = msq->msg_first; tmsg;
+ tmsg = tmsg->msg_next)
+ if (tmsg->msg_type != msgtyp)
+ break;
+ nmsg = tmsg;
+ } else {
+ for (tmsg = msq->msg_first; tmsg;
+ tmsg = tmsg->msg_next)
+ if (tmsg->msg_type == msgtyp)
+ break;
+ nmsg = tmsg;
+ }
+ } else {
+ for (leastp = tmsg = msq->msg_first; tmsg;
+ tmsg = tmsg->msg_next)
+ if (tmsg->msg_type < leastp->msg_type)
+ leastp = tmsg;
+ if (leastp->msg_type >= - msgtyp)
+ nmsg = leastp;
+ }
+
+ if (nmsg) { /* done finding a message */
+ if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR))
+ return -E2BIG;
+ msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
+
+ if (nmsg == msq->msg_first)
+ msq->msg_first = nmsg->msg_next;
+ else {
+ for (tmsg= msq->msg_first; tmsg;
+ tmsg = tmsg->msg_next)
+ if (tmsg->msg_next == nmsg)
+ break;
+ tmsg->msg_next = nmsg->msg_next;
+ if (nmsg == msq->msg_last)
+ msq->msg_last = tmsg;
+ }
+ if (!(--msq->msg_qnum))
+ msq->msg_last = msq->msg_first = NULL;
+
+ msq->msg_rtime = CURRENT_TIME;
+ msq->msg_lrpid = current->pid;
+ msgbytes -= nmsg->msg_ts;
+ msghdrs--;
+ msq->msg_cbytes -= nmsg->msg_ts;
+ if (msq->wwait)
+ wake_up (&msq->wwait);
+ put_fs_long (nmsg->msg_type, &msgp->mtype);
+ memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
+ kfree (nmsg);
+ return msgsz;
+ } else { /* did not find a message */
+ if (msgflg & IPC_NOWAIT)
+ return -ENOMSG;
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ interruptible_sleep_on (&msq->rwait);
+ }
+ } /* end while */
+ return -1;
+}
+
+
+static int findkey (key_t key)
+{
+ int id;
+ struct msqid_ds *msq;
+
+ for (id=0; id <= max_msqid; id++) {
+ while ((msq = msgque[id]) == IPC_NOID)
+ interruptible_sleep_on (&msg_lock);
+ if (msq == IPC_UNUSED)
+ continue;
+ if (key == msq->msg_perm.key)
+ return id;
+ }
+ return -1;
+}
+
+static int newque (key_t key, int msgflg)
+{
+ int id;
+ struct msqid_ds *msq;
+ struct ipc_perm *ipcp;
+
+ for (id=0; id < MSGMNI; id++)
+ if (msgque[id] == IPC_UNUSED) {
+ msgque[id] = IPC_NOID;
+ goto found;
+ }
+ return -ENOSPC;
+
+found:
+ msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
+ if (!msq) {
+ msgque[id] = IPC_UNUSED;
+ if (msg_lock)
+ wake_up (&msg_lock);
+ return -ENOMEM;
+ }
+ ipcp = &msq->msg_perm;
+ ipcp->mode = (msgflg & 0x01FF);
+ ipcp->key = key;
+ ipcp->cuid = ipcp->uid = current->euid;
+ ipcp->gid = ipcp->cgid = current->egid;
+ ipcp->seq = msg_seq;
+ msq->msg_first = msq->msg_last = NULL;
+ msq->rwait = msq->wwait = NULL;
+ msq->msg_cbytes = msq->msg_qnum = 0;
+ msq->msg_lspid = msq->msg_lrpid = 0;
+ msq->msg_stime = msq->msg_rtime = 0;
+ msq->msg_qbytes = MSGMNB;
+ msq->msg_ctime = CURRENT_TIME;
+ if (id > max_msqid)
+ max_msqid = id;
+ msgque[id] = msq;
+ used_queues++;
+ if (msg_lock)
+ wake_up (&msg_lock);
+ return msg_seq * MSGMNI + id;
+}
+
+int sys_msgget (key_t key, int msgflg)
+{
+ int id;
+ struct msqid_ds *msq;
+
+ if (key == IPC_PRIVATE)
+ return newque(key, msgflg);
+ if ((id = findkey (key)) == -1) { /* key not used */
+ if (!(msgflg & IPC_CREAT))
+ return -ENOENT;
+ return newque(key, msgflg);
+ }
+ if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
+ return -EEXIST;
+ msq = msgque[id];
+ if (msq == IPC_UNUSED || msq == IPC_NOID)
+ return -EIDRM;
+ if (ipcperms(&msq->msg_perm, msgflg))
+ return -EACCES;
+ return msq->msg_perm.seq * MSGMNI +id;
+}
+
+static void freeque (int id)
+{
+ struct msqid_ds *msq = msgque[id];
+ struct msg *msgp, *msgh;
+
+ msq->msg_perm.seq++;
+ if ((int)((++msg_seq + 1) * MSGMNI) < 0)
+ msg_seq = 0;
+ msgbytes -= msq->msg_cbytes;
+ if (id == max_msqid)
+ while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
+ msgque[id] = IPC_UNUSED;
+ used_queues--;
+ while (msq->rwait || msq->wwait) {
+ if (msq->rwait)
+ wake_up (&msq->rwait);
+ if (msq->wwait)
+ wake_up (&msq->wwait);
+ schedule();
+ }
+ for (msgp = msq->msg_first; msgp; msgp = msgh ) {
+ msgh = msgp->msg_next;
+ msghdrs--;
+ kfree ((void *) msgp);
+ }
+ kfree (msq);
+}
+
+int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
+{
+ int id, err;
+ struct msqid_ds *msq, tbuf;
+ struct ipc_perm *ipcp;
+
+ if (msqid < 0 || cmd < 0)
+ return -EINVAL;
+ switch (cmd) {
+ case IPC_INFO:
+ case MSG_INFO:
+ if (!buf)
+ return -EFAULT;
+ {
+ struct msginfo msginfo;
+ msginfo.msgmni = MSGMNI;
+ msginfo.msgmax = MSGMAX;
+ msginfo.msgmnb = MSGMNB;
+ msginfo.msgmap = MSGMAP;
+ msginfo.msgpool = MSGPOOL;
+ msginfo.msgtql = MSGTQL;
+ msginfo.msgssz = MSGSSZ;
+ msginfo.msgseg = MSGSEG;
+ if (cmd == MSG_INFO) {
+ msginfo.msgpool = used_queues;
+ msginfo.msgmap = msghdrs;
+ msginfo.msgtql = msgbytes;
+ }
+ err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
+ if (err)
+ return err;
+ memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
+ return max_msqid;
+ }
+ case MSG_STAT:
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_WRITE, buf, sizeof (*msq));
+ if (err)
+ return err;
+ if (msqid > max_msqid)
+ return -EINVAL;
+ msq = msgque[msqid];
+ if (msq == IPC_UNUSED || msq == IPC_NOID)
+ return -EINVAL;
+ if (ipcperms (&msq->msg_perm, 0444))
+ return -EACCES;
+ id = msqid + msq->msg_perm.seq * MSGMNI;
+ memcpy_tofs (buf, msq, sizeof(*msq));
+ return id;
+ case IPC_SET:
+ if (!buf)
+ return -EFAULT;
+ memcpy_fromfs (&tbuf, buf, sizeof (*buf));
+ break;
+ case IPC_STAT:
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_WRITE, buf, sizeof(*msq));
+ if (err)
+ return err;
+ break;
+ }
+
+ id = msqid % MSGMNI;
+ msq = msgque [id];
+ if (msq == IPC_UNUSED || msq == IPC_NOID)
+ return -EINVAL;
+ ipcp = &msq->msg_perm;
+ if (ipcp->seq != msqid / MSGMNI)
+ return -EIDRM;
+
+ switch (cmd) {
+ case IPC_STAT:
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ memcpy_tofs (buf, msq, sizeof (*msq));
+ return 0;
+ break;
+ case IPC_RMID: case IPC_SET:
+ if (!suser() && current->euid != ipcp->cuid &&
+ current->euid != ipcp->uid)
+ return -EPERM;
+ if (cmd == IPC_RMID) {
+ freeque (id);
+ return 0;
+ }
+ if (tbuf.msg_qbytes > MSGMNB && !suser())
+ return -EPERM;
+ msq->msg_qbytes = tbuf.msg_qbytes;
+ ipcp->uid = tbuf.msg_perm.uid;
+ ipcp->gid = tbuf.msg_perm.gid;
+ ipcp->mode = (ipcp->mode & ~0x1FF) |
+ (0x1FF & tbuf.msg_perm.mode);
+ msq->msg_ctime = CURRENT_TIME;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
diff --git a/ipc/sem.c b/ipc/sem.c
new file mode 100644
index 0000000..62d323d
--- /dev/null
+++ b/ipc/sem.c
@@ -0,0 +1,488 @@
+/*
+ * linux/ipc/sem.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ */
+
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/sem.h>
+#include <linux/ipc.h>
+
+extern int ipcperms (struct ipc_perm *ipcp, short semflg);
+static int newary (key_t, int, int);
+static int findkey (key_t key);
+static void freeary (int id);
+
+static struct semid_ds *semary[SEMMNI];
+static int used_sems = 0, used_semids = 0;
+static struct wait_queue *sem_lock = NULL;
+static int sem_seq = 0;
+static int max_semid = 0;
+
+void sem_init (void)
+{
+ int i=0;
+
+ sem_lock = NULL;
+ used_sems = used_semids = max_semid = sem_seq = 0;
+ for (i=0; i < SEMMNI; i++)
+ semary[i] = IPC_UNUSED;
+ return;
+}
+
+static int findkey (key_t key)
+{
+ int id;
+ struct semid_ds *sma;
+
+ for (id=0; id <= max_semid; id++) {
+ while ((sma = semary[id]) == IPC_NOID)
+ interruptible_sleep_on (&sem_lock);
+ if (sma == IPC_UNUSED)
+ continue;
+ if (key == sma->sem_perm.key)
+ return id;
+ }
+ return -1;
+}
+
+static int newary (key_t key, int nsems, int semflg)
+{
+ int id;
+ struct semid_ds *sma;
+ struct ipc_perm *ipcp;
+ int size;
+
+ if (!nsems)
+ return -EINVAL;
+ if (used_sems + nsems > SEMMNS)
+ return -ENOSPC;
+ for (id=0; id < SEMMNI; id++)
+ if (semary[id] == IPC_UNUSED) {
+ semary[id] = IPC_NOID;
+ goto found;
+ }
+ return -ENOSPC;
+found:
+ size = sizeof (*sma) + nsems * sizeof (struct sem);
+ used_sems += nsems;
+ sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
+ if (!sma) {
+ semary[id] = IPC_UNUSED;
+ used_sems -= nsems;
+ if (sem_lock)
+ wake_up (&sem_lock);
+ return -ENOMEM;
+ }
+ memset (sma, 0, size);
+ sma->sem_base = (struct sem *) &sma[1];
+ ipcp = &sma->sem_perm;
+ ipcp->mode = (semflg & 0x01FF);
+ ipcp->key = key;
+ ipcp->cuid = ipcp->uid = current->euid;
+ ipcp->gid = ipcp->cgid = current->egid;
+ ipcp->seq = sem_seq;
+ sma->eventn = sma->eventz = NULL;
+ sma->sem_nsems = nsems;
+ sma->sem_ctime = CURRENT_TIME;
+ if (id > max_semid)
+ max_semid = id;
+ used_semids++;
+ semary[id] = sma;
+ if (sem_lock)
+ wake_up (&sem_lock);
+ return sem_seq * SEMMNI + id;
+}
+
+int sys_semget (key_t key, int nsems, int semflg)
+{
+ int id;
+ struct semid_ds *sma;
+
+ if (nsems < 0 || nsems > SEMMSL)
+ return -EINVAL;
+ if (key == IPC_PRIVATE)
+ return newary(key, nsems, semflg);
+ if ((id = findkey (key)) == -1) { /* key not used */
+ if (!(semflg & IPC_CREAT))
+ return -ENOENT;
+ return newary(key, nsems, semflg);
+ }
+ if (semflg & IPC_CREAT && semflg & IPC_EXCL)
+ return -EEXIST;
+ sma = semary[id];
+ if (nsems > sma->sem_nsems)
+ return -EINVAL;
+ if (ipcperms(&sma->sem_perm, semflg))
+ return -EACCES;
+ return sma->sem_perm.seq*SEMMNI + id;
+}
+
+static void freeary (int id)
+{
+ struct semid_ds *sma = semary[id];
+
+ sma->sem_perm.seq++;
+ if ((int)((++sem_seq + 1) * SEMMNI) < 0)
+ sem_seq = 0;
+ used_sems -= sma->sem_nsems;
+ if (id == max_semid)
+ while (max_semid && (semary[--max_semid] == IPC_UNUSED));
+ semary[id] = IPC_UNUSED;
+ used_semids--;
+ while (sma->eventz || sma->eventn) {
+ if (sma->eventz)
+ wake_up (&sma->eventz);
+ if (sma->eventn)
+ wake_up (&sma->eventn);
+ schedule();
+ }
+ kfree (sma);
+ return;
+}
+
+int sys_semctl (int semid, int semnum, int cmd, void *arg)
+{
+ int i, id, val = 0;
+ struct semid_ds *sma, *buf, tbuf;
+ struct ipc_perm *ipcp;
+ struct sem *curr;
+ struct sem_undo *un;
+ ushort nsems, *array = NULL;
+ ushort sem_io[SEMMSL];
+
+ if (semid < 0 || semnum < 0 || cmd < 0)
+ return -EINVAL;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case SEM_INFO:
+ {
+ struct seminfo seminfo, *tmp;
+ if (!arg || ! (tmp = (struct seminfo *) get_fs_long (arg)))
+ return -EFAULT;
+ seminfo.semmni = SEMMNI;
+ seminfo.semmns = SEMMNS;
+ seminfo.semmsl = SEMMSL;
+ seminfo.semopm = SEMOPM;
+ seminfo.semvmx = SEMVMX;
+ seminfo.semmnu = SEMMNU;
+ seminfo.semmap = SEMMAP;
+ seminfo.semume = SEMUME;
+ seminfo.semusz = SEMUSZ;
+ seminfo.semaem = SEMAEM;
+ if (cmd == SEM_INFO) {
+ seminfo.semusz = used_semids;
+ seminfo.semaem = used_sems;
+ }
+ i= verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo));
+ if (i)
+ return i;
+ memcpy_tofs (tmp, &seminfo, sizeof(struct seminfo));
+ return max_semid;
+ }
+
+ case SEM_STAT:
+ if (!arg || ! (buf = (struct semid_ds *) get_fs_long (arg)))
+ return -EFAULT;
+ i = verify_area (VERIFY_WRITE, buf, sizeof (*sma));
+ if (i)
+ return i;
+ if (semid > max_semid)
+ return -EINVAL;
+ sma = semary[semid];
+ if (sma == IPC_UNUSED || sma == IPC_NOID)
+ return -EINVAL;
+ if (ipcperms (&sma->sem_perm, 0444))
+ return -EACCES;
+ id = semid + sma->sem_perm.seq * SEMMNI;
+ memcpy_tofs (buf, sma, sizeof(*sma));
+ return id;
+ }
+
+ id = semid % SEMMNI;
+ sma = semary [id];
+ if (sma == IPC_UNUSED || sma == IPC_NOID)
+ return -EINVAL;
+ ipcp = &sma->sem_perm;
+ nsems = sma->sem_nsems;
+ if (ipcp->seq != semid / SEMMNI)
+ return -EIDRM;
+ if (semnum >= nsems)
+ return -EINVAL;
+ curr = &sma->sem_base[semnum];
+
+ switch (cmd) {
+ case GETVAL:
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case GETALL:
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ switch (cmd) {
+ case GETVAL : return curr->semval;
+ case GETPID : return curr->sempid;
+ case GETNCNT: return curr->semncnt;
+ case GETZCNT: return curr->semzcnt;
+ case GETALL:
+ if (!arg || ! (array = (ushort *) get_fs_long (arg)))
+ return -EFAULT;
+ i = verify_area (VERIFY_WRITE, array, nsems* sizeof(short));
+ if (i)
+ return i;
+ }
+ break;
+ case SETVAL:
+ if (!arg)
+ return -EFAULT;
+ if ((val = (int) get_fs_long (arg)) > SEMVMX || val < 0)
+ return -ERANGE;
+ break;
+ case IPC_RMID:
+ if (suser() || current->euid == ipcp->cuid ||
+ current->euid == ipcp->uid) {
+ freeary (id);
+ return 0;
+ }
+ return -EPERM;
+ case SETALL: /* arg is a pointer to an array of ushort */
+ if (!arg || ! (array = (ushort *) get_fs_long (arg)) )
+ return -EFAULT;
+ if ((i = verify_area (VERIFY_READ, array, sizeof tbuf)))
+ return i;
+ memcpy_fromfs (sem_io, array, nsems*sizeof(ushort));
+ for (i=0; i< nsems; i++)
+ if (sem_io[i] > SEMVMX)
+ return -ERANGE;
+ break;
+ case IPC_STAT:
+ if (!arg || !(buf = (struct semid_ds *) get_fs_long (arg)))
+ return -EFAULT;
+ if ((i = verify_area (VERIFY_WRITE, arg, sizeof tbuf)))
+ return i;
+ break;
+ case IPC_SET:
+ if (!arg || !(buf = (struct semid_ds *) get_fs_long (arg)))
+ return -EFAULT;
+ if ((i = verify_area (VERIFY_READ, buf, sizeof tbuf)))
+ return i;
+ memcpy_fromfs (&tbuf, buf, sizeof tbuf);
+ break;
+ }
+
+ if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
+ return -EIDRM;
+ if (ipcp->seq != semid / SEMMNI)
+ return -EIDRM;
+
+ switch (cmd) {
+ case GETALL:
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ for (i=0; i< sma->sem_nsems; i++)
+ sem_io[i] = sma->sem_base[i].semval;
+ memcpy_tofs (array, sem_io, nsems*sizeof(ushort));
+ break;
+ case SETVAL:
+ if (ipcperms (ipcp, 0222))
+ return -EACCES;
+ for (un = sma->undo; un; un = un->id_next)
+ if (semnum == un->sem_num)
+ un->semadj = 0;
+ sma->sem_ctime = CURRENT_TIME;
+ curr->semval = val;
+ if (sma->eventn)
+ wake_up (&sma->eventn);
+ if (sma->eventz)
+ wake_up (&sma->eventz);
+ break;
+ case IPC_SET:
+ if (suser() || current->euid == ipcp->cuid ||
+ current->euid == ipcp->uid) {
+ ipcp->uid = tbuf.sem_perm.uid;
+ ipcp->gid = tbuf.sem_perm.gid;
+ ipcp->mode = (ipcp->mode & ~0777)
+ | (tbuf.sem_perm.mode & 0777);
+ sma->sem_ctime = CURRENT_TIME;
+ return 0;
+ }
+ return -EPERM;
+ case IPC_STAT:
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ memcpy_tofs (buf, sma, sizeof (*sma));
+ break;
+ case SETALL:
+ if (ipcperms (ipcp, 0222))
+ return -EACCES;
+ for (i=0; i<nsems; i++)
+ sma->sem_base[i].semval = sem_io[i];
+ for (un = sma->undo; un != NULL; un = un->id_next)
+ un->semadj = 0;
+ if (sma->eventn)
+ wake_up (&sma->eventn);
+ if (sma->eventz)
+ wake_up (&sma->eventz);
+ sma->sem_ctime = CURRENT_TIME;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
+{
+ int i, id;
+ struct semid_ds *sma;
+ struct sem *curr = NULL;
+ struct sembuf sops[SEMOPM], *sop;
+ struct sem_undo *un;
+ int undos = 0, alter = 0, semncnt = 0, semzcnt = 0;
+
+ if (nsops < 1 || semid < 0)
+ return -EINVAL;
+ if (nsops > SEMOPM)
+ return -E2BIG;
+ if (!tsops)
+ return -EFAULT;
+ memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
+ id = semid % SEMMNI;
+ if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
+ return -EINVAL;
+ for (i=0; i<nsops; i++) {
+ sop = &sops[i];
+ if (sop->sem_num > sma->sem_nsems)
+ return -EFBIG;
+ if (sop->sem_flg & SEM_UNDO)
+ undos++;
+ if (sop->sem_op) {
+ alter++;
+ if (sop->sem_op > 0)
+ semncnt ++;
+ }
+ }
+ if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444))
+ return -EACCES;
+ /*
+ * ensure every sop with undo gets an undo structure
+ */
+ if (undos) {
+ for (i=0; i<nsops; i++) {
+ if (!(sops[i].sem_flg & SEM_UNDO))
+ continue;
+ for (un = current->semun; un; un = un->proc_next)
+ if ((un->semid == semid) &&
+ (un->sem_num == sops[i].sem_num))
+ break;
+ if (un)
+ continue;
+ un = (struct sem_undo *)
+ kmalloc (sizeof(*un), GFP_ATOMIC);
+ if (!un)
+ return -ENOMEM; /* freed on exit */
+ un->semid = semid;
+ un->semadj = 0;
+ un->sem_num = sops[i].sem_num;
+ un->proc_next = current->semun;
+ current->semun = un;
+ un->id_next = sma->undo;
+ if (sma->undo)
+ sma->undo->id_prev = un;
+ sma->undo = un->id_prev = un;
+ }
+ }
+
+ slept:
+ if (sma->sem_perm.seq != semid / SEMMNI)
+ return -EIDRM;
+ for (i=0; i<nsops; i++) {
+ sop = &sops[i];
+ curr = &sma->sem_base[sop->sem_num];
+ if (sop->sem_op + curr->semval > SEMVMX)
+ return -ERANGE;
+ if (!sop->sem_op && curr->semval) {
+ if (sop->sem_flg & IPC_NOWAIT)
+ return -EAGAIN;
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ curr->semzcnt++;
+ interruptible_sleep_on (&sma->eventz);
+ curr->semzcnt--;
+ goto slept;
+ }
+ if ((sop->sem_op + curr->semval < 0) ) {
+ if (sop->sem_flg & IPC_NOWAIT)
+ return -EAGAIN;
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ curr->semncnt++;
+ interruptible_sleep_on (&sma->eventn);
+ curr->semncnt--;
+ goto slept;
+ }
+ }
+
+ for (i=0; i<nsops; i++) {
+ sop = &sops[i];
+ curr = &sma->sem_base[sop->sem_num];
+ curr->sempid = current->pid;
+ if (!(curr->semval += sop->sem_op))
+ semzcnt++;
+ if (!(sop->sem_flg & SEM_UNDO))
+ continue;
+ for (un = current->semun; un; un = un->proc_next)
+ if ((un->semid == semid) &&
+ (un->sem_num == sop->sem_num))
+ break;
+ if (!un) {
+ printk ("semop : no undo for op %d\n", i);
+ continue;
+ }
+ un->semadj -= sop->sem_op;
+ }
+ sma->sem_otime = CURRENT_TIME;
+ if (semncnt && sma->eventn)
+ wake_up(&sma->eventn);
+ if (semzcnt && sma->eventz)
+ wake_up(&sma->eventz);
+ return curr->semval;
+}
+
+/*
+ * add semadj values to semaphores if alowed. Silently ignore errors!
+ */
+void sem_exit (void)
+{
+ struct sem_undo *un, *tmp;
+ struct semid_ds *sma;
+ struct sem *sem = NULL;
+
+ for (un = current->semun; un; kfree(un), un = tmp) {
+ un->id_prev->id_next = un->id_next;
+ if (un->id_next)
+ un->id_next->id_prev = un->id_prev;
+ tmp = un->proc_next;
+ if (!un->semadj)
+ continue;
+ sma = semary[un->semid % SEMMNI];
+ if (sma == IPC_UNUSED || sma == IPC_NOID
+ || sma->sem_perm.seq != un->semid / SEMMNI)
+ continue;
+ sem = &sma->sem_base[un->sem_num];
+ if (sem->semval + un->semadj >= 0) {
+ sem->semval += un->semadj;
+ sem->sempid = current->pid;
+ sma->sem_otime = CURRENT_TIME;
+ if (un->semadj > 0 && sma->eventn)
+ wake_up (&sma->eventn);
+ if (!sem->semval && sma->eventz)
+ wake_up (&sma->eventz);
+ }
+ }
+ current->semun = NULL;
+ return;
+}
diff --git a/ipc/shm.c b/ipc/shm.c
new file mode 100644
index 0000000..00979d5
--- /dev/null
+++ b/ipc/shm.c
@@ -0,0 +1,730 @@
+/*
+ * linux/ipc/shm.c
+ * Copyright (C) 1992, 1993 Krishna Balasubramanian
+ * Many improvements/fixes by Bruno Haible.
+ * assume user segments start at 0x00
+ */
+
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/ipc.h>
+#include <linux/shm.h>
+
+extern int ipcperms (struct ipc_perm *ipcp, short semflg);
+extern unsigned int get_swap_page(void);
+static int findkey (key_t key);
+static int newseg (key_t key, int shmflg, int size);
+static int shm_map (struct shm_desc *shmd, int remap);
+static void killseg (int id);
+
+static int shm_tot = 0; /* total number of shared memory pages */
+static int shm_rss = 0; /* number of shared memory pages that are in memory */
+static int shm_swp = 0; /* number of shared memory pages that are in swap */
+static int shm_seq = 0; /* is incremented, for recognizing stale ids */
+static int max_shmid = 0; /* every used id is <= max_shmid */
+static struct wait_queue *shm_lock = NULL;
+static struct shmid_ds *shm_segs[SHMMNI];
+
+/* some statistics */
+static ulong swap_attempts = 0;
+static ulong swap_successes = 0;
+static ulong used_segs = 0;
+
+void shm_init (void)
+{
+ int id;
+
+ for (id = 0; id < SHMMNI; id++)
+ shm_segs[id] = IPC_UNUSED;
+ shm_tot = shm_rss = shm_seq = max_shmid = used_segs = 0;
+ shm_lock = NULL;
+ return;
+}
+
+static int findkey (key_t key)
+{
+ int id;
+ struct shmid_ds *shp;
+
+ for (id=0; id <= max_shmid; id++) {
+ while ((shp = shm_segs[id]) == IPC_NOID)
+ sleep_on (&shm_lock);
+ if (shp == IPC_UNUSED)
+ continue;
+ if (key == shp->shm_perm.key)
+ return id;
+ }
+ return -1;
+}
+
+/*
+ * allocate new shmid_ds and pgtable. protected by shm_segs[id] = NOID.
+ */
+static int newseg (key_t key, int shmflg, int size)
+{
+ struct shmid_ds *shp;
+ int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
+ int id, i;
+
+ if (size < SHMMIN)
+ return -EINVAL;
+ if (shm_tot + numpages >= SHMALL)
+ return -ENOSPC;
+ for (id=0; id < SHMMNI; id++)
+ if (shm_segs[id] == IPC_UNUSED) {
+ shm_segs[id] = IPC_NOID;
+ goto found;
+ }
+ return -ENOSPC;
+
+found:
+ shp = (struct shmid_ds *) kmalloc (sizeof (*shp), GFP_KERNEL);
+ if (!shp) {
+ shm_segs[id] = IPC_UNUSED;
+ if (shm_lock)
+ wake_up (&shm_lock);
+ return -ENOMEM;
+ }
+
+ shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL);
+ if (!shp->shm_pages) {
+ shm_segs[id] = IPC_UNUSED;
+ if (shm_lock)
+ wake_up (&shm_lock);
+ kfree (shp);
+ return -ENOMEM;
+ }
+
+ for (i=0; i< numpages; shp->shm_pages[i++] = 0);
+ shm_tot += numpages;
+ shp->shm_perm.key = key;
+ shp->shm_perm.mode = (shmflg & 0777);
+ shp->shm_perm.cuid = shp->shm_perm.uid = current->euid;
+ shp->shm_perm.cgid = shp->shm_perm.gid = current->egid;
+ shp->shm_perm.seq = shm_seq;
+ shp->shm_segsz = size;
+ shp->shm_cpid = current->pid;
+ shp->attaches = NULL;
+ shp->shm_lpid = shp->shm_nattch = 0;
+ shp->shm_atime = shp->shm_dtime = 0;
+ shp->shm_ctime = CURRENT_TIME;
+ shp->shm_npages = numpages;
+
+ if (id > max_shmid)
+ max_shmid = id;
+ shm_segs[id] = shp;
+ used_segs++;
+ if (shm_lock)
+ wake_up (&shm_lock);
+ return id + shm_seq*SHMMNI;
+}
+
+int sys_shmget (key_t key, int size, int shmflg)
+{
+ struct shmid_ds *shp;
+ int id = 0;
+
+ if (size < 0 || size > SHMMAX)
+ return -EINVAL;
+ if (key == IPC_PRIVATE)
+ return newseg(key, shmflg, size);
+ if ((id = findkey (key)) == -1) {
+ if (!(shmflg & IPC_CREAT))
+ return -ENOENT;
+ return newseg(key, shmflg, size);
+ }
+ if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
+ return -EEXIST;
+ shp = shm_segs[id];
+ if (shp->shm_perm.mode & SHM_DEST)
+ return -EIDRM;
+ if (size > shp->shm_segsz)
+ return -EINVAL;
+ if (ipcperms (&shp->shm_perm, shmflg))
+ return -EACCES;
+ return shp->shm_perm.seq*SHMMNI + id;
+}
+
+/*
+ * Only called after testing nattch and SHM_DEST.
+ * Here pages, pgtable and shmid_ds are freed.
+ */
+static void killseg (int id)
+{
+ struct shmid_ds *shp;
+ int i, numpages;
+ ulong page;
+
+ shp = shm_segs[id];
+ if (shp == IPC_NOID || shp == IPC_UNUSED) {
+ printk ("shm nono: killseg called on unused seg id=%d\n", id);
+ return;
+ }
+ shp->shm_perm.seq++; /* for shmat */
+ numpages = shp->shm_npages;
+ if ((int)((++shm_seq + 1) * SHMMNI) < 0)
+ shm_seq = 0;
+ shm_segs[id] = IPC_UNUSED;
+ used_segs--;
+ if (id == max_shmid)
+ while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED));
+ if (!shp->shm_pages) {
+ printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id);
+ return;
+ }
+ for (i=0; i< numpages ; i++) {
+ if (!(page = shp->shm_pages[i]))
+ continue;
+ if (page & 1) {
+ free_page (page & ~0xfff);
+ shm_rss--;
+ } else {
+ swap_free (page);
+ shm_swp--;
+ }
+ }
+ kfree(shp->shm_pages);
+ shm_tot -= numpages;
+ kfree (shp);
+ return;
+}
+
+int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
+{
+ struct shmid_ds *shp, tbuf;
+ struct ipc_perm *ipcp;
+ int id, err;
+
+ if (cmd < 0 || shmid < 0)
+ return -EINVAL;
+ if (cmd == IPC_SET) {
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_READ, buf, sizeof (*buf));
+ if (err)
+ return err;
+ memcpy_fromfs (&tbuf, buf, sizeof (*buf));
+ }
+
+ switch (cmd) { /* replace with proc interface ? */
+ case IPC_INFO:
+ {
+ struct shminfo shminfo;
+ if (!buf)
+ return -EFAULT;
+ shminfo.shmmni = SHMMNI;
+ shminfo.shmmax = SHMMAX;
+ shminfo.shmmin = SHMMIN;
+ shminfo.shmall = SHMALL;
+ shminfo.shmseg = SHMSEG;
+ err = verify_area (VERIFY_WRITE, buf, sizeof (struct shminfo));
+ if (err)
+ return err;
+ memcpy_tofs (buf, &shminfo, sizeof(struct shminfo));
+ return max_shmid;
+ }
+ case SHM_INFO:
+ {
+ struct shm_info shm_info;
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_WRITE, buf, sizeof (shm_info));
+ if (err)
+ return err;
+ shm_info.used_ids = used_segs;
+ shm_info.shm_rss = shm_rss;
+ shm_info.shm_tot = shm_tot;
+ shm_info.shm_swp = shm_swp;
+ shm_info.swap_attempts = swap_attempts;
+ shm_info.swap_successes = swap_successes;
+ memcpy_tofs (buf, &shm_info, sizeof(shm_info));
+ return max_shmid;
+ }
+ case SHM_STAT:
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_WRITE, buf, sizeof (*shp));
+ if (err)
+ return err;
+ if (shmid > max_shmid)
+ return -EINVAL;
+ shp = shm_segs[shmid];
+ if (shp == IPC_UNUSED || shp == IPC_NOID)
+ return -EINVAL;
+ if (ipcperms (&shp->shm_perm, 0444))
+ return -EACCES;
+ id = shmid + shp->shm_perm.seq * SHMMNI;
+ memcpy_tofs (buf, shp, sizeof(*shp));
+ return id;
+ }
+
+ shp = shm_segs[id = shmid % SHMMNI];
+ if (shp == IPC_UNUSED || shp == IPC_NOID)
+ return -EIDRM;
+ ipcp = &shp->shm_perm;
+ if (ipcp->seq != shmid / SHMMNI)
+ return -EIDRM;
+
+ switch (cmd) {
+ case SHM_UNLOCK:
+ if (!suser())
+ return -EPERM;
+ if (!(ipcp->mode & SHM_LOCKED))
+ return -EINVAL;
+ ipcp->mode &= ~SHM_LOCKED;
+ break;
+ case SHM_LOCK:
+/* Alow superuser to lock segment in memory */
+/* Should the pages be faulted in here or leave it to user? */
+/* need to determine interaction with current->swappable */
+ if (!suser())
+ return -EPERM;
+ if (ipcp->mode & SHM_LOCKED)
+ return -EINVAL;
+ ipcp->mode |= SHM_LOCKED;
+ break;
+ case IPC_STAT:
+ if (ipcperms (ipcp, 0444))
+ return -EACCES;
+ if (!buf)
+ return -EFAULT;
+ err = verify_area (VERIFY_WRITE, buf, sizeof (*shp));
+ if (err)
+ return err;
+ memcpy_tofs (buf, shp, sizeof(*shp));
+ break;
+ case IPC_SET:
+ if (suser() || current->euid == shp->shm_perm.uid ||
+ current->euid == shp->shm_perm.cuid) {
+ ipcp->uid = tbuf.shm_perm.uid;
+ ipcp->gid = tbuf.shm_perm.gid;
+ ipcp->mode = (ipcp->mode & ~0777)
+ | (tbuf.shm_perm.mode & 0777);
+ shp->shm_ctime = CURRENT_TIME;
+ break;
+ }
+ return -EPERM;
+ case IPC_RMID:
+ if (suser() || current->euid == shp->shm_perm.uid ||
+ current->euid == shp->shm_perm.cuid) {
+ shp->shm_perm.mode |= SHM_DEST;
+ if (shp->shm_nattch <= 0)
+ killseg (id);
+ break;
+ }
+ return -EPERM;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * check range is unmapped, ensure page tables exist
+ * mark page table entries with shm_sgn.
+ * if remap != 0 the range is remapped.
+ */
+static int shm_map (struct shm_desc *shmd, int remap)
+{
+ unsigned long invalid = 0;
+ unsigned long *page_table;
+ unsigned long tmp, shm_sgn;
+ unsigned long page_dir = shmd->task->tss.cr3;
+
+ /* check that the range is unmapped and has page_tables */
+ for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE) {
+ page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc));
+ if (*page_table & PAGE_PRESENT) {
+ page_table = (ulong *) (0xfffff000 & *page_table);
+ page_table += ((tmp >> PAGE_SHIFT) & 0x3ff);
+ if (*page_table) {
+ if (!remap)
+ return -EINVAL;
+ if (*page_table & PAGE_PRESENT) {
+ --current->rss;
+ free_page (*page_table & ~0xfff);
+ }
+ else
+ swap_free (*page_table);
+ invalid++;
+ }
+ continue;
+ }
+ {
+ unsigned long new_pt = get_free_page(GFP_KERNEL);
+ if (!new_pt)
+ return -ENOMEM;
+ *page_table = new_pt | PAGE_TABLE;
+ tmp = ((tmp + (PAGE_SIZE << 10) - 1) & 0xff400000) -PAGE_SIZE;
+ }}
+ if (invalid)
+ invalidate();
+
+ /* map page range */
+ shm_sgn = shmd->shm_sgn;
+ for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE,
+ shm_sgn += (1 << SHM_IDX_SHIFT)) {
+ page_table = (ulong *) (page_dir + ((tmp >> 20) & 0xffc));
+ page_table = (ulong *) (0xfffff000 & *page_table);
+ page_table += (tmp >> PAGE_SHIFT) & 0x3ff;
+ *page_table = shm_sgn;
+ }
+ return 0;
+}
+
+/*
+ * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
+ * raddr is needed to return addresses above 2Gig.
+ * Specific attaches are allowed over the executable....
+ */
+int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
+{
+ struct shmid_ds *shp;
+ struct shm_desc *shmd;
+ int err;
+ unsigned int id;
+ unsigned long addr;
+
+ if (shmid < 0)
+ return -EINVAL;
+
+ shp = shm_segs[id = shmid % SHMMNI];
+ if (shp == IPC_UNUSED || shp == IPC_NOID)
+ return -EINVAL;
+
+#define SHM_RANGE_END 0x60000000
+#define SHM_RANGE_START 0x40000000
+
+ if (!(addr = (ulong) shmaddr)) {
+ if (shmflg & SHM_REMAP)
+ return -EINVAL;
+ /* set addr below all current unspecified attaches */
+ addr = SHM_RANGE_END;
+ for (shmd = current->shm; shmd; shmd = shmd->task_next) {
+ if (shmd->start < SHM_RANGE_START)
+ continue;
+ if (addr >= shmd->start)
+ addr = shmd->start;
+ }
+ addr = (addr - shp->shm_segsz) & ~0xfff;
+ } else if (addr & (SHMLBA-1)) {
+ if (shmflg & SHM_RND)
+ addr &= ~(SHMLBA-1); /* round down */
+ else
+ return -EINVAL;
+ }
+ if ((addr > current->start_stack - 16384 - PAGE_SIZE*shp->shm_npages))
+ return -EINVAL;
+ if (shmflg & SHM_REMAP)
+ for (shmd = current->shm; shmd; shmd = shmd->task_next) {
+ if (addr >= shmd->start && addr < shmd->end)
+ return -EINVAL;
+ if (addr + shp->shm_segsz >= shmd->start &&
+ addr + shp->shm_segsz < shmd->end)
+ return -EINVAL;
+ }
+
+ if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? 0444 : 0666))
+ return -EACCES;
+ if (shp->shm_perm.seq != shmid / SHMMNI)
+ return -EIDRM;
+
+ shmd = (struct shm_desc *) kmalloc (sizeof(*shmd), GFP_KERNEL);
+ if (!shmd)
+ return -ENOMEM;
+ if ((shp != shm_segs[id]) || (shp->shm_perm.seq != shmid / SHMMNI)) {
+ kfree (shmd);
+ return -EIDRM;
+ }
+ shmd->shm_sgn = (SHM_SWP_TYPE << 1) | (id << SHM_ID_SHIFT) |
+ (shmflg & SHM_RDONLY ? SHM_READ_ONLY : 0);
+ shmd->start = addr;
+ shmd->end = addr + shp->shm_npages * PAGE_SIZE;
+ shmd->task = current;
+
+ shp->shm_nattch++; /* prevent destruction */
+ if (addr < current->end_data) {
+ iput (current->executable);
+ current->executable = NULL;
+/* current->end_data = current->end_code = 0; */
+ }
+
+ if ((err = shm_map (shmd, shmflg & SHM_REMAP))) {
+ if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
+ killseg(id);
+ kfree (shmd);
+ return err;
+ }
+
+ shmd->task_next = current->shm;
+ current->shm = shmd;
+ shmd->seg_next = shp->attaches;
+ shp->attaches = shmd;
+ shp->shm_lpid = current->pid;
+ shp->shm_atime = CURRENT_TIME;
+ put_fs_long (addr, raddr);
+ return 0;
+}
+
+/*
+ * remove the first attach descriptor from the list *shmdp.
+ * free memory for segment if it is marked destroyed.
+ * The descriptor is detached before the sleep in unmap_page_range.
+ */
+static void detach (struct shm_desc **shmdp)
+{
+ struct shm_desc *shmd = *shmdp;
+ struct shmid_ds *shp;
+ int id;
+
+ id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK;
+ shp = shm_segs[id];
+ *shmdp = shmd->task_next;
+ for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->seg_next)
+ if (*shmdp == shmd) {
+ *shmdp = shmd->seg_next;
+ goto found;
+ }
+ printk("detach: shm segment (id=%d) attach list inconsistent\n",id);
+
+ found:
+ unmap_page_range (shmd->start, shp->shm_segsz); /* sleeps */
+ kfree(shmd);
+ shp->shm_lpid = current->pid;
+ shp->shm_dtime = CURRENT_TIME;
+ if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
+ killseg (id); /* sleeps */
+ return;
+}
+
+/*
+ * detach and kill segment if marked destroyed.
+ * The work is done in detach.
+ */
+int sys_shmdt (char *shmaddr)
+{
+ struct shm_desc *shmd, **shmdp;
+
+ for (shmdp = &current->shm; (shmd = *shmdp); shmdp=&shmd->task_next) {
+ if (shmd->start == (ulong) shmaddr) {
+ detach (shmdp);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/*
+ * detach all attached segments.
+ */
+void shm_exit (void)
+{
+ while (current->shm)
+ detach(&current->shm);
+ return;
+}
+
+/*
+ * copy the parent shm descriptors and update nattch
+ * parent is stuck in fork so an attach on each segment is assured.
+ * copy_page_tables does the mapping.
+ */
+int shm_fork (struct task_struct *p1, struct task_struct *p2)
+{
+ struct shm_desc *shmd, *new = NULL, *tmp;
+ struct shmid_ds *shp;
+ int id;
+
+ if (!p1->shm)
+ return 0;
+ for (shmd = p1->shm; shmd; shmd = shmd->task_next) {
+ tmp = (struct shm_desc *) kmalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ while (new) {
+ tmp = new->task_next;
+ kfree(new);
+ new = tmp;
+ }
+ free_page_tables (p2);
+ return -ENOMEM;
+ }
+ *tmp = *shmd;
+ tmp->task = p2;
+ tmp->task_next = new;
+ new = tmp;
+ }
+ p2->shm = new;
+ for (shmd = new; shmd; shmd = shmd->task_next) {
+ id = (shmd->shm_sgn >> SHM_ID_SHIFT) & 0xfff;
+ shp = shm_segs[id];
+ if (shp == IPC_UNUSED) {
+ printk("shm_fork: unused id=%d PANIC\n", id);
+ return -ENOMEM;
+ }
+ shmd->seg_next = shp->attaches;
+ shp->attaches = shmd;
+ shp->shm_nattch++;
+ shp->shm_atime = CURRENT_TIME;
+ shp->shm_lpid = current->pid;
+ }
+ return 0;
+}
+
+/*
+ * page not present ... go through shm_pages .. called from swap_in()
+ */
+void shm_no_page (unsigned long *ptent)
+{
+ unsigned long page;
+ unsigned long code = *ptent;
+ struct shmid_ds *shp;
+ unsigned int id, idx;
+
+ id = (code >> SHM_ID_SHIFT) & SHM_ID_MASK;
+ if (id > max_shmid) {
+ printk ("shm_no_page: id=%d too big. proc mem corruptedn", id);
+ return;
+ }
+ shp = shm_segs[id];
+ if (shp == IPC_UNUSED || shp == IPC_NOID) {
+ printk ("shm_no_page: id=%d invalid. Race.\n", id);
+ return;
+ }
+ idx = (code >> SHM_IDX_SHIFT) & SHM_IDX_MASK;
+ if (idx >= shp->shm_npages) {
+ printk ("shm_no_page : too large page index. id=%d\n", id);
+ return;
+ }
+
+ if (!(shp->shm_pages[idx] & PAGE_PRESENT)) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page) {
+ oom(current);
+ *ptent = BAD_PAGE | PAGE_ACCESSED | 7;
+ return;
+ }
+ if (shp->shm_pages[idx] & PAGE_PRESENT) {
+ free_page (page);
+ goto done;
+ }
+ if (shp->shm_pages[idx]) {
+ read_swap_page (shp->shm_pages[idx], (char *) page);
+ if (shp->shm_pages[idx] & PAGE_PRESENT) {
+ free_page (page);
+ goto done;
+ }
+ swap_free (shp->shm_pages[idx]);
+ shm_swp--;
+ }
+ shm_rss++;
+ shp->shm_pages[idx] = page | (PAGE_SHARED | PAGE_DIRTY);
+ } else
+ --current->maj_flt; /* was incremented in do_no_page */
+
+done:
+ current->min_flt++;
+ page = shp->shm_pages[idx];
+ if (code & SHM_READ_ONLY) /* write-protect */
+ page &= ~2;
+ mem_map[MAP_NR(page)]++;
+ *ptent = page;
+ return;
+}
+
+/*
+ * Goes through counter = (shm_rss << prio) present shm pages.
+ */
+static unsigned long swap_id = 0; /* currently being swapped */
+static unsigned long swap_idx = 0; /* next to swap */
+
+int shm_swap (int prio)
+{
+ unsigned long page;
+ struct shmid_ds *shp;
+ struct shm_desc *shmd;
+ unsigned int swap_nr;
+ unsigned long id, idx, invalid = 0;
+ int counter;
+
+ counter = shm_rss >> prio;
+ if (!counter || !(swap_nr = get_swap_page()))
+ return 0;
+
+ check_id:
+ shp = shm_segs[swap_id];
+ if (shp == IPC_UNUSED || shp == IPC_NOID || shp->shm_perm.mode & SHM_LOCKED ) {
+ swap_idx = 0;
+ if (++swap_id > max_shmid)
+ swap_id = 0;
+ goto check_id;
+ }
+ id = swap_id;
+
+ check_table:
+ idx = swap_idx++;
+ if (idx >= shp->shm_npages) {
+ swap_idx = 0;
+ if (++swap_id > max_shmid)
+ swap_id = 0;
+ goto check_id;
+ }
+
+ page = shp->shm_pages[idx];
+ if (!(page & PAGE_PRESENT))
+ goto check_table;
+ swap_attempts++;
+
+ if (--counter < 0) { /* failed */
+ if (invalid)
+ invalidate();
+ swap_free (swap_nr);
+ return 0;
+ }
+ for (shmd = shp->attaches; shmd; shmd = shmd->seg_next) {
+ unsigned long tmp, *pte;
+ if ((shmd->shm_sgn >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
+ printk ("shm_swap: id=%d does not match shmd\n", id);
+ continue;
+ }
+ tmp = shmd->start + (idx << PAGE_SHIFT);
+ if (tmp >= shmd->end) {
+ printk ("shm_swap: too large idx=%d id=%d PANIC\n",idx, id);
+ continue;
+ }
+ pte = (ulong *) (shmd->task->tss.cr3 + ((tmp>>20) & 0xffc));
+ if (!(*pte & 1)) {
+ printk("shm_swap: bad pgtbl! id=%d start=%x idx=%d\n",
+ id, shmd->start, idx);
+ *pte = 0;
+ continue;
+ }
+ pte = (ulong *) (0xfffff000 & *pte);
+ pte += ((tmp >> PAGE_SHIFT) & 0x3ff);
+ tmp = *pte;
+ if (!(tmp & PAGE_PRESENT))
+ continue;
+ if (tmp & PAGE_ACCESSED) {
+ *pte &= ~PAGE_ACCESSED;
+ continue;
+ }
+ tmp = shmd->shm_sgn | idx << SHM_IDX_SHIFT;
+ *pte = tmp;
+ mem_map[MAP_NR(page)]--;
+ shmd->task->rss--;
+ invalid++;
+ }
+
+ if (mem_map[MAP_NR(page)] != 1)
+ goto check_table;
+ page &= ~0xfff;
+ shp->shm_pages[idx] = swap_nr;
+ if (invalid)
+ invalidate();
+ write_swap_page (swap_nr, (char *) page);
+ free_page (page);
+ swap_successes++;
+ shm_swp++;
+ shm_rss--;
+ return 1;
+}
diff --git a/ipc/util.c b/ipc/util.c
new file mode 100644
index 0000000..bd4e881
--- /dev/null
+++ b/ipc/util.c
@@ -0,0 +1,152 @@
+/*
+ * linux/ipc/util.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+
+void ipc_init (void);
+int sys_ipc (uint call, int first, int second, int third, void *ptr);
+
+#ifdef CONFIG_SYSVIPC
+
+int ipcperms (struct ipc_perm *ipcp, short flag);
+extern void sem_init (void), msg_init (void), shm_init (void);
+extern int sys_semget (key_t key, int nsems, int semflg);
+extern int sys_semop (int semid, struct sembuf *sops, unsigned nsops);
+extern int sys_semctl (int semid, int semnum, int cmd, void *arg);
+extern int sys_msgget (key_t key, int msgflg);
+extern int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
+extern int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
+ int msgflg);
+extern int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf);
+extern int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+extern int sys_shmget (key_t key, int size, int flag);
+extern int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr);
+extern int sys_shmdt (char *shmaddr);
+
+void ipc_init (void)
+{
+ sem_init();
+ msg_init();
+ shm_init();
+ return;
+}
+
+/*
+ * Check user, group, other permissions for access
+ * to ipc resources. return 0 if allowed
+ */
+int ipcperms (struct ipc_perm *ipcp, short flag)
+{
+ int i, perm = 0007, euid = current->euid, egid;
+
+ if (suser())
+ return 0;
+ if (euid == ipcp->cuid || euid == ipcp->uid)
+ perm = 0700;
+ else {
+ for (i = 0; (egid = current->groups[i]) != NOGROUP; i++)
+ if ((egid == ipcp->cgid) || (egid == ipcp->gid)) {
+ perm = 0070;
+ break;
+ }
+ }
+ if (!(flag & perm) || flag & perm & ~ipcp->mode)
+ return -1;
+ return 0;
+}
+
+int sys_ipc (uint call, int first, int second, int third, void *ptr)
+{
+
+ if (call <= SEMCTL)
+ switch (call) {
+ case SEMOP:
+ return sys_semop (first, (struct sembuf *)ptr, second);
+ case SEMGET:
+ return sys_semget (first, second, third);
+ case SEMCTL:
+ return sys_semctl (first, second, third, ptr);
+ default:
+ return -EINVAL;
+ }
+ if (call <= MSGCTL)
+ switch (call) {
+ case MSGSND:
+ return sys_msgsnd (first, (struct msgbuf *) ptr,
+ second, third);
+ case MSGRCV: {
+ struct ipc_kludge tmp;
+ if (!ptr)
+ return -EINVAL;
+ memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
+ sizeof (tmp));
+ return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp,
+ third);
+ }
+ case MSGGET:
+ return sys_msgget ((key_t) first, second);
+ case MSGCTL:
+ return sys_msgctl (first, second,
+ (struct msqid_ds *) ptr);
+ default:
+ return -EINVAL;
+ }
+ if (call <= SHMCTL)
+ switch (call) {
+ case SHMAT: /* returning shmaddr > 2G will screw up */
+ return sys_shmat (first, (char *) ptr, second,
+ (ulong *) third);
+ case SHMDT:
+ return sys_shmdt ((char *)ptr);
+ case SHMGET:
+ return sys_shmget (first, second, third);
+ case SHMCTL:
+ return sys_shmctl (first, second,
+ (struct shmid_ds *) ptr);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+#else /* not CONFIG_SYSVIPC */
+
+int sys_ipc (uint call, int first, int second, int third, void *ptr)
+{
+ return -ENOSYS;
+}
+
+int shm_fork (struct task_struct *p1, struct task_struct *p2)
+{
+ return 0;
+}
+
+void sem_exit (void)
+{
+ return;
+}
+
+void shm_exit (void)
+{
+ return;
+}
+
+int shm_swap (int prio)
+{
+ return 0;
+}
+
+void shm_no_page (unsigned long *ptent)
+{
+ return;
+}
+
+#endif /* CONFIG_SYSVIPC */
diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README
index 5b7c05d..f34b3ff 100644
--- a/kernel/FPU-emu/README
+++ b/kernel/FPU-emu/README
@@ -32,19 +32,21 @@ My target FPU for wm-FPU-emu is that described in the Intel486
Programmer's Reference Manual (1992 edition). Numerous facets of the
functioning of the FPU are not well covered in the Reference Manual;
in the absence of clear details I have made guesses about the most
-reasonable behaviour.
+reasonable behaviour. Recently, this situation has improved because
+I now have some access to the results produced by a real 80486 FPU.
wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
See "Limitations" later in this file for a partial list of some
differences. I believe that the missing features are never used by
normal C or FORTRAN programs.
+
Please report bugs, etc to me at:
apm233m@vaxc.cc.monash.edu.au
--Bill Metzenthen
- March 1993
+ May 1993
----------------------- Internals of wm-FPU-emu -----------------------
@@ -83,29 +85,28 @@ is confined to five files:
----------------------- Limitations of wm-FPU-emu -----------------------
There are a number of differences between the current wm-FPU-emu
-(version beta 1.3) and the 80486 FPU (apart from bugs). Some of the
+(version beta 1.4) and the 80486 FPU (apart from bugs). Some of the
more important differences are listed below:
-Internal computations do not use de-normal numbers (but External
-de-normals ARE recognised and generated). The design of wm-FPU-emu
-allows a larger exponent range than the 80486 FPU for internal
-computations.
-
-All internal computations are performed at 64 bit or higher precision.
-The results of the basic arithmetic functions and sqrt are then
-rounded to the precision required by the PC bits of the FPU control
-word. Under the crt0 version for Linux current at March 1993, the FPU
-PC bits specify 53 bits precision.
+All internal computations are performed at 64 bit or higher precision
+and rounded etc as required by the PC bits of the FPU control word.
+Under the crt0 version for Linux current at March 1993, the FPU PC
+bits specify 53 bits precision.
-The precision flag (PE of the FPU status word) is not implemented.
-Does anyone write code which uses this feature?
-
-The Roundup flag (C1) is not implemented.
+The precision flag (PE of the FPU status word) and the Roundup flag
+(C1 of the status word) are now partially implemented. Does anyone
+write code which uses these features?
The functions which load/store the FPU state are partially implemented,
but the implementation should be sufficient for handling FPU errors etc
in 32 bit protected mode.
+The implementation of the exception mechanism is flawed for unmasked
+interrupts.
+
+Detection of certain conditions, such as denormal operands, is not yet
+complete.
+
----------------------- Performance of wm-FPU-emu -----------------------
Speed.
@@ -167,6 +168,11 @@ original Linux emulator with the 4.1 'soft' lib.
exp() 619.3 4046.4
+These figures are now somewhat out-of-date. The emulator has become
+progressively slower for most functions as more of the 80486 features
+have been implemented.
+
+
----------------------- Accuracy of wm-FPU-emu -----------------------
@@ -200,4 +206,39 @@ arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
results which are exact to the 64th bit (unless there are any bugs
left). To ensure this, it was necessary to effectively get information
of up to about 128 bits precision. The emulator now passes the
-"paranoia" tests.
+"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
+bit precision numbers) when precision control is set to 24, 53 or 64
+bits, and for 'double' variables (53 bit precision numbers) when
+precision control is set to 53 bits (a properly performing FPU cannot
+pass the 'paranoia' tests for 'double' variables when precision
+control is set to 64 bits).
+
+------------------------- Contributors -------------------------------
+
+A number of people have contributed to the development of the
+emulator, often by just reporting bugs, sometimes with a suggested
+fix, and a few kind people have provided me with access in one way or
+another to an 80486 machine. Contributors include (to those people who
+I have forgotten, please excuse me):
+
+Linus Torvalds
+Tommy.Thorn@daimi.aau.dk
+Andrew.Tridgell@anu.edu.au
+Nick Holloway alfie@dcs.warwick.ac.uk
+Hermano Moura moura@dcs.gla.ac.uk
+Jon Jagger J.Jagger@scp.ac.uk
+Lennart Benschop
+Brian Gallew geek+@CMU.EDU
+Thomas Staniszewski ts3v+@andrew.cmu.edu
+Martin Howell mph@plasma.apana.org.au
+M Saggaf alsaggaf@athena.mit.edu
+Peter Barker PETER@socpsy.sci.fau.edu
+tom@vlsivie.tuwien.ac.at
+Dan Russel russed@rpi.edu
+Daniel Carosone danielce@ee.mu.oz.au
+cae@jpmorgan.com
+Hamish Coleman t933093@minyos.xx.rmit.oz.au
+
+...and numerous others who responded to my request for help with
+a real 80486.
+
diff --git a/kernel/FPU-emu/control_w.h b/kernel/FPU-emu/control_w.h
index f084b7c..42bb1bf 100644
--- a/kernel/FPU-emu/control_w.h
+++ b/kernel/FPU-emu/control_w.h
@@ -18,13 +18,15 @@
#define CW_RC _Const_(0x0C00) /* rounding control */
#define CW_PC _Const_(0x0300) /* precision control */
-#define CW_PM _Const_(0x0020) /* precision mask */
-#define CW_UM _Const_(0x0010) /* underflow mask */
-#define CW_OM _Const_(0x0008) /* overflow mask */
-#define CW_ZM _Const_(0x0004) /* divide by zero mask */
-#define CW_DM _Const_(0x0002) /* denormalized operand mask */
-#define CW_IM _Const_(0x0001) /* invalid operation mask */
-#define CW_EXM _Const_(0x007f) /* all masks */
+
+#define CW_Precision Const_(0x0020) /* loss of precision mask */
+#define CW_Underflow Const_(0x0010) /* underflow mask */
+#define CW_Overflow Const_(0x0008) /* overflow mask */
+#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
+#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
+#define CW_Invalid Const_(0x0001) /* invalid operation mask */
+
+#define CW_Exceptions _Const_(0x003f) /* all masks */
#define RC_RND _Const_(0x0000)
#define RC_DOWN _Const_(0x0400)
@@ -33,9 +35,10 @@
/* p 15-5: Precision control bits affect only the following:
ADD, SUB(R), MUL, DIV(R), and SQRT */
-#define FULL_PRECISION (CW_PC | RC_RND)
#define PR_24_BITS _Const_(0x000)
#define PR_53_BITS _Const_(0x200)
#define PR_64_BITS _Const_(0x300)
+/* FULL_PRECISION simulates all exceptions masked */
+#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
#endif _CONTROLW_H_
diff --git a/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c
index 1a003b8..17b958c 100644
--- a/kernel/FPU-emu/errors.c
+++ b/kernel/FPU-emu/errors.c
@@ -33,6 +33,7 @@
#undef PRINT_MESSAGES
/* */
+
void Un_impl(void)
{
unsigned char byte1, FPU_modrm;
@@ -69,23 +70,23 @@ void emu_printall()
FPU_modrm = get_fs_byte(1 + (unsigned char *) FPU_ORIG_EIP);
#ifdef DEBUGGING
-if ( status_word & SW_B ) printk("SW: backward compatibility (=ES)\n");
-if ( status_word & SW_C3 ) printk("SW: condition bit 3\n");
-if ( status_word & SW_C2 ) printk("SW: condition bit 2\n");
-if ( status_word & SW_C1 ) printk("SW: condition bit 1\n");
-if ( status_word & SW_C0 ) printk("SW: condition bit 0\n");
-if ( status_word & SW_ES ) printk("SW: exception summary\n");
-if ( status_word & SW_SF ) printk("SW: stack fault\n");
-if ( status_word & SW_PE ) printk("SW: loss of precision\n");
-if ( status_word & SW_UE ) printk("SW: underflow\n");
-if ( status_word & SW_OE ) printk("SW: overflow\n");
-if ( status_word & SW_ZE ) printk("SW: divide by zero\n");
-if ( status_word & SW_DE ) printk("SW: denormalized operand\n");
-if ( status_word & SW_IE ) printk("SW: invalid operation\n");
+if ( status_word & SW_Backward ) printk("SW: backward compatibility\n");
+if ( status_word & SW_C3 ) printk("SW: condition bit 3\n");
+if ( status_word & SW_C2 ) printk("SW: condition bit 2\n");
+if ( status_word & SW_C1 ) printk("SW: condition bit 1\n");
+if ( status_word & SW_C0 ) printk("SW: condition bit 0\n");
+if ( status_word & SW_Summary ) printk("SW: exception summary\n");
+if ( status_word & SW_Stack_Fault ) printk("SW: stack fault\n");
+if ( status_word & SW_Precision ) printk("SW: loss of precision\n");
+if ( status_word & SW_Underflow ) printk("SW: underflow\n");
+if ( status_word & SW_Overflow ) printk("SW: overflow\n");
+if ( status_word & SW_Zero_Div ) printk("SW: divide by zero\n");
+if ( status_word & SW_Denorm_Op ) printk("SW: denormalized operand\n");
+if ( status_word & SW_Invalid ) printk("SW: invalid operation\n");
#endif DEBUGGING
- status_word = status_word & ~SW_TOP;
- status_word |= (top&7) << SW_TOPS;
+ status_word = status_word & ~SW_Top;
+ status_word |= (top&7) << SW_Top_Shift;
printk("At %p: %02x ", FPU_ORIG_EIP, byte1);
if (FPU_modrm >= 0300)
@@ -101,18 +102,18 @@ if ( status_word & SW_IE ) printk("SW: invalid operation\n");
status_word & 0x40 ? 1 : 0, /* Stack flag */
status_word & SW_C3?1:0, status_word & SW_C2?1:0, /* cc */
status_word & SW_C1?1:0, status_word & SW_C0?1:0, /* cc */
- status_word & SW_PE?1:0, status_word & SW_UE?1:0, /* exception fl */
- status_word & SW_OE?1:0, status_word & SW_ZE?1:0, /* exception fl */
- status_word & SW_DE?1:0, status_word & SW_IE?1:0); /* exception fl */
+ status_word & SW_Precision?1:0, status_word & SW_Underflow?1:0,
+ status_word & SW_Overflow?1:0, status_word & SW_Zero_Div?1:0,
+ status_word & SW_Denorm_Op?1:0, status_word & SW_Invalid?1:0);
printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n",
control_word & 0x1000 ? 1 : 0,
(control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
(control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
control_word & 0x80 ? 1 : 0,
- control_word & SW_PE?1:0, control_word & SW_UE?1:0, /* exception */
- control_word & SW_OE?1:0, control_word & SW_ZE?1:0, /* exception */
- control_word & SW_DE?1:0, control_word & SW_IE?1:0); /* exception */
+ control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
+ control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
+ control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
for ( i = 0; i < 8; i++ )
{
@@ -216,6 +217,12 @@ static struct {
0x210 in reg_u_sub.S
0x211 in reg_u_sub.S
0x212 in reg_u_sub.S
+ 0x213 in wm_sqrt.S
+ 0x214 in wm_sqrt.S
+ 0x215 in wm_sqrt.S
+ 0x216 in reg_round.S
+ 0x217 in reg_round.S
+ 0x218 in reg_round.S
*/
void exception(int n)
@@ -228,19 +235,27 @@ void exception(int n)
int_type = n - EX_INTERNAL;
n = EX_INTERNAL;
/* Set lots of exception bits! */
- status_word |= (0x3f | EX_ErrorSummary | FPU_BUSY);
+ status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
}
else
{
+ /* Extract only the bits which we use to set the status word */
+ n &= (SW_Exc_Mask);
/* Set the corresponding exception bit */
- status_word |= (n | EX_ErrorSummary | FPU_BUSY);
- if (n == EX_StackUnder) /* Stack underflow */
- /* This bit distinguishes over- from underflow */
- status_word &= ~SW_C1;
+ status_word |= n;
+ if ( status_word & ~control_word & CW_Exceptions )
+ status_word |= SW_Summary;
+ if ( n & (SW_Stack_Fault | EX_Precision) )
+ {
+ if ( !(n & SW_C1) )
+ /* This bit distinguishes over- from underflow for a stack fault,
+ and roundup from round-down for precision loss. */
+ status_word &= ~SW_C1;
+ }
}
RE_ENTRANT_CHECK_OFF
- if ( (~control_word & n & CW_EXM) || (n == EX_INTERNAL) )
+ if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
{
#ifdef PRINT_MESSAGES
/* My message from the sponsor */
@@ -249,7 +264,7 @@ void exception(int n)
/* Get a name string for error reporting */
for (i=0; exception_names[i].type; i++)
- if (exception_names[i].type == n)
+ if ( (exception_names[i].type & n) == exception_names[i].type )
break;
if (exception_names[i].type)
@@ -271,7 +286,15 @@ void exception(int n)
emu_printall();
#endif PRINT_MESSAGES
- send_sig(SIGFPE, current, 1);
+ /*
+ * The 80486 generates an interrupt on the next non-control FPU
+ * instruction. So we need some means of flagging it.
+ * We use the ES (Error Summary) bit for this, assuming that
+ * this is the way a real FPU does it (until I can check it out),
+ * if not, then some method such as the following kludge might
+ * be needed.
+ */
+/* regs[0].tag |= TW_FPU_Interrupt; */
}
RE_ENTRANT_CHECK_ON
@@ -279,8 +302,6 @@ void exception(int n)
math_abort(FPU_info,SIGFPE);
#endif __DEBUG__
- /* Cause the look-ahead mechanism to terminate */
- FPU_lookahead = 0;
}
@@ -288,33 +309,54 @@ void exception(int n)
void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
{
FPU_REG *x;
-
+ int signalling;
+
x = a;
if (a->tag == TW_NaN)
{
if (b->tag == TW_NaN)
{
+ signalling = !(a->sigh & b->sigh & 0x40000000);
/* find the "larger" */
if ( *(long long *)&(a->sigl) < *(long long *)&(b->sigl) )
x = b;
}
- /* else return the quiet version of the NaN in a */
+ else
+ {
+ /* return the quiet version of the NaN in a */
+ signalling = !(a->sigh & 0x40000000);
+ }
}
- else if (b->tag == TW_NaN)
+ else
+#ifdef PARANOID
+ if (b->tag == TW_NaN)
+#endif PARANOID
{
+ signalling = !(b->sigh & 0x40000000);
x = b;
}
#ifdef PARANOID
else
{
+ signalling = 0;
EXCEPTION(EX_INTERNAL|0x113);
x = &CONST_QNaN;
}
#endif PARANOID
-
- if ( control_word & EX_Invalid )
+
+ if ( !signalling )
+ {
+ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ return;
+ }
+
+ if ( control_word & CW_Invalid )
{
/* The masked response */
+ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
reg_move(x, dest);
/* ensure a Quiet NaN */
dest->sigh |= 0x40000000;
@@ -325,11 +367,11 @@ void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
return;
}
-/* Invalid arith operation on valid registers */
+/* Invalid arith operation on Valid registers */
void arith_invalid(FPU_REG *dest)
{
- if ( control_word & EX_Invalid )
+ if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, dest);
@@ -346,7 +388,7 @@ void arith_invalid(FPU_REG *dest)
void divide_by_zero(int sign, FPU_REG *dest)
{
- if ( control_word & EX_ZeroDiv )
+ if ( control_word & CW_ZeroDiv )
{
/* The masked response */
reg_move(&CONST_INF, dest);
@@ -360,13 +402,53 @@ void divide_by_zero(int sign, FPU_REG *dest)
}
+/* This may be called often, so keep it lean */
+void set_precision_flag_up(void)
+{
+ if ( control_word & CW_Precision )
+ status_word |= (SW_Precision | SW_C1); /* The masked response */
+ else
+ exception(EX_Precision | SW_C1);
+
+}
+
+
+/* This may be called often, so keep it lean */
+void set_precision_flag_down(void)
+{
+ if ( control_word & CW_Precision )
+ { /* The masked response */
+ status_word &= ~SW_C1;
+ status_word |= SW_Precision;
+ }
+ else
+ exception(EX_Precision);
+}
+
+
+int denormal_operand(void)
+{
+ if ( control_word & CW_Denormal )
+ { /* The masked response */
+ status_word |= SW_Denorm_Op;
+ return 0;
+ }
+ else
+ {
+ exception(EX_Denormal);
+ return 1;
+ }
+}
+
+
void arith_overflow(FPU_REG *dest)
{
- if ( control_word & EX_Overflow )
+ if ( control_word & CW_Overflow )
{
char sign;
/* The masked response */
+/* **** The response here depends upon the rounding mode */
sign = dest->sign;
reg_move(&CONST_INF, dest);
dest->sign = sign;
@@ -377,7 +459,9 @@ void arith_overflow(FPU_REG *dest)
dest->exp -= (3 * (1 << 13));
}
- EXCEPTION(EX_Overflow);
+ /* By definition, precision is lost.
+ It appears that the roundup bit (C1) is also set by convention. */
+ EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
return;
@@ -387,7 +471,7 @@ void arith_overflow(FPU_REG *dest)
void arith_underflow(FPU_REG *dest)
{
- if ( control_word & EX_Underflow )
+ if ( control_word & CW_Underflow )
{
/* The masked response */
if ( dest->exp <= EXP_UNDER - 63 )
@@ -408,7 +492,7 @@ void arith_underflow(FPU_REG *dest)
void stack_overflow(void)
{
- if ( control_word & EX_Invalid )
+ if ( control_word & CW_Invalid )
{
/* The masked response */
top--;
@@ -425,7 +509,7 @@ void stack_overflow(void)
void stack_underflow(void)
{
- if ( control_word & EX_Invalid )
+ if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, FPU_st0_ptr);
@@ -437,3 +521,36 @@ void stack_underflow(void)
}
+
+void stack_underflow_i(int i)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void stack_underflow_pop(int i)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ pop();
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
diff --git a/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c
index 107a37c..0b9e2d1 100644
--- a/kernel/FPU-emu/fpu_aux.c
+++ b/kernel/FPU-emu/fpu_aux.c
@@ -17,9 +17,11 @@
-static void fclex(void)
+void fclex(void)
{
- status_word &= ~(SW_B|SW_ES|SW_SF|SW_PE|SW_UE|SW_OE|SW_ZE|SW_DE|SW_IE);
+ status_word &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
+ SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
+ SW_Invalid);
FPU_entry_eip = ip_offset; /* We want no net effect */
}
@@ -27,18 +29,14 @@ static void fclex(void)
void finit()
{
int r;
- control_word = 0x037e;
+ control_word = 0x037f;
status_word = 0;
- top = 0;
+ top = 0; /* We don't keep top in the status word internally. */
for (r = 0; r < 8; r++)
{
- regs[r].sign = 0;
regs[r].tag = TW_Empty;
- regs[r].exp = 0;
- regs[r].sigh = 0;
- regs[r].sigl = 0;
}
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset = 0;
}
static FUNC finit_table[] = {
@@ -54,8 +52,8 @@ void finit_()
static void fstsw_ax(void)
{
- status_word &= ~SW_TOP;
- status_word |= (top&7) << SW_TOPS;
+ status_word &= ~SW_Top;
+ status_word |= (top&7) << SW_Top_Shift;
*(short *) &FPU_EAX = status_word;
@@ -116,6 +114,25 @@ void fxch_i()
/* fxch st(i) */
FPU_REG t;
register FPU_REG *sti_ptr = &st(FPU_rm);
+
+ if ( FPU_st0_tag == TW_Empty )
+ {
+ if ( sti_ptr->tag == TW_Empty )
+ {
+ stack_underflow();
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ reg_move(sti_ptr, FPU_st0_ptr);
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ if ( sti_ptr->tag == TW_Empty )
+ {
+ reg_move(FPU_st0_ptr, sti_ptr);
+ stack_underflow();
+ return;
+ }
reg_move(FPU_st0_ptr, &t);
reg_move(sti_ptr, FPU_st0_ptr);
reg_move(&t, sti_ptr);
diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h
index 926db6d..43cc4bb 100644
--- a/kernel/FPU-emu/fpu_emu.h
+++ b/kernel/FPU-emu/fpu_emu.h
@@ -11,6 +11,23 @@
#ifndef _FPU_EMU_H_
#define _FPU_EMU_H_
+/*
+ * Define DENORM_OPERAND to make the emulator detect denormals
+ * and use the denormal flag of the status word. Note: this only
+ * affects the flag and corresponding interrupt, the emulator
+ * will always generate denormals and operate upon them as required.
+ */
+#define DENORM_OPERAND
+
+/*
+ * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
+ * rather than behaviour which appears to be cleaner.
+ * This is a matter of opinion: for all I know, the 80486 may simply
+ * be complying with the IEEE spec. Maybe one day I'll get to see the
+ * spec...
+ */
+#define PECULIAR_486
+
#ifdef __ASSEMBLER__
#include "fpu_asm.h"
#define Const(x) $##x
@@ -20,9 +37,7 @@
#define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
-/* #define EXP_MAX Const(16384) */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
-/* #define EXP_MIN Const(-16384) */
#define SIGN_POS Const(0)
#define SIGN_NEG Const(1)
@@ -37,6 +52,7 @@
#define TW_Empty Const(7) /* empty */
+/* #define TW_FPU_Interrupt Const(0x80) */ /* Signals an interrupt */
#ifndef __ASSEMBLER__
@@ -92,6 +108,7 @@ extern void poly_div16(long long *x);
extern void polynomial(unsigned accum[], unsigned x[],
unsigned short terms[][4], int n);
extern void normalize(FPU_REG *x);
+extern void normalize_nuo(FPU_REG *x);
extern void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
extern void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
@@ -106,6 +123,8 @@ extern void wm_sqrt(FPU_REG *n, unsigned int control_w);
extern unsigned shrx(void *l, unsigned x);
extern unsigned shrxs(void *v, unsigned x);
extern unsigned long div_small(unsigned long long *x, unsigned long y);
+extern void round_reg(FPU_REG *arg, unsigned int extent,
+ unsigned int control_w);
#ifndef MAKING_PROTO
#include "fpu_proto.h"
diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c
index 4112fb6..c44d5db 100644
--- a/kernel/FPU-emu/fpu_entry.c
+++ b/kernel/FPU-emu/fpu_entry.c
@@ -28,11 +28,13 @@
#ifdef CONFIG_MATH_EMULATION
#include <linux/signal.h>
+#include <linux/segment.h>
#include "fpu_system.h"
#include "fpu_emu.h"
#include "exception.h"
#include "control_w.h"
+#include "status_w.h"
#include <asm/segment.h>
@@ -89,33 +91,37 @@ static FUNC st_instr_table[64] = {
#define _REGi_ 0 /* Uses st(rm) */
#define _PUSH_ 3 /* Need to check for space to push onto stack */
#define _null_ 4 /* Function illegal or not implemented */
+#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
+#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
+#define _REGIc 0 /* Compare st(0) and st(rm) */
+#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
#ifndef NO_UNDOC_CODE
/* Un-documented FPU op-codes supported by default. (see above) */
static unsigned char type_table[64] = {
- _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _REGi_,
- _REGI_, _REGI_, _null_, _null_, _REGI_, _REGI_, _REGI_, _REGI_,
- _REGI_, _NONE_, _null_, _null_, _REGI_, _REG0_, _REGI_, _REG0_,
- _REGI_, _REG0_, _null_, _null_, _REGI_, _REG0_, _REGI_, _REG0_,
- _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
- _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
+ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#else /* Support only documented FPU op-codes */
static unsigned char type_table[64] = {
- _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _null_,
- _REGI_, _REGI_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
- _REGI_, _null_, _null_, _null_, _null_, _REG0_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
- _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
+ _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
};
#endif NO_UNDOC_CODE
@@ -151,8 +157,6 @@ void math_emulate(long arg)
{
finit();
current->used_math = 1;
- control_word = 0x037f;
- status_word = 0x0000;
}
FPU_info = (struct info *) &arg;
@@ -164,8 +168,8 @@ void math_emulate(long arg)
math_abort(FPU_info,SIGILL);
}
- /* 0x000f means user code space */
- if (FPU_CS != 0x000f)
+ /* user code space? */
+ if (FPU_CS != USER_CS)
{
printk("math_emulate: %04x:%08x\n",FPU_CS,FPU_EIP);
panic("Math emulation needed in kernel");
@@ -174,15 +178,72 @@ void math_emulate(long arg)
FPU_lookahead = 1;
if (current->flags & PF_PTRACED)
FPU_lookahead = 0;
-do_another:
- FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
+do_another_FPU_instruction:
RE_ENTRANT_CHECK_OFF
code = get_fs_word((unsigned short *) FPU_EIP);
RE_ENTRANT_CHECK_ON
- if ( (code & 0xff) == 0x66 )
+ if ( (code & 0xff) == 0x9b ) /* fwait */
+ {
+ if (status_word & SW_Summary)
+ goto do_the_FPU_interrupt;
+ else
+ {
+ FPU_EIP++;
+ goto FPU_instruction_done;
+ }
+ }
+
+ if (status_word & SW_Summary)
+ {
+ /* Ignore the error for now if the current instruction is a no-wait
+ control instruction */
+ /* The 80486 manual contradicts itself on this topic,
+ so I use the following list of such instructions until
+ I can check on a real 80486:
+ fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
+ */
+ if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
+ (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
+ fnstsw */
+ ((code & 0xc000) != 0xc000))) ) )
+ {
+ /* This is a guess about what a real FPU might do to this bit: */
+/* status_word &= ~SW_Summary; ****/
+
+ /*
+ * We need to simulate the action of the kernel to FPU
+ * interrupts here.
+ * Currently, the "real FPU" part of the kernel (0.99.10)
+ * clears the exception flags, sets the registers to empty,
+ * and passes information back to the interrupted process
+ * via the cs selector and operand selector, so we do the same.
+ */
+ do_the_FPU_interrupt:
+ cs_selector &= 0xffff0000;
+ cs_selector |= (status_word & ~SW_Top) | ((top&7) << SW_Top_Shift);
+ operand_selector = tag_word();
+ status_word = 0;
+ top = 0;
+ {
+ int r;
+ for (r = 0; r < 8; r++)
+ {
+ regs[r].tag = TW_Empty;
+ }
+ }
+
+ RE_ENTRANT_CHECK_OFF
+ send_sig(SIGFPE, current, 1);
+ return;
+ }
+ }
+
+ FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
+
+ if ( (code & 0xff) == 0x66 ) /* size prefix */
{
FPU_EIP++;
RE_ENTRANT_CHECK_OFF
@@ -200,28 +261,56 @@ do_another:
get_address(FPU_modrm);
if ( !(code & 1) )
{
- switch ( (code >> 1) & 3 )
- {
- case 0:
- reg_load_single();
- break;
- case 1:
- reg_load_int32();
- break;
- case 2:
- reg_load_double();
- break;
- case 3:
- reg_load_int16();
- break;
- }
-
- /* No more access to user memory, it is safe
- to use static data now */
+ unsigned short status1 = status_word;
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* Stack underflow has priority */
if ( NOT_EMPTY_0 )
{
+ switch ( (code >> 1) & 3 )
+ {
+ case 0:
+ reg_load_single();
+ break;
+ case 1:
+ reg_load_int32();
+ break;
+ case 2:
+ reg_load_double();
+ break;
+ case 3:
+ reg_load_int16();
+ break;
+ }
+
+ /* No more access to user memory, it is safe
+ to use static data now */
+ FPU_st0_ptr = &st(0);
+ FPU_st0_tag = FPU_st0_ptr->tag;
+
+ /* NaN operands have the next priority. */
+ /* We have to delay looking at st(0) until after
+ loading the data, because that data might contain an SNaN */
+ if ( (FPU_st0_tag == TW_NaN) ||
+ (FPU_loaded_data.tag == TW_NaN) )
+ {
+ /* Restore the status word; we might have loaded a
+ denormal. */
+ status_word = status1;
+ if ( (FPU_modrm & 0x30) == 0x10 )
+ {
+ /* fcom or fcomp */
+ EXCEPTION(EX_Invalid);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if ( FPU_modrm & 0x08 )
+ pop(); /* fcomp, so we pop. */
+ }
+ else
+ real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
+ goto reg_mem_instr_done;
+ }
+
switch ( (FPU_modrm >> 3) & 7 )
{
case 0: /* fadd */
@@ -252,84 +341,127 @@ do_another:
control_word);
break;
case 7: /* fdivr */
+ if ( FPU_st0_tag == TW_Zero )
+ status_word = status1; /* Undo any denorm tag,
+ zero-divide has priority. */
reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
}
}
else
- stack_underflow();
+ {
+ if ( (FPU_modrm & 0x30) == 0x10 )
+ {
+ /* The instruction is fcom or fcomp */
+ EXCEPTION(EX_StackUnder);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if ( FPU_modrm & 0x08 )
+ pop(); /* fcomp, Empty or not, we pop. */
+ }
+ else
+ stack_underflow();
+ }
}
else
{
load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
}
+ reg_mem_instr_done:
+
data_operand_offset = (unsigned long)FPU_data_address;
}
else
{
/* None of these instructions access user memory */
unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
+
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
switch ( type_table[(int) instr_index] )
{
- case _NONE_:
+ case _NONE_: /* also _REGIc: _REGIn */
break;
case _REG0_:
if ( !NOT_EMPTY_0 )
{
stack_underflow();
- goto instruction_done;
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIi:
+ if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow_i(FPU_rm);
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIp:
+ if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow_i(FPU_rm);
+ pop();
+ goto FPU_instruction_done;
}
break;
case _REGI_:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
stack_underflow();
- goto instruction_done;
+ goto FPU_instruction_done;
}
break;
case _PUSH_: /* Only used by the fld st(i) instruction */
break;
case _null_:
Un_impl();
- goto instruction_done;
+ goto FPU_instruction_done;
default:
EXCEPTION(EX_INTERNAL|0x111);
- goto instruction_done;
+ goto FPU_instruction_done;
}
(*st_instr_table[(int) instr_index])();
}
-instruction_done:
+FPU_instruction_done:
ip_offset = FPU_entry_eip;
bswapw(code);
*(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
+#ifdef DEBUG
+ RE_ENTRANT_CHECK_OFF
+ emu_printall();
+ RE_ENTRANT_CHECK_ON
+#endif DEBUG
+
if (FPU_lookahead && !need_resched)
{
unsigned char next;
-skip_fwait:
- RE_ENTRANT_CHECK_OFF
- next = get_fs_byte((unsigned char *) FPU_EIP);
- RE_ENTRANT_CHECK_ON
-test_for_fp:
- if ( (next & 0xf8) == 0xd8 )
- {
- goto do_another;
- }
- if ( next == 0x9b ) /* fwait */
- { FPU_EIP++; goto skip_fwait; }
- if ( next == 0x66 ) /* size prefix */
+
+ /* (This test should generate no machine code) */
+ while ( 1 )
{
RE_ENTRANT_CHECK_OFF
- next = get_fs_byte((unsigned char *) (FPU_EIP+1));
+ next = get_fs_byte((unsigned char *) FPU_EIP);
RE_ENTRANT_CHECK_ON
- if ( (next & 0xf8) == 0xd8 )
- goto test_for_fp;
+ if ( ((next & 0xf8) == 0xd8) || (next == 0x9b) ) /* fwait */
+ {
+ goto do_another_FPU_instruction;
+ }
+ else if ( next == 0x66 ) /* size prefix */
+ {
+ RE_ENTRANT_CHECK_OFF
+ next = get_fs_byte((unsigned char *) (FPU_EIP+1));
+ RE_ENTRANT_CHECK_ON
+ if ( (next & 0xf8) == 0xd8 )
+ {
+ FPU_EIP++;
+ goto do_another_FPU_instruction;
+ }
+ }
+ break;
}
}
diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c
index b7dc7e8..4afe891 100644
--- a/kernel/FPU-emu/fpu_etc.c
+++ b/kernel/FPU-emu/fpu_etc.c
@@ -48,21 +48,26 @@ static void ftst_(void)
setcc(SW_C3);
break;
case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
setcc(SW_C0);
break;
case TW_NaN:
- setcc(SW_C2); /* Operand is not comparable */
+ setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_Invalid);
break;
case TW_Infinity:
if (FPU_st0_ptr->sign == SIGN_POS)
setcc(0);
else
- setcc(SW_C3);
- /* setcc(SW_C0|SW_C2|SW_C3); */
+ setcc(SW_C0);
EXCEPTION(EX_Invalid);
break;
case TW_Empty:
@@ -70,7 +75,7 @@ static void ftst_(void)
EXCEPTION(EX_StackUnder);
break;
default:
- setcc(SW_C2); /* Operand is not comparable */
+ setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
EXCEPTION(EX_INTERNAL|0x14);
break;
}
@@ -88,10 +93,11 @@ static void fxam(void)
c = SW_C3;
break;
case TW_Valid:
- if (FPU_st0_ptr->sigh & 0x80000000)
- c = SW_C2;
+ /* This will need to be changed if TW_Denormal is ever used. */
+ if ( FPU_st0_ptr->exp <= EXP_UNDER )
+ c = SW_C2|SW_C3; /* Denormal */
else
- c = SW_C3|SW_C2;
+ c = SW_C3;
break;
case TW_NaN:
c = SW_C0;
diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h
index f7d71ba..ae8492d 100644
--- a/kernel/FPU-emu/fpu_proto.h
+++ b/kernel/FPU-emu/fpu_proto.h
@@ -5,10 +5,15 @@ extern void exception(int n);
extern void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
extern void arith_invalid(FPU_REG *dest);
extern void divide_by_zero(int sign, FPU_REG *dest);
+extern void set_precision_flag_up(void);
+extern void set_precision_flag_down(void);
+extern int denormal_operand(void);
extern void arith_overflow(FPU_REG *dest);
extern void arith_underflow(FPU_REG *dest);
extern void stack_overflow(void);
extern void stack_underflow(void);
+extern void stack_underflow_i(int i);
+extern void stack_underflow_pop(int i);
/* fpu_arith.c */
extern void fadd__(void);
extern void fmul__(void);
@@ -29,6 +34,7 @@ extern void fsubp_(void);
extern void fdivrp(void);
extern void fdivp_(void);
/* fpu_aux.c */
+extern void fclex(void);
extern void finit(void);
extern void finit_(void);
extern void fstsw_(void);
@@ -64,15 +70,12 @@ extern int poly_l2p1(FPU_REG *arg, FPU_REG *result);
extern void poly_sine(FPU_REG *arg, FPU_REG *result);
/* poly_tan.c */
extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg);
-/* precision.c */
-extern int round_to_53_bits(FPU_REG *reg);
-extern int round_to_24_bits(FPU_REG *reg);
/* reg_add_sub.c */
extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
/* reg_compare.c */
extern int compare(FPU_REG *b);
-extern void compare_st_data(void);
+extern int compare_st_data(void);
extern void fcom_st(void);
extern void fcompst(void);
extern void fcompp(void);
@@ -99,6 +102,7 @@ extern int reg_store_bcd(void);
extern int round_to_int(FPU_REG *r);
extern char *fldenv(void);
extern void frstor(void);
+extern unsigned short tag_word(void);
extern char *fstenv(void);
extern void fsave(void);
/* reg_mul.c */
diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c
index e653143..ddac32d 100644
--- a/kernel/FPU-emu/fpu_trig.c
+++ b/kernel/FPU-emu/fpu_trig.c
@@ -101,6 +101,11 @@ static void f2xm1(void)
case TW_Valid:
{
FPU_REG rv, tmp;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
if ( FPU_st0_ptr->sign == SIGN_POS )
{
@@ -151,6 +156,12 @@ static void fptan(void)
switch ( FPU_st0_tag )
{
case TW_Valid:
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
FPU_st0_ptr->sign = SIGN_POS;
if ( (q = trig_arg(FPU_st0_ptr)) != -1 )
{
@@ -177,8 +188,9 @@ static void fptan(void)
}
break;
case TW_Infinity:
- arith_invalid(FPU_st0_ptr);
- setcc(0);
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
return;
case TW_Zero:
push();
@@ -203,6 +215,11 @@ static void fxtract(void)
if ( !(FPU_st0_tag ^ TW_Valid) )
{
long e;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
push();
reg_move(st1_ptr, FPU_st0_ptr);
@@ -279,10 +296,15 @@ static void fsqrt_(void)
if (FPU_st0_ptr->sign == SIGN_NEG)
{
- arith_invalid(FPU_st0_ptr);
+ arith_invalid(FPU_st0_ptr); /* sqrt(negative) is invalid */
return;
}
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
expon = FPU_st0_ptr->exp - EXP_BIAS;
FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */
@@ -296,7 +318,7 @@ static void fsqrt_(void)
else if ( FPU_st0_tag == TW_Infinity )
{
if ( FPU_st0_ptr->sign == SIGN_NEG )
- arith_invalid(FPU_st0_ptr);
+ arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is invalid */
return;
}
else
@@ -312,6 +334,11 @@ static void frndint_(void)
if (FPU_st0_ptr->exp > EXP_BIAS+63)
return;
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
round_to_int(FPU_st0_ptr); /* Fortunately, this can't overflow to 2^64 */
FPU_st0_ptr->exp = EXP_BIAS + 63;
normalize(FPU_st0_ptr);
@@ -326,11 +353,18 @@ static void frndint_(void)
static void fsin(void)
{
+ char arg_sign = FPU_st0_ptr->sign;
+
if ( FPU_st0_tag == TW_Valid )
{
int q;
- char arg_sign = FPU_st0_ptr->sign;
FPU_st0_ptr->sign = SIGN_POS;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if ( (q = trig_arg(FPU_st0_ptr)) != -1 )
{
FPU_REG rv;
@@ -349,6 +383,8 @@ static void fsin(void)
if ( FPU_st0_ptr->exp <= EXP_UNDER )
arith_underflow(FPU_st0_ptr);
+ set_precision_flag_up(); /* We do not really know if up or down */
+
return;
}
else
@@ -356,7 +392,6 @@ static void fsin(void)
/* Operand is out of range */
setcc(SW_C2);
FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
- EXCEPTION(EX_Invalid);
return;
}
}
@@ -367,8 +402,9 @@ static void fsin(void)
}
else if ( FPU_st0_tag == TW_Infinity )
{
- arith_invalid(FPU_st0_ptr);
- setcc(0);
+ /* Operand is out of range */
+ setcc(SW_C2);
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
return;
}
else
@@ -378,10 +414,17 @@ static void fsin(void)
static int f_cos(FPU_REG *arg)
{
+ char arg_sign = arg->sign;
+
if ( arg->tag == TW_Valid )
{
int q;
- char arg_sign = arg->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return 1;
+#endif DENORM_OPERAND
+
arg->sign = SIGN_POS;
if ( (q = trig_arg(arg)) != -1 )
{
@@ -396,6 +439,8 @@ static int f_cos(FPU_REG *arg)
if ((q+1) & 2)
rv.sign ^= SIGN_POS ^ SIGN_NEG;
reg_move(&rv, arg);
+
+ set_precision_flag_up(); /* We do not really know if up or down */
return 0;
}
@@ -404,7 +449,6 @@ static int f_cos(FPU_REG *arg)
/* Operand is out of range */
setcc(SW_C2);
arg->sign = arg_sign; /* restore st(0) */
- EXCEPTION(EX_Invalid);
return 1;
}
}
@@ -416,8 +460,9 @@ static int f_cos(FPU_REG *arg)
}
else if ( FPU_st0_tag == TW_Infinity )
{
- arith_invalid(FPU_st0_ptr);
- setcc(0);
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
return 1;
}
else
@@ -468,6 +513,12 @@ static void fprem_kernel(int round)
FPU_REG tmp;
int old_cw = control_word;
int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp;
+
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
control_word &= ~CW_RC;
control_word |= round;
@@ -477,6 +528,7 @@ static void fprem_kernel(int round)
/* This should be the most common case */
long long q;
int c = 0;
+
reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
@@ -524,21 +576,60 @@ static void fprem_kernel(int round)
{ stack_underflow(); return; }
else if ( FPU_st0_tag == TW_Zero )
{
- if ( (st1_tag == TW_Valid) || (st1_tag == TW_Infinity) )
+ if ( st1_tag == TW_Valid )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ setcc(0); return;
+ }
+ else if ( st1_tag == TW_Zero )
+ { arith_invalid(FPU_st0_ptr); return; } /* fprem(?,0) always invalid */
+ else if ( st1_tag == TW_Infinity )
{ setcc(0); return; }
- if ( st1_tag == TW_Zero )
- { arith_invalid(FPU_st0_ptr); return; }
}
+ else if ( FPU_st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+ arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is invalid */
+ return;
+ }
+ else if ( st1_tag != TW_NaN )
+ {
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
- if ( (FPU_st0_tag == TW_NaN) | (st1_tag == TW_NaN) )
- { real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; }
+ if ( st1_tag == TW_Infinity )
+ {
+ /* fprem(Valid,Infinity) is o.k. */
+ setcc(0); return;
+ }
+ }
+ }
else if ( FPU_st0_tag == TW_Infinity )
- { arith_invalid(FPU_st0_ptr); return; }
+ {
+ if ( st1_tag != TW_NaN )
+ {
+ arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is invalid */
+ return;
+ }
+ }
+
+ /* One of the registers must contain a NaN is we got here. */
+
#ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x118);
+ if ( (FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
+ EXCEPTION(EX_INTERNAL | 0x118);
#endif PARANOID
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+
}
@@ -552,25 +643,44 @@ static void fyl2x(void)
{
if ( FPU_st0_ptr->sign == SIGN_POS )
{
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* We use the general purpose arithmetic,
+ so we need to save these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
poly_l2(FPU_st0_ptr, FPU_st0_ptr);
-
+
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
pop(); FPU_st0_ptr = &st(0);
- if ( FPU_st0_ptr->exp <= EXP_UNDER )
- { arith_underflow(FPU_st0_ptr); return; }
- else if ( FPU_st0_ptr->exp >= EXP_OVER )
- { arith_overflow(FPU_st0_ptr); return; }
}
else
{
/* negative */
pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr);
+ arith_invalid(FPU_st0_ptr); /* st(0) cannot be negative */
return;
}
}
else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
- { stack_underflow(); return; }
+ {
+ stack_underflow_pop(1);
+ return;
+ }
else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
{
real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
@@ -584,36 +694,61 @@ static void fyl2x(void)
{
pop(); FPU_st0_ptr = &st(0);
if ( FPU_st0_ptr->tag == TW_Zero )
- arith_invalid(FPU_st0_ptr);
+ arith_invalid(FPU_st0_ptr); /* Both args zero is invalid */
+#ifdef PECULIAR_486
+ /* This case is not specifically covered in the manual,
+ but divide-by-zero would seem to be the best response.
+ However, a real 80486 does it this way... */
+ else if ( FPU_st0_ptr->tag == TW_Infinity )
+ {
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ return;
+ }
+#endif PECULIAR_486
else
- divide_by_zero(st1_ptr->sign ^ SIGN_NEG, FPU_st0_ptr);
+ divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, FPU_st0_ptr);
return;
}
- else if ( st1_ptr->sign == SIGN_POS )
+ else
{
+ /* st(1) contains zero, st(0) valid <> 0 */
/* Zero is the valid answer */
- char sign = FPU_st0_ptr->sign;
- if ( FPU_st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG;
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ if ( FPU_st0_ptr->sign == SIGN_NEG )
+ {
+ pop(); FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ return;
+ }
+
+ if ( FPU_st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
pop(); FPU_st0_ptr = &st(0);
reg_move(&CONST_Z, FPU_st0_ptr);
FPU_st0_ptr->sign = sign;
return;
}
- else
- {
- pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr);
- return;
- }
}
/* One or both arg must be an infinity */
else if ( FPU_st0_tag == TW_Infinity )
{
if ( (FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
- { pop(); FPU_st0_ptr = &st(0); arith_invalid(FPU_st0_ptr); return; }
+ { pop(); FPU_st0_ptr = &st(0);
+ arith_invalid(FPU_st0_ptr); /* log(-infinity) or 0*log(infinity) */
+ return; }
else
{
char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
pop(); FPU_st0_ptr = &st(0);
reg_move(&CONST_INF, FPU_st0_ptr);
FPU_st0_ptr->sign = sign;
@@ -629,24 +764,45 @@ static void fyl2x(void)
(FPU_st0_ptr->sigh == 0x80000000) &&
(FPU_st0_ptr->sigl == 0) )
{
+ /* st(0) holds 1.0 */
pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr);
+ arith_invalid(FPU_st0_ptr); /* infinity*log(1) */
return;
}
+ /* st(0) is positive and > 1.0 */
pop();
}
else
{
- pop(); FPU_st0_ptr = &st(0);
- FPU_st0_ptr->sign ^= SIGN_NEG;
+ /* st(0) is positive and < 1.0 */
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ st1_ptr->sign ^= SIGN_NEG;
+ pop();
}
return;
}
else
{
/* st(0) must be zero or negative */
- pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr);
+ if ( FPU_st0_ptr->tag == TW_Zero )
+ {
+ pop(); FPU_st0_ptr = st1_ptr;
+ st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
+ /* This should be invalid, but a real 80486 is happy with it. */
+#ifndef PECULIAR_486
+ divide_by_zero(st1_ptr->sign, FPU_st0_ptr);
+#endif PECULIAR_486
+ }
+ else
+ {
+ pop(); FPU_st0_ptr = st1_ptr;
+ arith_invalid(FPU_st0_ptr); /* log(negative) */
+ }
return;
}
}
@@ -659,17 +815,30 @@ static void fpatan(void)
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
+ int saved_control, saved_status;
FPU_REG sum;
int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign)<<1);
+
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* We use the general purpose arithmetic so we need to save these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS;
- if (compare(st1_ptr) == COMP_A_LT_B)
+ if ( compare(st1_ptr) == COMP_A_lt_B )
{
quadrant |= 4;
reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION);
}
else
reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
-
+
poly_atan(&sum);
if (quadrant & 4)
@@ -682,14 +851,18 @@ static void fpatan(void)
}
if (quadrant & 1)
sum.sign ^= SIGN_POS^SIGN_NEG;
-
+
+ /* All of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
reg_move(&sum, st1_ptr);
- pop(); FPU_st0_ptr = &st(0);
- if ( FPU_st0_ptr->exp <= EXP_UNDER )
- { arith_underflow(FPU_st0_ptr); return; }
}
else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
- { stack_underflow(); return; }
+ {
+ stack_underflow_pop(1);
+ return;
+ }
else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
{
real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
@@ -710,42 +883,66 @@ static void fpatan(void)
}
else
{
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if ( FPU_st0_ptr->sign == SIGN_POS )
- { reg_move(&CONST_Z, st1_ptr); }
+ { reg_move(&CONST_Z, st1_ptr); pop(); return; }
else
reg_move(&CONST_PI, st1_ptr);
}
}
else
{
+ /* st(1) is infinity, st(0) not infinity */
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
reg_move(&CONST_PI2, st1_ptr);
}
st1_ptr->sign = sign;
- pop();
}
else if ( st1_tag == TW_Zero )
{
- char sign = st1_ptr->sign;
/* st(0) must be valid or zero */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if ( FPU_st0_ptr->sign == SIGN_POS )
- { reg_move(&CONST_Z, st1_ptr); }
+ { reg_move(&CONST_Z, st1_ptr); pop(); return; }
else
reg_move(&CONST_PI, st1_ptr);
st1_ptr->sign = sign;
- pop();
}
else if ( FPU_st0_tag == TW_Zero )
{
- char sign = st1_ptr->sign;
/* st(1) must be TW_Valid here */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
reg_move(&CONST_PI2, st1_ptr);
st1_ptr->sign = sign;
- pop();
}
#ifdef PARANOID
else
EXCEPTION(EX_INTERNAL | 0x220);
#endif PARANOID
+
+ pop();
+ set_precision_flag_up(); /* We do not really know if up or down */
}
@@ -768,33 +965,63 @@ static void fyl2xp1(void)
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
+ int saved_control, saved_status;
+
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* We use the general purpose arithmetic so we need to save these. */
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = FULL_PRECISION;
+
if ( poly_l2p1(FPU_st0_ptr, FPU_st0_ptr) )
{
- arith_invalid(st1_ptr); pop(); return;
+ arith_invalid(st1_ptr); /* poly_l2p1() returned invalid */
+ pop(); return;
}
+ /* Enough of the basic arithmetic is done now */
+ control_word = saved_control;
+ status_word = saved_status;
+
+ /* Let the multiply set the flags */
reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
+
pop();
}
else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
- { stack_underflow(); return; }
+ {
+ stack_underflow_pop(1);
+ return;
+ }
else if ( FPU_st0_tag == TW_Zero )
{
if ( st1_tag <= TW_Zero )
{
+
+#ifdef DENORM_OPERAND
+ if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+ (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
st1_ptr->sign ^= FPU_st0_ptr->sign;
reg_move(FPU_st0_ptr, st1_ptr);
}
else if ( st1_tag == TW_Infinity )
{
- arith_invalid(st1_ptr);
+ arith_invalid(st1_ptr); /* Infinity*log(1) */
+ pop();
return;
}
else if ( st1_tag == TW_NaN )
{
- if ( !(st1_ptr->sigh & 0x40000000) )
- EXCEPTION(EX_Invalid); /* signaling NaN */
- st1_ptr->sigh |= 0x40000000; /* QNaN */
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
return;
}
#ifdef PARANOID
@@ -804,7 +1031,64 @@ static void fyl2xp1(void)
return;
}
#endif PARANOID
- pop();
+ pop(); return;
+ }
+ else if ( FPU_st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+ if ( FPU_st0_ptr->sign == SIGN_NEG )
+ {
+ if ( FPU_st0_ptr->exp >= EXP_BIAS )
+ {
+ /* st(0) holds <= -1.0 */
+ arith_invalid(st1_ptr); /* infinity*log(1) */
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ pop(); return;
+ }
+ if ( st1_tag == TW_Infinity )
+ {
+ if ( FPU_st0_ptr->sign == SIGN_NEG )
+ {
+ if ( (FPU_st0_ptr->exp >= EXP_BIAS) &&
+ !((FPU_st0_ptr->sigh == 0x80000000) &&
+ (FPU_st0_ptr->sigl == 0)) )
+ {
+ /* st(0) holds < -1.0 */
+ arith_invalid(st1_ptr);
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ pop(); return;
+ }
+ if ( st1_tag == TW_NaN )
+ {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
}
else if ( FPU_st0_tag == TW_NaN )
{
@@ -815,15 +1099,40 @@ static void fyl2xp1(void)
else if ( FPU_st0_tag == TW_Infinity )
{
if ( st1_tag == TW_NaN )
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- else
- arith_invalid(st1_ptr);
+ {
+ real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+ else if ( (FPU_st0_ptr->sign == SIGN_NEG) ||
+ (st1_tag == TW_Zero) )
+ {
+ arith_invalid(st1_ptr); /* log(infinity) */
+ pop();
+ return;
+ }
+
+ /* st(1) must be valid here. */
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* The Manual says that log(Infinity) is invalid, but a real
+ 80486 sensibly says that it is o.k. */
+ { char sign = st1_ptr->sign;
+ reg_move(&CONST_INF, st1_ptr);
+ st1_ptr->sign = sign;
+ }
pop();
return;
}
#ifdef PARANOID
else
- EXCEPTION(EX_INTERNAL | 0x117);
+ {
+ EXCEPTION(EX_INTERNAL | 0x117);
+ }
#endif PARANOID
}
@@ -832,49 +1141,76 @@ static void fscale(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
+ int old_cw = control_word;
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
long scale;
FPU_REG tmp;
- /* 2^31 is far too large, 2^-31 is far too small */
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if ( st1_ptr->exp > EXP_BIAS + 30 )
{
+ /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
char sign;
- EXCEPTION(EX_Overflow);
- sign = FPU_st0_ptr->sign;
- reg_move(&CONST_INF, FPU_st0_ptr);
- FPU_st0_ptr->sign = sign;
- return;
- }
- else if ( st1_ptr->exp < EXP_BIAS - 30 )
- {
- EXCEPTION(EX_Underflow);
- reg_move(&CONST_Z, FPU_st0_ptr);
+
+ if ( st1_ptr->sign == SIGN_POS )
+ {
+ EXCEPTION(EX_Overflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_INF, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ }
+ else
+ {
+ EXCEPTION(EX_Underflow);
+ sign = FPU_st0_ptr->sign;
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+ }
return;
}
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
reg_move(st1_ptr, &tmp);
round_to_int(&tmp); /* This can never overflow here */
+ control_word = old_cw;
scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
scale += FPU_st0_ptr->exp;
FPU_st0_ptr->exp = scale;
- if ( scale <= EXP_UNDER )
- { arith_underflow(FPU_st0_ptr); return; }
- else if ( scale >= EXP_OVER )
- { arith_overflow(FPU_st0_ptr); return; }
+ /* Use round_reg() to properly detect under/overflow etc */
+ round_reg(FPU_st0_ptr, 0, control_word);
return;
}
else if ( FPU_st0_tag == TW_Valid )
{
if ( st1_tag == TW_Zero )
- { return; }
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
if ( st1_tag == TW_Infinity )
{
char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
if ( sign == SIGN_POS )
{ reg_move(&CONST_INF, FPU_st0_ptr); }
else
@@ -887,24 +1223,50 @@ static void fscale(void)
}
else if ( FPU_st0_tag == TW_Zero )
{
- if ( st1_tag <= TW_Zero ) { return; }
+ if ( st1_tag == TW_Valid )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
+ else if ( st1_tag == TW_Zero ) { return; }
else if ( st1_tag == TW_Infinity )
{
if ( st1_ptr->sign == SIGN_NEG )
return;
else
- { arith_invalid(FPU_st0_ptr); return; }
+ {
+ arith_invalid(FPU_st0_ptr); /* Zero scaled by +Infinity */
+ return;
+ }
}
else if ( st1_tag == TW_NaN )
{ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; }
}
else if ( FPU_st0_tag == TW_Infinity )
{
+ if ( st1_tag == TW_Valid )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
- || (st1_tag <= TW_Zero) )
+ || (st1_tag == TW_Zero) )
return;
else if ( st1_tag == TW_Infinity )
- { arith_invalid(FPU_st0_ptr); return; }
+ {
+ arith_invalid(FPU_st0_ptr); /* Infinity scaled by -Infinity */
+ return;
+ }
else if ( st1_tag == TW_NaN )
{ real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; }
}
diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c
index a038d17..2eeaa57 100644
--- a/kernel/FPU-emu/load_store.c
+++ b/kernel/FPU-emu/load_store.c
@@ -81,18 +81,22 @@ switch ( type )
{
case 000: /* fld m32real */
reg_load_single();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 001: /* fild m32int */
reg_load_int32();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 002: /* fld m64real */
reg_load_double();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 003: /* fild m16int */
reg_load_int16();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 010: /* fst m32real */
@@ -135,6 +139,7 @@ switch ( type )
break;
case 023: /* fbld m80dec */
reg_load_bcd();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 024: /* fldcw */
@@ -152,10 +157,12 @@ switch ( type )
break;
case 025: /* fld m80real */
reg_load_extended();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 027: /* fild m64int */
reg_load_int64();
+ setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 030: /* fstenv m14/28byte */
@@ -187,8 +194,8 @@ switch ( type )
(see the 80486 manual p16-28) */
break;
case 036: /* fstsw m2byte */
- status_word &= ~SW_TOP;
- status_word |= (top&7) << SW_TOPS;
+ status_word &= ~SW_Top;
+ status_word |= (top&7) << SW_Top_Shift;
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(status_word,(short *) FPU_data_address);
diff --git a/kernel/FPU-emu/poly_2xm1.c b/kernel/FPU-emu/poly_2xm1.c
index 749df16..5dcccf2 100644
--- a/kernel/FPU-emu/poly_2xm1.c
+++ b/kernel/FPU-emu/poly_2xm1.c
@@ -55,13 +55,13 @@ int poly_2xm1(FPU_REG *arg, FPU_REG *result)
if ( exponent >= 0 ) /* Can't hack a number >= 1.0 */
{
- arith_invalid(result);
+ arith_invalid(result); /* Number too large */
return 1;
}
if ( arg->sign != SIGN_POS ) /* Can't hack a number < 0.0 */
{
- arith_invalid(result);
+ arith_invalid(result); /* Number negative */
return 1;
}
diff --git a/kernel/FPU-emu/poly_atan.c b/kernel/FPU-emu/poly_atan.c
index a8609e4..a549d2b 100644
--- a/kernel/FPU-emu/poly_atan.c
+++ b/kernel/FPU-emu/poly_atan.c
@@ -58,7 +58,7 @@ void poly_atan(FPU_REG *arg)
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
- { arith_invalid(arg); return; }
+ { arith_invalid(arg); return; } /* Need a positive number */
#endif PARANOID
exponent = arg->exp - EXP_BIAS;
@@ -164,7 +164,6 @@ void poly_atan(FPU_REG *arg)
/* The complete odd polynomial */
reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
- odd_poly.exp -= EXP_BIAS - 1;
/* will be a valid positive nr with expon = 0 */
*(short *)&(even_poly.sign) = 0;
diff --git a/kernel/FPU-emu/poly_l2.c b/kernel/FPU-emu/poly_l2.c
index 6365fce..3cca359 100644
--- a/kernel/FPU-emu/poly_l2.c
+++ b/kernel/FPU-emu/poly_l2.c
@@ -71,6 +71,7 @@ void poly_l2(FPU_REG *arg, FPU_REG *result)
num.sigh = arg->sigh;
}
+
/* shift num left, lose the ms bit */
num.sigh <<= 1;
if ( num.sigl & 0x80000000 ) num.sigh |= 1;
@@ -130,7 +131,7 @@ void poly_l2(FPU_REG *arg, FPU_REG *result)
{
normalize(&num);
}
-
+
denom.tag = TW_Valid; /* set the tags to Valid */
denom.sign = SIGN_POS; /* set the sign to positive */
denom.exp = EXP_BIAS;
@@ -138,12 +139,12 @@ void poly_l2(FPU_REG *arg, FPU_REG *result)
reg_div(&num, &denom, &lXx, FULL_PRECISION);
reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
- accum.exp += - EXP_BIAS + 1;
reg_u_add(&lXx, &accum, result, FULL_PRECISION);
normalize(result);
}
+
result->sign = sign;
return;
}
@@ -276,7 +277,6 @@ int poly_l2p1(FPU_REG *arg, FPU_REG *result)
normalize(&accum);
reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
- accum.exp -= EXP_BIAS - 1;
reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
diff --git a/kernel/FPU-emu/poly_tan.c b/kernel/FPU-emu/poly_tan.c
index 149d840..b56d0e9 100644
--- a/kernel/FPU-emu/poly_tan.c
+++ b/kernel/FPU-emu/poly_tan.c
@@ -82,6 +82,7 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
+ return;
#endif PARANOID
}
/* The argument is in the range [0.5 .. 1.0) */
@@ -94,7 +95,7 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
- { arith_invalid(y_reg); return; }
+ { arith_invalid(y_reg); return; } /* Need a positive number */
#endif PARANOID
*(long long *)&arg_signif = *(long long *)&(arg->sigl);
diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c
index 3366a6d..380b481 100644
--- a/kernel/FPU-emu/reg_add_sub.c
+++ b/kernel/FPU-emu/reg_add_sub.c
@@ -89,23 +89,53 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
}
}
else
- reg_move(b, dest);
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(b, dest);
+ }
return;
}
else if (b->tag == TW_Zero)
- { reg_move(a, dest); return; }
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return;
+ }
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
- { reg_move(a, dest); return; }
- /* They are both + or - infinity */
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return;
+ }
if (a->sign == b->sign)
- { reg_move(a, dest); return; }
- reg_move(&CONST_QNaN, dest); /* inf - inf is undefined. */
+ {
+ /* They are both + or - infinity */
+ reg_move(a, dest); return;
+ }
+ arith_invalid(dest); /* Infinity-Infinity is undefined. */
return;
}
else if (b->tag == TW_Infinity)
- { reg_move(b, dest); return; }
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(b, dest); return;
+ }
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x101);
@@ -144,6 +174,11 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
}
else if ( diff == 0 )
{
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
@@ -184,11 +219,23 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
}
}
else
- reg_move(a, dest);
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(a, dest);
+ }
return;
}
else if (a->tag == TW_Zero)
{
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return;
@@ -196,14 +243,30 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
else if (a->tag == TW_Infinity)
{
if (b->tag != TW_Infinity)
- { reg_move(a, dest); return; }
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return;
+ }
+ /* Both args are Infinity */
if (a->sign == b->sign)
- { reg_move(&CONST_QNaN, dest); return; }
+ {
+ arith_invalid(dest); /* Infinity-Infinity is undefined. */
+ return;
+ }
reg_move(a, dest);
return;
}
else if (b->tag == TW_Infinity)
{
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
return;
diff --git a/kernel/FPU-emu/reg_compare.c b/kernel/FPU-emu/reg_compare.c
index 7d4c6ac..d3969b1 100644
--- a/kernel/FPU-emu/reg_compare.c
+++ b/kernel/FPU-emu/reg_compare.c
@@ -17,6 +17,7 @@
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
+#include "control_w.h"
#include "status_w.h"
@@ -28,17 +29,25 @@ int compare(FPU_REG *b)
{
if ( FPU_st0_ptr->tag == TW_Zero )
{
- if ( b->tag == TW_Zero ) return COMP_A_EQ_B;
+ if ( b->tag == TW_Zero ) return COMP_A_eq_B;
if ( b->tag == TW_Valid )
{
- return (b->sign == SIGN_POS) ? COMP_A_LT_B : COMP_A_GT_B ;
+#ifdef DENORM_OPERAND
+ if ( (b->exp <= EXP_UNDER) && (denormal_operand()) )
+ return COMP_Denormal;
+#endif DENORM_OPERAND
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ;
}
}
else if ( b->tag == TW_Zero )
{
if ( FPU_st0_ptr->tag == TW_Valid )
{
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B ;
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return COMP_Denormal;
+#endif DENORM_OPERAND
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ;
}
}
@@ -46,13 +55,18 @@ int compare(FPU_REG *b)
{
if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
{
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B;
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER)
+ && (denormal_operand()) )
+ return COMP_Denormal;
+#endif DENORM_OPERAND
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
}
else if ( b->tag == TW_Infinity )
{
/* The 80486 book says that infinities can be equal! */
- return (FPU_st0_ptr->sign == b->sign) ? COMP_A_EQ_B :
- ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B);
+ return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
+ ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
}
/* Fall through to the NaN code */
}
@@ -60,7 +74,13 @@ int compare(FPU_REG *b)
{
if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) )
{
- return (b->sign == SIGN_POS) ? COMP_A_LT_B : COMP_A_GT_B;
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->tag == TW_Valid)
+ && (FPU_st0_ptr->exp <= EXP_UNDER)
+ && (denormal_operand()) )
+ return COMP_Denormal;
+#endif DENORM_OPERAND
+ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
}
/* Fall through to the NaN code */
}
@@ -72,10 +92,10 @@ int compare(FPU_REG *b)
if ( ((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
|| ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
/* At least one arg is a signaling NaN */
- return COMP_NOCOMP | COMP_SNAN | COMP_NAN;
+ return COMP_No_Comp | COMP_SNaN | COMP_NaN;
else
/* Neither is a signaling NaN */
- return COMP_NOCOMP | COMP_NAN;
+ return COMP_No_Comp | COMP_NaN;
}
EXCEPTION(EX_Invalid);
@@ -85,9 +105,15 @@ int compare(FPU_REG *b)
if (!(FPU_st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
#endif PARANOID
+
+#ifdef DENORM_OPERAND
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (b->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return COMP_Denormal;
+#endif DENORM_OPERAND
if (FPU_st0_ptr->sign != b->sign)
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B;
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
diff = FPU_st0_ptr->exp - b->exp;
if ( diff == 0 )
@@ -103,37 +129,47 @@ int compare(FPU_REG *b)
}
if ( diff > 0 )
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B ;
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ;
if ( diff < 0 )
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_LT_B : COMP_A_GT_B ;
- return COMP_A_EQ_B;
+ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B ;
+ return COMP_A_eq_B;
}
-void compare_st_data(void)
+/* This function requires that st(0) is not empty */
+int compare_st_data(void)
{
- int f;
- int c = compare(&FPU_loaded_data);
+ int f, c;
+
+ c = compare(&FPU_loaded_data);
- if (c & COMP_NAN)
+ if (c & (COMP_NaN|COMP_Denormal))
{
- EXCEPTION(EX_Invalid);
- f = SW_C3 | SW_C2 | SW_C0;
+ if (c & COMP_NaN)
+ {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ }
+ else
+ {
+ /* One of the operands is a de-normal */
+ return 0;
+ }
}
else
switch (c)
{
- case COMP_A_LT_B:
+ case COMP_A_lt_B:
f = SW_C0;
break;
- case COMP_A_EQ_B:
+ case COMP_A_eq_B:
f = SW_C3;
break;
- case COMP_A_GT_B:
+ case COMP_A_gt_B:
f = 0;
break;
- case COMP_NOCOMP:
+ case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
@@ -144,31 +180,50 @@ void compare_st_data(void)
#endif PARANOID
}
setcc(f);
+ return 1;
}
-static void compare_st_st(int nr)
+static int compare_st_st(int nr)
{
- int c = compare(&st(nr));
- int f;
- if (c & COMP_NAN)
+ int f, c;
+
+ if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
- EXCEPTION(EX_Invalid);
- f = SW_C3 | SW_C2 | SW_C0;
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+
+ c = compare(&st(nr));
+ if (c & (COMP_NaN|COMP_Denormal))
+ {
+ if (c & COMP_NaN)
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ }
+ else
+ {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
}
else
switch (c)
{
- case COMP_A_LT_B:
+ case COMP_A_lt_B:
f = SW_C0;
break;
- case COMP_A_EQ_B:
+ case COMP_A_eq_B:
f = SW_C3;
break;
- case COMP_A_GT_B:
+ case COMP_A_gt_B:
f = 0;
break;
- case COMP_NOCOMP:
+ case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
@@ -179,33 +234,55 @@ static void compare_st_st(int nr)
#endif PARANOID
}
setcc(f);
+ return 1;
}
-static void compare_u_st_st(int nr)
+static int compare_u_st_st(int nr)
{
- int f;
- int c = compare(&st(nr));
- if (c & COMP_NAN)
+ int f, c;
+
+ if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) )
{
- if (c & COMP_SNAN) /* This is the only difference between
- un-ordered and ordinary comparisons */
- EXCEPTION(EX_Invalid);
- f = SW_C3 | SW_C2 | SW_C0;
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return control_word & CW_Invalid;
+ }
+
+ c = compare(&st(nr));
+ if (c & (COMP_NaN|COMP_Denormal))
+ {
+ if (c & COMP_NaN)
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (c & COMP_SNaN) /* This is the only difference between
+ un-ordered and ordinary comparisons */
+ {
+ EXCEPTION(EX_Invalid);
+ return control_word & CW_Invalid;
+ }
+ return 1;
+ }
+ else
+ {
+ /* One of the operands is a de-normal */
+ return control_word & CW_Denormal;
+ }
}
else
switch (c)
{
- case COMP_A_LT_B:
+ case COMP_A_lt_B:
f = SW_C0;
break;
- case COMP_A_EQ_B:
+ case COMP_A_eq_B:
f = SW_C3;
break;
- case COMP_A_GT_B:
+ case COMP_A_gt_B:
f = 0;
break;
- case COMP_NOCOMP:
+ case COMP_No_Comp:
f = SW_C3 | SW_C2 | SW_C0;
break;
#ifdef PARANOID
@@ -216,6 +293,7 @@ static void compare_u_st_st(int nr)
#endif PARANOID
}
setcc(f);
+ return 1;
}
/*---------------------------------------------------------------------------*/
@@ -230,8 +308,8 @@ void fcom_st()
void fcompst()
{
/* fcomp st(i) */
- compare_st_st(FPU_rm);
- pop();
+ if ( compare_st_st(FPU_rm) )
+ pop();
}
@@ -240,9 +318,11 @@ void fcompp()
/* fcompp */
if (FPU_rm != 1)
return Un_impl();
- compare_st_st(1);
- pop(); FPU_st0_ptr = &st(0);
- pop();
+ if ( compare_st_st(1) )
+ {
+ pop(); FPU_st0_ptr = &st(0);
+ pop();
+ }
}
@@ -250,14 +330,15 @@ void fucom_()
{
/* fucom st(i) */
compare_u_st_st(FPU_rm);
+
}
void fucomp()
{
/* fucomp st(i) */
- compare_u_st_st(FPU_rm);
- pop();
+ if ( compare_u_st_st(FPU_rm) )
+ pop();
}
@@ -266,9 +347,11 @@ void fucompp()
/* fucompp */
if (FPU_rm == 1)
{
- compare_u_st_st(1);
- pop(); FPU_st0_ptr = &st(0);
- pop();
+ if ( compare_u_st_st(1) )
+ {
+ pop(); FPU_st0_ptr = &st(0);
+ pop();
+ }
}
else
Un_impl();
diff --git a/kernel/FPU-emu/reg_div.S b/kernel/FPU-emu/reg_div.S
index eb25dc3..854e617 100644
--- a/kernel/FPU-emu/reg_div.S
+++ b/kernel/FPU-emu/reg_div.S
@@ -38,22 +38,38 @@ _reg_div:
jne L_div_special // Not (both numbers TW_Valid)
+#ifdef DENORM_OPERAND
+// Check for denormals
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xL_arg1_not_denormal
-// Both arguments are TW_Valid
- movl EXP(%esi),%edx
- movl EXP(%ebx),%eax
- subl %eax,%edx
- addl EXP_BIAS,%edx
- movl %edx,EXP(%edi)
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xL_arg1_not_denormal:
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg xL_arg2_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+xL_arg2_not_denormal:
+#endif DENORM_OPERAND
+
+// Both arguments are TW_Valid
movb TW_Valid,TAG(%edi)
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
setne (%edi) // Set the sign, requires SIGN_NEG=1, SIGN_POS=0
-// add $SIGL_OFFSET,%ebx
-// add $SIGL_OFFSET,%esi
+ movl EXP(%esi),%edx
+ movl EXP(%ebx),%eax
+ subl %eax,%edx
+ addl EXP_BIAS,%edx
+ movl %edx,EXP(%edi)
jmp _divide_kernel
@@ -69,7 +85,7 @@ L_div_special:
// Operations on NaNs
L_arg1_NaN:
L_arg2_NaN:
- pushl %edi
+ pushl %edi // Destination
pushl %ebx
pushl %esi
call _real_2op_NaN
@@ -78,8 +94,8 @@ L_arg2_NaN:
// Invalid operations
L_zero_zero:
L_inf_inf:
- pushl %esi
- call _arith_invalid
+ pushl %edi // Destination
+ call _arith_invalid // 0/0 or Infinity/Infinity
jmp LDiv_exit
L_no_NaN_arg:
@@ -89,9 +105,30 @@ L_no_NaN_arg:
cmpb TW_Infinity,TAG(%ebx)
je L_inf_inf // invalid operation
+ cmpb TW_Valid,TAG(%ebx)
+ je L_inf_valid
+
+#ifdef PARANOID
+ // arg2 must be zero or valid
+ cmpb TW_Zero,TAG(%ebx)
+ ja L_unknown_tags
+#endif PARANOID
+
// Note that p16-9 says that infinity/0 returns infinity
jmp L_copy_arg1 // Answer is Inf
+L_inf_valid:
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 // Answer is Inf
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_copy_arg1 // Answer is Inf
+
L_arg1_not_inf:
cmpb TW_Zero,TAG(%ebx) // Priority to div-by-zero error
jne L_arg2_not_zero
@@ -99,8 +136,14 @@ L_arg1_not_inf:
cmpb TW_Zero,TAG(%esi)
je L_zero_zero // invalid operation
+#ifdef PARANOID
+ // arg1 must be valid
+ cmpb TW_Valid,TAG(%esi)
+ ja L_unknown_tags
+#endif PARANOID
+
// Division by zero error
- pushl %esi
+ pushl %edi // destination
movb SIGN(%esi),%al
xorb SIGN(%ebx),%al
pushl %eax // lower 8 bits have the sign
@@ -111,11 +154,37 @@ L_arg2_not_zero:
cmpb TW_Infinity,TAG(%ebx)
jne L_arg2_not_inf
+#ifdef DENORM_OPERAND
+ cmpb TW_Valid,TAG(%esi)
+ jne L_return_zero
+
+ cmpl EXP_UNDER,EXP(%esi)
+ jg L_return_zero // Answer is zero
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
+
jmp L_return_zero // Answer is zero
L_arg2_not_inf:
+
+#ifdef PARANOID
cmpb TW_Zero,TAG(%esi)
jne L_unknown_tags
+#endif PARANOID
+
+ // arg1 is zero, arg2 is not Infinity or a NaN
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 // Answer is zero
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+#endif DENORM_OPERAND
L_copy_arg1:
movb TAG(%esi),%ax
@@ -142,7 +211,6 @@ LDiv_set_result_sign:
movb SIGN_POS,SIGN(%ebx)
jmp LDiv_exit
- .align 2,0x90
LDiv_negative_result:
movb SIGN_NEG,SIGN(%edi)
@@ -160,6 +228,7 @@ L_return_zero:
movb TW_Zero,TAG(%edi)
jmp LDiv_set_result_sign
+#ifdef PARANOID
L_unknown_tags:
push EX_INTERNAL | 0x208
call EXCEPTION
@@ -172,3 +241,4 @@ L_unknown_tags:
movl _CONST_QNaN+8,%eax
movl %eax,SIGH(%edi)
jmp LDiv_exit
+#endif PARANOID
diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c
index 710b7a4..3b1d6d9 100644
--- a/kernel/FPU-emu/reg_ld_str.c
+++ b/kernel/FPU-emu/reg_ld_str.c
@@ -24,6 +24,7 @@
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
+#include "status_w.h"
#define EXTENDED_Emax 0x3fff /* largest valid exponent */
@@ -38,6 +39,8 @@
#define SINGLE_Ebias 127
#define SINGLE_Emin (-126) /* smallest valid exponent */
+#define LOST_UP (EX_Precision | SW_C1)
+#define LOST_DOWN EX_Precision
FPU_REG FPU_loaded_data;
@@ -71,8 +74,13 @@ void reg_load_extended(void)
FPU_loaded_data.tag = TW_Zero;
return;
}
- /* The number is de-normal */
- /* The default behaviour will take care of this */
+ /* The number is a de-normal or pseudodenormal. */
+ /* The 80486 doesn't regard pseudodenormals as denormals here. */
+ if ( !(FPU_loaded_data.sigh & 0x80000000) )
+ EXCEPTION(EX_Denormal);
+ FPU_loaded_data.exp++;
+
+ /* The default behaviour will now take care of it. */
}
else if ( FPU_loaded_data.exp == 0x7fff )
{
@@ -83,9 +91,9 @@ void reg_load_extended(void)
FPU_loaded_data.tag = TW_Infinity;
return;
}
- if ( !(FPU_loaded_data.sigh & 0x80000000) )
+ else if ( !(FPU_loaded_data.sigh & 0x80000000) )
{
- /* Unsupported data type */
+ /* Unsupported NaN data type */
EXCEPTION(EX_Invalid);
FPU_loaded_data.tag = TW_NaN;
return;
@@ -97,7 +105,12 @@ void reg_load_extended(void)
+ EXP_BIAS;
FPU_loaded_data.tag = TW_Valid;
- normalize(&FPU_loaded_data);
+ if ( !(sigh & 0x80000000) )
+ {
+ /* Unsupported data type */
+ EXCEPTION(EX_Invalid);
+ normalize_nuo(&FPU_loaded_data);
+ }
}
@@ -154,12 +167,13 @@ void reg_load_double(void)
else
{
/* De-normal */
+ EXCEPTION(EX_Denormal);
FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
FPU_loaded_data.tag = TW_Valid;
FPU_loaded_data.sigh = m64 << 11;
FPU_loaded_data.sigh |= l64 >> 21;
FPU_loaded_data.sigl = l64 << 11;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
return;
}
}
@@ -204,11 +218,12 @@ void reg_load_single(void)
if ( exp < SINGLE_Emin )
{
/* De-normals */
+ EXCEPTION(EX_Denormal);
FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
FPU_loaded_data.tag = TW_Valid;
FPU_loaded_data.sigh = m32;
FPU_loaded_data.sigl = 0;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
return;
}
else if ( exp > SINGLE_Emax )
@@ -268,7 +283,7 @@ void reg_load_int64(void)
*((long long *)&FPU_loaded_data.sigl) = s;
FPU_loaded_data.exp = e;
FPU_loaded_data.tag = TW_Valid;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
}
@@ -299,7 +314,7 @@ void reg_load_int32(void)
FPU_loaded_data.sigl = 0;
FPU_loaded_data.exp = e;
FPU_loaded_data.tag = TW_Valid;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
}
@@ -310,7 +325,8 @@ void reg_load_int16(void)
int s, e;
RE_ENTRANT_CHECK_OFF
- s = (int)get_fs_word((unsigned short *) _s);
+ /* Cast as short to get the sign extended. */
+ s = (short)get_fs_word((unsigned short *) _s);
RE_ENTRANT_CHECK_ON
if (s == 0)
@@ -330,7 +346,7 @@ void reg_load_int16(void)
FPU_loaded_data.sigl = 0;
FPU_loaded_data.exp = e;
FPU_loaded_data.tag = TW_Valid;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
}
@@ -372,7 +388,7 @@ void reg_load_bcd(void)
*((long long *)&FPU_loaded_data.sigl) = l;
FPU_loaded_data.exp = EXP_BIAS + 63;
FPU_loaded_data.tag = TW_Valid;
- normalize(&FPU_loaded_data);
+ normalize_nuo(&FPU_loaded_data);
}
}
@@ -405,26 +421,30 @@ int reg_store_extended(void)
}
else if ( e <= 0 )
{
- if ( e == 0 )
- {
- EXCEPTION(EX_Denormal); /* Pseudo de-normal */
- ls = FPU_st0_ptr->sigl;
- ms = FPU_st0_ptr->sigh;
- }
- else if ( e > -64 )
+ if ( e > -63 )
{
- /* Make a de-normal */
+ /* Correctly format the de-normal */
+ int precision_loss;
FPU_REG tmp;
- EXCEPTION(EX_Denormal); /* De-normal */
+
+ EXCEPTION(EX_Denormal);
reg_move(FPU_st0_ptr, &tmp);
- tmp.exp += -EXTENDED_Emin + 64; /* largest exp to be 63 */
- round_to_int(&tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
+ if ( (precision_loss = round_to_int(&tmp)) )
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec 16.2.5.1 of
+ the 80486 book */
+ if ( !(control_word & EX_Underflow) )
+ return 0;
+ }
e = 0;
ls = tmp.sigl;
ms = tmp.sigh;
}
else
{
+ /* ****** ??? This should not be possible */
EXCEPTION(EX_Underflow); /* Underflow */
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
if ( control_word & EX_Underflow )
@@ -432,7 +452,7 @@ int reg_store_extended(void)
/* Underflow to zero */
ls = 0;
ms = 0;
- e = 0;
+ e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
}
else
return 0;
@@ -509,113 +529,128 @@ int reg_store_double(void)
FPU_REG tmp;
reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
- if ( tmp.sigl & 0x000007ff )
+ if ( exp < DOUBLE_Emin ) /* It may be a denormal */
{
- unsigned long increment = 0; /* avoid gcc warnings */
-
- switch (control_word & CW_RC)
+ /* Make a de-normal */
+ int precision_loss;
+
+ if ( exp <= -EXTENDED_Ebias )
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
+
+ if ( (precision_loss = round_to_int(&tmp)) )
{
- case RC_RND:
- /* Rounding can get a little messy.. */
- increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
- ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
- break;
- case RC_DOWN: /* towards -infinity */
- increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
- break;
- case RC_UP: /* towards +infinity */
- increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
- break;
- case RC_CHOP:
- increment = 0;
- break;
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as peculiar, it appears
+ that the 80486 rounds to the dest precision, then
+ converts to decide underflow. */
+ if ( (tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+ (FPU_st0_ptr->sigl & 0x000007ff) )
+ EXCEPTION(precision_loss);
+ else
+#endif PECULIAR_486
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec 16.2.5.1 of
+ the 80486 book */
+ if ( !(control_word & EX_Underflow) )
+ return 0;
+ }
}
-
- /* Truncate the mantissa */
- tmp.sigl &= 0xfffff800;
-
- if ( increment )
+ l[0] = tmp.sigl;
+ l[1] = tmp.sigh;
+ }
+ else
+ {
+ if ( tmp.sigl & 0x000007ff )
{
- if ( tmp.sigl >= 0xfffff800 )
+ unsigned long increment = 0; /* avoid gcc warnings */
+
+ switch (control_word & CW_RC)
{
- /* the sigl part overflows */
- if ( tmp.sigh == 0xffffffff )
+ case RC_RND:
+ /* Rounding can get a little messy.. */
+ increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
+ ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate the mantissa */
+ tmp.sigl &= 0xfffff800;
+
+ if ( increment )
+ {
+ set_precision_flag_up();
+
+ if ( tmp.sigl >= 0xfffff800 )
{
- /* The sigh part overflows */
- tmp.sigh = 0x80000000;
- tmp.exp++;
- if (tmp.exp >= EXP_OVER)
- goto overflow;
+ /* the sigl part overflows */
+ if ( tmp.sigh == 0xffffffff )
+ {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ }
+ else
+ {
+ tmp.sigh ++;
+ }
+ tmp.sigl = 0x00000000;
}
else
{
- tmp.sigh ++;
+ /* We only need to increment sigl */
+ tmp.sigl += 0x00000800;
}
- tmp.sigl = 0x00000000;
}
else
- {
- /* We only need to increment sigl */
- tmp.sigl += 0x00000800;
- }
+ set_precision_flag_down();
}
- }
-
- l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
- l[1] = ((tmp.sigh >> 11) & 0xfffff);
- exp = tmp.exp - EXP_BIAS;
+
+ l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
+ l[1] = ((tmp.sigh >> 11) & 0xfffff);
- if ( exp > DOUBLE_Emax )
- {
- overflow:
- EXCEPTION(EX_Overflow);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- if ( control_word & EX_Overflow )
- {
- /* Overflow to infinity */
- l[0] = 0x00000000; /* Set to */
- l[1] = 0x7ff00000; /* + INF */
- }
- else
- return 0;
- }
- else if ( exp < DOUBLE_Emin )
- {
- if ( exp > DOUBLE_Emin-53 )
+ if ( exp > DOUBLE_Emax )
{
- /* Make a de-normal */
- FPU_REG tmp;
- EXCEPTION(EX_Denormal);
- reg_move(FPU_st0_ptr, &tmp);
- tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
- round_to_int(&tmp);
- l[0] = tmp.sigl;
- l[1] = tmp.sigh;
- }
- else
- {
- EXCEPTION(EX_Underflow);
+ overflow:
+ EXCEPTION(EX_Overflow);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
- if ( control_word & EX_Underflow )
+ if ( control_word & EX_Overflow )
{
- /* Underflow to zero */
- l[0] = l[1] = 0;
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
}
else
return 0;
}
- }
- else
- {
- /* Add the exponent */
- l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
+ else
+ {
+ /* Add the exponent */
+ l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
+ }
}
}
else if (FPU_st0_tag == TW_Zero)
{
/* Number is zero */
- l[0] = l[1] = 0;
+ l[0] = 0;
+ l[1] = 0;
}
else if (FPU_st0_tag == TW_Infinity)
{
@@ -655,12 +690,14 @@ put_indefinite:
else
return 0;
}
+#if 0 /* TW_Denormal is not used yet, and probably won't be */
else if (FPU_st0_tag == TW_Denormal)
{
/* Extended real -> double real will always underflow */
l[0] = l[1] = 0;
EXCEPTION(EX_Underflow);
}
+#endif
if (FPU_st0_ptr->sign)
l[1] |= 0x80000000;
@@ -686,97 +723,114 @@ int reg_store_single(void)
FPU_REG tmp;
reg_move(FPU_st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
- if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
+ if ( exp < SINGLE_Emin )
{
- unsigned long increment = 0; /* avoid gcc warnings */
- unsigned long sigh = tmp.sigh;
- unsigned long sigl = tmp.sigl;
-
- switch (control_word & CW_RC)
+ /* Make a de-normal */
+ int precision_loss;
+
+ if ( exp <= -EXTENDED_Ebias )
+ EXCEPTION(EX_Denormal);
+
+ tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
+
+ if ( (precision_loss = round_to_int(&tmp)) )
{
- case RC_RND:
- increment = ((sigh & 0xff) > 0x80) /* more than half */
- || (((sigh & 0xff) == 0x80) && sigl) /* more than half */
- || ((sigh & 0x180) == 0x180); /* round to even */
- break;
- case RC_DOWN: /* towards -infinity */
- increment = (tmp.sign == SIGN_POS) ? 0 : (sigl | (sigh & 0xff));
- break;
- case RC_UP: /* towards +infinity */
- increment = (tmp.sign == SIGN_POS) ? (sigl | (sigh & 0xff)) : 0;
- break;
- case RC_CHOP:
- increment = 0;
- break;
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as peculiar, it appears
+ that the 80486 rounds to the dest precision, then
+ converts to decide underflow. */
+ if ( (tmp.sigl == 0x00800000) &&
+ ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl) )
+ EXCEPTION(precision_loss);
+ else
+#endif PECULIAR_486
+ {
+ EXCEPTION(EX_Underflow | precision_loss);
+ /* This is a special case: see sec 16.2.5.1 of
+ the 80486 book */
+ if ( !(control_word & EX_Underflow) )
+ return 0;
+ }
}
+ templ = tmp.sigl;
+ }
+ else
+ {
+ if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
+ {
+ unsigned long increment = 0; /* avoid gcc warnings */
+ unsigned long sigh = tmp.sigh;
+ unsigned long sigl = tmp.sigl;
+
+ switch (control_word & CW_RC)
+ {
+ case RC_RND:
+ increment = ((sigh & 0xff) > 0x80) /* more than half */
+ || (((sigh & 0xff) == 0x80) && sigl) /* more than half */
+ || ((sigh & 0x180) == 0x180); /* round to even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? 0 : (sigl | (sigh & 0xff));
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? (sigl | (sigh & 0xff)) : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
- /* Truncate part of the mantissa */
- tmp.sigl = 0;
+ /* Truncate part of the mantissa */
+ tmp.sigl = 0;
- if (increment)
- {
- if ( sigh >= 0xffffff00 )
+ if (increment)
{
- /* The sigh part overflows */
- tmp.sigh = 0x80000000;
- tmp.exp++;
- if (tmp.exp >= EXP_OVER)
- goto overflow;
+ set_precision_flag_up();
+
+ if ( sigh >= 0xffffff00 )
+ {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if ( exp >= EXP_OVER )
+ goto overflow;
+ }
+ else
+ {
+ tmp.sigh &= 0xffffff00;
+ tmp.sigh += 0x100;
+ }
}
else
{
- tmp.sigh &= 0xffffff00;
- tmp.sigh += 0x100;
+ set_precision_flag_down();
+ tmp.sigh &= 0xffffff00; /* Finish the truncation */
}
}
- else
- tmp.sigh &= 0xffffff00; /* Finish the truncation */
- }
- templ = (tmp.sigh >> 8) & 0x007fffff;
- exp = tmp.exp - EXP_BIAS;
+ templ = (tmp.sigh >> 8) & 0x007fffff;
- if ( exp > SINGLE_Emax )
- {
- overflow:
- EXCEPTION(EX_Overflow);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- if ( control_word & EX_Overflow )
+ if ( exp > SINGLE_Emax )
{
- /* Overflow to infinity */
- templ = 0x7f800000;
- }
- else
- return 0;
- }
- else if ( exp < SINGLE_Emin )
- {
- if ( exp > SINGLE_Emin-24 )
- {
- /* Make a de-normal */
- FPU_REG tmp;
- EXCEPTION(EX_Denormal);
- reg_move(FPU_st0_ptr, &tmp);
- tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
- round_to_int(&tmp);
- templ = tmp.sigl;
- }
- else
- {
- EXCEPTION(EX_Underflow);
+ overflow:
+ EXCEPTION(EX_Overflow);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
- if ( control_word & EX_Underflow )
+ if ( control_word & EX_Overflow )
{
- /* Underflow to zero */
- templ = 0;
+ /* Overflow to infinity */
+ templ = 0x7f800000;
}
else
return 0;
}
+ else
+ templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
}
- else
- templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
}
else if (FPU_st0_tag == TW_Zero)
{
@@ -817,12 +871,14 @@ put_indefinite:
else
return 0;
}
+#if 0 /* TW_Denormal is not used yet, and probably won't be */
else if (FPU_st0_tag == TW_Denormal)
{
/* Extended real -> real will always underflow */
templ = 0;
EXCEPTION(EX_Underflow);
}
+#endif
#ifdef PARANOID
else
{
@@ -1069,7 +1125,10 @@ put_indefinite:
/*===========================================================================*/
/* r gets mangled such that sig is int, sign:
- it is NOT normalized*/
+ it is NOT normalized */
+/* The return value (in eax) is zero if the result is exact,
+ if bits are changed due to rounding, truncation, etc, then
+ a non-zero value is returned */
/* Overflow is signalled by a non-zero return value (in eax).
In the case of overflow, the returned significand always has the
the largest possible value */
@@ -1105,6 +1164,7 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
+ return LOST_UP;
}
break;
case RC_DOWN:
@@ -1112,6 +1172,7 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
+ return LOST_UP;
}
break;
case RC_UP:
@@ -1119,13 +1180,15 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
+ return LOST_UP;
}
break;
case RC_CHOP:
break;
}
- return 0; /* o.k. */
+ return eax ? LOST_DOWN : 0;
+
}
/*===========================================================================*/
@@ -1147,11 +1210,12 @@ char *fldenv(void)
operand_selector = get_fs_long((unsigned long *) (s+0x18));
RE_ENTRANT_CHECK_ON
+ top = (status_word >> SW_Top_Shift) & 7;
- for ( i = 7; i >= 0; i-- )
+ for ( i = 0; i < 8; i++ )
{
tag = tag_word & 3;
- tag_word <<= 2;
+ tag_word >>= 2;
switch ( tag )
{
@@ -1179,45 +1243,57 @@ char *fldenv(void)
void frstor(void)
{
- int i;
+ int i, stnr;
unsigned char tag;
- FPU_REG *s = (FPU_REG *)fldenv();
+ unsigned short saved_status, saved_control;
+ char *s = (char *)fldenv();
+ saved_status = status_word;
+ saved_control = control_word;
+ control_word = 0x037f; /* Mask all interrupts while we load. */
for ( i = 0; i < 8; i++ )
{
/* load each register */
- FPU_data_address = (void *)&(s[i]);
+ FPU_data_address = (void *)(s+i*10);
reg_load_extended();
- tag = regs[i].tag;
- reg_move(&FPU_loaded_data, &regs[i]);
+ stnr = (i+top) & 7;
+ tag = regs[stnr].tag; /* derived from the loaded tag word */
+ reg_move(&FPU_loaded_data, &regs[stnr]);
if ( tag == TW_NaN )
{
- unsigned char t = regs[i].tag;
- if ( (t == TW_Valid) || (t == TW_Zero) )
- regs[i].tag = TW_NaN;
+ /* The current data is a special, i.e. NaN, unsupported, infinity,
+ or denormal */
+ unsigned char t = regs[stnr].tag; /* derived from the new data */
+ if ( /* (t == TW_Valid) || ****/ (t == TW_Zero) )
+ regs[stnr].tag = TW_NaN;
}
else
- regs[i].tag = tag;
+ regs[stnr].tag = tag;
}
+ control_word = saved_control;
+ status_word = saved_status;
FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
}
-char *fstenv(void)
+unsigned short tag_word(void)
{
- char *d = (char *)FPU_data_address;
- unsigned short tag_word = 0;
+ unsigned short word = 0;
unsigned char tag;
int i;
- verify_area(VERIFY_WRITE,d,28);
-
for ( i = 7; i >= 0; i-- )
{
switch ( tag = regs[i].tag )
{
+#if 0 /* TW_Denormal is not used yet, and probably won't be */
case TW_Denormal:
+#endif
+ case TW_Valid:
+ if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
+ tag = 2;
+ break;
case TW_Infinity:
case TW_NaN:
tag = 2;
@@ -1227,18 +1303,29 @@ char *fstenv(void)
break;
/* TW_Valid and TW_Zero already have the correct value */
}
- tag_word <<= 2;
- tag_word |= tag;
+ word <<= 2;
+ word |= tag;
}
+ return word;
+}
- /* This is not what should be done ... but saves overheads. */
- *(unsigned short *)&cs_selector = FPU_CS;
- *(unsigned short *)&operand_selector = FPU_DS;
+
+char *fstenv(void)
+{
+ char *d = (char *)FPU_data_address;
+
+ verify_area(VERIFY_WRITE,d,28);
+
+#if 0 /****/
+ *(unsigned short *)&cs_selector = fpu_cs;
+ *(unsigned short *)&operand_selector = fpu_os;
+#endif /****/
RE_ENTRANT_CHECK_OFF
put_fs_word(control_word, (unsigned short *) d);
- put_fs_word(status_word, (unsigned short *) (d+4));
- put_fs_word(tag_word, (unsigned short *) (d+8));
+ put_fs_word((status_word & ~SW_Top) | ((top&7) << SW_Top_Shift),
+ (unsigned short *) (d+4));
+ put_fs_word(tag_word(), (unsigned short *) (d+8));
put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
put_fs_long(cs_selector, (unsigned long *) (d+0x10));
put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
@@ -1260,8 +1347,8 @@ void fsave(void)
verify_area(VERIFY_WRITE,d,80);
for ( i = 0; i < 8; i++ )
{
- /* store each register */
- rp = &regs[i];
+ /* Store each register in the order: st(0), st(1), ... */
+ rp = &regs[(top+i) & 7];
e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
@@ -1271,72 +1358,63 @@ void fsave(void)
{
/* Overflow to infinity */
RE_ENTRANT_CHECK_OFF
- put_fs_long(0, (unsigned long *) (d+i*10+2));
- put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
+ put_fs_long(0, (unsigned long *) (d+i*10));
+ put_fs_long(0x80000000, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
e = 0x7fff;
}
else if ( e <= 0 )
{
- if ( e == 0 )
- {
- /* Pseudo de-normal */
- RE_ENTRANT_CHECK_OFF
- put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
- put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
- RE_ENTRANT_CHECK_ON
- }
- else if ( e > -64 )
+ if ( e > -63 )
{
/* Make a de-normal */
reg_move(rp, &tmp);
- tmp.exp += -EXTENDED_Emin + 64; /* largest exp to be 63 */
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
round_to_int(&tmp);
- e = 0;
RE_ENTRANT_CHECK_OFF
- put_fs_long(tmp.sigl, (unsigned long *) (d+i*10+2));
- put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+6));
+ put_fs_long(tmp.sigl, (unsigned long *) (d+i*10));
+ put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
}
else
{
/* Underflow to zero */
RE_ENTRANT_CHECK_OFF
- put_fs_long(0, (unsigned long *) (d+i*10+2));
- put_fs_long(0, (unsigned long *) (d+i*10+6));
+ put_fs_long(0, (unsigned long *) (d+i*10));
+ put_fs_long(0, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
- e = 0;
}
+ e = 0;
}
else
{
RE_ENTRANT_CHECK_OFF
- put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
- put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
}
}
else if ( rp->tag == TW_Zero )
{
RE_ENTRANT_CHECK_OFF
- put_fs_long(0, (unsigned long *) (d+i*10+2));
- put_fs_long(0, (unsigned long *) (d+i*10+6));
+ put_fs_long(0, (unsigned long *) (d+i*10));
+ put_fs_long(0, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
e = 0;
}
else if ( rp->tag == TW_Infinity )
{
RE_ENTRANT_CHECK_OFF
- put_fs_long(0, (unsigned long *) (d+i*10+2));
- put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
+ put_fs_long(0, (unsigned long *) (d+i*10));
+ put_fs_long(0x80000000, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
e = 0x7fff;
}
else if ( rp->tag == TW_NaN )
{
RE_ENTRANT_CHECK_OFF
- put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
- put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
e = 0x7fff;
}
@@ -1344,15 +1422,18 @@ void fsave(void)
{
/* just copy the reg */
RE_ENTRANT_CHECK_OFF
- put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
- put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
RE_ENTRANT_CHECK_ON
}
+ e |= rp->sign == SIGN_POS ? 0 : 0x8000;
RE_ENTRANT_CHECK_OFF
- put_fs_word(e, (unsigned short *) (d+i*10));
+ put_fs_word(e, (unsigned short *) (d+i*10+8));
RE_ENTRANT_CHECK_ON
}
+ finit();
+
}
/*===========================================================================*/
diff --git a/kernel/FPU-emu/reg_mul.c b/kernel/FPU-emu/reg_mul.c
index 88272cd..67a08c1 100644
--- a/kernel/FPU-emu/reg_mul.c
+++ b/kernel/FPU-emu/reg_mul.c
@@ -23,26 +23,36 @@
/* This routine must be called with non-empty source registers */
void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
{
+ char sign = (a->sign ^ b->sign);
+
if (!(a->tag | b->tag))
{
/* This should be the most common case */
- dest->sign = (a->sign ^ b->sign);
reg_u_mul(a, b, dest, control_w);
- dest->exp += - EXP_BIAS + 1;
-/* dest->tag = TW_Valid; ****** */
- if ( dest->exp <= EXP_UNDER )
- { arith_underflow(FPU_st0_ptr); }
- else if ( dest->exp >= EXP_OVER )
- { arith_overflow(FPU_st0_ptr); }
+ dest->sign = sign;
return;
}
else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
{
+#ifdef DENORM_OPERAND
+ if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
+ ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
+ {
+ if ( denormal_operand() ) return;
+ }
+#endif DENORM_OPERAND
/* Must have either both arguments == zero, or
one valid and the other zero.
The result is therefore zero. */
reg_move(&CONST_Z, dest);
+#ifdef PECULIAR_486
+ /* The 80486 book says that the answer is +0, but a real
+ 80486 appears to behave this way... */
+ dest->sign = sign;
+#endif PECULIAR_486
+ return;
}
+#if 0 /* TW_Denormal is not used yet... perhaps never will be. */
else if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal))
{
/* One or both arguments are de-normalized */
@@ -50,6 +60,7 @@ void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
EXCEPTION(EX_INTERNAL|0x105);
reg_move(&CONST_Z, dest);
}
+#endif
else
{
/* Must have infinities, NaNs, etc */
@@ -58,22 +69,34 @@ void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
else if (a->tag == TW_Infinity)
{
if (b->tag == TW_Zero)
- { arith_invalid(dest); return; }
+ { arith_invalid(dest); return; } /* Zero*Infinity is invalid */
else
{
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
reg_move(a, dest);
- dest->sign = a->sign == b->sign ? SIGN_POS : SIGN_NEG;
+ dest->sign = sign;
}
+ return;
}
else if (b->tag == TW_Infinity)
{
if (a->tag == TW_Zero)
- { arith_invalid(dest); return; }
+ { arith_invalid(dest); return; } /* Zero*Infinity is invalid */
else
{
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return;
+#endif DENORM_OPERAND
reg_move(b, dest);
- dest->sign = a->sign == b->sign ? SIGN_POS : SIGN_NEG;
+ dest->sign = sign;
}
+ return;
}
#ifdef PARANOID
else
@@ -81,6 +104,5 @@ void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
EXCEPTION(EX_INTERNAL|0x102);
}
#endif PARANOID
- dest->sign = (a->sign ^ b->sign);
}
}
diff --git a/kernel/FPU-emu/reg_norm.S b/kernel/FPU-emu/reg_norm.S
index 0a271da..22fddfa 100644
--- a/kernel/FPU-emu/reg_norm.S
+++ b/kernel/FPU-emu/reg_norm.S
@@ -1,7 +1,8 @@
/*---------------------------------------------------------------------------+
| reg_norm.S |
| |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
| |
| Normalize the value in a FPU_REG. |
@@ -9,6 +10,8 @@
| Call from C as: |
| void normalize(FPU_REG *n) |
| |
+ | void normalize_nuo(FPU_REG *n) |
+ | |
+---------------------------------------------------------------------------*/
#include "fpu_asm.h"
@@ -36,7 +39,7 @@ _normalize:
orl %eax,%eax
jz L_zero // The contents are zero
-L_shift_32:
+// L_shift_32:
movl %eax,%edx
xorl %eax,%eax
subl $32,EXP(%ebx) // This can cause an underflow
@@ -82,3 +85,46 @@ L_overflow:
call _arith_overflow
pop %ebx
jmp L_exit
+
+
+
+// Normalise without reporting underflow or overflow
+ .align 2,144
+.globl _normalize_nuo
+
+_normalize_nuo:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx // ms bits
+ js L_exit // Already normalized
+ jnz L_nuo_shift_1 // Shift left 1 - 31 bits
+
+ orl %eax,%eax
+ jz L_zero // The contents are zero
+
+// L_nuo_shift_32:
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) // This can cause an underflow
+
+/* We need to shift left by 1 - 31 bits */
+L_nuo_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) // This can cause an underflow
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+ jmp L_exit
+
+
diff --git a/kernel/FPU-emu/reg_round.S b/kernel/FPU-emu/reg_round.S
index 78e136a..4097606 100644
--- a/kernel/FPU-emu/reg_round.S
+++ b/kernel/FPU-emu/reg_round.S
@@ -8,15 +8,20 @@
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
| |
- | Not callable from C. |
- | Must be entered by a jmp intruction. |
+ | This code has four possible entry points. |
+ | The following must be entered by a jmp intruction: |
+ | FPU_round, FPU_round_sqrt, and FPU_Arith_exit. |
+ | |
+ | The _round_reg entry point is intended to be used by C code. |
+ | From C, call as: |
+ | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
- | Two entry points. |
+ | Four entry points. |
| |
- | Needed by both entry points: |
+ | Needed by both the FPU_round and FPU_round_sqrt entry points: |
| %eax:%ebx 64 bit significand |
| %edx 32 bit extension of the significand |
| %edi pointer to an FPU_REG for the result to be stored |
@@ -50,22 +55,74 @@
| |
+---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------+
+ | The code in this module has become quite complex, but it should handle |
+ | all of the FPU flags which are set at this stage of the basic arithmetic |
+ | computations. |
+ | There are a few rare cases where the results are not set identically to |
+ | a real FPU. These require a bit more thought because at this stage the |
+ | results of the code here appear to be more consistent... |
+ | This may be changed in a future version. |
+ +---------------------------------------------------------------------------*/
+
+
#include "fpu_asm.h"
-#include "fpu_emu.h"
#include "exception.h"
#include "control_w.h"
+#define LOST_DOWN $1
+#define LOST_UP $2
+#define DENORMAL $1
+#define UNMASKED_UNDERFLOW $2
+
+.data
+ .align 2,0
+FPU_bits_lost:
+ .byte 0
+FPU_denormal:
+ .byte 0
.text
.align 2,144
.globl FPU_round
.globl FPU_round_sqrt
+.globl FPU_Arith_exit
+.globl _round_reg
+
+// Entry point when called from C
+_round_reg:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
-FPU_round:
- /* Round the result */
+ movl PARAM1,%edi
+ movl SIGH(%edi),%eax
+ movl SIGL(%edi),%ebx
+ movl PARAM2,%edx
+ movl PARAM3,%ecx
+ jmp FPU_round_sqrt
+
+FPU_round: // Normal entry point
movl PARAM4,%ecx
-FPU_round_sqrt: // entry point from wm_sqrt.S
+FPU_round_sqrt: // Entry point from wm_sqrt.S
+
+#ifdef PARANOID
+// Cannot use this here yet
+// orl %eax,%eax
+// jns L_entry_bugged
+#endif PARANOID
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xMake_denorm // The number is a de-normal
+
+ movb $0,FPU_denormal // 0 -> not a de-normal
+
+xDenorm_done:
+ movb $0,FPU_bits_lost // No bits yet lost in rounding
+
movl %ecx,%esi
andl CW_PC,%ecx
cmpl PR_64_BITS,%ecx
@@ -82,6 +139,7 @@ FPU_round_sqrt: // entry point from wm_sqrt.S
#endif PARANOID
+// Round etc to 24 bit precision
LRound_To_24:
movl %esi,%ecx
andl CW_RC,%ecx
@@ -89,7 +147,7 @@ LRound_To_24:
je LRound_nearest_24
cmpl RC_CHOP,%ecx
- je LTruncate_24
+ je LCheck_truncate_24
cmpl RC_UP,%ecx // Towards +infinity
je LUp_24
@@ -103,75 +161,65 @@ LRound_To_24:
LUp_24:
cmpb SIGN_POS,SIGN(%edi)
- je LUp_24_pos
+ jne LCheck_truncate_24 // If negative then up==truncate
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- orl %ebx,%ecx
- orl %edx,%ecx
- jnz LTruncate_24
- jmp L_store
-
-LUp_24_pos:
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- orl %ebx,%ecx
- orl %edx,%ecx
- jnz LDo_24_round_up
- jmp L_store
+ jmp LCheck_24_round_up
LDown_24:
cmpb SIGN_POS,SIGN(%edi)
- je LDown_24_pos
+ je LCheck_truncate_24 // If positive then down==truncate
+LCheck_24_round_up:
movl %eax,%ecx
andl $0x000000ff,%ecx
orl %ebx,%ecx
orl %edx,%ecx
jnz LDo_24_round_up
- jmp L_store
-
-LDown_24_pos:
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- orl %ebx,%ecx
- orl %edx,%ecx
- jnz LTruncate_24
- jmp L_store
+ jmp LRe_normalise
LRound_nearest_24:
// Do rounding of the 24th bit if needed (nearest or even)
movl %eax,%ecx
andl $0x000000ff,%ecx
cmpl $0x00000080,%ecx
- jc LTruncate_24 // less than half, no increment needed
+ jc LCheck_truncate_24 // less than half, no increment needed
jne LGreater_Half_24 // greater than half, increment needed
// Possibly half, we need to check the ls bits
orl %ebx,%ebx
- jne LGreater_Half_24 // greater than half, increment needed
+ jnz LGreater_Half_24 // greater than half, increment needed
orl %edx,%edx
- jne LGreater_Half_24 // greater than half, increment needed
+ jnz LGreater_Half_24 // greater than half, increment needed
// Exactly half, increment only if 24th bit is 1 (round to even)
testl $0x00000100,%eax
- jz LTruncate_24
+ jz LDo_truncate_24
LGreater_Half_24: // Rounding: increment at the 24th bit
LDo_24_round_up:
andl $0xffffff00,%eax // Truncate to 24 bits
xorl %ebx,%ebx
+ movb LOST_UP,FPU_bits_lost
addl $0x00000100,%eax
jmp LCheck_Round_Overflow
-LTruncate_24:
+LCheck_truncate_24:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise // No truncation needed
+
+LDo_truncate_24:
andl $0xffffff00,%eax // Truncate to 24 bits
xorl %ebx,%ebx
- jmp L_store
+ movb LOST_DOWN,FPU_bits_lost
+ jmp LRe_normalise
+// Round etc to 53 bit precision
LRound_To_53:
movl %esi,%ecx
andl CW_RC,%ecx
@@ -179,7 +227,7 @@ LRound_To_53:
je LRound_nearest_53
cmpl RC_CHOP,%ecx
- je LTruncate_53
+ je LCheck_truncate_53
cmpl RC_UP,%ecx // Towards +infinity
je LUp_53
@@ -193,50 +241,33 @@ LRound_To_53:
LUp_53:
cmpb SIGN_POS,SIGN(%edi)
- je LUp_53_pos
-
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- orl %edx,%ecx
- jnz LTruncate_53
- jmp L_store
+ jne LCheck_truncate_53 // If negative then up==truncate
-LUp_53_pos:
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- orl %edx,%ecx
- jnz LDo_53_round_up
- jmp L_store
+ jmp LCheck_53_round_up
LDown_53:
cmpb SIGN_POS,SIGN(%edi)
- je LDown_53_pos
+ je LCheck_truncate_53 // If positive then down==truncate
+LCheck_53_round_up:
movl %ebx,%ecx
andl $0x000007ff,%ecx
orl %edx,%ecx
jnz LDo_53_round_up
- jmp L_store
-
-LDown_53_pos:
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- orl %edx,%ecx
- jnz LTruncate_53
- jmp L_store
+ jmp LRe_normalise
LRound_nearest_53:
// Do rounding of the 53rd bit if needed (nearest or even)
movl %ebx,%ecx
andl $0x000007ff,%ecx
cmpl $0x00000400,%ecx
- jc LTruncate_53 // less than half, no increment needed
+ jc LCheck_truncate_53 // less than half, no increment needed
- jne LGreater_Half_53 // greater than half, increment needed
+ jnz LGreater_Half_53 // greater than half, increment needed
// Possibly half, we need to check the ls bits
orl %edx,%edx
- jne LGreater_Half_53 // greater than half, increment needed
+ jnz LGreater_Half_53 // greater than half, increment needed
// Exactly half, increment only if 53rd bit is 1 (round to even)
testl $0x00000800,%ebx
@@ -244,16 +275,25 @@ LRound_nearest_53:
LGreater_Half_53: // Rounding: increment at the 53rd bit
LDo_53_round_up:
+ movb LOST_UP,FPU_bits_lost
andl $0xfffff800,%ebx // Truncate to 53 bits
addl $0x00000800,%ebx
adcl $0,%eax
jmp LCheck_Round_Overflow
+LCheck_truncate_53:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise
+
LTruncate_53:
+ movb LOST_DOWN,FPU_bits_lost
andl $0xfffff800,%ebx // Truncate to 53 bits
- jmp L_store
+ jmp LRe_normalise
+// Round etc to 64 bit precision
LRound_To_64:
movl %esi,%ecx
andl CW_RC,%ecx
@@ -261,7 +301,7 @@ LRound_To_64:
je LRound_nearest_64
cmpl RC_CHOP,%ecx
- je LTruncate_64
+ je LCheck_truncate_64
cmpl RC_UP,%ecx // Towards +infinity
je LUp_64
@@ -275,68 +315,74 @@ LRound_To_64:
LUp_64:
cmpb SIGN_POS,SIGN(%edi)
- je LUp_64_pos
-
- orl %edx,%edx
- jnz LTruncate_64
- jmp L_store
+ jne LCheck_truncate_64 // If negative then up==truncate
-LUp_64_pos:
orl %edx,%edx
jnz LDo_64_round_up
- jmp L_store
+ jmp LRe_normalise
LDown_64:
cmpb SIGN_POS,SIGN(%edi)
- je LDown_64_pos
+ je LCheck_truncate_64 // If positive then down==truncate
orl %edx,%edx
jnz LDo_64_round_up
- jmp L_store
-
-LDown_64_pos:
- orl %edx,%edx
- jnz LTruncate_64
- jmp L_store
+ jmp LRe_normalise
LRound_nearest_64:
cmpl $0x80000000,%edx
- jc LTruncate_64
+ jc LCheck_truncate_64
jne LDo_64_round_up
/* Now test for round-to-even */
testb $1,%ebx
- jz LTruncate_64
+ jz LCheck_truncate_64
LDo_64_round_up:
+ movb LOST_UP,FPU_bits_lost
addl $1,%ebx
adcl $0,%eax
LCheck_Round_Overflow:
- jnc L_store /* Rounding done, no overflow */
+ jnc LRe_normalise /* Rounding done, no overflow */
/* Overflow, adjust the result (to 1.0) */
rcrl $1,%eax
rcrl $1,%ebx
incl EXP(%edi)
+ jmp LRe_normalise
+
+LCheck_truncate_64:
+ orl %edx,%edx
+ jz LRe_normalise
LTruncate_64:
-L_store:
- /* store the result */
- movl %eax,SIGH(%edi)
- movl %ebx,SIGL(%edi)
+ movb LOST_DOWN,FPU_bits_lost
+
+LRe_normalise:
+ testb $0xff,FPU_denormal
+ jnz xNormalise_result
+
+xL_Normalised:
+ cmpb LOST_UP,FPU_bits_lost
+ je xL_precision_lost_up
- movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
+ cmpb LOST_DOWN,FPU_bits_lost
+ je xL_precision_lost_down
- // The number may have overflowed
+xL_no_precision_loss:
cmpl EXP_OVER,EXP(%edi)
jge L_overflow
- cmpl EXP_UNDER,EXP(%edi)
- jle L_underflow
+ /* store the result */
+ movb TW_Valid,TAG(%edi)
+
+xL_Store_significand:
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
-L_exit:
+FPU_Arith_exit:
popl %ebx
popl %edi
popl %esi
@@ -344,19 +390,193 @@ L_exit:
ret
-/* The operations resulted in a number too large to represent */
+// Set the FPU status flags to represent precision loss due to
+// round-up.
+xL_precision_lost_up:
+ push %eax
+ call _set_precision_flag_up
+ popl %eax
+ jmp xL_no_precision_loss
+
+// Set the FPU status flags to represent precision loss due to
+// truncation.
+xL_precision_lost_down:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+ jmp xL_no_precision_loss
+
+
+// The number is a denormal (which might get rounded up to a normal)
+// Shift the number right the required number of bits, which will
+// have to be undone later...
+xMake_denorm:
+ // The action to be taken depends upon whether the underflow
+ // exception is masked
+ testb CW_Underflow,%cl // Underflow mask.
+ jz xUnmasked_underflow // Do not make a denormal.
+
+ movb DENORMAL,FPU_denormal
+
+ pushl %ecx // Save
+ movl EXP(%edi),%ecx
+ subl EXP_UNDER+1,%ecx
+ negl %ecx
+
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_32
+
+// We got here without jumps by assuming that the most common requirement
+// is for a small de-normalising shift.
+// Shift by [1..31] bits
+ addl %ecx,EXP(%edi)
+ orl %edx,%edx // extension
+ setne %ch
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orb %ch,%dl
+ popl %ecx
+ jmp xDenorm_done
+
+// Shift by [32..63] bits
+xDenorm_shift_more_than_32:
+ addl %ecx,EXP(%edi)
+ subb $32,%cl
+ orl %edx,%edx
+ setne %ch
+ orb %ch,%bl
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %edx,%edx // test these 32 bits
+ setne %cl
+ orb %ch,%bl
+ orb %cl,%bl
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ popl %ecx
+ jmp xDenorm_done
+
+// Shift by [64..) bits
+xDenorm_shift_more_than_63:
+ cmpl $64,%ecx
+ jne xDenorm_shift_more_than_64
+
+// Exactly 64 bit shift
+ addl %ecx,EXP(%edi)
+ xorl %ecx,%ecx
+ orl %edx,%edx
+ setne %cl
+ orl %ebx,%ebx
+ setne %ch
+ orb %ch,%cl
+ orb %cl,%al
+ movl %eax,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+xDenorm_shift_more_than_64:
+ movl EXP_UNDER+1,EXP(%edi)
+// This is easy, %eax must be non-zero, so..
+ movl $1,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+
+xUnmasked_underflow:
+ // Increase the exponent by the magic number
+ addl $(3*(1<<13)),EXP(%edi)
+ movb UNMASKED_UNDERFLOW,FPU_denormal
+ jmp xDenorm_done
+
+
+// Undo the de-normalisation.
+xNormalise_result:
+ cmpb UNMASKED_UNDERFLOW,FPU_denormal
+ je xSignal_underflow
+
+// The number must be a denormal if we got here.
+#ifdef PARANOID
+ // But check it... just in case.
+ cmpl EXP_UNDER+1,EXP(%edi)
+ jne L_norm_bugged
+#endif PARANOID
+
+ orl %eax,%eax // ms bits
+ jnz LNormalise_shift_up_to_31 // Shift left 0 - 31 bits
+
+ orl %ebx,%ebx
+ jz L_underflow_to_zero // The contents are zero
+
+// Shift left 32 - 63 bits
+ movl %ebx,%eax
+ xorl %ebx,%ebx
+ subl $32,EXP(%edi)
+
+LNormalise_shift_up_to_31:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shl %cl,%ebx
+ subl %ecx,EXP(%edi)
+
+LNormalise_shift_done:
+ testb $0xff,FPU_bits_lost // bits lost == underflow
+ jz xL_Normalised
+
+ // There must be a masked underflow
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+// The operations resulted in a number too small to represent.
+// Masked response.
+L_underflow_to_zero:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+
+ movb TW_Zero,TAG(%edi)
+ jmp xL_Store_significand
+
+
+// The operations resulted in a number too large to represent.
L_overflow:
push %edi
call _arith_overflow
pop %edi
- jmp L_exit
+ jmp FPU_Arith_exit
-/* The operations resulted in a number too small to represent */
-L_underflow:
- pushl %edi
- call _arith_underflow
- popl %edi
- jmp L_exit
+
+xSignal_underflow:
+ push %eax
+ pushl EX_Underflow
+ call EXCEPTION
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
#ifdef PARANOID
@@ -364,6 +584,18 @@ L_underflow:
L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
- pop %ebx
- jmp L_exit
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_norm_bugged:
+ pushl EX_INTERNAL|0x216
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
+
+L_entry_bugged:
+ pushl EX_INTERNAL|0x217
+ call EXCEPTION
+ popl %ebx
+ jmp FPU_Arith_exit
#endif PARANOID
diff --git a/kernel/FPU-emu/reg_u_add.S b/kernel/FPU-emu/reg_u_add.S
index f203261..3aca1bc 100644
--- a/kernel/FPU-emu/reg_u_add.S
+++ b/kernel/FPU-emu/reg_u_add.S
@@ -42,7 +42,26 @@ _reg_u_add:
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
- xorl %ecx,%ecx
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+// xorl %ecx,%ecx
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
// jnc L_arg1_larger
diff --git a/kernel/FPU-emu/reg_u_div.S b/kernel/FPU-emu/reg_u_div.S
index 0a6a0dd..2083bc5 100644
--- a/kernel/FPU-emu/reg_u_div.S
+++ b/kernel/FPU-emu/reg_u_div.S
@@ -71,6 +71,27 @@ _reg_u_div:
movl PARAM2,%ebx /* pointer to denom */
movl PARAM3,%edi /* pointer to answer */
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%ebx),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
_divide_kernel:
#ifdef PARANOID
// testl $0x80000000, SIGH(%esi) // Dividend
diff --git a/kernel/FPU-emu/reg_u_mul.S b/kernel/FPU-emu/reg_u_mul.S
index 102c2c7..5fa32a4 100644
--- a/kernel/FPU-emu/reg_u_mul.S
+++ b/kernel/FPU-emu/reg_u_mul.S
@@ -26,7 +26,6 @@
#include "control_w.h"
-
.data
.align 2,0
accum_0:
@@ -56,6 +55,27 @@ _reg_u_mul:
jz L_bugged
#endif PARANOID
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%edi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
xorl %ecx,%ecx
xorl %ebx,%ebx
@@ -83,6 +103,7 @@ _reg_u_mul:
movl EXP(%esi),%eax /* Compute the exponent */
addl EXP(%edi),%eax
+ subl EXP_BIAS-1,%eax
// Have now finished with the sources
movl PARAM3,%edi // Point to the destination
movl %eax,EXP(%edi)
diff --git a/kernel/FPU-emu/reg_u_sub.S b/kernel/FPU-emu/reg_u_sub.S
index c14fc81..88f7f5c 100644
--- a/kernel/FPU-emu/reg_u_sub.S
+++ b/kernel/FPU-emu/reg_u_sub.S
@@ -29,7 +29,6 @@
#include "fpu_asm.h"
#include "control_w.h"
-
.text
.align 2,144
.globl _reg_u_sub
@@ -43,6 +42,25 @@ _reg_u_sub:
movl PARAM1,%esi /* source 1 */
movl PARAM2,%edi /* source 2 */
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz FPU_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
// xorl %ecx,%ecx
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
diff --git a/kernel/FPU-emu/status_w.h b/kernel/FPU-emu/status_w.h
index d7f23c0..00e291f 100644
--- a/kernel/FPU-emu/status_w.h
+++ b/kernel/FPU-emu/status_w.h
@@ -1,7 +1,8 @@
/*---------------------------------------------------------------------------+
| status_w.h |
| |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
| |
+---------------------------------------------------------------------------*/
@@ -16,31 +17,33 @@
#define Const__(x) x
#endif
-#define SW_B Const__(0x8000) /* backward compatibility (=ES) */
+#define SW_Backward Const__(0x8000) /* backward compatibility */
#define SW_C3 Const__(0x4000) /* condition bit 3 */
-#define SW_TOP Const__(0x3800) /* top of stack */
-#define SW_TOPS Const__(11) /* shift for top of stack bits */
+#define SW_Top Const__(0x3800) /* top of stack */
+#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
#define SW_C2 Const__(0x0400) /* condition bit 2 */
#define SW_C1 Const__(0x0200) /* condition bit 1 */
#define SW_C0 Const__(0x0100) /* condition bit 0 */
-#define SW_ES Const__(0x0080) /* exception summary */
-#define SW_SF Const__(0x0040) /* stack fault */
-#define SW_PE Const__(0x0020) /* loss of precision */
-#define SW_UE Const__(0x0010) /* underflow */
-#define SW_OE Const__(0x0008) /* overflow */
-#define SW_ZE Const__(0x0004) /* divide by zero */
-#define SW_DE Const__(0x0002) /* denormalized operand */
-#define SW_IE Const__(0x0001) /* invalid operation */
+#define SW_Summary Const__(0x0080) /* exception summary */
+#define SW_Stack_Fault Const__(0x0040) /* stack fault */
+#define SW_Precision Const__(0x0020) /* loss of precision */
+#define SW_Underflow Const__(0x0010) /* underflow */
+#define SW_Overflow Const__(0x0008) /* overflow */
+#define SW_Zero_Div Const__(0x0004) /* divide by zero */
+#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
+#define SW_Invalid Const__(0x0001) /* invalid operation */
+#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
#ifndef __ASSEMBLER__
-#define COMP_A_GT_B 1
-#define COMP_A_EQ_B 2
-#define COMP_A_LT_B 3
-#define COMP_NOCOMP 4
-#define COMP_NAN 0x40
-#define COMP_SNAN 0x80
+#define COMP_A_gt_B 1
+#define COMP_A_eq_B 2
+#define COMP_A_lt_B 3
+#define COMP_No_Comp 4
+#define COMP_Denormal 0x20
+#define COMP_NaN 0x40
+#define COMP_SNaN 0x80
#define setcc(cc) ({ \
status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h
index c4a37cb..b0b1fe6 100644
--- a/kernel/FPU-emu/version.h
+++ b/kernel/FPU-emu/version.h
@@ -9,5 +9,5 @@
| |
+---------------------------------------------------------------------------*/
-#define FPU_VERSION "wm-FPU-emu version BETA 1.3"
+#define FPU_VERSION "wm-FPU-emu version BETA 1.4"
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index e771184..da471ba 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -2,6 +2,7 @@
#define _BLK_H
#include <linux/fs.h>
+#include <linux/locks.h>
/*
* NR_REQUEST is the number of entries in the request-queue.
@@ -103,6 +104,9 @@ extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
#elif (MAJOR_NR == 2)
/* floppy */
+static void floppy_on(unsigned int nr);
+static void floppy_off(unsigned int nr);
+
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
@@ -196,14 +200,6 @@ else \
#endif
static void (DEVICE_REQUEST)(void);
-extern inline void unlock_buffer(struct buffer_head * bh)
-{
- if (!bh->b_lock)
- printk(DEVICE_NAME ": free buffer being unlocked\n");
- bh->b_lock=0;
- wake_up(&bh->b_wait);
-}
-
/* SCSI devices have their own version */
#if (MAJOR_NR != 8 && MAJOR_NR != 9 && MAJOR_NR != 11)
static void end_request(int uptodate)
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 64057a2..0e34b04 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -58,6 +58,12 @@
* floppy as the first thing after bootup.
*/
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
#define REALLY_SLOW_IO
#define FLOPPY_IRQ 6
#define FLOPPY_DMA 2
@@ -87,7 +93,8 @@ static int reset = 0;
static int recover = 0; /* recalibrate immediately after resetting */
static int seek = 0;
-extern unsigned char current_DOR;
+static unsigned char current_DOR = 0x0C;
+static unsigned char running = 0;
#define TYPE(x) ((x)>>2)
#define DRIVE(x) ((x)&0x03)
@@ -280,15 +287,85 @@ static unsigned char seek_track = 0;
static unsigned char current_track = NO_TRACK;
static unsigned char command = 0;
static unsigned char fdc_version = FDC_TYPE_STD; /* FDC version code */
-unsigned char selected = 0;
-struct wait_queue * wait_on_floppy_select = NULL;
-void floppy_deselect(unsigned int nr)
+static void floppy_ready(void);
+
+static void select_callback(unsigned long unused)
+{
+ floppy_ready();
+}
+
+static void floppy_select(unsigned int nr)
+{
+ static struct timer_list select = { NULL, 0, 0, select_callback };
+
+ if (current_drive == (current_DOR & 3)) {
+ floppy_ready();
+ return;
+ }
+ seek = 1;
+ current_track = NO_TRACK;
+ current_DOR &= 0xFC;
+ current_DOR |= current_drive;
+ outb(current_DOR,FD_DOR);
+ del_timer(&select);
+ select.expires = 2;
+ add_timer(&select);
+}
+
+static void motor_on_callback(unsigned long nr)
+{
+ running |= 0x10 << nr;
+ floppy_select(nr);
+}
+
+static struct timer_list motor_on_timer[4] = {
+ { NULL, 0, 0, motor_on_callback },
+ { NULL, 0, 1, motor_on_callback },
+ { NULL, 0, 2, motor_on_callback },
+ { NULL, 0, 3, motor_on_callback }
+};
+
+static void motor_off_callback(unsigned long nr)
{
- if (nr != (current_DOR & 3))
- printk("floppy_deselect: drive not selected\n");
- selected = 0;
- wake_up(&wait_on_floppy_select);
+ unsigned char mask = ~(0x10 << nr);
+ cli();
+ running &= mask;
+ current_DOR &= mask;
+ outb(current_DOR,FD_DOR);
+ sti();
+}
+
+static struct timer_list motor_off_timer[4] = {
+ { NULL, 0, 0, motor_off_callback },
+ { NULL, 0, 1, motor_off_callback },
+ { NULL, 0, 2, motor_off_callback },
+ { NULL, 0, 3, motor_off_callback }
+};
+
+static void floppy_on(unsigned int nr)
+{
+ unsigned char mask = 0x10 << nr;
+
+ del_timer(motor_off_timer + nr);
+ if (mask & running)
+ floppy_select(nr);
+ if (!(mask & current_DOR)) {
+ del_timer(motor_on_timer + nr);
+ motor_on_timer[nr].expires = HZ;
+ add_timer(motor_on_timer + nr);
+ }
+ current_DOR &= 0xFC;
+ current_DOR |= mask;
+ current_DOR |= nr;
+ outb(current_DOR,FD_DOR);
+}
+
+static void floppy_off(unsigned int nr)
+{
+ del_timer(motor_off_timer+nr);
+ motor_off_timer[nr].expires = 3*HZ;
+ add_timer(motor_off_timer+nr);
}
void request_done(int uptodate)
@@ -446,7 +523,6 @@ static void bad_flp_intr(void)
} else
errors = ++CURRENT->errors;
if (errors > MAX_ERRORS) {
- floppy_deselect(current_drive);
request_done(0);
}
if (errors > MAX_ERRORS/2)
@@ -545,7 +621,6 @@ static void rw_interrupt(void)
bad = 1;
if (ST1 & ST1_WP) {
printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive);
- floppy_deselect(current_drive);
request_done(0);
bad = 0;
} else if (ST1 & ST1_OR) {
@@ -615,7 +690,6 @@ static void rw_interrupt(void)
} else if (command == FD_READ &&
(unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR)
copy_buffer(tmp_floppy_area,CURRENT->buffer);
- floppy_deselect(current_drive);
request_done(1);
redo_fd_request();
}
@@ -849,7 +923,7 @@ static void shake_one(void)
output_byte(1);
}
-static void floppy_on_interrupt(void)
+static void floppy_ready(void)
{
if (inb(FD_DIR) & 0x80) {
changed_floppies |= 1<<current_drive;
@@ -857,8 +931,7 @@ static void floppy_on_interrupt(void)
if (keep_data[current_drive]) {
if (keep_data[current_drive] > 0)
keep_data[current_drive]--;
- }
- else {
+ } else {
if (ftd_msg[current_drive] && current_type[current_drive] != NULL)
printk("Disk type is undefined after disk "
"change in fd%d\n",current_drive);
@@ -884,17 +957,7 @@ static void floppy_on_interrupt(void)
recalibrate_floppy();
return;
}
-/* We cannot do a floppy-select, as that might sleep. We just force it */
- selected = 1;
- if (current_drive != (current_DOR & 3)) {
- seek = 1;
- current_track = NO_TRACK;
- current_DOR &= 0xFC;
- current_DOR |= current_drive;
- outb(current_DOR,FD_DOR);
- add_timer(2,&transfer);
- } else
- transfer();
+ transfer();
}
static void setup_format_params(void)
@@ -1018,7 +1081,8 @@ repeat:
if (seek_track != current_track)
seek = 1;
sector++;
- add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
+ del_timer(motor_off_timer + current_drive);
+ floppy_on(current_drive);
}
void do_fd_request(void)
@@ -1224,7 +1288,8 @@ static struct file_operations floppy_fops = {
fd_ioctl, /* ioctl */
NULL, /* mmap */
floppy_open, /* open */
- floppy_release /* release */
+ floppy_release, /* release */
+ block_fsync /* fsync */
};
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index 455a1c9..8d62113 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -99,12 +99,12 @@ unsigned long read_timer(void)
int i;
cli();
- outb_p(0xc2, 0x43);
- t = jiffies * 11931 + (inb_p(0x40) & 0x80 ? 5966 : 11932);
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
sti();
- return(t - i / 2);
+ return(t - i);
}
#endif
@@ -700,7 +700,8 @@ static struct file_operations hd_fops = {
hd_ioctl, /* ioctl */
NULL, /* mmap */
hd_open, /* open */
- hd_release /* release */
+ hd_release, /* release */
+ block_fsync /* fsync */
};
unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index 8deb1e2..1691fc5 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -316,7 +316,10 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
};
sti();
for (i=0;i<nr; i++)
- if (bh[i]) make_request(major, rw, bh[i]);
+ if (bh[i]) {
+ bh[i]->b_req = 1;
+ make_request(major, rw, bh[i]);
+ }
if(plugged){
cli();
blk_dev[major].current_request = plug.next;
diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c
index a151827..d90c070 100644
--- a/kernel/blk_drv/ramdisk.c
+++ b/kernel/blk_drv/ramdisk.c
@@ -2,6 +2,9 @@
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o, 12/2/91
+ *
+ * Modifications by Fred N. van Kempen to allow for bootable root
+ * disks (which are used in LINUX/Pro). Also some cleanups. 03/03/93
*/
@@ -14,9 +17,14 @@
#include <asm/system.h>
#include <asm/segment.h>
-#define MAJOR_NR 1
+#define MAJOR_RAMDISK 1 /* should be in <linux/major.h> */
+#define MAJOR_FLOPPY 2 /* should be in <linux/major.h> */
+#define MINOR_RAMDISK 1
+
+#define MAJOR_NR MAJOR_RAMDISK /* weird hack- FvK */
#include "blk.h"
+
char *rd_start;
int rd_length = 0;
@@ -29,7 +37,8 @@ repeat:
INIT_REQUEST;
addr = rd_start + (CURRENT->sector << 9);
len = CURRENT->nr_sectors << 9;
- if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) {
+ if ((MINOR(CURRENT->dev) != MINOR_RAMDISK) ||
+ (addr+len > rd_start+rd_length)) {
end_request(0);
goto repeat;
}
@@ -42,7 +51,7 @@ repeat:
addr,
len);
} else
- panic("unknown ramdisk-command");
+ panic("RAMDISK: unknown RAM disk command !\n");
end_request(1);
goto repeat;
}
@@ -56,7 +65,8 @@ static struct file_operations rd_fops = {
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* no special open code */
- NULL /* no special release code */
+ NULL, /* no special release code */
+ block_fsync /* fsync */
};
/*
@@ -67,11 +77,11 @@ long rd_init(long mem_start, int length)
int i;
char *cp;
- if (register_blkdev(MAJOR_NR,"rd",&rd_fops)) {
- printk("Unable to get major %d for ramdisk\n",MAJOR_NR);
+ if (register_blkdev(MAJOR_RAMDISK,"rd",&rd_fops)) {
+ printk("RAMDISK: Unable to get major %d.\n", MAJOR_RAMDISK);
return 0;
}
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_dev[MAJOR_RAMDISK].request_fn = DEVICE_REQUEST;
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
@@ -81,62 +91,83 @@ long rd_init(long mem_start, int length)
}
/*
- * If the root device is the ram disk, try to load it.
+ * If the root device is the RAM disk, try to load it.
* In order to do this, the root device is originally set to the
- * floppy, and we later change it to be ram disk.
+ * floppy, and we later change it to be RAM disk.
*/
void rd_load(void)
{
struct buffer_head *bh;
struct minix_super_block s;
- int block = 512; /* Start at block 512 */
+ int block, try;
int i = 1;
int nblocks;
- char *cp; /* Move pointer */
-
- if (!rd_length)
- return;
- printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length,
- (int) rd_start);
- if (MAJOR(ROOT_DEV) != 2)
- return;
- bh = breada(ROOT_DEV,block+1,block,block+2,-1);
- if (!bh) {
- printk("Disk error while looking for ramdisk!\n");
- return;
- }
- *((struct minix_super_block *) &s) = *((struct minix_super_block *) bh->b_data);
- brelse(bh);
- if (s.s_magic != MINIX_SUPER_MAGIC)
- /* No ram disk image present, assume normal floppy boot */
- return;
- nblocks = s.s_nzones << s.s_log_zone_size;
- if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
- printk("Ram disk image too big! (%d blocks, %d avail)\n",
- nblocks, rd_length >> BLOCK_SIZE_BITS);
- return;
- }
- printk("Loading %d bytes into ram disk\n",
- nblocks << BLOCK_SIZE_BITS);
- cp = rd_start;
- while (nblocks) {
- if (nblocks > 2)
- bh = breada(ROOT_DEV, block, block+1, block+2, -1);
- else
- bh = bread(ROOT_DEV, block, BLOCK_SIZE);
+ char *cp;
+
+ /* If no RAM disk specified, give up early. */
+ if (!rd_length) return;
+ printk("RAMDISK: %d bytes, starting at 0x%x\n",
+ rd_length, (int) rd_start);
+
+ /* If we are doing a diskette boot, we might have to pre-load it. */
+ if (MAJOR(ROOT_DEV) != MAJOR_FLOPPY) return;
+
+ /*
+ * Check for a super block on the diskette.
+ * The old-style boot/root diskettes had their RAM image
+ * starting at block 512 of the boot diskette. LINUX/Pro
+ * uses the enire diskette as a file system, so in that
+ * case, we have to look at block 0. Be intelligent about
+ * this, and check both... - FvK
+ */
+ for (try = 0; try < 1000; try += 512) {
+ block = try;
+ bh = breada(ROOT_DEV,block+1,block,block+2,-1);
if (!bh) {
- printk("I/O error on block %d, aborting load\n",
- block);
+ printk("RAMDISK: I/O error while looking for super block!\n");
return;
}
- (void) memcpy(cp, bh->b_data, BLOCK_SIZE);
+
+ /* This is silly- why do we require it to be a MINIX FS? */
+ *((struct minix_super_block *) &s) =
+ *((struct minix_super_block *) bh->b_data);
brelse(bh);
- if (!(nblocks-- & 15))
- printk(".");
- cp += BLOCK_SIZE;
- block++;
- i++;
+ nblocks = s.s_nzones << s.s_log_zone_size;
+ if (s.s_magic != MINIX_SUPER_MAGIC) {
+ printk("RAMDISK: trying old-style RAM image.\n");
+ continue;
+ }
+
+ if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
+ printk("RAMDISK: image too big! (%d/%d blocks)\n",
+ nblocks, rd_length >> BLOCK_SIZE_BITS);
+ return;
+ }
+ printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
+
+ /* We found an image file system. Load it into core! */
+ cp = rd_start;
+ while (nblocks) {
+ if (nblocks > 2)
+ bh = breada(ROOT_DEV, block, block+1, block+2, -1);
+ else
+ bh = bread(ROOT_DEV, block, BLOCK_SIZE);
+ if (!bh) {
+ printk("RAMDISK: I/O error on block %d, aborting!\n",
+ block);
+ return;
+ }
+ (void) memcpy(cp, bh->b_data, BLOCK_SIZE);
+ brelse(bh);
+ if (!(nblocks-- & 15)) printk(".");
+ cp += BLOCK_SIZE;
+ block++;
+ i++;
+ }
+ printk("\ndone\n");
+
+ /* We loaded the file system image. Prepare for mounting it. */
+ ROOT_DEV = ((MAJOR_RAMDISK << 8) | MINOR_RAMDISK);
+ return;
}
- printk("\ndone\n");
- ROOT_DEV=0x0101;
}
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
index 719c67e..6919e51 100644
--- a/kernel/blk_drv/scsi/aha1542.c
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -7,6 +7,8 @@
* Use request_irq and request_dma to help prevent unexpected conflicts
* Set up on-board DMA controller, such that we do not have to
* have the bios enabled to use the aha1542.
+ * Modified by David Gentzel
+ * Don't call request_dma if dma mask is 0 (for BusLogic BT-445S VL-Bus controller).
*/
#include <linux/kernel.h>
@@ -576,9 +578,14 @@ static int aha1542_getconfig(int hostnum)
case 0x20:
dma_chan = 5;
break;
- case 1:
+ case 0x01:
printk("DMA priority 0 not available for Adaptec driver\n");
return -1;
+ case 0:
+ /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
+ Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
+ dma_chan = 0xFF;
+ break;
default:
printk("Unable to determine Adaptec DMA priority. Disabling board\n");
return -1;
@@ -679,8 +686,10 @@ int aha1542_detect(int hostnum)
if (aha1542_getconfig(hostnum) == -1) return 0;
- printk("Configuring Adaptec at IO:%x, IRQ %d, DMA priority %d\n",base,
- irq_level, dma_chan);
+ printk("Configuring Adaptec at IO:%x, IRQ %d",base, irq_level);
+ if (dma_chan != 0xFF)
+ printk(", DMA priority %d", dma_chan);
+ printk("\n");
DEB(aha1542_stat());
setup_mailboxes();
@@ -690,20 +699,22 @@ int aha1542_detect(int hostnum)
DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
if (request_irq(irq_level,aha1542_intr_handle)) {
- printk("Unable to allocate IRQ for adaptec controller.\n");
- return 0;
- };
+ printk("Unable to allocate IRQ for adaptec controller.\n");
+ return 0;
+ }
- if(request_dma(dma_chan)){
- printk("Unable to allocate DMA channel for Adaptec.\n");
- free_irq(irq_level);
- return 0;
- };
+ if (dma_chan != 0xFF) {
+ if (request_dma(dma_chan)) {
+ printk("Unable to allocate DMA channel for Adaptec.\n");
+ free_irq(irq_level);
+ return 0;
+ }
- if(dma_chan >= 5){
- outb(((dma_chan - 4)|CASCADE),DMA_MODE_REG);
- outb((dma_chan-4),DMA_MASK_REG);
- };
+ if (dma_chan >= 5) {
+ outb((dma_chan - 4) | CASCADE, DMA_MODE_REG);
+ outb(dma_chan - 4, DMA_MASK_REG);
+ }
+ }
#if 0
DEB(printk(" *** READ CAPACITY ***\n"));
diff --git a/kernel/blk_drv/scsi/fdomain.c b/kernel/blk_drv/scsi/fdomain.c
index e9a74db..0be2f75 100644
--- a/kernel/blk_drv/scsi/fdomain.c
+++ b/kernel/blk_drv/scsi/fdomain.c
@@ -1,6 +1,6 @@
/* fdomain.c -- Future Domain TMC-1660/TMC-1680 driver
* Created: Sun May 3 18:53:19 1992 by faith
- * Revised: Thu Feb 18 21:02:12 1993 by faith@cs.unc.edu
+ * Revised: Sat May 15 15:29:19 1993 by faith@cs.unc.edu
* Author: Rickard E. Faith, faith@cs.unc.edu
* Copyright 1992, 1993 Rickard E. Faith
*
@@ -22,7 +22,10 @@
DESCRIPTION:
This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
- and TMC-1670 SCSI host adapters.
+ and TMC-1650/1670 SCSI host adapters. The older boards are based on the
+ TMC-1800 chip, and the driver was originally written for a TMC-1680 with
+ this chip. More recently, boards are being produced with the TMC-18C50
+ chip. This driver may not work with the more recent boards.
REFERENCES USED:
@@ -30,11 +33,18 @@
"TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
1990.
+ "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
+ Corporation, January 1992.
+
"LXT SCSI Products: Specifications and OEM Technical Manual (Revision
B/September 1991)", Maxtor Corporation, 1991.
"7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
+ "Draft Proposed American National Standard: Small Computer System
+ Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
+ revision 10h, October 17, 1991)
+
Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
Youngdale (eric@tantalus.nrl.navy.mil), 1992.
@@ -44,17 +54,21 @@
The Maxtor manuals were free. Maxtor telephone technical support is
great!
- The Future Domain manual is $25. It documents the chip, not the TMC-16x0
- boards, so some information I had to guess at. Future Domain sells DOS
- BIOS source for $250 and the UN*X driver source was $750, but these
- require a non-disclosure agreement, so even if I could afford them, they
- would *not* have been useful for writing this publically distributable
- driver. Future Domain technical support has provided some information on
- the phone, and this has been somewhat helpful.
+ The Future Domain manuals were $25 and $35. They document the chip, not
+ the TMC-16x0 boards, so some information I had to guess at. In 1992,
+ Future Domain sells DOS BIOS source for $250 and the UN*X driver source
+ was $750, but these require a non-disclosure agreement, so even if I could
+ afford them, they would *not* have been useful for writing this publically
+ distributable driver. Future Domain technical support has provided some
+ information on the phone and have sent a few useful FAXs.
ALPHA TESTERS:
+ There are many other alpha testers that come and go as the driver
+ develops. The people listed here were most helpful in times of greatest
+ need. However, all of the alpha testers deserve much thanks.
+
Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@dutiba.tudelft.nl),
Sakari Aaltonen (sakaria@vipunen.hit.fi), John Rice
@@ -108,7 +122,7 @@
#include <asm/system.h>
#include <linux/errno.h>
-#define VERSION "3.5" /* Change with each revision */
+#define VERSION "3.6" /* Change with each revision */
/* START OF USER DEFINABLE OPTIONS */
@@ -127,6 +141,7 @@
#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
#define DEBUG_MESSAGES 0 /* Debug MESSAGE IN PHASE */
#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
#else
#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
#define ERRORS_ONLY 0
@@ -147,9 +162,64 @@
#define PARITY_MASK 0x00
#endif
-static int port_base = 0;
-static void *bios_base = NULL;
-static int interrupt_level = 0;
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+};
+
+enum {
+ non_queueing = 0x01,
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50 only */
+ Interrupt_Cond = 4, /* tmc18c50 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Mask = 9,
+ Option_Select = 10,
+ Configuration = 11, /* tmc18c50 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ Data_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50 only */
+ Write_Loopback = 7,
+ Write_FIFO = 12
+};
+
+static int port_base = 0;
+static void *bios_base = NULL;
+static int bios_major = 0;
+static int bios_minor = 0;
+static int interrupt_level = 0;
+static int this_host = 0;
+static int can_queue = QUEUE;
+static volatile int in_command = 0;
+static Scsi_Cmnd *current_SC = NULL;
+static enum chip_type chip = unknown;
+#if DEBUG_RACE
+static volatile int in_interrupt_flag = 0;
+#endif
+
static int Data_Mode_Cntl_port;
static int FIFO_Data_Count_port;
@@ -165,33 +235,8 @@ static int TMC_Status_port;
static int Write_FIFO_port;
static int Write_SCSI_Data_port;
-static int this_host = 0;
-static int can_queue = QUEUE;
-
-static volatile int in_command = 0;
-static Scsi_Cmnd *current_SC = NULL;
-
-enum { non_queueing = 0x01,
- in_arbitration = 0x02,
- in_selection = 0x04,
- in_other = 0x08,
- disconnect = 0x10,
- aborted = 0x20,
- sent_ident = 0x40,
- };
-
extern void fdomain_16x0_intr( int unused );
-enum in_port_type { Read_SCSI_Data = 0, SCSI_Status = 1, TMC_Status = 2,
- LSB_ID_Code = 5, MSB_ID_Code = 6, Read_Loopback = 7,
- SCSI_Data_NoACK = 8, Interrupt_Mask = 9,
- Option_Select = 10, Read_FIFO = 12,
- FIFO_Data_Count = 14 };
-
-enum out_port_type { Write_SCSI_Data = 0, SCSI_Cntl = 1, Interrupt_Cntl = 2,
- Data_Mode_Cntl = 3, TMC_Cntl = 4, Write_Loopback = 7,
- Write_FIFO = 12 };
-
static void *addresses[] = {
(void *)0xc8000,
(void *)0xca000,
@@ -229,11 +274,14 @@ struct signature {
char *signature;
int sig_offset;
int sig_length;
+ int major_bios_version;
+ int minor_bios_version;
} signatures[] = {
/* 1 2 3 4 5 6 */
/* 123456789012345678901234567890123456789012345678901234567890 */
- { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50 },
- { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0 },
+ { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2 },
/* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE */
};
@@ -246,30 +294,47 @@ inline static unsigned short inw( unsigned short port )
{
unsigned short _v;
- __asm__ volatile ("inw %1,%0"
- :"=a" (_v):"d" ((unsigned short) port));
+ __asm__ volatile ( "inw %1,%0"
+ :"=a" (_v):"d" ((unsigned short) port) );
return _v;
}
inline static void outw( unsigned short value, unsigned short port )
{
- __asm__ volatile ("outw %0,%1"
- ::"a" ((unsigned short) value),
- "d" ((unsigned short) port));
+ __asm__ volatile ( "outw %0,%1"
+ ::"a" ((unsigned short) value),
+ "d" ((unsigned short) port) );
}
/* These defines are copied from kernel/blk_drv/hd.c */
#define insw( buf, count, port ) \
- __asm__ volatile \
- ( "cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" )
+ __asm__ volatile \
+ ("cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" )
#define outsw( buf, count, port ) \
- __asm__ volatile \
- ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si")
+ __asm__ volatile \
+ ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si")
+static void print_banner( void )
+{
+ printk( "%s", fdomain_16x0_info() );
+ printk( "Future Domain: BIOS version %d.%d, %s\n",
+ bios_major, bios_minor,
+ chip == tmc1800 ? "TMC-1800"
+ : (chip == tmc18c50 ? "TMC-18C50" : "Unknown") );
+
+ if (interrupt_level) {
+ printk( "Future Domain: BIOS at %x; port base at %x; using IRQ %d\n",
+ (unsigned)bios_base, port_base, interrupt_level );
+ } else {
+ printk( "Future Domain: BIOS at %x; port base at %x; *NO* IRQ\n",
+ (unsigned)bios_base, port_base );
+ }
+}
+
static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
{
unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
@@ -290,7 +355,7 @@ static int fdomain_is_valid_port( int port )
#if DEBUG_DETECT
printk( " (%x%x),",
- inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
+ inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
#endif
/* The MCA ID is a unique id for each MCA compatible board. We
@@ -302,10 +367,12 @@ static int fdomain_is_valid_port( int port )
if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
if (inb( port + LSB_ID_Code ) != 0x27) return 0;
if (inb( port + MSB_ID_Code ) != 0x61) return 0;
+ chip = tmc1800;
} else { /* test for 0xe960 id */
if (inb( port + MSB_ID_Code ) != 0x60) return 0;
+ chip = tmc18c50;
}
-
+
/* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board.
Now, check to be sure the bios_base matches these ports.
If someone was unlucky enough to have purchased more than one
@@ -349,7 +416,8 @@ int fdomain_16x0_detect( int hostnum )
Scsi_Cmnd SCinit;
unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
unsigned char do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
- unsigned char do_read_capacity[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char do_read_capacity[] = { READ_CAPACITY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned char buf[buflen];
#endif
@@ -363,7 +431,9 @@ int fdomain_16x0_detect( int hostnum )
#endif
for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
- signatures[j].signature, signatures[j].sig_length )) {
+ signatures[j].signature, signatures[j].sig_length )) {
+ bios_major = signatures[j].major_bios_version;
+ bios_minor = signatures[j].minor_bios_version;
bios_base = addresses[i];
}
}
@@ -395,7 +465,8 @@ int fdomain_16x0_detect( int hostnum )
if (port_base == ports[i]) ++flag;
}
- if (flag) flag = fdomain_is_valid_port( port_base );
+ if (flag)
+ flag = fdomain_is_valid_port( port_base );
if (!flag) { /* Cannot get port base from BIOS RAM */
@@ -432,20 +503,12 @@ int fdomain_16x0_detect( int hostnum )
return 0; /* Cannot find valid set of ports */
}
-#if DEBUG_DETECT
- printk( "\n" );
- printk( "SCSI: bios_base = %x, port_base = %x, interrupt_level = %d\n",
- (unsigned)bios_base, port_base, interrupt_level );
-#endif
+ print_banner();
+ if (chip == tmc18c50)
+ printk( "Future Domain WARNING: This driver may not work with the"
+ " TMC-18C50 chip!\n"
+ " Send mail to faith@cs.unc.edu\n" );
- if (interrupt_level) {
- printk( "Future Domain: BIOS at %x; port base at %x; using IRQ %d\n",
- (unsigned)bios_base, port_base, interrupt_level );
- } else {
- printk( "Future Domain: BIOS at %x; port base at %x; *NO* IRQ\n",
- (unsigned)bios_base, port_base );
- }
-
Data_Mode_Cntl_port = port_base + Data_Mode_Cntl;
FIFO_Data_Count_port = port_base + FIFO_Data_Count;
Interrupt_Cntl_port = port_base + Interrupt_Cntl;
@@ -460,7 +523,7 @@ int fdomain_16x0_detect( int hostnum )
Write_FIFO_port = port_base + Write_FIFO;
Write_SCSI_Data_port = port_base + Write_SCSI_Data;
- fdomain_16x0_reset();
+ fdomain_16x0_reset();
if (fdomain_test_loopback()) {
#if DEBUG_DETECT
@@ -479,10 +542,10 @@ int fdomain_16x0_detect( int hostnum )
debugging.
*/
- SCinit.request_buffer = SCinit.buffer = buf;
+ SCinit.request_buffer = SCinit.buffer = buf;
SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
- SCinit.use_sg = 0;
- SCinit.lun = 0;
+ SCinit.use_sg = 0;
+ SCinit.lun = 0;
printk( "Future Domain detection routine scanning for devices:\n" );
for (i = 0; i < 8; i++) {
@@ -506,9 +569,9 @@ int fdomain_16x0_detect( int hostnum )
| (buf[2] << 8) | buf[3];
size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L;
-
+
printk( "%lu MB (%lu byte blocks)",
- ((capacity + 5L) / 10L), size );
+ ((capacity + 5L) / 10L), size );
} else {
memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
retcode = fdomain_16x0_command(&SCinit);
@@ -522,7 +585,7 @@ int fdomain_16x0_detect( int hostnum )
}
#endif
- this_host = hostnum;
+ this_host = hostnum;
if (!QUEUE || !interrupt_level) {
printk( "Future Domain: *NO* interrupt level selected!\n" );
@@ -542,22 +605,24 @@ int fdomain_16x0_detect( int hostnum )
if (retcode < 0) {
if (retcode == -EINVAL) {
printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
- printk( " This shouldn't happen: REPORT TO RIK!\n" );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
} else if (retcode == -EBUSY) {
printk( "Future Domain: IRQ %d is already in use!\n",
- interrupt_level );
- printk( " Please use another IRQ for the FD card!\n" );
+ interrupt_level );
+ printk( " Please use another IRQ!\n" );
} else {
printk( "Future Domain: Error getting IRQ %d\n", interrupt_level );
- printk( " This shouldn't happen: REPORT TO RIK!\n" );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
}
printk( " COMMAND QUEUEING DISABLED!\n" );
-
+
can_queue = scsi_hosts[this_host].can_queue = 0;
scsi_hosts[this_host].sg_tablesize = SG_NONE;
} else {
printk( "Future Domain: IRQ %d requested from kernel\n",
- interrupt_level );
+ interrupt_level );
}
}
@@ -568,8 +633,8 @@ const char *fdomain_16x0_info(void)
{
static char buffer[] =
"Future Domain TMC-16x0 SCSI driver version "
- VERSION
- "\n";
+ VERSION
+ "\n";
return buffer;
}
@@ -589,7 +654,7 @@ static int fdomain_arbitrate( void )
timeout = jiffies + 50; /* 500 mS */
while (jiffies < timeout) {
status = inb( TMC_Status_port ); /* Read adapter status */
- if (status & 0x02) return 0; /* Arbitration complete */
+ if (status & 0x02) return 0; /* Arbitration complete */
}
/* Make bus idle */
@@ -600,7 +665,7 @@ static int fdomain_arbitrate( void )
#endif
#if ERRORS_ONLY
printk( "SCSI (Future Domain): Arbitration failed, status = %x",
- status );
+ status );
#endif
return 1;
}
@@ -653,6 +718,9 @@ void my_done( int error )
} else {
panic( "SCSI (Future Domain): my_done() called outside of command\n" );
}
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
}
void fdomain_16x0_intr( int unused )
@@ -669,17 +737,20 @@ void fdomain_16x0_intr( int unused )
if (!in_command || !current_SC) { /* Spurious interrupt */
return;
}
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
if (current_SC->SCp.phase & aborted) {
#if EVERY_ACCESS
if (current_SC->SCp.phase & (in_other || disconnect))
printk( "aborted (%s) = %d, ",
- current_SC->SCp.phase & in_other
- ? "in_other" : "disconnect",
- current_SC->result );
+ current_SC->SCp.phase & in_other
+ ? "in_other" : "disconnect",
+ current_SC->result );
else
printk( "aborted = %d, ",
- current_SC->result );
+ current_SC->result );
#endif
/* Force retry for timeouts after selection complete */
if (current_SC->SCp.phase & (in_other || disconnect)) {
@@ -703,7 +774,8 @@ void fdomain_16x0_intr( int unused )
outb( 0x80, SCSI_Cntl_port );
} else
#endif
- if (current_SC->SCp.phase & in_arbitration) {
+
+ if (current_SC->SCp.phase & in_arbitration) {
status = inb( TMC_Status_port ); /* Read adapter status */
if (!(status & 0x02)) {
#if EVERY_ACCESS
@@ -713,20 +785,22 @@ void fdomain_16x0_intr( int unused )
return;
}
current_SC->SCp.phase = in_selection;
-
+
outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port );
-
+
outb( 0x40 | (1 << current_SC->target), SCSI_Data_NoACK_port );
-
+
#if RESELECTION
outb( 0x8a, SCSI_Cntl_port ); /* Bus Enable + Attention + Select */
#else
outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
#endif
-
+
/* Stop arbitration (also set FIFO for output and enable parity) */
outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
-
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
return;
} else if (current_SC->SCp.phase & in_selection) {
status = inb( SCSI_Status_port );
@@ -740,11 +814,11 @@ void fdomain_16x0_intr( int unused )
return;
} else {
#if EVERY_ACCESS
- printk( " AltSel " );
+ printk( " AltSel " );
#endif
- /* Stop arbitration (also set FIFO for output and enable parity) */
- outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
- }
+ /* Stop arbitration (also set FIFO for output and enable parity) */
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ }
}
current_SC->SCp.phase = in_other;
outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
@@ -753,17 +827,20 @@ void fdomain_16x0_intr( int unused )
#else
outb( 0x80, SCSI_Cntl_port );
#endif
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
return;
}
-
+
/* current_SC->SCp.phase == in_other: this is the body of the routine */
-
+
switch (current_SC->cmnd[0]) {
case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a:
case 0x2e: case 0x3b: case 0xea: case 0x3f:
while ( (data_count = 0x2000 - inw( FIFO_Data_Count_port )) > 512 ) {
#if EVERY_ACCESS
- printk( "DC=%d, ", data_count );
+ printk( "DC=%d, ", data_count ) ;
#endif
if (data_count > current_SC->SCp.this_residual)
data_count = current_SC->SCp.this_residual;
@@ -817,7 +894,8 @@ void fdomain_16x0_intr( int unused )
current_SC->SCp.this_residual -= 2 * data_count;
}
}
- if (!current_SC->SCp.this_residual && current_SC->SCp.buffers_residual) {
+ if (!current_SC->SCp.this_residual
+ && current_SC->SCp.buffers_residual) {
--current_SC->SCp.buffers_residual;
++current_SC->SCp.buffer;
current_SC->SCp.ptr = current_SC->SCp.buffer->address;
@@ -827,9 +905,9 @@ void fdomain_16x0_intr( int unused )
}
break;
}
-
+
status = inb( SCSI_Status_port );
-
+
if (status & 0x10) { /* REQ */
switch (status & 0x0e) {
@@ -854,7 +932,7 @@ void fdomain_16x0_intr( int unused )
printk( "CMD = %x,",
current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
#endif
-
+
#endif
break;
case 0x0c: /* STATUS IN */
@@ -864,11 +942,13 @@ void fdomain_16x0_intr( int unused )
#endif
#if ERRORS_ONLY
if (current_SC->SCp.Status && current_SC->SCp.Status != 2) {
- printk( "SCSI (Future Domain): target = %d, command = %x, Status = %x\n",
- current_SC->target, current_SC->cmnd[0], current_SC->SCp.Status );
+ printk( "SCSI (Future Domain): target = %d, command = %x, "
+ "Status = %x\n",
+ current_SC->target, current_SC->cmnd[0],
+ current_SC->SCp.Status );
}
#endif
- break;
+ break;
case 0x0a: /* MESSAGE OUT */
#if RESELECTION
if (!(current_SC->SCp.phase & sent_ident)) {
@@ -898,13 +978,13 @@ void fdomain_16x0_intr( int unused )
#if DEBUG_MESSAGES || EVERY_ACCESS
if (current_SC->SCp.Message) {
printk( "SCSI (Future Domain): Message = %x\n",
- current_SC->SCp.Message );
+ current_SC->SCp.Message );
}
#endif
break;
}
}
-
+
if (done) {
#if EVERY_ACCESS
printk( " ** IN DONE ** " );
@@ -928,7 +1008,7 @@ void fdomain_16x0_intr( int unused )
current_SC->SCp.this_residual -= 2 * data_count;
}
}
-
+
if (!current_SC->SCp.this_residual
&& current_SC->SCp.buffers_residual) {
@@ -945,25 +1025,27 @@ void fdomain_16x0_intr( int unused )
#if ERRORS_ONLY
if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
- if ((unsigned char)(*((char *)current_SC->request_buffer + 2)) & 0x0f) {
+ if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
unsigned char key;
unsigned char code;
- key = (unsigned char)(*((char *)current_SC->request_buffer + 2)) & 0x0f;
+ key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
+ & 0x0f;
code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
if (!(key == UNIT_ATTENTION && (code == 0x29 || !code))
&& !(key == ILLEGAL_REQUEST && (code == 0x25 || !code)))
-
- printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
- key, code );
+
+ printk( "SCSI REQUEST SENSE: Key = %x, Code = %x\n",
+ key, code );
}
}
#endif
#if EVERY_ACCESS
printk( "BEFORE MY_DONE. . ." );
#endif
- my_done( (current_SC->SCp.Status & 0xff) | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
+ my_done( (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
#if EVERY_ACCESS
printk( "RETURNING.\n" );
#endif
@@ -976,7 +1058,9 @@ void fdomain_16x0_intr( int unused )
outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
}
}
-
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
return;
}
@@ -1033,19 +1117,19 @@ int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
{
- const char *cmd_pt = SCpnt->cmnd;
+ const char *cmd_pt = SCpnt->cmnd;
const char *the_command = SCpnt->cmnd;
- unsigned char *out_buf_pt = SCpnt->request_buffer;
- unsigned char *in_buf_pt = SCpnt->request_buffer;
- unsigned char target = SCpnt->target;
- void *buff = SCpnt->request_buffer;
- int bufflen = SCpnt->request_bufflen;
- int Status = 0;
- int Message = 0;
+ unsigned char *out_buf_pt = SCpnt->request_buffer;
+ unsigned char *in_buf_pt = SCpnt->request_buffer;
+ unsigned char target = SCpnt->target;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int Status = 0;
+ int Message = 0;
int status;
- int done = 0;
+ int done = 0;
unsigned long timeout;
- unsigned data_sent = 0;
+ unsigned data_sent = 0;
unsigned data_count;
int have_data_in = 0;
@@ -1058,7 +1142,7 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
if (fdomain_arbitrate()) {
#if ERRORS_ONLY
printk( ", target = %d, command = %x\n",
- target, (unsigned char)*the_command );
+ target, (unsigned char)*the_command );
#endif
return DID_TIME_OUT << 16;
}
@@ -1066,7 +1150,7 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
if (fdomain_select( target )) {
#if ERRORS_ONLY
if (!target) printk( ", target = %d, command = %x\n",
- target, (unsigned char)*the_command );
+ target, (unsigned char)*the_command );
#endif
return DID_NO_CONNECT << 16;
}
@@ -1144,8 +1228,9 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
#endif
#if ERRORS_ONLY
if (Status) {
- printk( "SCSI (Future Domain): target = %d, command = %x, Status = %x\n",
- target, (unsigned char)*the_command, Status );
+ printk( "SCSI (Future Domain): target = %d, command = %x, "
+ "Status = %x\n",
+ target, (unsigned char)*the_command, Status );
}
#endif
break;
@@ -1170,8 +1255,8 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
#endif
#if ERRORS_ONLY
printk( "SCSI (Future Domain): "
- "Time out, status = %x (target = %d, command = %x)\n",
- status, target, (unsigned char)*the_command );
+ "Time out, status = %x (target = %d, command = %x)\n",
+ status, target, (unsigned char)*the_command );
#endif
fdomain_make_bus_idle();
return DID_BUS_BUSY << 16;
@@ -1183,7 +1268,7 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
#endif
#if ERRORS_ONLY
printk( "SCSI (Future Domain): Aborted (command = %x)\n",
- (unsigned char)*the_command );
+ (unsigned char)*the_command );
#endif
fdomain_16x0_reset();
return DID_RESET << 16;
@@ -1205,14 +1290,14 @@ int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
#if EVERY_ACCESS
printk( "Retcode = %x\n",
- (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
+ (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) );
#endif
#if ERRORS_ONLY
if (*the_command == REQUEST_SENSE && !Status) {
if ((unsigned char)(*((char *)buff + 2)) & 0x0f) {
printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n",
- (unsigned char)(*((char *)buff + 2)) & 0x0f,
- (unsigned char)(*((char *)buff + 12)) );
+ (unsigned char)(*((char *)buff + 2)) & 0x0f,
+ (unsigned char)(*((char *)buff + 12)) );
}
}
#endif
@@ -1228,12 +1313,24 @@ int fdomain_16x0_abort( Scsi_Cmnd *SCpnt, int code )
#endif
#if DEBUG_ABORT
- printk( "Phase = %d, target = %d cmnd = 0x%02x pieces = %d size = %u\n",
- current_SC->SCp.phase,
- current_SC->target,
- *(unsigned char *)current_SC->cmnd,
- current_SC->use_sg,
- current_SC->request_bufflen );
+ print_banner();
+ switch (current_SC->SCp.phase) {
+ case non_queueing: printk( "nonqueueing " ); break;
+ case in_arbitration: printk( "arbitration " ); break;
+ case in_selection: printk( "selection " ); break;
+ case in_other: printk( "other " ); break;
+ default: printk( "unknown " ); break;
+ }
+
+ printk( "phase = %d, target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ current_SC->SCp.phase,
+ current_SC->target,
+ *(unsigned char *)current_SC->cmnd,
+ current_SC->use_sg,
+ current_SC->request_bufflen );
+#if DEBUG_RACE
+ printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
+#endif
printk( "IMR = 0x%02x%02x\n", inb( 0x0a1 ), inb( 0x21 ) );
outb( 0x0a, 0xa0 );
printk( "IRR = 0x%02x", inb( 0xa0 ) );
@@ -1246,6 +1343,12 @@ int fdomain_16x0_abort( Scsi_Cmnd *SCpnt, int code )
printk( "SCSI Status = %x\n", inb( SCSI_Status_port ) );
printk( "TMC Status = %x\n", inb( TMC_Status_port ) );
printk( "Interrupt Mask = %x\n", inb( Interrupt_Mask_port ) );
+ printk( "Option Select = %x\n", inb( port_base + Option_Select ) );
+ if (chip == tmc18c50) {
+ printk( "FIFO Status = %x\n", inb( port_base + FIFO_Status ) );
+ printk( "Int. Condition = %x\n", inb( port_base + Interrupt_Cond ) );
+ printk( "Configuration = %x\n", inb( port_base + Configuration ) );
+ }
#else
cli();
if (!in_command) {
@@ -1294,7 +1397,7 @@ int fdomain_16x0_biosparam( int size, int dev, int *info )
unsigned char heads;
unsigned char sectors;
} *i;
-
+
/* NOTES:
The RAM area starts at 0x1f00 from the bios_base address.
The drive parameter table seems to start at 0x1f30.
diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c
index 15cb278..16fd727 100644
--- a/kernel/blk_drv/scsi/scsi.c
+++ b/kernel/blk_drv/scsi/scsi.c
@@ -590,6 +590,8 @@ update_timeout(SCpnt, SCpnt->timeout_per_command);
static void scsi_request_sense (Scsi_Cmnd * SCpnt)
{
+ int old_use_sg;
+
cli();
SCpnt->flags |= WAS_SENSE;
update_timeout(SCpnt, SENSE_TIMEOUT);
@@ -604,7 +606,10 @@ static void scsi_request_sense (Scsi_Cmnd * SCpnt)
SCpnt->request_buffer = &SCpnt->sense_buffer;
SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+ old_use_sg = SCpnt->use_sg;
+ SCpnt->use_sg = 0;
internal_cmnd (SCpnt);
+ SCpnt->use_sg = old_use_sg;
}
@@ -739,8 +744,8 @@ static int check_sense (Scsi_Cmnd * SCpnt)
return 0;
case ABORTED_COMMAND:
- case NOT_READY:
return SUGGEST_RETRY;
+ case NOT_READY:
case UNIT_ATTENTION:
return SUGGEST_ABORT;
@@ -1182,7 +1187,7 @@ static void scsi_main_timeout(void)
for(host = 0; host < max_scsi_hosts; host++) {
SCpnt = host_queue[host];
while (SCpnt){
- if (SCpnt->timeout != 0 && SCpnt->timeout <= time_elapsed)
+ if (SCpnt->timeout > 0 && SCpnt->timeout <= time_elapsed)
{
sti();
SCpnt->timeout = 0;
diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c
index 2aafe29..afdb6e8 100644
--- a/kernel/blk_drv/scsi/sd.c
+++ b/kernel/blk_drv/scsi/sd.c
@@ -112,7 +112,8 @@ static struct file_operations sd_fops = {
sd_ioctl, /* ioctl */
NULL, /* mmap */
sd_open, /* open code */
- sd_release /* release */
+ sd_release, /* release */
+ block_fsync /* fsync */
};
static struct gendisk sd_gendisk = {
diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c
index ed29802..44d6bfc 100644
--- a/kernel/blk_drv/scsi/sr.c
+++ b/kernel/blk_drv/scsi/sr.c
@@ -29,7 +29,7 @@
#include "scsi_ioctl.h" /* For the door lock/unlock commands */
#define MAX_RETRIES 1
-#define SR_TIMEOUT 250
+#define SR_TIMEOUT 500
int NR_SR=0;
int MAX_SR=0;
@@ -37,6 +37,7 @@ Scsi_CD * scsi_CDs;
static int * sr_sizes;
static int sr_open(struct inode *, struct file *);
+static void get_sectorsize(int);
extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
@@ -59,7 +60,8 @@ static struct file_operations sr_fops =
sr_ioctl, /* ioctl */
NULL, /* mmap */
sr_open, /* no special open code */
- sr_release /* release */
+ sr_release, /* release */
+ NULL /* fsync */
};
/*
@@ -263,6 +265,15 @@ static int sr_open(struct inode * inode, struct file * filp)
if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+
+ /* If this device did not have media in the drive at boot time, then
+ we would have been unable to get the sector size. Check to see if
+ this is the case, and try again.
+ */
+
+ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+ get_sectorsize(MINOR(inode->i_rdev));
+
return 0;
}
@@ -625,13 +636,69 @@ static void sr_init_done (Scsi_Cmnd * SCpnt)
}
}
+static void get_sectorsize(int i){
+ unsigned char cmd[10];
+ unsigned char buffer[513];
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ SCpnt = allocate_device(NULL, scsi_CDs[i].device->index, 1);
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->request.dev = 0xffff; /* Mark as really busy */
+
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sr_init_done, SR_TIMEOUT,
+ MAX_RETRIES);
+
+ if (current == task[0])
+ while(SCpnt->request.dev != 0xfffe);
+ else
+ if (SCpnt->request.dev != 0xfffe){
+ SCpnt->request.waiting = current;
+ current->state = TASK_UNINTERRUPTIBLE;
+ while (SCpnt->request.dev != 0xfffe) schedule();
+ };
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.dev = -1; /* Mark as not busy */
+
+ wake_up(&scsi_devices[SCpnt->index].device_wait);
+
+ if (the_result) {
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+ } else {
+ scsi_CDs[i].capacity = (buffer[0] << 24) |
+ (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+ scsi_CDs[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ if(scsi_CDs[i].sector_size == 0) scsi_CDs[i].sector_size = 2048;
+ if(scsi_CDs[i].sector_size != 2048 &&
+ scsi_CDs[i].sector_size != 512) {
+ printk ("scd%d : unsupported sector size %d.\n",
+ i, scsi_CDs[i].sector_size);
+ scsi_CDs[i].capacity = 0;
+ };
+ if(scsi_CDs[i].sector_size == 2048)
+ scsi_CDs[i].capacity *= 4;
+ scsi_CDs[i].needs_sector_size = 0;
+ };
+}
+
unsigned long sr_init(unsigned long memory_start, unsigned long memory_end)
{
int i;
- unsigned char cmd[10];
- unsigned char buffer[513];
- int the_result, retries;
- Scsi_Cmnd * SCpnt;
if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) {
printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
@@ -645,60 +712,12 @@ unsigned long sr_init(unsigned long memory_start, unsigned long memory_end)
for (i = 0; i < NR_SR; ++i)
{
- SCpnt = allocate_device(NULL, scsi_CDs[i].device->index, 1);
-
- retries = 3;
- do {
- cmd[0] = READ_CAPACITY;
- cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
- memset ((void *) &cmd[2], 0, 8);
- SCpnt->request.dev = 0xffff; /* Mark as really busy */
-
- scsi_do_cmd (SCpnt,
- (void *) cmd, (void *) buffer,
- 512, sr_init_done, SR_TIMEOUT,
- MAX_RETRIES);
-
- if (current == task[0])
- while(SCpnt->request.dev != 0xfffe);
- else
- if (SCpnt->request.dev != 0xfffe){
- SCpnt->request.waiting = current;
- current->state = TASK_UNINTERRUPTIBLE;
- while (SCpnt->request.dev != 0xfffe) schedule();
- };
-
- the_result = SCpnt->result;
- retries--;
-
- } while(the_result && retries);
-
- SCpnt->request.dev = -1; /* Mark as not busy */
-
- wake_up(&scsi_devices[SCpnt->index].device_wait);
- if (the_result) {
- scsi_CDs[i].capacity = 0x1fffff;
- scsi_CDs[i].sector_size = 2048;
- } else {
- scsi_CDs[i].capacity = (buffer[0] << 24) |
- (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
- scsi_CDs[i].sector_size = (buffer[4] << 24) |
- (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
- if(scsi_CDs[i].sector_size != 2048 &&
- scsi_CDs[i].sector_size != 512) {
- printk ("scd%d : unsupported sector size %d.\n",
- i, scsi_CDs[i].sector_size);
- scsi_CDs[i].capacity = 0;
- };
- if(scsi_CDs[i].sector_size == 2048)
- scsi_CDs[i].capacity *= 4;
- };
-
+ get_sectorsize(i);
printk("Scd sectorsize = %d bytes\n", scsi_CDs[i].sector_size);
- scsi_CDs[i].use = 1;
- scsi_CDs[i].ten = 1;
- scsi_CDs[i].remap = 1;
- sr_sizes[i] = scsi_CDs[i].capacity;
+ scsi_CDs[i].use = 1;
+ scsi_CDs[i].ten = 1;
+ scsi_CDs[i].remap = 1;
+ sr_sizes[i] = scsi_CDs[i].capacity;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
diff --git a/kernel/blk_drv/scsi/sr.h b/kernel/blk_drv/scsi/sr.h
index 4ac2e44..c023567 100644
--- a/kernel/blk_drv/scsi/sr.h
+++ b/kernel/blk_drv/scsi/sr.h
@@ -26,6 +26,7 @@ typedef struct
Scsi_Device *device;
unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */
unsigned char sector_bit_shift; /* sectors/FS block = 2^sector_bit_shift*/
+ unsigned needs_sector_size:1; /* needs to get sector size */
unsigned ten:1; /* support ten byte commands */
unsigned remap:1; /* support remapping */
unsigned use:1; /* is this device still supportable */
diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c
index 3593470..9815f5b 100644
--- a/kernel/blk_drv/scsi/st.c
+++ b/kernel/blk_drv/scsi/st.c
@@ -1211,7 +1211,8 @@ static struct file_operations st_fops = {
st_ioctl, /* ioctl */
NULL, /* mmap */
scsi_tape_open, /* open */
- scsi_tape_close /* release */
+ scsi_tape_close, /* release */
+ NULL /* fsync */
};
void st_attach(Scsi_Device * SDp){
diff --git a/kernel/blk_drv/scsi/wd7000.c b/kernel/blk_drv/scsi/wd7000.c
index 774796b..baf8ced 100644
--- a/kernel/blk_drv/scsi/wd7000.c
+++ b/kernel/blk_drv/scsi/wd7000.c
@@ -446,7 +446,7 @@ int wd7000_init(void)
WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
DEB(printk("wd7000_init: Power-on Diagnostics finished\n");)
- if ((i=inb(INTR_STAT)) != 1) {
+ if (((i=inb(INTR_STAT)) != 1) && (i != 7)) {
panic("wd7000_init: Power-on Diagnostics error\n");
return 0;
}
diff --git a/kernel/blk_drv/xd.c b/kernel/blk_drv/xd.c
index 2615072..1af6f82 100644
--- a/kernel/blk_drv/xd.c
+++ b/kernel/blk_drv/xd.c
@@ -68,7 +68,7 @@ static u_char *xd_bases[] = { (u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0
static struct hd_struct xd[XD_MAXDRIVES << 6];
static int xd_sizes[XD_MAXDRIVES << 6],xd_access[XD_MAXDRIVES] = { 0,0 };
static struct gendisk xd_gendisk = { MAJOR_NR,"xd",6,1 << 6,XD_MAXDRIVES,xd_geninit,xd,xd_sizes,0,(void *) xd_info,NULL };
-static struct file_operations xd_fops = { NULL,block_read,block_write,NULL,NULL,xd_ioctl,NULL,xd_open,xd_release };
+static struct file_operations xd_fops = { NULL,block_read,block_write,NULL,NULL,xd_ioctl,NULL,xd_open,xd_release,block_fsync };
static struct wait_queue *xd_wait_exclusive = NULL,*xd_wait_int = NULL,*xd_wait_open = NULL;
static u_char xd_valid[XD_MAXDRIVES] = { 0,0 };
diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile
index 6b76915..6d9347a 100644
--- a/kernel/chr_drv/Makefile
+++ b/kernel/chr_drv/Makefile
@@ -28,12 +28,6 @@ chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
-console.o: console.c
- $(CC) $(CFLAGS) -c -o console.o console.c
-
-keyboard.o: keyboard.c
- $(CC) $(CFLAGS) $(KEYBOARD) -c -o keyboard.o keyboard.c
-
subdirs: dummy
@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c
index d56cdd0..d93ede7 100644
--- a/kernel/chr_drv/console.c
+++ b/kernel/chr_drv/console.c
@@ -110,16 +110,14 @@ static struct {
unsigned long vc_need_wrap : 1;
unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */
unsigned char vc_kbdmode;
- char * vc_translate;
- char * vc_G0_charset;
- char * vc_G1_charset;
- char * vc_saved_G0;
- char * vc_saved_G1;
+ unsigned char * vc_translate;
+ unsigned char * vc_G0_charset;
+ unsigned char * vc_G1_charset;
+ unsigned char * vc_saved_G0;
+ unsigned char * vc_saved_G1;
/* additional information is in vt_kern.h */
} vc_cons [NR_CONSOLES];
-#define MEM_BUFFER_SIZE (2*80*50*8)
-
unsigned short *vc_scrbuf[NR_CONSOLES];
static unsigned short * vc_scrmembuf;
static int console_blanked = 0;
@@ -189,7 +187,7 @@ static int console_blanked = 0;
int blankinterval = 10*60*HZ;
static int screen_size = 0;
-static void sysbeep(void);
+extern void kd_mksound(int freq, int time);
/*
* this is what the terminal answers to a ESC-Z or csi0c query.
@@ -197,7 +195,7 @@ static void sysbeep(void);
#define VT100ID "\033[?1;2c"
#define VT102ID "\033[?6c"
-static char * translations[] = {
+static unsigned char * translations[] = {
/* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -227,7 +225,7 @@ static char * translations[] = {
"\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
"\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
-/* IBM grapgics: minimal translations (CR, LF, LL, SO, SI and ESC) */
+/* IBM graphics: minimal translations (CR, LF, LL, SO, SI and ESC) */
"\000\001\002\003\004\005\006\007\010\011\000\013\000\000\000\000"
"\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
@@ -285,17 +283,77 @@ static void gotoxy(int currcons, int new_x, int new_y)
need_wrap = 0;
}
+/*
+ * *Very* limited hardware scrollback support..
+ */
+static unsigned short __real_origin;
+static unsigned short __origin;
+
+static inline void __set_origin(unsigned short offset)
+{
+ cli();
+ __origin = offset;
+ outb_p(12, video_port_reg);
+ outb_p(offset >> 8, video_port_val);
+ outb_p(13, video_port_reg);
+ outb_p(offset, video_port_val);
+ sti();
+}
+
+void scrollback(int lines)
+{
+ if (!lines)
+ lines = video_num_lines/2;
+ lines *= video_num_columns;
+ lines = __origin - lines;
+ if (lines < 0)
+ lines = 0;
+ __set_origin(lines);
+}
+
+void scrollfront(int lines)
+{
+ if (!lines)
+ lines = video_num_lines/2;
+ lines *= video_num_columns;
+ lines = __origin + lines;
+ if (lines > __real_origin)
+ lines = __real_origin;
+ __set_origin(lines);
+}
+
static void set_origin(int currcons)
{
if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
return;
if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
return;
+ __real_origin = (origin-video_mem_base) >> 1;
+ __set_origin(__real_origin);
+}
+
+static inline void hide_cursor(int currcons)
+{
+ outb_p(14, video_port_reg);
+ outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val);
+ outb_p(15, video_port_reg);
+ outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val);
+}
+
+static inline void set_cursor(int currcons)
+{
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+ if (__real_origin != __origin)
+ set_origin(__real_origin);
cli();
- outb_p(12, video_port_reg);
- outb_p(0xff&((origin-video_mem_base)>>9), video_port_val);
- outb_p(13, video_port_reg);
- outb_p(0xff&((origin-video_mem_base)>>1), video_port_val);
+ if (deccm) {
+ outb_p(14, video_port_reg);
+ outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
+ outb_p(15, video_port_reg);
+ outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
+ } else
+ hide_cursor(currcons);
sti();
}
@@ -573,29 +631,6 @@ static void csi_m(int currcons)
update_attr(currcons);
}
-static inline void hide_cursor(int currcons)
-{
- outb_p(14, video_port_reg);
- outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val);
- outb_p(15, video_port_reg);
- outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val);
-}
-
-static inline void set_cursor(int currcons)
-{
- if (currcons != fg_console || console_blanked)
- return;
- cli();
- if (deccm) {
- outb_p(14, video_port_reg);
- outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
- outb_p(15, video_port_reg);
- outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
- } else
- hide_cursor(currcons);
- sti();
-}
-
static void respond_string(char * p, int currcons, struct tty_struct * tty)
{
while (*p) {
@@ -910,8 +945,7 @@ void con_write(struct tty_struct * tty)
if (decim)
insert_char(currcons);
c = translate[c];
- *(char *) pos = c;
- *(char *) (pos+1) = attr;
+ *(unsigned short *) pos = (attr << 8) + c;
if (x == video_num_columns - 1)
need_wrap = decawm;
else {
@@ -927,7 +961,7 @@ void con_write(struct tty_struct * tty)
*/
switch (c) {
case 7:
- sysbeep();
+ kd_mksound(0x637, HZ/8);
continue;
case 8:
bs(currcons);
@@ -1410,34 +1444,13 @@ void update_screen(int new_console)
lock = 0;
}
-/* from bsd-net-2: */
-
-static void sysbeepstop(void)
-{
- /* disable counter 2 */
- outb(inb_p(0x61)&0xFC, 0x61);
-}
-
-static void sysbeep(void)
-{
- /* enable counter 2 */
- outb_p(inb_p(0x61)|3, 0x61);
- /* set command for counter 2, 2 byte write */
- outb_p(0xB6, 0x43);
- /* send 0x637 for 750 HZ */
- outb_p(0x37, 0x42);
- outb(0x06, 0x42);
- /* 1/8 second */
- timer_table[BEEP_TIMER].expires = jiffies + HZ/8;
- timer_table[BEEP_TIMER].fn = sysbeepstop;
- timer_active |= 1<<BEEP_TIMER;
-}
-
int do_screendump(int arg)
{
char *sptr, *buf = (char *)arg;
int currcons, l;
+ if (!suser())
+ return -EPERM;
l = verify_area(VERIFY_WRITE, buf,2+video_num_columns*video_num_lines);
if (l)
return l;
@@ -1456,7 +1469,7 @@ int do_screendump(int arg)
void console_print(const char * b)
{
int currcons = fg_console;
- char c;
+ unsigned char c;
if (!printable || currcons<0 || currcons>=NR_CONSOLES)
return;
@@ -1468,8 +1481,7 @@ void console_print(const char * b)
if (c == 10 || c == 13)
continue;
}
- *(char *) pos = c;
- *(char *) (pos+1) = attr;
+ *(unsigned short *) pos = (attr << 8) + c;
if (x == video_num_columns - 1) {
need_wrap = 1;
continue;
diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c
index d0e70d2..ffccc3c 100644
--- a/kernel/chr_drv/keyboard.c
+++ b/kernel/chr_drv/keyboard.c
@@ -7,12 +7,13 @@
* the assembly version by Linus (with diacriticals added)
*
* Some additional features added by Christoph Niemann (ChN), March 1993
+ *
+ * Loadable keymaps by Risto Kankkunen, May 1993
*/
#define KEYBOARD_IRQ 1
#include <linux/sched.h>
-#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
@@ -29,6 +30,11 @@
#endif
#endif
+#define SHIFT_KEYS ((1 << KG_LSHIFT) | (1 << KG_RSHIFT))
+#define CTRL_KEYS ((1 << KG_LCTRL) | (1 << KG_RCTRL))
+#define ALT_KEYS ((1 << KG_LALT) | (1 << KG_RALT))
+#define ALTGR_KEYS ((1 << KG_LALTGR) | (1 << KG_RALTGR))
+
/*
* The default IO slowdown is doing 'inb()'s from 0x61, which should be
* safe. But as that is the keyboard controller chip address, we do our
@@ -43,6 +49,8 @@
extern void do_keyboard_interrupt(void);
extern void ctrl_alt_del(void);
extern void change_console(unsigned int new_console);
+extern void scrollback(int);
+extern void scrollfront(int);
#define fake_keyboard_interrupt() \
__asm__ __volatile__("int $0x21")
@@ -53,7 +61,7 @@ unsigned long kbd_prev_dead_keys = 0;
static int want_console = -1;
static int last_console = 0; /* last used VC */
-static unsigned char rep = 0xff; /* last pressed key */
+static char rep = 0; /* flag telling character repeat */
struct kbd_struct kbd_table[NR_CONSOLES];
static struct kbd_struct * kbd = kbd_table;
static struct tty_struct * tty = NULL;
@@ -61,15 +69,36 @@ static struct tty_struct * tty = NULL;
static volatile unsigned char acknowledge = 0;
static volatile unsigned char resend = 0;
-typedef void (*fptr)(int);
+typedef unsigned short u_word;
+typedef void (*k_hand)(unsigned char value, char up_flag);
+
+static void do_self(unsigned char value, char up_flag);
+static void do_fn(unsigned char value, char up_flag);
+static void do_spec(unsigned char value, char up_flag);
+static void do_pad(unsigned char value, char up_flag);
+static void do_dead(unsigned char value, char up_flag);
+static void do_cons(unsigned char value, char up_flag);
+static void do_cur(unsigned char value, char up_flag);
+static void do_shift(unsigned char value, char up_flag);
+
+static k_hand key_handler[] = {
+ do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift
+};
+
+/* maximum values each key_handler can handle */
+const u_char max_vals[] = {
+ 255, 25, 13, 16, 4, 255, 3, 255
+};
+
+const int NR_TYPES = (sizeof(max_vals) / sizeof(u_char));
+#define E0_BASE 96
+
+static int shift_state = 0;
static int diacr = -1;
static int npadch = 0;
-fptr key_table[];
static void put_queue(int);
-static void applkey(int);
-static void cur(int);
static unsigned int handle_diacr(unsigned int);
static struct pt_regs * pt_regs;
@@ -86,6 +115,7 @@ static inline void kb_wait(void)
static void keyboard_interrupt(int int_pt_regs)
{
unsigned char scancode;
+ static unsigned char prev_scancode = 0;
pt_regs = (struct pt_regs *) int_pt_regs;
kbd_prev_dead_keys |= kbd_dead_keys;
@@ -130,976 +160,269 @@ static void keyboard_interrupt(int int_pt_regs)
/*
* Repeat a key only if the input buffers are empty or the
* characters get echoed locally. This makes key repeat usable
- * with slow applications and unders heavy loads.
+ * with slow applications and under heavy loads.
*/
- if ((scancode != rep) ||
+ rep = scancode == prev_scancode;
+ prev_scancode = scancode;
+ if (!rep ||
(vc_kbd_flag(kbd,VC_REPEAT) && tty &&
- (L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q)))))
- key_table[scancode](scancode);
- rep = scancode;
+ (L_ECHO(tty) || (EMPTY(&tty->secondary) && EMPTY(&tty->read_q))))) {
+ static unsigned char e0_keys[] = {
+ 0x1c, /* keypad enter */
+ 0x1d, /* right control */
+ 0x35, /* keypad slash */
+ 0x37, /* print screen */
+ 0x38, /* right alt */
+ 0x46, /* break (control-pause) */
+ 0x47, /* editpad home */
+ 0x48, /* editpad up */
+ 0x49, /* editpad pgup */
+ 0x4b, /* editpad left */
+ 0x4d, /* editpad right */
+ 0x4f, /* editpad end */
+ 0x50, /* editpad dn */
+ 0x51, /* editpad pgdn */
+ 0x52, /* editpad ins */
+ 0x53 /* editpad del */
+ };
+ u_word key_code;
+ char break_flag = scancode > 0x7f;
+
+ scancode &= 0x7f;
+ if (scancode >= E0_BASE) {
+#if 0
+ printk("keyboard: scancode (%02x) not in range 00 - %2x\n", scancode, E0_BASE - 1);
+#endif
+ scancode = 0;
+ }
+
+ if (kbd_dead(KGD_E0)) {
+ int i;
+ for (i = 0; i < sizeof(e0_keys); i++)
+ if (scancode == e0_keys[i]) {
+ scancode = E0_BASE + i;
+ i = -1;
+ break;
+ }
+ if (i != -1)
+ printk("keyboard: unknown scancode e0 %02x\n", scancode);
+ }
+
+ key_code = key_map[shift_state][scancode];
+ (*key_handler[key_code >> 8])(key_code & 0xff, break_flag);
+ }
end_kbd_intr:
}
static void put_queue(int ch)
{
struct tty_queue *qp;
- unsigned long new_head;
wake_up(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
- qp->buf[qp->head]=ch;
- if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1)) != qp->tail)
- qp->head=new_head;
- wake_up_interruptible(&qp->proc_list);
+ if (LEFT(qp)) {
+ qp->buf[qp->head] = ch;
+ INC(qp->head);
+ wake_up_interruptible(&qp->proc_list);
+ }
}
static void puts_queue(char *cp)
{
struct tty_queue *qp;
- unsigned long new_head;
char ch;
+ /* why interruptible here, plain wake_up above? */
wake_up_interruptible(&keypress_wait);
if (!tty)
return;
qp = &tty->read_q;
while ((ch = *(cp++)) != 0) {
- qp->buf[qp->head]=ch;
- if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1))
- != qp->tail)
- qp->head=new_head;
- }
- wake_up_interruptible(&qp->proc_list);
-}
-
-static void ctrl(int sc)
-{
- if (kbd_dead(KGD_E0))
- set_kbd_flag(KG_RCTRL);
- else
- set_kbd_flag(KG_LCTRL);
-}
-
-static void alt(int sc)
-{
- if (kbd_dead(KGD_E0))
- set_kbd_flag(KG_ALTGR);
- else
- set_kbd_flag(KG_ALT);
-}
-
-static void unctrl(int sc)
-{
- if (kbd_dead(KGD_E0))
- clr_kbd_flag(KG_RCTRL);
- else
- clr_kbd_flag(KG_LCTRL);
-}
-
-static void unalt(int sc)
-{
- if (kbd_dead(KGD_E0))
- clr_kbd_flag(KG_ALTGR);
- else {
- clr_kbd_flag(KG_ALT);
- if (npadch != 0) {
- put_queue(npadch);
- npadch=0;
+ if (LEFT(qp)) {
+ qp->buf[qp->head] = ch;
+ INC(qp->head);
}
}
+ wake_up_interruptible(&qp->proc_list);
}
-static void lshift(int sc)
-{
- set_kbd_flag(KG_LSHIFT);
-}
-
-static void unlshift(int sc)
+static void applkey(int key, char mode)
{
- clr_kbd_flag(KG_LSHIFT);
-}
+ static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
-static void rshift(int sc)
-{
- set_kbd_flag(KG_RSHIFT);
+ buf[1] = (mode ? 'O' : '[');
+ buf[2] = key;
+ puts_queue(buf);
}
-static void unrshift(int sc)
+static void enter(void)
{
- clr_kbd_flag(KG_RSHIFT);
+ put_queue(13);
+ if (vc_kbd_flag(kbd,VC_CRLF))
+ put_queue(10);
}
-static void caps(int sc)
+static void caps_toggle(void)
{
- if (kbd_flag(KG_CAPSLOCK))
- return; /* key already pressed: defeat repeat */
+ if (rep)
+ return;
set_kbd_flag(KG_CAPSLOCK);
chg_vc_kbd_flag(kbd,VC_CAPSLOCK);
}
-static void uncaps(int sc)
-{
- clr_kbd_flag(KG_CAPSLOCK);
-}
-
static void show_ptregs(void)
{
if (!pt_regs)
return;
- printk("\nEIP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip);
+ printk("\n");
+ printk("EIP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip);
if (pt_regs->cs & 3)
printk(" ESP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip);
- printk(" EFLAGS: %08x",pt_regs->eflags);
- printk("\nEAX: %08x EBX: %08x ECX: %08x EDX: %08x",
+ printk(" EFLAGS: %08x\n",pt_regs->eflags);
+ printk("EAX: %08x EBX: %08x ECX: %08x EDX: %08x\n",
pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
- printk("\nESI: %08x EDI: %08x EBP: %08x",
+ printk("ESI: %08x EDI: %08x EBP: %08x",
pt_regs->esi, pt_regs->edi, pt_regs->ebp);
printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
0xffff & pt_regs->ds,0xffff & pt_regs->es,
0xffff & pt_regs->fs,0xffff & pt_regs->gs);
}
-static void scroll(int sc)
+static void hold(void)
{
- if (kbd_dead(KGD_E0))
- put_queue(INTR_CHAR(tty));
- else if ( sc != rep ) /* no autorepeat for scroll lock, ChN */
- if (kbd_flag(KG_LSHIFT) || kbd_flag(KG_RSHIFT))
- show_mem();
- else if (kbd_flag(KG_ALT) || kbd_flag(KG_ALTGR))
- show_ptregs();
- else if (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL))
- show_state();
- else {
- if (vc_kbd_flag(kbd, VC_SCROLLOCK))
- /* pressing srcoll lock 2nd time sends ^Q, ChN */
- put_queue(START_CHAR(tty));
- else
-
- /* pressing srcoll lock 1st time sends ^S, ChN */
- put_queue(STOP_CHAR(tty));
- chg_vc_kbd_flag(kbd,VC_SCROLLOCK);
- }
-
+ /* kludge, should have a control key table */
+ if (kbd_flags & CTRL_KEYS) {
+ show_state();
+ return;
+ }
+
+ if (rep || !tty)
+ return;
+ if (vc_kbd_flag(kbd, VC_SCROLLOCK))
+ /* pressing srcoll lock 2nd time sends ^Q, ChN */
+ put_queue(START_CHAR(tty));
+ else
+ /* pressing srcoll lock 1st time sends ^S, ChN */
+ put_queue(STOP_CHAR(tty));
+ chg_vc_kbd_flag(kbd,VC_SCROLLOCK);
}
-static void num(int sc)
+static void num(void)
{
- if (kbd_flag(KG_LCTRL))
+#if 0
+ if (kbd_flags & CTRL_KEYS) {
/* pause key pressed, sends E1 1D 45, ChN */
chg_vc_kbd_flag(kbd,VC_PAUSE);
- else if (vc_kbd_flag(kbd,VC_APPLIC))
- applkey(0x50);
- else if ( rep != sc ) /* no autorepeat for numlock, ChN */
+ return;
+ }
+#endif
+ if (vc_kbd_flag(kbd,VC_APPLIC)) {
+ applkey('P', 1);
+ return;
+ }
+ if (!rep) /* no autorepeat for numlock, ChN */
chg_vc_kbd_flag(kbd,VC_NUMLOCK);
}
-static void applkey(int key)
+static void lastcons(void)
{
- char buf[] = { 0x1b, 0x4f, 0x00, 0x00 };
-
- buf[2] = key;
- puts_queue(buf);
+ /* pressing alt-printscreen switches to the last used console, ChN */
+ want_console = last_console;
}
-static void sysreq(int sc)
+static void send_intr(void)
{
- /* pressing alt-printscreen switches to the last used console, ChN */
- if ( sc != rep)
- want_console = last_console;
+ if (tty)
+ put_queue(INTR_CHAR(tty));
}
-#if defined KBD_FINNISH
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '+', '\'', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '}', 0, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|',
- '{', 0, 0, '\'', 'z', '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, '!', '\"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', ']', '^', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\',
- '[', 0, 0, '*', 'Z', '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, '@', 163, '$', 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, 0,
- 0 };
-
-#elif defined KBD_FINNISH_LATIN1
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '+', 180, 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', 229, 168, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246,
- 228, 167, 0, '\'', 'z', '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, '!', '"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', 197, '^', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214,
- 196, 189, 0, '*', 'Z', '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, '@', 163, '$', 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, 0,
- 0 };
-
-#elif defined KBD_US
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '-', '=', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '[', ']', 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
- '\'', '`', 0, '\\', 'z', '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, '!', '@', '#', '$', '%', '^',
- '&', '*', '(', ')', '_', '+', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', '{', '}', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
- '"', '~', '0', '|', 'Z', '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, '~', 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, 0,
- 0 };
-
-#elif defined KBD_UK
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '-', '=', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '[', ']', 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
- '\'', '`', 0, '#', 'z', '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, '!', '"', 163, '$', '%', '^',
- '&', '*', '(', ')', '_', '+', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', '{', '}', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
- '@', '~', '0', '~', 'Z', '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, '~', 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, 0,
- 0 };
-
-#elif defined KBD_GR
-
-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', '@', '+', 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', '[',
- ']', '^', 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, '!', '"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
- 'O', 'P', '\\', '*', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', '{',
- '}', '~', 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, '~', 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, 0,
- 0 };
-
-#elif defined KBD_GR_LATIN1
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', 223, 180, 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, 94, 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, '!', '"', 167, '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
- 'O', 'P', 220, '*', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214,
- 196, 176, 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, 178, 179, '$', 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, 181, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 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_FR
-
-static unsigned char key_map[] = {
- 0, 27, '&', '{', '"', '\'', '(', '-',
- '}', '_', '/', '@', ')', '=', 127, 9,
- 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '^', '$', 13, 0, 'q', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
- '|', '`', 0, 42, 'w', 'x', 'c', 'v',
- 'b', 'n', ',', ';', ':', '!', 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, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', ']', '+', 127, 9,
- 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', '<', '>', 13, 0, 'Q', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
- '%', '~', 0, '#', 'W', 'X', 'C', 'V',
- 'B', 'N', '?', '.', '/', '\\', 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, '~', 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, 0,
- 0 };
-
-#elif defined KBD_FR_LATIN1
-
-static unsigned char key_map[] = {
- 0, 27, '&', 233, '"', '\'', '(', '-',
- 232, '_', 231, 224, ')', '=', 127, 9,
- 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '^', '$', 13, 0, 'q', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
- 249, 178, 0, 42, 'w', 'x', 'c', 'v',
- 'b', 'n', ',', ';', ':', '!', 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, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', 176, '+', 127, 9,
- 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', 168, 163, 13, 0, 'Q', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
- '%', 0, 0, 181, 'W', 'X', 'C', 'V',
- 'B', 'N', '?', '.', '/', 167, 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, 164, 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, 0,
- 0 };
-
-#elif defined KBD_DK
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '+', '\'', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', 229, 0, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230,
- 162, 0, 0, '\'', 'z', '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, '!', '\"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', 197, '^', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198,
- 165, 0, 0, '*', 'Z', '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, '@', 163, '$', 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, 0,
- 0 };
-
-#elif defined KBD_DK_LATIN1
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '+', 180, 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', 229, 168, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230,
- 162, 189, 0, '\'', 'z', '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, '!', '\"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', 197, '^', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198,
- 165, 167, 0, '*', 'Z', '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, '@', 163, '$', 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, 0,
- 0 };
-
-#elif defined KBD_DVORAK
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '\\', '=', 127, 9,
- '\'', ',', '.', 'p', 'y', 'f', 'g', 'c',
- 'r', 'l', '/', ']', 13, 0, 'a', 'o',
- 'e', 'u', 'i', 'd', 'h', 't', 'n', 's',
- '-', '`', 0, '[', ';', 'q', 'j', 'k',
- 'x', 'b', 'm', 'w', 'v', 'z', 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, '!', '@', '#', '$', '%', '^',
- '&', '*', '(', ')', '|', '+', 127, 9,
- '"', '<', '>', 'P', 'Y', 'F', 'G', 'C',
- 'R', 'L', '?', '}', 13, 0, 'A', 'O',
- 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S',
- '_', '~', 0, '{', ':', 'Q', 'J', 'K',
- 'X', 'B', 'M', 'W', 'V', 'Z', 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, '~', 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, 0,
- 0 };
-
-#elif defined KBD_SG
-
-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, '[', ']', 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_SG_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', 252, 0, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246,
- 228, 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', 220, '!', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214,
- 196, 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, '\'', '~', 0, 0,
- '@', 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '[', ']', 13, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 233,
- '{', 0, 0, '}', 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 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_NO
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '+', '\\', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
- 'o', 'p', '}', '~', 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|',
- '{', '|', 0, '\'', 'z', '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, '!', '\"', '#', '$', '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
- 'O', 'P', ']', '^', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\',
- '[', 0, 0, '*', 'Z', '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, '~', 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, 0, 0,
- 0 };
-
-#elif defined KBD_SF
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '\'', '^', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
- 'o', 'p', 0, 0, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0,
- 0, 0, 0, '$', 'y', 'x', 'c', 'v',
- 'b', 'n', 'm', ',', '.', '-', 0, '*',
- 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '-', 0, 0, 0, '+', 0,
- 0, 0, 0, 0, 0, 0, '<', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-static unsigned char shift_map[] = {
- 0, 27, '+', '"', '*', 0, '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
- 'O', 'P', 0, '!', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0,
- 0, 0, 0, 0, 'Y', 'X', 'C', 'V',
- 'B', 'N', 'M', ';', ':', '_', 0, '*',
- 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '-', 0, 0, 0, '+', 0,
- 0, 0, 0, 0, 0, 0, '>', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-static unsigned char alt_map[] = {
- 0, 0, 0, '@', '#', 0, 0, 0,
- '|', 0, 0, 0, '\'', '~', 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '[', ']', 13, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- '{', 0, 0, '}', 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, '\\', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-
-#elif defined KBD_SF_LATIN1
-
-static unsigned char key_map[] = {
- 0, 27, '1', '2', '3', '4', '5', '6',
- '7', '8', '9', '0', '\'', '^', 127, 9,
- 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
- 'o', 'p', 232, 168, 13, 0, 'a', 's',
- 'd', 'f', 'g', 'h', 'j', 'k', 'l', 233,
- 224, 167, 0, '$', 'y', 'x', 'c', 'v',
- 'b', 'n', 'm', ',', '.', '-', 0, '*',
- 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '-', 0, 0, 0, '+', 0,
- 0, 0, 0, 0, 0, 0, '<', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-static unsigned char shift_map[] = {
- 0, 27, '+', '"', '*', 231, '%', '&',
- '/', '(', ')', '=', '?', '`', 127, 9,
- 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
- 'O', 'P', 252, '!', 13, 0, 'A', 'S',
- 'D', 'F', 'G', 'H', 'J', 'K', 'L', 246,
- 228, 176, 0, 163, 'Y', 'X', 'C', 'V',
- 'B', 'N', 'M', ';', ':', '_', 0, '*',
- 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '-', 0, 0, 0, '+', 0,
- 0, 0, 0, 0, 0, 0, '>', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-static unsigned char alt_map[] = {
- 0, 0, 0, '@', '#', 0, 0, 172,
- '|', 162, 0, 0, 180, '~', 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, '[', ']', 13, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- '{', 0, 0, '}', 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, '\\', 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0 };
-#else
-#error "KBD-type not defined"
-#endif
-
-static void do_self(int sc)
+static void do_spec(unsigned char value, char up_flag)
{
- unsigned char ch;
+ typedef void (*fnp)(void);
+ fnp fn_table[] = {
+ NULL, enter, show_ptregs, show_mem,
+ show_state, send_intr, lastcons, caps_toggle,
+ num, hold
+ };
- if (kbd_flag(KG_ALTGR))
- ch = alt_map[sc];
- else if (kbd_flag(KG_LSHIFT) || kbd_flag(KG_RSHIFT) ||
- kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL))
- ch = shift_map[sc];
- else
- ch = key_map[sc];
-
- if (ch == 0)
+ if (value >= sizeof(fn_table)/sizeof(fnp))
return;
-
- if ((ch = handle_diacr(ch)) == 0)
+ if (up_flag)
return;
+ if (!fn_table[value])
+ return;
+ fn_table[value]();
+}
+
+static void do_self(unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return; /* no action, if this is a key release */
- if (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL) ||
- vc_kbd_flag(kbd,VC_CAPSLOCK)) /* ctrl or caps */
- if ((ch >= 'a' && ch <= 'z') || (ch >= 224 && ch <= 254))
- ch -= 32;
- if (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL)) /* ctrl */
- ch &= 0x1f;
+ value = handle_diacr(value);
- if (kbd_flag(KG_ALT))
+ /* kludge... */
+ if (vc_kbd_flag(kbd,VC_CAPSLOCK))
+ if ((value >= 'a' && value <= 'z')
+ || (value >= 224 && value <= 254)) {
+ value -= 32;
+ }
+ /* kludge... */
+ if (kbd_flags & CTRL_KEYS) {
+ if (value >= 64 && value < 127)
+ value &= 0x1f;
+ else if (value == ' ' || value == '2')
+ value = 0;
+ else if (value >= '3' && value < '8')
+ value -= 24;
+ else if (value == '?' || value == '8' || value == '/')
+ value = 127;
+ else if (value == '-' || value == '_')
+ value = 0x1f;
+ else
+ return;
+ }
+
+ if (kbd_flags & ALT_KEYS)
if (vc_kbd_flag(kbd,VC_META)) {
put_queue('\033');
- put_queue(ch);
+ put_queue(value);
} else
- put_queue(ch|0x80);
+ put_queue(value|0x80);
else
- put_queue(ch);
+ put_queue(value);
+}
+
+static unsigned char ret_diacr[] =
+ {'`', '\'', '^', '~', '"' }; /* Must not end with 0 */
+
+/* If a dead key pressed twice, output a character corresponding it, */
+/* otherwise just remember the dead key. */
+
+static void do_dead(unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ if (diacr == value) { /* pressed twice */
+ diacr = -1;
+ put_queue(ret_diacr[value]);
+ return;
+ }
+ diacr = value;
}
-unsigned char accent_table[5][64] = {
+/* If no pending dead key, return the character unchanged. Otherwise, */
+/* if space if pressed, return a character corresponding the pending */
+/* dead key, otherwise try to combine the two. */
+
+unsigned int handle_diacr(unsigned int ch)
+{
+ static unsigned char accent_table[5][64] = {
" \300BCD\310FGH\314JKLMN\322PQRST\331VWXYZ[\\]^_"
"`\340bcd\350fgh\354jklmn\362pqrst\371vwxyz{|}~", /* accent grave */
@@ -1114,220 +437,209 @@ unsigned char accent_table[5][64] = {
" \304BCD\313FGH\316JKLMN\326PQRST\334VWXYZ[\\]^_"
"`\344bcd\353fgh\357jklmn\366pqrst\374vwx\377z{|}~" /* dieresis */
-};
+ };
+ int d = diacr;
-/*
- * Check if dead key pressed. If so, check if same key pressed twice;
- * in that case return the char, otherwise store char and return 0.
- * If dead key not pressed, check if accented character pending. If
- * not: return the char, otherwise check if char is a space. If it is
- * a space return the diacritical. Else combine char with diacritical
- * mark and return.
- */
-
-unsigned int handle_diacr(unsigned int ch)
-{
- static unsigned char diacr_table[] =
- {'`', 180, '^', '~', 168, 0}; /* Must end with 0 */
- static unsigned char ret_diacr[] =
- {'`', '\'', '^', '~', '"' }; /* Must not end with 0 */
- int i;
-
- for(i=0; diacr_table[i]; i++)
- if (ch==diacr_table[i] && ((1<<i)&kbd->kbd_flags)) {
- if (diacr == i) {
- diacr=-1;
- return ret_diacr[i]; /* pressed twice */
- } else {
- diacr=i; /* key is dead */
- return 0;
- }
- }
if (diacr == -1)
return ch;
- else if (ch == ' ') {
- ch=ret_diacr[diacr];
- diacr=-1;
- return ch;
- } else if (ch<64 || ch>122) {
- diacr=-1;
- return ch;
- } else {
- ch=accent_table[diacr][ch-64];
- diacr=-1;
- return ch;
- }
-}
-#if defined KBD_FR || defined KBD_US || defined KBD_UK || defined KBD_FR_LATIN1
-static unsigned char num_table[] = "789-456+1230.";
-#else
-static unsigned char num_table[] = "789-456+1230,";
-#endif
+ diacr = -1;
+ if (ch == ' ')
+ return ret_diacr[d];
-static unsigned char cur_table[] = "1A5-DGC+4B623";
-static unsigned int pad_table[] = { 7,8,9,0,4,5,6,0,1,2,3,0,0 };
+ if (ch < 64 || ch > 122)
+ return ch;
-/*
- Keypad / 35 B7 Q
- Keypad * (PrtSc) 37 B7 R
- Keypad NumLock 45 ?? P
- Keypad 7 (Home) 47 C7 w
- Keypad 8 (Up arrow) 48 C8 x
- Keypad 9 (PgUp) 49 C9 y
- Keypad - 4A CA S
- Keypad 4 (Left arrow) 4B CB t
- Keypad 5 4C CC u
- Keypad 6 (Right arrow) 4D CD v
- Keypad + 4E CE l
- Keypad 1 (End) 4F CF q
- Keypad 2 (Down arrow) 50 D0 r
- Keypad 3 (PgDn) 51 D1 s
- Keypad 0 (Ins) 52 D2 p
- Keypad . (Del) 53 D3 n
-*/
-
-static unsigned char appl_table[] = "wxyStuvlqrspn";
+ return accent_table[d][ch - 64];
+}
-/*
- Set up keyboard to generate DEC VT200 F-keys.
- DEC F1 - F5 not implemented (DEC HOLD, LOCAL PRINT, SETUP, SW SESS, BREAK)
- DEC F6 - F10 are mapped to F6 - F10
- DEC F11 - F20 are mapped to Shift-F1 - Shift-F10
- DEC HELP and DEC DO are mapped to F11, F12 or Shift- F11, F12.
- Regular (?) Linux F1-F5 remain the same.
-*/
-
-static char *func_table[2][12] = { /* DEC F1 - F10 */ {
- "\033[[A", "\033[[B", "\033[[C", "\033[[D",
- "\033[[E", "\033[17~", "\033[18~", "\033[19~",
- "\033[20~", "\033[21~", "\033[28~", "\033[29~"
-}, /* DEC F11 - F20 */ {
- "\033[23~", "\033[24~", "\033[25~", "\033[26~",
- "\033[28~", "\033[29~", "\033[31~", "\033[32~",
- "\033[33~", "\033[34~", "\033[28~", "\033[29~"
-}};
-
-static void cursor(int sc)
+static void do_cons(unsigned char value, char up_flag)
{
- if (sc < 0x47 || sc > 0x53)
+ if (up_flag)
return;
- sc -= 0x47;
- if (sc == 12 &&
- (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL)) &&
- (kbd_flag(KG_ALT) || kbd_flag(KG_ALTGR))) {
- ctrl_alt_del();
+ want_console = value;
+}
+
+static char *func_table[] = {
+ "\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", /* F1 -F5 */
+ "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~",/* F6 -F10 */
+ "\033[23~", "\033[24~", "\033[25~", "\033[26~", "\033[28~",/* F11-F15 */
+ "\033[29~", "\033[31~", "\033[32~", "\033[33~", "\033[34~",/* F16-F20 */
+ "\033[1~", /* Find */
+ "\033[2~", /* Insert */
+ "\033[3~", /* Remove */
+ "\033[4~", /* Select */
+ "\033[5~", /* Prev page */
+ "\033[6~" /* Next page */
+};
+
+static void do_fn(unsigned char value, char up_flag)
+{
+ if (up_flag)
return;
+ if (kbd_flags & SHIFT_KEYS) {
+ if (value == KVAL(K_PGDN)) {
+ scrollfront(0);
+ return;
+ }
+ if (value == KVAL(K_PGUP)) {
+ scrollback(0);
+ return;
+ }
}
- if (kbd_dead(KGD_E0)) {
- cur(sc);
+ if (kbd_flags & ALT_KEYS) {
+ want_console = value;
return;
}
+ puts_queue(func_table[value]);
+}
- if (kbd_flag(KG_ALT) && sc != 12) { /* Alt-numpad */
- npadch=npadch*10+pad_table[sc];
+static void do_pad(unsigned char value, char up_flag)
+{
+ static char *pad_chars = "0123456789+-*/\015,.";
+ static char *app_map = "pqrstuvwxylSRQMnn";
+
+ if (up_flag)
+ return; /* no action, if this is a key release */
+
+ if ((value == KVAL(K_PCOMMA) || value == KVAL(K_PDOT)) &&
+ (kbd_flags & CTRL_KEYS) && (kbd_flags & (ALT_KEYS | ALTGR_KEYS))) {
+ ctrl_alt_del();
return;
}
- if (vc_kbd_flag(kbd,VC_APPLIC) &&
- !kbd_flag(KG_LSHIFT) && /* shift forces cursor */
- !kbd_flag(KG_RSHIFT)) {
- applkey(appl_table[sc]);
+ if ((kbd_flags & ALT_KEYS) && value <= 9) { /* Alt-numpad */
+ npadch = (npadch * 10 + value) % 1000;
return;
}
- if (vc_kbd_flag(kbd,VC_NUMLOCK)) {
- put_queue(num_table[sc]);
- } else
- cur(sc);
-}
-
-static void cur(int sc)
-{
- char buf[] = { 0x1b, '[', 0, 0, 0 }; /* must not be static */
-
- buf[2]=cur_table[sc];
- if (buf[2] < '9')
- buf[3]='~';
- else
- if ((buf[2] >= 'A' && buf[2] <= 'D') ?
- vc_kbd_flag(kbd,VC_CKMODE) :
- vc_kbd_flag(kbd,VC_APPLIC))
- buf[1]='O';
- puts_queue(buf);
-}
-
-static void func(int sc)
-{
- if (sc < 0x3b)
+ /* kludge... shift forces cursor/number keys */
+ if (vc_kbd_flag(kbd,VC_APPLIC) && shift_state != 1) {
+ applkey(app_map[value], 1);
return;
- sc-=0x3b;
- if (sc > 9) {
- sc-=18;
- if (sc < 10 || sc > 11)
- return;
}
- if (kbd_flag(KG_ALT))
- want_console = sc;
- else
- if (kbd_flag(KG_LSHIFT) || kbd_flag(KG_RSHIFT)) /* DEC F11 - F20 */
- puts_queue(func_table[1][sc]);
- else /* DEC F1 - F10 */
- puts_queue(func_table[0][sc]);
-}
-static void slash(int sc)
-{
- if (!kbd_dead(KGD_E0))
- do_self(sc);
- else if (vc_kbd_flag(kbd,VC_APPLIC))
- applkey('Q');
- else
- put_queue('/');
-}
+ if (!vc_kbd_flag(kbd,VC_NUMLOCK))
+ switch (value) {
+ case KVAL(K_PCOMMA):
+ case KVAL(K_PDOT):
+ do_fn(KVAL(K_REMOVE), 0);
+ return;
+ case KVAL(K_P0):
+ do_fn(KVAL(K_INSERT), 0);
+ return;
+ case KVAL(K_P1):
+ do_fn(KVAL(K_SELECT), 0);
+ return;
+ case KVAL(K_P2):
+ do_cur(KVAL(K_DOWN), 0);
+ return;
+ case KVAL(K_P3):
+ do_fn(KVAL(K_PGDN), 0);
+ return;
+ case KVAL(K_P4):
+ do_cur(KVAL(K_LEFT), 0);
+ return;
+ case KVAL(K_P6):
+ do_cur(KVAL(K_RIGHT), 0);
+ return;
+ case KVAL(K_P7):
+ do_fn(KVAL(K_FIND), 0);
+ return;
+ case KVAL(K_P8):
+ do_cur(KVAL(K_UP), 0);
+ return;
+ case KVAL(K_P9):
+ do_fn(KVAL(K_PGUP), 0);
+ return;
+ case KVAL(K_P5):
+ applkey('G', vc_kbd_flag(kbd,VC_APPLIC));
+ return;
+ }
-static void star(int sc)
-{
- if (kbd_dead(KGD_E0))
- /* Print screen key sends E0 2A E0 37 and puts
- the VT100-ESC sequence ESC [ i into the queue, ChN */
- puts_queue("\033\133\151");
- else if (vc_kbd_flag(kbd,VC_APPLIC))
- applkey('R');
- else
- do_self(sc);
+ put_queue(pad_chars[value]);
+ if (value == KVAL(K_PENTER) && vc_kbd_flag(kbd,VC_CRLF))
+ put_queue(10);
}
-static void enter(int sc)
+static void do_cur(unsigned char value, char up_flag)
{
- if (kbd_dead(KGD_E0) && vc_kbd_flag(kbd,VC_APPLIC))
- applkey('M');
- else {
- put_queue(13);
- if (vc_kbd_flag(kbd,VC_CRLF))
- put_queue(10);
- }
-}
+ static char *cur_chars = "BDCA";
+ if (up_flag)
+ return;
-static void minus(int sc)
-{
- if (vc_kbd_flag(kbd,VC_APPLIC))
- applkey('S');
- else
- do_self(sc);
+ applkey(cur_chars[value], vc_kbd_flag(kbd,VC_CKMODE));
}
-static void plus(int sc)
+static void do_shift(unsigned char value, char up_flag)
{
- if (vc_kbd_flag(kbd,VC_APPLIC))
- applkey('l');
+ shift_state = 0;
+ if (up_flag)
+ clr_kbd_flag(value);
else
- do_self(sc);
+ set_kbd_flag(value);
+ if (kbd_flags & SHIFT_KEYS)
+ shift_state = 1;
+ if (kbd_flags & ALTGR_KEYS)
+ shift_state = 2;
+ /* cludge */
+ if (up_flag && value == KG_LALT && npadch != 0) {
+ put_queue(npadch);
+ npadch=0;
+ }
}
-static void none(int sc)
-{
-}
+#define C(x) ((KT_CONS<<8)|x)
+
+u_word key_map[NR_KEYMAPS][NR_KEYS] = {
+ { /* unshifted keys */
+ K_HOLE, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', K_ENTER, K_LCTRL, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', K_LSHIFT, '\\', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '/', K_RSHIFT, K_PSTAR,
+ K_ALT, ' ', K_CAPS, K_F1, K_F2, K_F3, K_F4, K_F5,
+ K_F6, K_F7, K_F8, K_F9, K_F10, K_NUM, K_HOLD, K_P7,
+ K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1,
+ K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '<', K_F11,
+ K_F12, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ /* unshifted e0 keys */
+ K_PENTER, K_RCTRL, K_PSLASH, 28, K_ALTGR, K_BREAK, K_FIND, K_UP,
+ K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE },
+ { /* shifted keys */
+ K_HOLE, 27, '!', '@', '#', '$', '%', '^',
+ '&', '*', '(', ')', '_', '+', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '{', '}', K_ENTER, K_LCTRL, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+ '"', '~', K_LSHIFT, '|', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', '<', '>', '?', K_RSHIFT, K_PSTAR,
+ K_ALT, 32, K_CAPS, K_F11, K_F12, K_F13, K_F14, K_F15,
+ K_F16, K_F17, K_F18, K_F19, K_F20, K_NUM, K_SH_MEM, K_P7,
+ K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1,
+ K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '>', K_F11,
+ K_F12, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ /* shifted e0 keys */
+ K_PENTER, K_RCTRL, K_PSLASH, K_HOLE, K_ALTGR, K_BREAK, K_FIND, K_UP,
+ K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE },
+ { /* alted keys */
+ K_HOLE, K_HOLE, K_HOLE, '@', K_HOLE, '$', K_HOLE, K_HOLE,
+ '{', '[', ']', '}', '\\', K_HOLE, K_HOLE, K_HOLE,
+ K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ K_HOLE, K_HOLE, K_HOLE, '~', K_ENTER, K_LCTRL, K_HOLE, K_HOLE,
+ K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ K_HOLE, K_HOLE, K_LSHIFT, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_RSHIFT, K_HOLE,
+ K_ALT, K_HOLE, K_CAPS, C(12), C(13), C(14), C(15), C(16),
+ C(17), C(18), C(19), C(20), C(21), K_NUM, K_SH_REGS, K_P7,
+ K_P8, K_P9, K_PMINUS, K_P4, K_P5, K_P6, K_PPLUS, K_P1,
+ K_P2, K_P3, K_P0, K_PDOT, K_CONS, K_HOLE, '|', C(22),
+ C(23), K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE, K_HOLE,
+ /* alted e0 keys */
+ K_PENTER, K_RCTRL, K_PSLASH, K_HOLE, K_ALTGR, K_BREAK, K_FIND, K_UP,
+ K_PGUP, K_LEFT, K_RIGHT, K_SELECT, K_DOWN, K_PGDN, K_INSERT, K_REMOVE }};
/*
* send_data sends a character to the keyboard and waits
@@ -1421,73 +733,6 @@ void hard_reset_now(void)
}
}
-static fptr key_table[] = {
- none,do_self,do_self,do_self, /* 00-03 s0 esc 1 2 */
- do_self,do_self,do_self,do_self, /* 04-07 3 4 5 6 */
- do_self,do_self,do_self,do_self, /* 08-0B 7 8 9 0 */
- do_self,do_self,do_self,do_self, /* 0C-0F + ' bs tab */
- do_self,do_self,do_self,do_self, /* 10-13 q w e r */
- do_self,do_self,do_self,do_self, /* 14-17 t y u i */
- do_self,do_self,do_self,do_self, /* 18-1B o p } ^ */
- enter,ctrl,do_self,do_self, /* 1C-1F enter ctrl a s */
- do_self,do_self,do_self,do_self, /* 20-23 d f g h */
- do_self,do_self,do_self,do_self, /* 24-27 j k l | */
- do_self,do_self,lshift,do_self, /* 28-2B { para lshift , */
- do_self,do_self,do_self,do_self, /* 2C-2F z x c v */
- do_self,do_self,do_self,do_self, /* 30-33 b n m , */
- do_self,slash,rshift,star, /* 34-37 . - rshift * */
- alt,do_self,caps,func, /* 38-3B alt sp caps f1 */
- func,func,func,func, /* 3C-3F f2 f3 f4 f5 */
- func,func,func,func, /* 40-43 f6 f7 f8 f9 */
- func,num,scroll,cursor, /* 44-47 f10 num scr home */
- cursor,cursor,minus,cursor, /* 48-4B up pgup - left */
- cursor,cursor,plus,cursor, /* 4C-4F n5 right + end */
- cursor,cursor,cursor,cursor, /* 50-53 dn pgdn ins del */
- sysreq,none,do_self,func, /* 54-57 sysreq ? < f11 */
- func,none,none,none, /* 58-5B f12 ? ? ? */
- none,none,none,none, /* 5C-5F ? ? ? ? */
- none,none,none,none, /* 60-63 ? ? ? ? */
- none,none,none,none, /* 64-67 ? ? ? ? */
- none,none,none,none, /* 68-6B ? ? ? ? */
- none,none,none,none, /* 6C-6F ? ? ? ? */
- none,none,none,none, /* 70-73 ? ? ? ? */
- none,none,none,none, /* 74-77 ? ? ? ? */
- none,none,none,none, /* 78-7B ? ? ? ? */
- none,none,none,none, /* 7C-7F ? ? ? ? */
- none,none,none,none, /* 80-83 ? br br br */
- none,none,none,none, /* 84-87 br br br br */
- none,none,none,none, /* 88-8B br br br br */
- none,none,none,none, /* 8C-8F br br br br */
- none,none,none,none, /* 90-93 br br br br */
- none,none,none,none, /* 94-97 br br br br */
- none,none,none,none, /* 98-9B br br br br */
- none,unctrl,none,none, /* 9C-9F br unctrl br br */
- none,none,none,none, /* A0-A3 br br br br */
- none,none,none,none, /* A4-A7 br br br br */
- none,none,unlshift,none, /* A8-AB br br unlshift br */
- none,none,none,none, /* AC-AF br br br br */
- none,none,none,none, /* B0-B3 br br br br */
- none,none,unrshift,none, /* B4-B7 br br unrshift br */
- unalt,none,uncaps,none, /* B8-BB unalt br uncaps br */
- none,none,none,none, /* BC-BF br br br br */
- none,none,none,none, /* C0-C3 br br br br */
- none,none,none,none, /* C4-C7 br br br br */
- none,none,none,none, /* C8-CB br br br br */
- none,none,none,none, /* CC-CF br br br br */
- none,none,none,none, /* D0-D3 br br br br */
- none,none,none,none, /* D4-D7 br br br br */
- none,none,none,none, /* D8-DB br ? ? ? */
- none,none,none,none, /* DC-DF ? ? ? ? */
- none,none,none,none, /* E0-E3 e0 e1 ? ? */
- none,none,none,none, /* E4-E7 ? ? ? ? */
- none,none,none,none, /* E8-EB ? ? ? ? */
- none,none,none,none, /* EC-EF ? ? ? ? */
- none,none,none,none, /* F0-F3 ? ? ? ? */
- none,none,none,none, /* F4-F7 ? ? ? ? */
- none,none,none,none, /* F8-FB ? ? ? ? */
- none,none,none,none /* FC-FF ? ? ? ? */
-};
-
unsigned long kbd_init(unsigned long kmem_start)
{
int i;
@@ -1497,7 +742,6 @@ unsigned long kbd_init(unsigned long kmem_start)
for (i = 0 ; i < NR_CONSOLES ; i++,kbd++) {
kbd->flags = KBD_DEFFLAGS;
kbd->default_flags = KBD_DEFFLAGS;
- kbd->kbd_flags = KBDFLAGS;
}
bh_base[KEYBOARD_BH].routine = kbd_bh;
request_irq(KEYBOARD_IRQ,keyboard_interrupt);
diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c
index 3212305..7bcfc7a 100644
--- a/kernel/chr_drv/mem.c
+++ b/kernel/chr_drv/mem.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/mouse.h>
-#include <linux/soundcard.h>
#include <linux/user.h>
#include <linux/a.out.h>
@@ -19,6 +18,8 @@
#include <asm/segment.h>
#include <asm/io.h>
+extern long soundcard_init(long mem_start);
+
static int read_ram(struct inode * inode, struct file * file,char * buf, int count)
{
return -EIO;
diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c
index 9316d5e..9dca885 100644
--- a/kernel/chr_drv/pty.c
+++ b/kernel/chr_drv/pty.c
@@ -23,11 +23,22 @@
static void pty_close(struct tty_struct * tty, struct file * filp)
{
- if (!tty || (tty->count > 1))
+ if (!tty)
return;
+ if (IS_A_PTY_MASTER(tty->line)) {
+ if (tty->count > 1)
+ return;
+ } else {
+ if (tty->count > 2)
+ return;
+ }
+ wake_up_interruptible(&tty->secondary.proc_list);
wake_up_interruptible(&tty->read_q.proc_list);
+ wake_up_interruptible(&tty->write_q.proc_list);
if (!tty->link)
return;
+ wake_up_interruptible(&tty->link->secondary.proc_list);
+ wake_up_interruptible(&tty->link->read_q.proc_list);
wake_up_interruptible(&tty->link->write_q.proc_list);
if (IS_A_PTY_MASTER(tty->line)) {
tty_hangup(tty->link);
@@ -75,6 +86,10 @@ int pty_open(struct tty_struct *tty, struct file * filp)
{
if (!tty || !tty->link)
return -ENODEV;
+ if (IS_A_PTY_MASTER(tty->line))
+ clear_bit(TTY_SLAVE_OPENED, &tty->flags);
+ else
+ set_bit(TTY_SLAVE_OPENED, &tty->link->flags);
tty->write = tty->link->write = pty_write;
tty->close = tty->link->close = pty_close;
wake_up_interruptible(&tty->read_q.proc_list);
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index cff68c5..6034b00 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -67,6 +67,11 @@ static struct async_struct *IRQ_ports[16];
static int IRQ_active;
static unsigned long IRQ_timer[16];
static int IRQ_timeout[16];
+static volatile int rs_irq_triggered;
+static volatile int rs_triggered;
+static int rs_wild_int_mask;
+
+static void autoconfig(struct async_struct * info);
/*
* This assumes you have a 1.8432 MHz clock for your UART.
@@ -75,62 +80,74 @@ static int IRQ_timeout[16];
* clock, since the 16550A is capable of handling a top speed of 1.5
* megabits/second; but this requires the faster clock.
*/
-#define BASE_BAUD ( 1843200 / 16 )
+#define BASE_BAUD ( 1843200 / 16 )
+#ifdef CONFIG_AUTO_IRQ
+#define AUTO_IRQ_FLAG ASYNC_AUTO_IRQ
+#else
+#define AUTO_IRQ_FLAG 0
+#endif
+
+/* Standard COM flags (except for COM4, because of the 8514 problem) */
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | AUTO_IRQ_FLAG)
+#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
+
+#ifdef CONFIG_AST_FOURPORT
+#define FOURPORT_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_FOURPORT | AUTO_IRQ_FLAG)
+#else
+#define FOURPORT_FLAGS (ASYNC_FOURPORT | AUTO_IRQ_FLAG)
+#endif
+
+#ifdef CONFIG_ACCENT_ASYNC
+#define ACCENT_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
+#else
+#define ACCENT_FLAGS AUTO_IRQ_FLAG
+#endif
+
+#ifdef CONFIG_BOCA
+#define BOCA_FLAGS (ASYNC_BOOT_AUTOCONF | AUTO_IRQ_FLAG)
+#else
+#define BOCA_FLAGS AUTO_IRQ_FLAG
+#endif
+
struct async_struct rs_table[] = {
/* UART CLK PORT IRQ FLAGS */
- { BASE_BAUD, 0x3F8, 4, ASYNC_SKIP_TEST, }, /* ttyS0 */
- { BASE_BAUD, 0x2F8, 3, ASYNC_SKIP_TEST, }, /* ttyS1 */
- { BASE_BAUD, 0x3E8, 4, ASYNC_SKIP_TEST, }, /* ttyS2 */
- { BASE_BAUD, 0x2E8, 3, ASYNC_SKIP_TEST, }, /* ttyS3 */
-#ifdef CONFIG_AST_FOURPORT
- { BASE_BAUD, 0x1A0, 9, ASYNC_FOURPORT }, /* ttyS4 */
- { BASE_BAUD, 0x1A8, 9, ASYNC_FOURPORT }, /* ttyS5 */
- { BASE_BAUD, 0x1B0, 9, ASYNC_FOURPORT }, /* ttyS6 */
- { BASE_BAUD, 0x1B8, 9, ASYNC_FOURPORT }, /* ttyS7 */
-
- { BASE_BAUD, 0x2A0, 5, ASYNC_FOURPORT }, /* ttyS8 */
- { BASE_BAUD, 0x2A8, 5, ASYNC_FOURPORT }, /* ttyS9 */
- { BASE_BAUD, 0x2B0, 5, ASYNC_FOURPORT }, /* ttyS10 */
- { BASE_BAUD, 0x2B8, 5, ASYNC_FOURPORT }, /* ttyS11 */
-#else /* CONFIG_AST_FOURPORT */
- { BASE_BAUD, 0x000, 0 }, /* ttyS4 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS5 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS6 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS7 */
-
- { BASE_BAUD, 0x000, 0 }, /* ttyS8 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS9 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS10 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS11 */
-#endif /* CONFIG_AST_FOURPORT */
+ { BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS, }, /* ttyS0 */
+ { BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS, }, /* ttyS1 */
+ { BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS, }, /* ttyS2 */
+ { BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS, }, /* ttyS3 */
+
+ { BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */
+ { BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */
+ { BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */
+ { BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */
+
+ { BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */
+ { BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */
+ { BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */
+ { BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */
-#ifdef CONFIG_ACCENT_ASYNC
- { BASE_BAUD, 0x330, 4, 0 }, /* ttyS12 */
- { BASE_BAUD, 0x338, 4, 0 }, /* ttyS13 */
-#else /* CONFIG_ACCENT_ASYNC */
- { BASE_BAUD, 0x000, 0 }, /* ttyS12 */
- { BASE_BAUD, 0x000, 0 }, /* ttyS13 */
-#endif /* CONFIG_ACCENT_ASYNC */
+ { BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */
+ { BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */
{ BASE_BAUD, 0x000, 0 }, /* ttyS14 (spare; user configurable) */
{ BASE_BAUD, 0x000, 0 }, /* ttyS15 (spare; user configurable) */
- { BASE_BAUD, 0x100, 12, 0 }, /* ttyS16 */
- { BASE_BAUD, 0x108, 12, 0 }, /* ttyS17 */
- { BASE_BAUD, 0x110, 12, 0 }, /* ttyS18 */
- { BASE_BAUD, 0x118, 12, 0 }, /* ttyS19 */
- { BASE_BAUD, 0x120, 12, 0 }, /* ttyS20 */
- { BASE_BAUD, 0x128, 12, 0 }, /* ttyS21 */
- { BASE_BAUD, 0x130, 12, 0 }, /* ttyS22 */
- { BASE_BAUD, 0x138, 12, 0 }, /* ttyS23 */
- { BASE_BAUD, 0x140, 12, 0 }, /* ttyS24 */
- { BASE_BAUD, 0x148, 12, 0 }, /* ttyS25 */
- { BASE_BAUD, 0x150, 12, 0 }, /* ttyS26 */
- { BASE_BAUD, 0x158, 12, 0 }, /* ttyS27 */
- { BASE_BAUD, 0x160, 12, 0 }, /* ttyS28 */
- { BASE_BAUD, 0x168, 12, 0 }, /* ttyS29 */
- { BASE_BAUD, 0x170, 12, 0 }, /* ttyS30 */
- { BASE_BAUD, 0x178, 12, 0 }, /* ttyS31 */
+ { BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */
+ { BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */
+ { BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */
+ { BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */
+ { BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */
+ { BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */
+ { BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */
+ { BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */
+ { BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */
+ { BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */
+ { BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */
+ { BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */
+ { BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */
+ { BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */
+ { BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */
+ { BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
@@ -187,6 +204,17 @@ static inline void serial_outp(struct async_struct *info, int offset,
*/
/*
+ * This is the serial driver's interrupt routine while we are probing
+ * for submarines.
+ */
+static void rs_probe(int irq)
+{
+ rs_irq_triggered = irq;
+ rs_triggered |= 1 << irq;
+ return;
+}
+
+/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
@@ -214,13 +242,13 @@ static inline void receive_chars(struct async_struct *info,
head = queue->head;
tail = queue->tail;
do {
- ch = serial_in(info, UART_RX);
+ ch = serial_inp(info, UART_RX);
/*
* There must be at least 2 characters
* free in the queue; otherwise we punt.
*/
if (VLEFT < 2)
- continue;
+ break;
if (*status & info->read_status_mask) {
set_bit(head, &info->tty->readq_flags);
if (*status & (UART_LSR_BI)) {
@@ -327,13 +355,17 @@ static void rs_interrupt(int irq)
struct async_struct * info;
int done, done_work, pass_number;
+ rs_irq_triggered = irq;
+ rs_triggered |= 1 << irq;
+
info = IRQ_ports[irq];
done = 1;
done_work = 0;
pass_number = 0;
while (info) {
- if (!pass_number ||
- !(serial_inp(info, UART_IIR) & UART_IIR_NO_INT)) {
+ if (info->tty &&
+ (!pass_number ||
+ !(serial_inp(info, UART_IIR) & UART_IIR_NO_INT))) {
done = 0;
status = serial_inp(info, UART_LSR);
if (status & UART_LSR_DR) {
@@ -368,20 +400,6 @@ static void rs_interrupt(int irq)
}
/*
- * This is the serial driver's interrupt routine while we are probing
- * for submarines.
- */
-static volatile int rs_irq_triggered;
-static volatile int rs_triggered;
-
-static void rs_probe(int irq)
-{
- rs_irq_triggered = irq;
- rs_triggered |= 1 << irq;
- return;
-}
-
-/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
@@ -486,6 +504,44 @@ static void rs_timer(void)
*/
/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection. dontgrab is a mask of irq's _not_ to grab. Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int grab_all_interrupts(int dontgrab)
+{
+ int irq_lines = 0;
+ int i, mask;
+ struct sigaction sa;
+
+ sa.sa_handler = rs_probe;
+ sa.sa_flags = (SA_INTERRUPT);
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+ if (!(mask & dontgrab) && !irqaction(i, &sa)) {
+ irq_lines |= mask;
+ }
+ }
+ return irq_lines;
+}
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void free_all_interrupts(int irq_lines)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (irq_lines & (1 << i))
+ free_irq(i);
+ }
+}
+
+/*
* This routine figures out the correct timeout for a particular IRQ.
* It uses the smallest timeout of all of the serial ports in a
* particular interrupt chain.
@@ -510,6 +566,27 @@ static void figure_IRQ_timeout(int irq)
IRQ_timeout[irq] = timeout;
}
+static inline void unlink_port(struct async_struct *info)
+{
+ if (info->next_port)
+ info->next_port->prev_port = info->prev_port;
+ if (info->prev_port)
+ info->prev_port->next_port = info->next_port;
+ else
+ IRQ_ports[info->irq] = info->next_port;
+ figure_IRQ_timeout(info->irq);
+}
+
+static inline void link_port(struct async_struct *info)
+{
+ info->prev_port = 0;
+ info->next_port = IRQ_ports[info->irq];
+ if (info->next_port)
+ info->next_port->prev_port = info;
+ IRQ_ports[info->irq] = info;
+ figure_IRQ_timeout(info->irq);
+}
+
static void startup(struct async_struct * info)
{
unsigned short ICP;
@@ -689,6 +766,8 @@ static void restart_port(struct async_struct *info)
struct tty_queue * queue;
int head, tail, count;
+ if (!info)
+ return;
if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
@@ -739,7 +818,7 @@ static void rs_throttle(struct tty_struct * tty, int status)
struct async_struct *info;
unsigned char mcr;
-#ifdef notdef
+#if 0
printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
status, LEFT(&tty->read_q), LEFT(&tty->secondary));
#endif
@@ -793,6 +872,7 @@ static int get_serial_info(struct async_struct * info,
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
+ tmp.custom_divisor = info->custom_divisor;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
@@ -801,18 +881,27 @@ static int set_serial_info(struct async_struct * info,
struct serial_struct * new_info)
{
struct serial_struct new;
- unsigned int irq,check_irq;
+ struct async_struct old_info;
+ unsigned int i,change_irq,change_port;
int retval;
struct sigaction sa;
- struct async_struct old_info;
if (!new_info)
return -EFAULT;
memcpy_fromfs(&new,new_info,sizeof(new));
-
- check_irq = 0;
old_info = *info;
+
+ change_irq = new.irq != info->irq;
+ change_port = new.port != info->port;
+
if (!suser()) {
+ if (change_irq || change_port ||
+ (new.baud_base != info->baud_base) ||
+ (new.type != info->type) ||
+ (new.close_delay != info->close_delay) ||
+ ((new.flags & ~ASYNC_FLAGS) !=
+ (info->flags & ~ASYNC_FLAGS)))
+ return -EPERM;
info->flags = ((info->flags & ~ASYNC_SPD_MASK) |
(new.flags & ASYNC_SPD_MASK));
info->custom_divisor = new.custom_divisor;
@@ -820,78 +909,75 @@ static int set_serial_info(struct async_struct * info,
goto check_and_exit;
}
+ if (new.irq == 2)
+ new.irq = 9;
+
if ((new.irq > 15) || (new.port > 0xffff) ||
(new.type < PORT_UNKNOWN) || (new.type > PORT_MAX)) {
return -EINVAL;
}
-
- info->baud_base = new.baud_base;
- info->flags = ((info->flags & ~ASYNC_FLAGS) |
- (new.flags & ASYNC_FLAGS));
- info->custom_divisor = new.custom_divisor;
- info->type = new.type;
- info->close_delay = new.close_delay;
-
- if (new.irq == 2)
- new.irq = 9;
- irq = info->irq;
- if (irq == 2)
- irq = 9;
-
+
+ /* Make sure address is not already in use */
+ for (i = 0 ; i < NR_PORTS; i++)
+ if ((info != &rs_table[i]) &&
+ (rs_table[i].port == new.port) && rs_table[i].type)
+ return -EADDRINUSE;
+
/*
* If necessary, first we try to grab the new IRQ for serial
* interrupts. (We have to do this early, since we may get an
* error trying to do this.)
*/
- if (new.port && info->type &&
- ((irq != new.irq) || !(info->flags & ASYNC_INITIALIZED))) {
- if (new.irq && !IRQ_ports[new.irq]) {
+ if (new.port && new.type && new.irq &&
+ (change_irq || !(info->flags & ASYNC_INITIALIZED))) {
+ if (!IRQ_ports[new.irq]) {
sa.sa_handler = rs_interrupt;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
sa.sa_restorer = NULL;
retval = irqaction(new.irq,&sa);
- if (retval) {
- *info = old_info;
+ if (retval)
return retval;
- }
}
}
- if ((new.irq != irq) ||
- (new.port != info->port)) {
+ if ((change_port || change_irq) && (info->count > 1))
+ return -EBUSY;
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->baud_base = new.baud_base;
+ info->flags = ((info->flags & ~ASYNC_FLAGS) |
+ (new.flags & ASYNC_FLAGS));
+ info->custom_divisor = new.custom_divisor;
+ info->type = new.type;
+ info->close_delay = new.close_delay;
+
+ if (change_port || change_irq) {
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
if (info->flags & ASYNC_INITIALIZED) {
shutdown(info);
- if (info->next_port)
- info->next_port->prev_port = info->prev_port;
- if (info->prev_port)
- info->prev_port->next_port = info->next_port;
- else
- IRQ_ports[irq] = info->next_port;
- figure_IRQ_timeout(irq);
- check_irq = irq; /* Check later if we need to */
- /* free the IRQ */
+ unlink_port(info);
+ if (change_irq && info->irq && !IRQ_ports[info->irq])
+ free_irq(info->irq);
}
info->irq = new.irq;
info->port = new.port;
}
check_and_exit:
- if (new.port && info->type &&
+ if (info->port && info->type &&
!(info->flags & ASYNC_INITIALIZED)) {
/*
* Link the port into the new interrupt chain.
*/
- info->prev_port = 0;
- info->next_port = IRQ_ports[info->irq];
- if (info->next_port)
- info->next_port->prev_port = info;
- IRQ_ports[info->irq] = info;
- figure_IRQ_timeout(info->irq);
+ link_port(info);
startup(info);
change_speed(info->line);
} else if (((old_info.flags & ASYNC_SPD_MASK) !=
@@ -899,9 +985,6 @@ check_and_exit:
(old_info.custom_divisor != info->custom_divisor))
change_speed(info->line);
- if (check_irq && !IRQ_ports[check_irq])
- free_irq(check_irq);
-
return 0;
}
@@ -955,6 +1038,46 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd,
return 0;
}
+static int do_autoconfig(struct async_struct * info)
+{
+ struct sigaction sa;
+ int retval;
+
+ if (!suser())
+ return -EPERM;
+
+ if (info->count > 1)
+ return -EBUSY;
+
+ if (info->flags & ASYNC_INITIALIZED) {
+ shutdown(info);
+ unlink_port(info);
+ if (info->irq)
+ free_irq(info->irq);
+ }
+
+ cli();
+ autoconfig(info);
+ sti();
+
+ if (info->port && info->type) {
+ if (info->irq && !IRQ_ports[info->irq]) {
+ sa.sa_handler = rs_interrupt;
+ sa.sa_flags = (SA_INTERRUPT);
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+ retval = irqaction(info->irq,&sa);
+ if (retval)
+ return retval;
+ }
+ link_port(info);
+ startup(info);
+ change_speed(info->line);
+ }
+ return 0;
+}
+
+
/*
* This routine sends a break character out the serial port.
*/
@@ -969,6 +1092,50 @@ static void send_break( struct async_struct * info, int duration)
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
}
+/*
+ * This routine returns a bitfield of "wild interrupts". Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int check_wild_interrupts(int doprint)
+{
+ int i, mask;
+ int wild_interrupts = 0;
+ int irq_lines;
+ unsigned long timeout;
+ unsigned long flags;
+
+ /* Turn on interrupts (they may be off) */
+ save_flags(flags); sti();
+
+ irq_lines = grab_all_interrupts(0);
+
+ /*
+ * Delay for 0.1 seconds -- we use a busy loop since this may
+ * occur during the bootup sequence
+ */
+ timeout = jiffies+10;
+ while (timeout >= jiffies)
+ ;
+
+ rs_triggered = 0; /* Reset after letting things settle */
+
+ timeout = jiffies+10;
+ while (timeout >= jiffies)
+ ;
+
+ for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+ if ((rs_triggered & (1 << i)) &&
+ (irq_lines & (1 << i))) {
+ wild_interrupts |= mask;
+ if (doprint)
+ printk("Wild interrupt? (IRQ %d)\n", i);
+ }
+ }
+ free_all_interrupts(irq_lines);
+ restore_flags(flags);
+ return wild_interrupts;
+}
+
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
@@ -1022,10 +1189,28 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
case TIOCSSERIAL:
return set_serial_info(info,
(struct serial_struct *) arg);
-
- default:
- return -EINVAL;
- }
+ case TIOCSERCONFIG:
+ return do_autoconfig(info);
+
+ case TIOCSERGWILD:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(int));
+ if (error)
+ return error;
+ put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
+ return 0;
+
+ case TIOCSERSWILD:
+ if (!suser())
+ return -EPERM;
+ rs_wild_int_mask = get_fs_long((unsigned long *) arg);
+ if (rs_wild_int_mask < 0)
+ rs_wild_int_mask = check_wild_interrupts(0);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
return 0;
}
@@ -1070,7 +1255,7 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
static void rs_close(struct tty_struct *tty, struct file * filp)
{
struct async_struct * info;
- int irq, line;
+ int line;
line = DEV_TO_SL(tty->line);
if ((line < 0) || (line >= NR_PORTS))
@@ -1086,32 +1271,28 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
clear_bit(line, rs_event);
info->event = 0;
info->count = 0;
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
if (info->blocked_open) {
shutdown(info);
if (info->close_delay) {
+ tty->count++; /* avoid race condition */
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + info->close_delay;
schedule();
+ tty->count--;
}
startup(info);
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ if (tty->termios->c_cflag & CLOCAL)
+ wake_up_interruptible(&info->open_wait);
return;
}
if (info->flags & ASYNC_INITIALIZED) {
shutdown(info);
- irq = info->irq;
- if (irq == 2)
- irq = 9;
- if (info->next_port)
- info->next_port->prev_port = info->prev_port;
- if (info->prev_port)
- info->prev_port->next_port = info->next_port;
- else
- IRQ_ports[irq] = info->next_port;
- if (irq && !IRQ_ports[irq])
- free_irq(irq);
- figure_IRQ_timeout(irq);
+ unlink_port(info);
+ if (info->irq && !IRQ_ports[info->irq])
+ free_irq(info->irq);
}
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
}
@@ -1164,8 +1345,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
info->count--;
info->blocked_open++;
while (1) {
- serial_out(info, UART_MCR,
- serial_inp(info, UART_MCR) | UART_MCR_DTR);
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+ serial_out(info, UART_MCR,
+ serial_inp(info, UART_MCR) | UART_MCR_DTR);
current->state = TASK_INTERRUPTIBLE;
if (tty_hung_up_p(filp)) {
if (info->flags & ASYNC_HUP_NOTIFY)
@@ -1211,7 +1393,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
int rs_open(struct tty_struct *tty, struct file * filp)
{
struct async_struct *info;
- int irq, retval, line;
+ int retval, line;
struct sigaction sa;
line = DEV_TO_SL(tty->line);
@@ -1235,31 +1417,23 @@ int rs_open(struct tty_struct *tty, struct file * filp)
set_bit(TTY_IO_ERROR, &tty->flags);
return 0;
}
- irq = info->irq;
- if (irq == 2)
- irq = 9;
- if (irq && !IRQ_ports[irq]) {
+ if (info->irq && !IRQ_ports[info->irq]) {
sa.sa_handler = rs_interrupt;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
sa.sa_restorer = NULL;
- retval = irqaction(irq,&sa);
+ retval = irqaction(info->irq,&sa);
if (retval)
return retval;
}
/*
* Link in port to IRQ chain
*/
- info->prev_port = 0;
- info->next_port = IRQ_ports[irq];
- if (info->next_port)
- info->next_port->prev_port = info;
- IRQ_ports[irq] = info;
- figure_IRQ_timeout(irq);
-
+ link_port(info);
startup(info);
change_speed(info->line);
- if (!irq) {
+ if (!info->irq) {
+ IRQ_active |= info->line;
cli();
figure_RS_timer();
sti();
@@ -1288,7 +1462,7 @@ int rs_open(struct tty_struct *tty, struct file * filp)
*/
static void show_serial_version(void)
{
- printk("Serial driver version 3.94 with");
+ printk("Serial driver version 3.95 with");
#ifdef CONFIG_AST_FOURPORT
printk(" AST_FOURPORT");
#define SERIAL_OPT
@@ -1310,8 +1484,8 @@ static void show_serial_version(void)
}
/*
- * This routine is called by init(); it attempts to determine which
- * interrupt a serial port is configured to use. It is not
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use. It is not
* fool-proof, but it works a large part of the time.
*/
static int get_auto_irq(struct async_struct *info)
@@ -1362,25 +1536,58 @@ static int get_auto_irq(struct async_struct *info)
}
/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int do_auto_irq(struct async_struct * info)
+{
+ unsigned port = info->port;
+ int irq_lines = 0;
+ int irq_try_1 = 0, irq_try_2 = 0;
+ int retries;
+ unsigned long flags;
+
+ if (!port)
+ return 0;
+
+ /* Turn on interrupts (they may be off) */
+ save_flags(flags); sti();
+
+ irq_lines = grab_all_interrupts(rs_wild_int_mask);
+
+ for (retries = 0; retries < 5; retries++) {
+ if (!irq_try_1)
+ irq_try_1 = get_auto_irq(info);
+ if (!irq_try_2)
+ irq_try_2 = get_auto_irq(info);
+ if (irq_try_1 && irq_try_2) {
+ if (irq_try_1 == irq_try_2)
+ break;
+ irq_try_1 = irq_try_2 = 0;
+ }
+ }
+ restore_flags(flags);
+ free_all_interrupts(irq_lines);
+ return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+}
+
+/*
* This routine is called by rs_init() to initialize a specific serial
- * port. If CONFIG_AUTO_IRQ is defined, it will attempt to figure out
- * which IRQ the serial port is on by calling get_auto_irq(). (See
- * above).
- *
- * It also determines what type of UART ship this serial port is
+ * port. It determines what type of UART ship this serial port is
* using: 8250, 16450, 16550, 16550A. The important question is
* whether or not this UART is a 16550A or not, since this will
* determine whether or not we can use its FIFO features or not.
*/
-static void init(struct async_struct * info)
+static void autoconfig(struct async_struct * info)
{
unsigned char status1, status2, scratch, scratch2;
unsigned port = info->port;
- int retries;
+ info->type = PORT_UNKNOWN;
+
if (!port)
return;
-
+
/*
* Do a simple existence test first; if we fail this, there's
* no point trying anything else.
@@ -1409,49 +1616,17 @@ static void init(struct async_struct * info)
status1 = serial_inp(info, UART_MSR) & 0xF0;
serial_outp(info, UART_MCR, scratch);
serial_outp(info, UART_MSR, scratch2);
- if (status1 != 0x90) {
- info->type = PORT_UNKNOWN;
+ if (status1 != 0x90)
return;
- }
- }
-
+ }
+
/*
- * Here's where we do the automatic IRQ detection. We always
- * try to do this test; CONFIG_AUTO_IRQ merely determins
- * whether or not we pay attention to the results. If
- * CONFIG_AUTO_IRQ is off, then we merely print a warning
- * message if the default IRQ does not match the results made
- * by the automatic IRQ detection system.
+ * If the AUTO_IRQ flag is set, try to do the automatic IRQ
+ * detection.
*/
- scratch = scratch2 = 0;
- for (retries = 0; retries < 5; retries++) {
- if (!scratch)
- scratch = get_auto_irq(info);
- if (!scratch2)
- scratch2 = get_auto_irq(info);
- if (scratch && scratch2) {
- if (scratch == scratch2)
- break;
- scratch = scratch2 = 0;
- }
- }
- if (scratch && (scratch == scratch2)) {
-#ifdef CONFIG_AUTO_IRQ
- info->irq = scratch;
-#else
- if (info->irq != scratch)
- printk("Warning: auto IRQ detection for tty%d found IRQ %d, not %d.\n",
- info->line, scratch, info->irq);
-#endif
- } else {
-#ifdef CONFIG_AUTO_IRQ
- info->type = PORT_UNKNOWN;
- return;
-#else
- printk("Warning: auto IRQ detection for tty%d failed; using default IRQ.\n", info->line);
-#endif
- }
-
+ if (info->flags & ASYNC_AUTO_IRQ)
+ info->irq = do_auto_irq(info);
+
outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
scratch = inb(UART_IIR + port) >> 6;
info->xmit_fifo_size = 1;
@@ -1490,44 +1665,19 @@ long rs_init(long kmem_start)
{
int i;
struct async_struct * info;
- int irq_lines = 0;
- struct sigaction sa;
- unsigned long timeout;
- /*
- * We will be auto probing for irq's, so turn on interrupts now!
- */
- sti();
-
- sa.sa_handler = rs_probe;
- sa.sa_flags = (SA_INTERRUPT);
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
memset(&rs_event, 0, sizeof(rs_event));
bh_base[SERIAL_BH].routine = do_softint;
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
IRQ_active = 0;
-
- rs_triggered = 0;
+#ifdef CONFIG_AUTO_IRQ
+ rs_wild_int_mask = check_wild_interrupts(1);
+#endif
+
for (i = 0; i < 16; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
- if (!irqaction(i, &sa))
- irq_lines |= 1 << i;
- }
-
- timeout = jiffies+10;
- while (timeout >= jiffies)
- ;
- for (i = 0; i < 16; i++) {
- if ((rs_triggered & (1 << i)) &&
- (irq_lines & (1 << i))) {
- irq_lines &= ~(1 << i);
- printk("Wild interrupt? (IRQ %d)\n", i);
- free_irq(i);
- }
}
show_serial_version();
@@ -1544,7 +1694,11 @@ long rs_init(long kmem_start)
info->open_wait = 0;
info->next_port = 0;
info->prev_port = 0;
- init(info);
+ if (info->irq == 2)
+ info->irq = 9;
+ if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+ continue;
+ autoconfig(info);
if (info->type == PORT_UNKNOWN)
continue;
printk("tty%02d%s at 0x%04x (irq = %d)", info->line,
@@ -1568,15 +1722,6 @@ long rs_init(long kmem_start)
break;
}
}
- /*
- * Turn interrupts back off, since they were off when we
- * started this. See start_kernel() in init/main.c.
- */
- cli();
- for (i = 0; i < 16; i++) {
- if (irq_lines & (1 << i))
- free_irq(i);
- }
return kmem_start;
}
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index 4739573..0551af6 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -28,6 +28,10 @@
* NOTE: pay no attention to the line discpline code (yet); its
* interface is still subject to change in this version...
* -- TYT, 1/31/92
+ *
+ * Added functionality to the OPOST tty handling. No delays, but all
+ * other bits should be there.
+ * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
*/
#include <linux/types.h>
@@ -52,8 +56,8 @@
#define MAX_TTYS 256
struct tty_struct *tty_table[MAX_TTYS];
-struct termios *tty_termios[MAX_TTYS]; /* We need to keep the termios state */
- /* around, even when a tty is closed */
+struct termios *tty_termios[MAX_TTYS]; /* We need to keep the termios state */
+ /* around, even when a tty is closed */
struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
int tty_check_write[MAX_TTYS/32]; /* bitfield for the bh handler */
@@ -230,6 +234,7 @@ static struct file_operations vhung_up_tty_fops = {
void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
{
+ int i;
struct file * filp;
struct task_struct **p;
int dev;
@@ -237,8 +242,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
if (!tty)
return;
dev = 0x0400 + tty->line;
- filp = file_table + NR_FILE;
- while (filp-- > file_table) {
+ for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
if (!filp->f_count)
continue;
if (filp->f_rdev != dev)
@@ -369,8 +373,7 @@ void complete_change_console(unsigned int new_console)
{
if (vt_cons[new_console].vc_mode == KD_TEXT)
unblank_screen();
- else
- {
+ else {
timer_active &= ~(1<<BLANK_TIMER);
blank_screen();
}
@@ -510,10 +513,10 @@ void copy_to_cooked(struct tty_struct * tty)
if (I_IGNBRK(tty))
continue;
if (I_PARMRK(tty)) {
- put_tty_queue(0377, &tty->secondary);
- put_tty_queue(0, &tty->secondary);
+ put_tty_queue('\377', &tty->secondary);
+ put_tty_queue('\0', &tty->secondary);
}
- put_tty_queue(0, &tty->secondary);
+ put_tty_queue('\0', &tty->secondary);
continue;
}
/* If not a break, then a parity or frame error */
@@ -522,17 +525,17 @@ void copy_to_cooked(struct tty_struct * tty)
continue;
}
if (I_PARMRK(tty)) {
- put_tty_queue(0377, &tty->secondary);
- put_tty_queue(0, &tty->secondary);
+ put_tty_queue('\377', &tty->secondary);
+ put_tty_queue('\0', &tty->secondary);
put_tty_queue(c, &tty->secondary);
} else
- put_tty_queue(0, &tty->secondary);
+ put_tty_queue('\0', &tty->secondary);
continue;
}
if (I_STRP(tty))
c &= 0x7f;
- else if (I_PARMRK(tty) && (c == 0377))
- put_tty_queue(0377, &tty->secondary);
+ else if (I_PARMRK(tty) && (c == '\377'))
+ put_tty_queue('\377', &tty->secondary);
if (c==13) {
if (I_CRNL(tty))
c=10;
@@ -565,13 +568,13 @@ void copy_to_cooked(struct tty_struct * tty)
}
if (L_ECHO(tty)) {
if (c<32) {
- put_tty_queue(8, &tty->write_q);
+ put_tty_queue('\b', &tty->write_q);
put_tty_queue(' ', &tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
put_tty_queue(' ',&tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
DEC(tty->secondary.head);
}
@@ -585,13 +588,13 @@ void copy_to_cooked(struct tty_struct * tty)
continue;
if (L_ECHO(tty)) {
if (c<32) {
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
put_tty_queue(' ',&tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
- put_tty_queue(8,&tty->write_q);
- put_tty_queue(32,&tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
+ put_tty_queue(' ',&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
DEC(tty->secondary.head);
continue;
@@ -600,7 +603,7 @@ void copy_to_cooked(struct tty_struct * tty)
tty->lnext = 1;
if (L_ECHO(tty)) {
put_tty_queue('^',&tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
continue;
}
@@ -651,16 +654,16 @@ void copy_to_cooked(struct tty_struct * tty)
c==EOF_CHAR(tty)))
tty->secondary.data++;
if ((c==10) && (L_ECHO(tty) || (L_CANON(tty) && L_ECHONL(tty)))) {
- put_tty_queue(10,&tty->write_q);
- put_tty_queue(13,&tty->write_q);
+ put_tty_queue('\n',&tty->write_q);
+ put_tty_queue('\r',&tty->write_q);
} else if (L_ECHO(tty)) {
if (c<32 && L_ECHOCTL(tty)) {
put_tty_queue('^',&tty->write_q);
- put_tty_queue(c+64, &tty->write_q);
+ put_tty_queue(c+'A'-1, &tty->write_q);
if (EOF_CHAR(tty) != __DISABLED_CHAR &&
c==EOF_CHAR(tty) && !tty->lnext) {
- put_tty_queue(8,&tty->write_q);
- put_tty_queue(8,&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
+ put_tty_queue('\b',&tty->write_q);
}
} else
put_tty_queue(c, &tty->write_q);
@@ -724,10 +727,8 @@ static int read_chan(struct tty_struct * tty, struct file * file, char * buf, in
}
if (file->f_flags & O_NONBLOCK) {
time = current->timeout = 0;
- if (L_CANON(tty)) {
- if (!available_canon_input(tty))
+ if (L_CANON(tty) && !available_canon_input(tty))
return -EAGAIN;
- }
} else if (L_CANON(tty)) {
wait_for_canon_input(file, tty);
if (current->signal & ~current->blocked)
@@ -787,8 +788,16 @@ static int read_chan(struct tty_struct * tty, struct file * file, char * buf, in
break;
if (current->signal & ~current->blocked)
break;
- if (tty->link && !tty->link->count)
- break;
+ if (tty->link) {
+ if (IS_A_PTY_MASTER(tty->line)) {
+ if ((tty->flags & (1 << TTY_SLAVE_OPENED))
+ && tty->link->count <= 1)
+ break;
+ } else {
+ if (!tty->link->count)
+ break;
+ }
+ }
TTY_READ_FLUSH(tty);
if (tty->link)
TTY_WRITE_FLUSH(tty->link);
@@ -819,6 +828,8 @@ static int read_chan(struct tty_struct * tty, struct file * file, char * buf, in
return -ERESTARTSYS;
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
+ if (IS_A_PTY_MASTER(tty->line))
+ return -EIO;
return 0;
}
@@ -887,20 +898,54 @@ static int write_chan(struct tty_struct * tty, struct file * file, char * buf, i
while (nr>0 && !FULL(&tty->write_q)) {
c=get_fs_byte(b);
if (O_POST(tty)) {
- if (c=='\r' && O_CRNL(tty))
- c='\n';
- else if (c=='\n' && O_NLRET(tty))
- c='\r';
- if (c=='\n' && O_NLCR(tty) &&
- !set_bit(TTY_CR_PENDING,&tty->flags)) {
- put_tty_queue(13,&tty->write_q);
- continue;
+ switch (c) {
+ case '\n':
+ if (O_NLRET(tty)) {
+ tty->column = 0;
+ }
+ if (O_NLCR(tty)) {
+ if (!set_bit(TTY_CR_PENDING,&tty->flags)) {
+ c = '\r';
+ tty->column = 0;
+ b--; nr++;
+ } else {
+ clear_bit(TTY_CR_PENDING,&tty->flags);
+ }
+ }
+ break;
+ case '\r':
+ if (O_NOCR(tty) && tty->column == 0) {
+ b++; nr--;
+ continue;
+ }
+ if (O_CRNL(tty)) {
+ c = '\n';
+ if (O_NLRET(tty))
+ tty->column = 0;
+ break;
+ }
+ tty->column = 0;
+ break;
+ case '\t':
+ if (O_TABDLY(tty) == XTABS) {
+ c = ' ';
+ tty->column++;
+ if (tty->column % 8 != 0) {
+ b--; nr++;
+ }
+ }
+ break;
+ case '\b':
+ tty->column--;
+ break;
+ default:
+ if (O_LCUC(tty))
+ c = toupper(c);
+ tty->column++;
+ break;
}
- if (O_LCUC(tty))
- c=toupper(c);
}
b++; nr--;
- clear_bit(TTY_CR_PENDING,&tty->flags);
put_tty_queue(c,&tty->write_q);
}
if (need_resched)
@@ -1289,8 +1334,16 @@ static int tty_select(struct inode * inode, struct file * filp, int sel_type, se
return 1;
} else if (!EMPTY(&tty->secondary))
return 1;
- if (tty->link && !tty->link->count)
- return 1;
+ if (tty->link) {
+ if (IS_A_PTY_MASTER(tty->line)) {
+ if ((tty->flags & (1 << TTY_SLAVE_OPENED))
+ && tty->link->count <= 1)
+ return 1;
+ } else {
+ if (!tty->link->count)
+ return 1;
+ }
+ }
/* see if the status byte can be read. */
if (tty->packet && tty->link &&
@@ -1305,8 +1358,16 @@ static int tty_select(struct inode * inode, struct file * filp, int sel_type, se
select_wait(&tty->write_q.proc_list, wait);
return 0;
case SEL_EX:
- if (tty->link && !tty->link->count)
- return 1;
+ if (tty->link) {
+ if (IS_A_PTY_MASTER(tty->line)) {
+ if ((tty->flags & (1 << TTY_SLAVE_OPENED))
+ && tty->link->count <= 1)
+ return 1;
+ } else {
+ if (!tty->link->count)
+ return 1;
+ }
+ }
return 0;
}
return 0;
@@ -1391,6 +1452,7 @@ int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
while (count && VLEFT > 0) {
tty->write_q.buf[head++] = *p++;
head &= TTY_BUF_SIZE-1;
+ count--;
}
tty->write_q.head = head;
if (count) {
@@ -1400,6 +1462,7 @@ int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
tty->write_data_arg = callarg;
}
__asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ tty->write(tty);
return count;
}
@@ -1437,6 +1500,7 @@ void tty_bh_routine(void * unused)
while (count && VLEFT > 0) {
tty->write_q.buf[head++] = *p++;
head &= TTY_BUF_SIZE-1;
+ count--;
}
tty->write_q.head = head;
tty->write_data_ptr = p;
@@ -1486,6 +1550,7 @@ static void initialize_termios(int line, struct termios * tp)
ECHOCTL | ECHOKE;
} else if (IS_A_SERIAL(line)) {
tp->c_cflag = B2400 | CS8 | CREAD | HUPCL | CLOCAL;
+ tp->c_oflag = OPOST | ONLCR | XTABS;
} else if (IS_A_PTY_MASTER(line)) {
tp->c_cflag = B9600 | CS8 | CREAD;
} else if (IS_A_PTY_SLAVE(line)) {
diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c
index bb74911..d1bb6df 100644
--- a/kernel/chr_drv/vt.c
+++ b/kernel/chr_drv/vt.c
@@ -62,8 +62,8 @@ extern int vt_waitactive(void);
* We also return immediately, which is what was implied within the X
* comments - KDMKTONE doesn't put the process to sleep.
*/
-void
-kd_nosound(void)
+static void
+kd_nosound(unsigned long ignored)
{
/* disable counter 2 */
outb(inb_p(0x61)&0xFC, 0x61);
@@ -73,8 +73,11 @@ kd_nosound(void)
void
kd_mksound(unsigned int count, unsigned int ticks)
{
- if (count)
- {
+ static struct timer_list sound_timer = { NULL, 0, 0, kd_nosound };
+
+ cli();
+ del_timer(&sound_timer);
+ if (count) {
/* enable counter 2 */
outb_p(inb_p(0x61)|3, 0x61);
/* set command for counter 2, 2 byte write */
@@ -83,17 +86,13 @@ kd_mksound(unsigned int count, unsigned int ticks)
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
- if (ticks)
- {
- timer_table[BEEP_TIMER].expires = jiffies + ticks;
- timer_table[BEEP_TIMER].fn = kd_nosound;
- timer_active |= (1 << BEEP_TIMER);
+ if (ticks) {
+ sound_timer.expires = ticks;
+ add_timer(&sound_timer);
}
- }
-
- else
- kd_nosound();
-
+ } else
+ kd_nosound(0);
+ sti();
return;
}
@@ -129,7 +128,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
*/
kd_mksound(arg & 0xffff, ticks);
if (ticks == 0)
- kd_nosound();
+ kd_nosound(0);
return 0;
}
@@ -223,6 +222,41 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
}
return i;
+ case KDGKBENT:
+ {
+ struct kbentry * const a = (struct kbentry *)arg;
+ u_char i;
+ u_char s;
+
+ verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
+ if ((i = get_fs_byte(&a->kb_index)) >= NR_KEYS)
+ return -EINVAL;
+ if ((s = get_fs_byte(&a->kb_table)) >= NR_KEYMAPS)
+ return -EINVAL;
+ put_fs_word(key_map[s][i], &a->kb_value);
+ return 0;
+ }
+
+ case KDSKBENT:
+ {
+ const struct kbentry * a = (struct kbentry *)arg;
+ u_char i;
+ u_char s;
+ u_short v;
+
+ verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
+ if ((i = get_fs_byte(&a->kb_index)) >= NR_KEYS)
+ return -EINVAL;
+ if ((s = get_fs_byte(&a->kb_table)) >= NR_KEYMAPS)
+ return -EINVAL;
+ if (KTYP(v = get_fs_word(&a->kb_value)) >= NR_TYPES)
+ return -EINVAL;
+ if (KVAL(v) > max_vals[KTYP(v)])
+ return -EINVAL;
+ key_map[s][i] = v;
+ return 0;
+ }
+
case KDGETLED:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
diff --git a/kernel/exit.c b/kernel/exit.c
index 40bd1f0..3e0c7fb 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -16,10 +16,33 @@
#include <linux/tty.h>
#include <asm/segment.h>
+extern void shm_exit (void);
+extern void sem_exit (void);
int sys_close(int fd);
int getrusage(struct task_struct *, int, struct rusage *);
+static int generate(unsigned long sig, struct task_struct * p)
+{
+ unsigned long mask = 1 << (sig-1);
+ struct sigaction * sa = sig + p->sigaction - 1;
+
+ /* always generate signals for traced processes ??? */
+ if (p->flags & PF_PTRACED) {
+ p->signal |= mask;
+ return 1;
+ }
+ /* don't bother with ignored signals (but SIGCHLD is special) */
+ if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
+ return 0;
+ /* some signals are ignored by default.. (but SIGCONT already did its deed) */
+ if ((sa->sa_handler == SIG_DFL) &&
+ (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH))
+ return 0;
+ p->signal |= mask;
+ return 1;
+}
+
int send_sig(unsigned long sig,struct task_struct * p,int priv)
{
if (!p || sig > 32)
@@ -35,27 +58,24 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv)
p->exit_code = 0;
p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
- }
+ }
/* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
p->signal &= ~(1<<(SIGCONT-1));
- /* Actually deliver the signal */
- p->signal |= (1<<(sig-1));
- if (p->flags & PF_PTRACED) {
- /* save the signal number for wait. */
- p->exit_code = sig;
-
- /* we have to make sure the parent process is awake. */
- if (p->p_pptr != NULL && p->p_pptr->state == TASK_INTERRUPTIBLE)
- p->p_pptr->state = TASK_RUNNING;
-
- /* we have to make sure that the process stops. */
- if (p->state == TASK_INTERRUPTIBLE || p->state == TASK_RUNNING)
- p->state = TASK_STOPPED;
- }
+ /* Actually generate the signal */
+ if (!generate(sig,p))
+ return 0;
return 0;
}
+void notify_parent(struct task_struct * tsk)
+{
+ if (tsk->p_pptr == task[1])
+ tsk->exit_signal = SIGCHLD;
+ send_sig(tsk->exit_signal, tsk->p_pptr, 1);
+ wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
+}
+
void release(struct task_struct * p)
{
int i;
@@ -331,6 +351,10 @@ volatile void do_exit(long code)
int i;
fake_volatile:
+ if (current->semun)
+ sem_exit();
+ if (current->shm)
+ shm_exit();
free_page_tables(current);
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
@@ -366,7 +390,7 @@ fake_volatile:
kill_pg(current->pgrp,SIGCONT,1);
}
/* Let father know we died */
- send_sig (SIGCHLD, current->p_pptr, 1);
+ notify_parent(current);
/*
* This loop does two things:
@@ -388,7 +412,7 @@ fake_volatile:
p->p_osptr->p_ysptr = p;
p->p_pptr->p_cptr = p;
if (p->state == TASK_ZOMBIE)
- send_sig(SIGCHLD,p->p_pptr,1);
+ notify_parent(p);
/*
* process group orphan check
* Case ii: Our child is in a different pgrp
@@ -449,17 +473,17 @@ int sys_exit(int error_code)
int sys_wait4(pid_t pid,unsigned long * stat_addr, int options, struct rusage * ru)
{
- int flag;
+ int flag, retval;
+ struct wait_queue wait = { current, NULL };
struct task_struct *p;
- unsigned long oldblocked;
if (stat_addr) {
flag = verify_area(VERIFY_WRITE, stat_addr, 4);
if (flag)
return flag;
}
+ add_wait_queue(&current->wait_chldexit,&wait);
repeat:
- current->signal &= ~(1<<(SIGCHLD-1));
flag=0;
for (p = current->p_cptr ; p ; p = p->p_osptr) {
if (pid>0) {
@@ -472,6 +496,10 @@ repeat:
if (p->pgrp != -pid)
continue;
}
+ /* wait for cloned processes iff the __WCLONE flag is set */
+ if ((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
+ continue;
+ flag = 1;
switch (p->state) {
case TASK_STOPPED:
if (!p->exit_code)
@@ -484,7 +512,8 @@ repeat:
p->exit_code = 0;
if (ru != NULL)
getrusage(p, RUSAGE_BOTH, ru);
- return p->pid;
+ retval = p->pid;
+ goto end_wait4;
case TASK_ZOMBIE:
current->cutime += p->utime + p->cutime;
current->cstime += p->stime + p->cstime;
@@ -499,32 +528,34 @@ repeat:
REMOVE_LINKS(p);
p->p_pptr = p->p_opptr;
SET_LINKS(p);
- send_sig(SIGCHLD,p->p_pptr,1);
+ notify_parent(p);
} else
release(p);
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
- return flag;
+ retval = flag;
+ goto end_wait4;
default:
- flag=1;
continue;
}
}
if (flag) {
+ retval = 0;
if (options & WNOHANG)
- return 0;
+ goto end_wait4;
current->state=TASK_INTERRUPTIBLE;
- oldblocked = current->blocked;
- current->blocked &= ~(1<<(SIGCHLD-1));
schedule();
- current->blocked = oldblocked;
- if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
- return -ERESTARTSYS;
- else
- goto repeat;
+ current->signal &= ~(1<<(SIGCHLD-1));
+ retval = -ERESTARTSYS;
+ if (current->signal & ~current->blocked)
+ goto end_wait4;
+ goto repeat;
}
- return -ECHILD;
+ retval = -ECHILD;
+end_wait4:
+ remove_wait_queue(&current->wait_chldexit,&wait);
+ return retval;
}
/*
diff --git a/kernel/fork.c b/kernel/fork.c
index b8ba3a2..777c36b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -17,12 +17,16 @@
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
+#include <linux/segment.h>
#include <asm/segment.h>
#include <asm/system.h>
+extern void lcall7(void);
+
#define MAX_TASKS_PER_USER (NR_TASKS/2)
+extern int shm_fork (struct task_struct *, struct task_struct *);
long last_pid=0;
static int find_empty_process(void)
@@ -57,8 +61,30 @@ repeat:
return -EAGAIN;
}
+static struct file * copy_fd(struct file * old)
+{
+ struct file * new = get_empty_filp();
+ int error;
+
+ if (new) {
+ memcpy(new,old,sizeof(*new));
+ new->f_count = 1;
+ if (new->f_inode)
+ new->f_inode->i_count++;
+ if (new->f_op && new->f_op->open) {
+ error = new->f_op->open(new->f_inode,new);
+ if (error) {
+ iput(new->f_inode);
+ new->f_count = 0;
+ new = NULL;
+ }
+ }
+ }
+ return new;
+}
+
#define IS_CLONE (orig_eax == __NR_clone)
-#define copy_vm(p) (IS_CLONE?clone_page_tables:copy_page_tables)(p)
+#define copy_vm(p) ((clone_flags & COPYVM)?copy_page_tables:clone_page_tables)(p)
/*
* Ok, this is the main fork-routine. It copies the system process
@@ -73,6 +99,7 @@ int sys_fork(long ebx,long ecx,long edx,
struct task_struct *p;
int i,nr;
struct file *f;
+ unsigned long clone_flags = COPYVM | SIGCHLD;
p = (struct task_struct *) get_free_page(GFP_KERNEL);
if (!p)
@@ -103,7 +130,7 @@ int sys_fork(long ebx,long ecx,long edx,
p->cmin_flt = p->cmaj_flt = 0;
p->start_time = jiffies;
p->tss.back_link = 0;
- p->tss.ss0 = 0x10;
+ p->tss.ss0 = KERNEL_DS;
p->tss.eip = eip;
p->tss.eflags = eflags & 0xffffcfff; /* iopl is always 0 for a new process */
p->tss.eax = 0;
@@ -111,8 +138,14 @@ int sys_fork(long ebx,long ecx,long edx,
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
- if (IS_CLONE) /* clone() gets the new stack value */
- p->tss.esp = ebx;
+ if (IS_CLONE) {
+ if (ebx)
+ p->tss.esp = ebx;
+ clone_flags = ecx;
+ if (p->tss.esp == current->tss.esp)
+ clone_flags |= COPYVM;
+ }
+ p->exit_signal = clone_flags & CSIGNAL;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
@@ -124,12 +157,14 @@ int sys_fork(long ebx,long ecx,long edx,
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = offsetof(struct tss_struct,io_bitmap) << 16;
+ set_call_gate(p->ldt+0,lcall7);
for (i = 0; i<IO_BITMAP_SIZE ; i++)
p->tss.io_bitmap[i] = ~0;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));
p->kernel_stack_page = get_free_page(GFP_KERNEL);
- if (!p->kernel_stack_page || copy_vm(p)) {
+ p->semun = NULL; p->shm = NULL;
+ if (!p->kernel_stack_page || copy_vm(p) || shm_fork (current, p)) {
task[nr] = NULL;
REMOVE_LINKS(p);
free_page(p->kernel_stack_page);
@@ -137,9 +172,15 @@ int sys_fork(long ebx,long ecx,long edx,
return -EAGAIN;
}
p->tss.esp0 = PAGE_SIZE + p->kernel_stack_page;
- for (i=0; i<NR_OPEN;i++)
- if ((f = p->filp[i]) != NULL)
- f->f_count++;
+ if (clone_flags & COPYFD) {
+ for (i=0; i<NR_OPEN;i++)
+ if ((f = p->filp[i]) != NULL)
+ p->filp[i] = copy_fd(f);
+ } else {
+ for (i=0; i<NR_OPEN;i++)
+ if ((f = p->filp[i]) != NULL)
+ f->f_count++;
+ }
if (current->pwd)
current->pwd->i_count++;
if (current->root)
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 84fd1b8..fbe35d5 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -114,7 +114,7 @@ repeat:
static void put_long(struct task_struct * tsk, unsigned long addr,
unsigned long data)
{
- unsigned long page;
+ unsigned long page, pte;
repeat:
page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
@@ -122,8 +122,7 @@ repeat:
if (page & PAGE_PRESENT) {
page &= 0xfffff000;
page += (addr >> 10) & 0xffc;
-/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
- *(unsigned long *) page |= PAGE_DIRTY;
+ pte = page;
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
@@ -134,6 +133,8 @@ repeat:
do_wp_page(0,addr,tsk,0);
goto repeat;
}
+/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
+ *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW);
page &= 0xfffff000;
page += addr & 0xfff;
*(unsigned long *) page = data;
@@ -234,8 +235,6 @@ int sys_ptrace(long request, long pid, long addr, long data)
if (!(child = get_task(pid)))
return -ESRCH;
if (request == PTRACE_ATTACH) {
- long tmp;
-
if (child == current)
return -EPERM;
if ((!child->dumpable || (current->uid != child->euid) ||
@@ -250,12 +249,7 @@ int sys_ptrace(long request, long pid, long addr, long data)
child->p_pptr = current;
SET_LINKS(child);
}
- tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
- put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
- if (child->state == TASK_INTERRUPTIBLE ||
- child->state == TASK_STOPPED)
- child->state = TASK_RUNNING;
- child->signal = 0;
+ send_sig(SIGSTOP, child, 1);
return 0;
}
if (!(child->flags & PF_PTRACED))
@@ -269,7 +263,8 @@ int sys_ptrace(long request, long pid, long addr, long data)
/* when I and D space are seperate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
- int tmp,res;
+ unsigned long tmp;
+ int res;
res = read_long(child, addr, &tmp);
if (res < 0)
@@ -282,7 +277,9 @@ int sys_ptrace(long request, long pid, long addr, long data)
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
- int tmp, res;
+ unsigned long tmp;
+ int res;
+
addr = addr >> 2; /* temporary hack. */
if (addr < 0 || addr >= 17)
return -EIO;
@@ -317,13 +314,13 @@ int sys_ptrace(long request, long pid, long addr, long data)
case PTRACE_CONT: { /* restart after signal. */
long tmp;
+ if ((unsigned long) data > NSIG)
+ return -EIO;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
child->flags &= ~PF_TRACESYS;
- child->signal = 0;
- if (data > 0 && data <= NSIG)
- child->signal = 1<<(data-1);
+ child->exit_code = data;
child->state = TASK_RUNNING;
/* make sure the single step bit is not set. */
tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
@@ -340,7 +337,7 @@ int sys_ptrace(long request, long pid, long addr, long data)
long tmp;
child->state = TASK_RUNNING;
- child->signal = 1 << (SIGKILL-1);
+ child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
@@ -350,13 +347,13 @@ int sys_ptrace(long request, long pid, long addr, long data)
case PTRACE_SINGLESTEP: { /* set the trap flag. */
long tmp;
+ if ((unsigned long) data > NSIG)
+ return -EIO;
child->flags &= ~PF_TRACESYS;
tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
child->state = TASK_RUNNING;
- child->signal = 0;
- if (data > 0 && data <= NSIG)
- child->signal= 1<<(data-1);
+ child->exit_code = data;
/* give it a chance to run. */
return 0;
}
@@ -364,9 +361,11 @@ int sys_ptrace(long request, long pid, long addr, long data)
case PTRACE_DETACH: { /* detach a process that was attached. */
long tmp;
+ if ((unsigned long) data > NSIG)
+ return -EIO;
child->flags &= ~(PF_PTRACED|PF_TRACESYS);
- child->signal=0;
- child->state = 0;
+ child->state = TASK_RUNNING;
+ child->exit_code = data;
REMOVE_LINKS(child);
child->p_pptr = child->p_opptr;
SET_LINKS(child);
@@ -380,3 +379,22 @@ int sys_ptrace(long request, long pid, long addr, long data)
return -EIO;
}
}
+
+void syscall_trace(void)
+{
+ if ((current->flags & (PF_PTRACED|PF_TRACESYS))
+ != (PF_PTRACED|PF_TRACESYS))
+ return;
+ current->exit_code = SIGTRAP;
+ current->state = TASK_STOPPED;
+ notify_parent(current);
+ schedule();
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (current->exit_code)
+ current->signal |= (1 << (current->exit_code - 1));
+ current->exit_code = 0;
+}
diff --git a/kernel/sched.c b/kernel/sched.c
index f84c2ed..79c7f5b 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -23,6 +23,7 @@
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/ptrace.h>
+#include <linux/segment.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -65,7 +66,7 @@ long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
- } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
+ } stack_start = { & user_stack [PAGE_SIZE>>2] , KERNEL_DS };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
@@ -237,109 +238,47 @@ void sleep_on(struct wait_queue **p)
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
-/*
- * OK, here are some floppy things that shouldn't be in the kernel
- * proper. They are here because the floppy needs a timer, and this
- * was the easiest way of doing it.
- */
-static struct wait_queue * wait_motor[4] = {NULL,NULL,NULL,NULL};
-static int mon_timer[4]={0,0,0,0};
-static int moff_timer[4]={0,0,0,0};
-unsigned char current_DOR = 0x0C;
-
-int ticks_to_floppy_on(unsigned int nr)
-{
- extern unsigned char selected;
- unsigned char mask = 0x10 << nr;
-
- if (nr>3)
- panic("floppy_on: nr>3");
- moff_timer[nr]=10000; /* 100 s = very big :-) */
- cli(); /* use floppy_off to turn it off */
- mask |= current_DOR;
- if (!selected) {
- mask &= 0xFC;
- mask |= nr;
- }
- if (mask != current_DOR) {
- outb(mask,FD_DOR);
- if ((mask ^ current_DOR) & 0xf0)
- mon_timer[nr] = HZ/2;
- else if (mon_timer[nr] < 2)
- mon_timer[nr] = 2;
- current_DOR = mask;
- }
- sti();
- return mon_timer[nr];
-}
-
-void floppy_off(unsigned int nr)
-{
- moff_timer[nr]=3*HZ;
-}
+static struct timer_list * next_timer = NULL;
-void do_floppy_timer(void)
+void add_timer(struct timer_list * timer)
{
- int i;
- unsigned char mask = 0x10;
+ unsigned long flags;
+ struct timer_list ** p;
- for (i=0 ; i<4 ; i++,mask <<= 1) {
- if (!(mask & current_DOR))
- continue;
- if (mon_timer[i]) {
- if (!--mon_timer[i])
- wake_up(i+wait_motor);
- } else if (!moff_timer[i]) {
- current_DOR &= ~mask;
- outb(current_DOR,FD_DOR);
- } else
- moff_timer[i]--;
+ if (!timer)
+ return;
+ timer->next = NULL;
+ p = &next_timer;
+ save_flags(flags);
+ cli();
+ while (*p) {
+ if ((*p)->expires > timer->expires) {
+ (*p)->expires -= timer->expires;
+ timer->next = *p;
+ break;
+ }
+ timer->expires -= (*p)->expires;
+ p = &(*p)->next;
}
+ *p = timer;
+ restore_flags(flags);
}
-#define TIME_REQUESTS 64
-
-static struct timer_list {
- long jiffies;
- void (*fn)(void);
- struct timer_list * next;
-} timer_list[TIME_REQUESTS] = { { 0, NULL, NULL }, };
-
-static struct timer_list * next_timer = NULL;
-
-void add_timer(long jiffies, void (*fn)(void))
+void del_timer(struct timer_list * timer)
{
- struct timer_list * p;
unsigned long flags;
+ struct timer_list **p;
- if (!fn)
- return;
+ p = &next_timer;
save_flags(flags);
cli();
- if (jiffies <= 0)
- (fn)();
- else {
- for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
- if (!p->fn)
- break;
- if (p >= timer_list + TIME_REQUESTS)
- panic("No more time requests free");
- p->fn = fn;
- p->jiffies = jiffies;
- p->next = next_timer;
- next_timer = p;
- while (p->next && p->next->jiffies < p->jiffies) {
- p->jiffies -= p->next->jiffies;
- fn = p->fn;
- p->fn = p->next->fn;
- p->next->fn = fn;
- jiffies = p->jiffies;
- p->jiffies = p->next->jiffies;
- p->next->jiffies = jiffies;
- p = p->next;
+ while (*p) {
+ if (*p == timer) {
+ if ((*p = timer->next) != NULL)
+ (*p)->expires += timer->expires;
+ break;
}
- if (p->next)
- p->next->jiffies -= p->jiffies;
+ p = &(*p)->next;
}
restore_flags(flags);
}
@@ -443,19 +382,18 @@ static void do_timer(struct pt_regs * regs)
tp->fn();
sti();
}
- if (next_timer) {
- next_timer->jiffies--;
- while (next_timer && next_timer->jiffies <= 0) {
- void (*fn)(void);
-
- fn = next_timer->fn;
- next_timer->fn = NULL;
- next_timer = next_timer->next;
- (fn)();
- }
+ cli();
+ while (next_timer && next_timer->expires == 0) {
+ void (*fn)(unsigned long) = next_timer->function;
+ unsigned long data = next_timer->data;
+ next_timer = next_timer->next;
+ sti();
+ fn(data);
+ cli();
}
- if (current_DOR & 0xf0)
- do_floppy_timer();
+ if (next_timer)
+ next_timer->expires--;
+ sti();
}
int sys_alarm(long seconds)
diff --git a/kernel/signal.c b/kernel/signal.c
index 9892b26..0e0cd15 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -47,7 +47,7 @@ int sys_sigpending(sigset_t *set)
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
-int sys_sigsuspend(volatile int restart, volatile unsigned long oldmask, unsigned long set)
+int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
{
unsigned long mask;
struct pt_regs * regs = (struct pt_regs *) &restart;
@@ -97,15 +97,17 @@ static void check_pending(int signum)
}
}
-int sys_signal(int signum, long handler)
+int sys_signal(int signum, unsigned long handler)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
return -EINVAL;
+ if (handler >= TASK_SIZE)
+ return -EFAULT;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
- tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT;
+ tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
tmp.sa_restorer = NULL;
handler = (long) current->sigaction[signum-1].sa_handler;
current->sigaction[signum-1] = tmp;
@@ -129,6 +131,8 @@ int sys_sigaction(int signum, const struct sigaction * action,
new.sa_mask |= _S(signum);
new.sa_mask &= _BLOCKABLE;
}
+ if (TASK_SIZE <= (unsigned long) new.sa_handler)
+ return -EFAULT;
}
if (oldaction) {
if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
@@ -146,95 +150,123 @@ extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
/*
* This sets regs->esp even though we don't actually use sigstacks yet..
*/
-int sys_sigreturn(int signr, unsigned long oldmask, unsigned long esp)
+int sys_sigreturn(unsigned long oldmask, unsigned long eip, unsigned long esp)
{
struct pt_regs * regs;
- regs = (struct pt_regs *) &signr;
+ regs = (struct pt_regs *) &oldmask;
current->blocked = oldmask & _BLOCKABLE;
+ regs->eip = eip;
regs->esp = esp;
return 0;
}
/*
- * This routine sets up the return stack for the first signal found
- * (== last delivered). It makes room for the registers we need to save,
- * but the actual saving is left until the very last moment when we
- * know whether we can restart system calls etc.
+ * Set up a signal frame... Make the stack look the way iBCS2 expects
+ * it to look.
*/
-static unsigned long * setup_first(struct pt_regs * regs,
- int signr, unsigned long sa_handler, unsigned long oldmask)
-{
- unsigned long * tmp_esp;
-
- regs->esp -= 18*4;
- tmp_esp = (unsigned long *) regs->esp;
- verify_area(VERIFY_WRITE,tmp_esp,18*4);
-/* set up the "normal" stack seen by the signal handler */
- put_fs_long(regs->esp+15*4,tmp_esp); /* points to the stack.. */
- put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */
- put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2);
- put_fs_long(oldmask,tmp_esp+3); /* second .. */
- put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */
-/* save this frame so that we later can fill in the saved registers */
- return tmp_esp+5;
-}
-
-/*
- * This sets up the stack for any stacked signals other than the
- * first one: no need to restore registers etc, as that is done
- * by the very last signal handler return code..
- */
-static void setup_other(unsigned long eip, struct pt_regs * regs, int signr,
+static void setup_frame(unsigned long ** fp, unsigned long eip,
+ struct pt_regs * regs, int signr,
unsigned long sa_handler, unsigned long oldmask)
{
- unsigned long * tmp_esp;
+ unsigned long * frame;
- regs->esp -= 9*4;
- tmp_esp = (unsigned long *) regs->esp;
- verify_area(VERIFY_WRITE,tmp_esp,9*4);
-/* set up the "normal" stack seen by the signal handler */
- put_fs_long(regs->esp+6*4,tmp_esp); /* points to the stack.. */
- put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */
- put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2);
- put_fs_long(oldmask,tmp_esp+3); /* second .. */
- put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */
- put_fs_long(eip,tmp_esp+5); /* return address */
+#define __CODE ((unsigned long)(frame+24))
+#define CODE(x) ((void *) ((x)+__CODE))
+ frame = *fp - 32;
+ verify_area(VERIFY_WRITE,frame,32*4);
+/* set up the "normal" stack seen by the signal handler (iBCS2) */
+ put_fs_long(__CODE,frame);
+ put_fs_long(signr, frame+1);
+ put_fs_long(regs->gs, frame+2);
+ put_fs_long(regs->fs, frame+3);
+ put_fs_long(regs->es, frame+4);
+ put_fs_long(regs->ds, frame+5);
+ put_fs_long(regs->edi, frame+6);
+ put_fs_long(regs->esi, frame+7);
+ put_fs_long(regs->ebp, frame+8);
+ put_fs_long(regs->esp, frame+9);
+ put_fs_long(regs->ebx, frame+10);
+ put_fs_long(regs->edx, frame+11);
+ put_fs_long(regs->ecx, frame+12);
+ put_fs_long(regs->eax, frame+13);
+ put_fs_long(0, frame+14); /* trapno */
+ put_fs_long(0, frame+15); /* err */
+ put_fs_long(regs->eip, frame+16);
+ put_fs_long(regs->cs, frame+17);
+ put_fs_long(regs->eflags, frame+18);
+ put_fs_long(regs->esp, frame+19);
+ put_fs_long(regs->ss, frame+20);
+ put_fs_long(0,frame+21); /* 387 state pointer */
+/* linux extended stack - easier to handle.. */
+ put_fs_long(regs->eflags, frame+22);
+ put_fs_long(eip, frame+23);
/* set up the return code... */
- put_fs_long(0x58595a5b,tmp_esp+6); /* pop bx,dx,cx,ax */
- put_fs_long(0x909080cd,tmp_esp+7); /* int $0x80 + nop + nop */
- put_fs_long(0x000cc290,tmp_esp+8); /* nop + "ret 12" */
+ put_fs_long(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */
+ put_fs_long(0x00bb0000, CODE(4)); /* movl $,%ebx */
+ put_fs_long(0xcd000000, CODE(8)); /* int $0x80 */
+ put_fs_long(0x0fa90f80, CODE(12)); /* pop %gs ; pop %fs */
+ put_fs_long(0x611f07a1, CODE(16)); /* pop %es ; pop %ds ; popad */
+ put_fs_long(0x20c48390, CODE(20)); /* nop ; addl $32,%esp */
+ put_fs_long(0x0020c29d, CODE(24)); /* popfl ; ret $32 */
+ put_fs_long(__NR_ssetmask, CODE(2));
+ put_fs_long(oldmask, CODE(7));
+ *fp = frame;
+#undef __CODE
+#undef CODE
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
*/
int do_signal(unsigned long oldmask, struct pt_regs * regs)
{
+ unsigned long mask = ~current->blocked;
+ unsigned long handler_signal = 0;
unsigned long *frame = NULL;
unsigned long eip = 0;
unsigned long signr;
unsigned long sa_handler;
struct sigaction * sa;
- while ((signr = current->signal & ~current->blocked)) {
+ while ((signr = current->signal & mask)) {
__asm__("bsf %2,%1\n\t"
"btrl %1,%0"
:"=m" (current->signal),"=r" (signr)
:"1" (signr));
sa = current->sigaction + signr;
signr++;
- sa_handler = (unsigned long) sa->sa_handler;
- if (sa_handler==1) {
-/* check for SIGCHLD: it's special */
- if (signr == SIGCHLD)
- while (sys_waitpid(-1,NULL,WNOHANG) > 0)
- /* nothing */;
+ if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+ current->exit_code = signr;
+ current->state = TASK_STOPPED;
+ notify_parent(current);
+ schedule();
+ if (!(signr = current->exit_code))
+ continue;
+ current->exit_code = 0;
+ if (signr == SIGSTOP)
+ continue;
+ if (_S(signr) & current->blocked) {
+ current->signal |= _S(signr);
+ continue;
+ }
+ sa = current->sigaction + signr - 1;
+ }
+ if (sa->sa_handler == SIG_IGN) {
+ if (signr != SIGCHLD)
+ continue;
+ /* check for SIGCHLD: it's special */
+ while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+ /* nothing */;
continue;
}
- if (!sa_handler) {
+ if (sa->sa_handler == SIG_DFL) {
if (current->pid == 1)
continue;
switch (signr) {
@@ -246,7 +278,7 @@ int do_signal(unsigned long oldmask, struct pt_regs * regs)
current->exit_code = signr;
if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
- send_sig(SIGCHLD, current->p_pptr, 1);
+ notify_parent(current);
schedule();
continue;
@@ -265,20 +297,11 @@ int do_signal(unsigned long oldmask, struct pt_regs * regs)
*/
if (regs->orig_eax >= 0) {
if (regs->eax == -ERESTARTNOHAND ||
- (regs->eax == -ERESTARTSYS && (sa->sa_flags & SA_INTERRUPT)))
+ (regs->eax == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
regs->eax = -EINTR;
}
- if (sa->sa_flags & SA_ONESHOT)
- sa->sa_handler = NULL;
-/* force a supervisor-mode page-in of the signal handler to reduce races */
- __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
- if (!frame) {
- frame = setup_first(regs,signr,sa_handler,oldmask);
- } else
- setup_other(eip,regs,signr,sa_handler,oldmask);
- eip = sa_handler;
- current->blocked |= sa->sa_mask;
- oldmask |= sa->sa_mask;
+ handler_signal |= 1 << (signr-1);
+ mask &= ~sa->sa_mask;
}
if (regs->orig_eax >= 0 &&
(regs->eax == -ERESTARTNOHAND ||
@@ -287,23 +310,28 @@ int do_signal(unsigned long oldmask, struct pt_regs * regs)
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
- if (!frame) /* no handlers installed - return 0 */
+ if (!handler_signal) /* no handler will be called - return 0 */
return 0;
-/* save registers if one or more handlers are called.. */
- put_fs_long(regs->edi,frame); /* suitable order for "popad" */
- put_fs_long(regs->esi,frame+1);
- put_fs_long(regs->ebp,frame+2); /* using 'frame++' instead of the 'frame+x' */
- put_fs_long(regs->esp,frame+3); /* form used now results in atrocious code */
- put_fs_long(regs->ebx,frame+4); /* due to gcc not being very good at optimizing */
- put_fs_long(regs->edx,frame+5); /* things with inline-assembly/functions.. */
- put_fs_long(regs->ecx,frame+6);
- put_fs_long(regs->eax,frame+7);
- put_fs_long(regs->eflags,frame+8); /* flags */
- put_fs_long(regs->eip,frame+9); /* original return address */
-/* set up the return code... */
- put_fs_long(0x58595a5b,frame+10); /* pop bx,dx,cx,ax */
- put_fs_long(0x906180cd,frame+11); /* int $0x80 + popad + nop */
- put_fs_long(0x000cc29d,frame+12); /* popfl + "ret 12" */
+ eip = regs->eip;
+ frame = (unsigned long *) regs->esp;
+ signr = 1;
+ sa = current->sigaction;
+ for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
+ if (mask > handler_signal)
+ break;
+ if (!(mask & handler_signal))
+ continue;
+ sa_handler = (unsigned long) sa->sa_handler;
+ if (sa->sa_flags & SA_ONESHOT)
+ sa->sa_handler = NULL;
+/* force a supervisor-mode page-in of the signal handler to reduce races */
+ __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
+ setup_frame(&frame,eip,regs,signr,sa_handler,oldmask);
+ eip = sa_handler;
+ current->blocked |= sa->sa_mask;
+ oldmask |= sa->sa_mask;
+ }
+ regs->esp = (unsigned long) frame;
regs->eip = eip; /* "return" to the first handler */
return 1;
}
diff --git a/kernel/sys.c b/kernel/sys.c
index 5ce28e5..f902651 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -131,11 +131,6 @@ int sys_prof(void)
return -ENOSYS;
}
-int sys_ipc(void)
-{
- return -ENOSYS;
-}
-
unsigned long save_v86_state(struct vm86_regs * regs)
{
unsigned long stack;
@@ -324,6 +319,11 @@ int sys_ulimit(void)
return -ENOSYS;
}
+int sys_old_syscall(void)
+{
+ return -ENOSYS;
+}
+
int sys_time(long * tloc)
{
int i, error;
@@ -572,6 +572,27 @@ int sys_uname(struct old_utsname * name)
error = verify_area(VERIFY_WRITE, name,sizeof *name);
if (error)
return error;
+ memcpy_tofs(&name->sysname,&system_utsname.sysname,
+ sizeof (system_utsname.sysname));
+ memcpy_tofs(&name->nodename,&system_utsname.nodename,
+ sizeof (system_utsname.nodename));
+ memcpy_tofs(&name->release,&system_utsname.release,
+ sizeof (system_utsname.release));
+ memcpy_tofs(&name->version,&system_utsname.version,
+ sizeof (system_utsname.version));
+ memcpy_tofs(&name->machine,&system_utsname.machine,
+ sizeof (system_utsname.machine));
+ return 0;
+}
+
+int sys_olduname(struct oldold_utsname * name)
+{
+ int error;
+ if (!name)
+ return -EFAULT;
+ error = verify_area(VERIFY_WRITE, name,sizeof *name);
+ if (error)
+ return error;
memcpy_tofs(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
put_fs_byte(0,name->sysname+__OLD_UTS_LEN);
memcpy_tofs(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
@@ -604,6 +625,26 @@ int sys_sethostname(char *name, int len)
return 0;
}
+/*
+ * Only setdomainname; getdomainname can be implemented by calling
+ * uname()
+ */
+int sys_setdomainname(char *name, int len)
+{
+ int i;
+
+ if (!suser())
+ return -EPERM;
+ if (len > __NEW_UTS_LEN)
+ return -EINVAL;
+ for (i=0; i < len; i++) {
+ if ((system_utsname.domainname[i] = get_fs_byte(name+i)) == 0)
+ return 0;
+ }
+ system_utsname.domainname[i] = 0;
+ return 0;
+}
+
int sys_getrlimit(unsigned int resource, struct rlimit *rlim)
{
int error;
@@ -699,7 +740,7 @@ int sys_getrusage(int who, struct rusage *ru)
#define LATCH ((1193180 + HZ/2)/HZ)
/*
- * This version of gettimeofday has near nanosecond resolution.
+ * This version of gettimeofday has near microsecond resolution.
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*/
static inline void do_gettimeofday(struct timeval *tv)
@@ -726,7 +767,7 @@ static inline void do_gettimeofday(struct timeval *tv)
}
nowtime += jiffies_offset;
tv->tv_sec = startup_time + CT_TO_SECS(nowtime);
- /* the correction term is always in the range [0, 1 clocktick) */
+ /* the correction term is always in the range [0, 1) clocktick */
tv->tv_usec = CT_TO_USECS(nowtime)
+ ((LATCH - 1) - count)*(1000000/HZ)/LATCH;
#else /* not __i386__ */
diff --git a/kernel/sys_call.S b/kernel/sys_call.S
index 9c4fd61..c74b44f 100644
--- a/kernel/sys_call.S
+++ b/kernel/sys_call.S
@@ -40,6 +40,8 @@
* 40(%esp) - %oldss
*/
+#include <linux/segment.h>
+
EBX = 0x00
ECX = 0x04
EDX = 0x08
@@ -76,7 +78,7 @@ errno = 24
ENOSYS = 38
-.globl _system_call,_sys_execve
+.globl _system_call,_sys_execve,_lcall7
.globl _device_not_available, _coprocessor_error
.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _double_fault,_coprocessor_segment_overrun
@@ -98,10 +100,10 @@ ENOSYS = 38
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
- movl $0x10,%edx; \
+ movl $KERNEL_DS,%edx; \
mov %dx,%ds; \
mov %dx,%es; \
- movl $0x17,%edx; \
+ movl $USER_DS,%edx; \
mov %dx,%fs
#define RESTORE_ALL \
@@ -120,6 +122,23 @@ ENOSYS = 38
iret
.align 4
+_lcall7:
+ pushfl # We get a different stack layout with call gates,
+ pushl %eax # which has to be cleaned up later..
+ SAVE_ALL
+ movl EIP(%esp),%eax # due to call gates, this is eflags, not eip..
+ movl CS(%esp),%edx # this is eip..
+ movl EFLAGS(%esp),%ecx # and this is cs..
+ movl %eax,EFLAGS(%esp) #
+ movl %edx,EIP(%esp) # Now we move them to their "normal" places
+ movl %ecx,CS(%esp) #
+ movl %esp,%eax
+ pushl %eax
+ call _iABI_emulate
+ popl %eax
+ jmp ret_from_sys_call
+
+.align 4
reschedule:
pushl $ret_from_sys_call
jmp _schedule
@@ -134,40 +153,37 @@ _system_call:
movl $0,errno(%ebx)
andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors
testl $0x20,flags(%ebx) # PF_TRACESYS
- je 1f
- pushl $0
- pushl %ebx
- pushl $5 # SIGTRAP
- call _send_sig
- addl $12,%esp
- call _schedule
+ jne 1f
+ call _sys_call_table(,%eax,4)
+ movl %eax,EAX(%esp) # save the return value
+ movl _current,%eax
+ movl errno(%eax),%edx
+ negl %edx
+ je ret_from_sys_call
+ movl %edx,EAX(%esp)
+ orl $CF_MASK,EFLAGS(%esp) # set carry to indicate error
+ jmp ret_from_sys_call
+.align 4
+1: call _syscall_trace
movl ORIG_EAX(%esp),%eax
-1: call _sys_call_table(,%eax,4)
+ call _sys_call_table(,%eax,4)
movl %eax,EAX(%esp) # save the return value
movl _current,%eax
movl errno(%eax),%edx
negl %edx
- je 2f
+ je 1f
movl %edx,EAX(%esp)
orl $CF_MASK,EFLAGS(%esp) # set carry to indicate error
-2: testl $0x20,flags(%eax) # PF_TRACESYS
- je ret_from_sys_call
- cmpl $0,signal(%eax)
- jne ret_from_sys_call # ptrace would clear signal
- pushl $0
- pushl %eax
- pushl $5 # SIGTRAP
- call _send_sig
- addl $12,%esp
- call _schedule
+1: call _syscall_trace
+
.align 4,0x90
ret_from_sys_call:
movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $VM_MASK,%eax # different then
jne 1f
- cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
+ cmpw $USER_CS,CS(%esp) # was old code segment supervisor ?
jne 2f
- cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
+ cmpw $USER_DS,OLDSS(%esp) # was stack segment user segment ?
jne 2f
1: sti # slow interrupts get here with interrupts disabled
orl $IF_MASK,%eax # these just try to make sure
@@ -220,7 +236,7 @@ _sys_execve:
.align 4
_divide_error:
- pushl $0 # no error code
+ pushl $0 # no error code
pushl $_do_divide_error
.align 4,0x90
error_code:
@@ -241,12 +257,12 @@ error_code:
mov %gs,%bx # get the lower order bits of gs
xchgl %ebx, GS(%esp) # get the address and save gs.
pushl %eax # push the error code
- lea 52(%esp),%edx
+ lea 4(%esp),%edx
pushl %edx
- movl $0x10,%edx
+ movl $KERNEL_DS,%edx
mov %dx,%ds
mov %dx,%es
- movl $0x17,%edx
+ movl $USER_DS,%edx
mov %dx,%fs
call *%ebx
addl $8,%esp
diff --git a/kernel/traps.c b/kernel/traps.c
index 0e35019..3cf1697 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -15,6 +15,8 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
+#include <linux/segment.h>
+#include <linux/ptrace.h>
#include <asm/system.h>
#include <asm/segment.h>
@@ -58,125 +60,151 @@ void coprocessor_error(void);
void reserved(void);
void alignment_check(void);
-/*static*/ void die_if_kernel(char * str,long esp_ptr,long nr)
+/*static*/ void die_if_kernel(char * str, struct pt_regs * regs, long err)
{
- long * esp = (long *) esp_ptr;
int i;
- if ((esp[2] & VM_MASK) || ((0xffff & esp[1]) == 0xf))
+ if ((regs->eflags & VM_MASK) || ((0xffff & regs->cs) == USER_CS))
return;
- printk("%s: %04x\n",str,nr&0xffff);
- printk("EIP: %04x:%p\nEFLAGS: %p\n", 0xffff & esp[1],esp[0],esp[2]);
- printk("fs: %04x\n",_fs());
- printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
+ printk("%s: %04x\n", str, err & 0xffff);
+ printk("EIP: %04x:%p\nEFLAGS: %p\n", 0xffff & regs->cs,regs->eip,regs->eflags);
+ printk("eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
+ regs->eax, regs->ebx, regs->ecx, regs->edx);
+ printk("esi: %08x edi: %08x ebp: %08x\n",
+ regs->esi, regs->edi, regs->ebp);
+ printk("ds: %04x es: %04x fs: %04x gs: %04x\n",
+ regs->ds, regs->es, regs->fs, regs->gs);
store_TR(i);
- printk("Pid: %d, process nr: %d\n",current->pid,0xffff & i);
+ printk("Pid: %d, process nr: %d\n", current->pid, 0xffff & i);
for(i=0;i<10;i++)
- printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
+ printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip)));
printk("\n");
do_exit(SIGSEGV);
}
-void do_double_fault(long esp, long error_code)
+void do_double_fault(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("double fault",esp,error_code);
+ die_if_kernel("double fault",regs,error_code);
}
-void do_general_protection(long esp, long error_code)
+void do_general_protection(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("general protection",esp,error_code);
+ die_if_kernel("general protection",regs,error_code);
}
-void do_alignment_check(long esp, long error_code)
+void do_alignment_check(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("alignment check",esp,error_code);
+ die_if_kernel("alignment check",regs,error_code);
}
-void do_divide_error(long esp, long error_code)
+void do_divide_error(struct pt_regs * regs, long error_code)
{
send_sig(SIGFPE, current, 1);
- die_if_kernel("divide error",esp,error_code);
+ die_if_kernel("divide error",regs,error_code);
}
-void do_int3(long esp, long error_code)
+void do_int3(struct pt_regs * regs, long error_code)
{
+ if (current->flags & PF_PTRACED)
+ current->blocked &= ~(1 << (SIGTRAP-1));
send_sig(SIGTRAP, current, 1);
- die_if_kernel("int3",esp,error_code);
+ die_if_kernel("int3",regs,error_code);
}
-void do_nmi(long esp, long error_code)
+void do_nmi(struct pt_regs * regs, long error_code)
{
printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
}
-void do_debug(long esp, long error_code)
+void do_debug(struct pt_regs * regs, long error_code)
{
+ if (current->flags & PF_PTRACED)
+ current->blocked &= ~(1 << (SIGTRAP-1));
send_sig(SIGTRAP, current, 1);
- die_if_kernel("debug",esp,error_code);
+ die_if_kernel("debug",regs,error_code);
}
-void do_overflow(long esp, long error_code)
+void do_overflow(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("overflow",esp,error_code);
+ die_if_kernel("overflow",regs,error_code);
}
-void do_bounds(long esp, long error_code)
+void do_bounds(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("bounds",esp,error_code);
+ die_if_kernel("bounds",regs,error_code);
}
-void do_invalid_op(long esp, long error_code)
+void do_invalid_op(struct pt_regs * regs, long error_code)
{
send_sig(SIGILL, current, 1);
- die_if_kernel("invalid operand",esp,error_code);
+ die_if_kernel("invalid operand",regs,error_code);
}
-void do_device_not_available(long esp, long error_code)
+void do_device_not_available(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("device not available",esp,error_code);
+ die_if_kernel("device not available",regs,error_code);
}
-void do_coprocessor_segment_overrun(long esp, long error_code)
+void do_coprocessor_segment_overrun(struct pt_regs * regs, long error_code)
{
send_sig(SIGFPE, last_task_used_math, 1);
- die_if_kernel("coprocessor segment overrun",esp,error_code);
+ die_if_kernel("coprocessor segment overrun",regs,error_code);
}
-void do_invalid_TSS(long esp,long error_code)
+void do_invalid_TSS(struct pt_regs * regs,long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("invalid TSS",esp,error_code);
+ die_if_kernel("invalid TSS",regs,error_code);
}
-void do_segment_not_present(long esp,long error_code)
+void do_segment_not_present(struct pt_regs * regs,long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("segment not present",esp,error_code);
+ die_if_kernel("segment not present",regs,error_code);
}
-void do_stack_segment(long esp,long error_code)
+void do_stack_segment(struct pt_regs * regs,long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("stack segment",esp,error_code);
+ die_if_kernel("stack segment",regs,error_code);
}
-void do_coprocessor_error(long esp, long error_code)
+void do_coprocessor_error(struct pt_regs * regs, long error_code)
{
+ /*
+ Allow the process which triggered the interrupt to recover the error
+ condition.
+ The status word is saved in the cs selector.
+ The tag word is saved in the operand selector.
+ The status word is then cleared and the tags all set to Empty.
+ This will give sufficient information for complete recovery provided that
+ the affected process knows or can deduce the code and data segments
+ which were in force when the exception condition arose.
+ */
+ #define FPU_ENV (*(struct i387_hard_struct *)env)
+ char env[28];
+
ignore_irq13 = 1;
send_sig(SIGFPE, last_task_used_math, 1);
- __asm__("fninit");
+
+ __asm__ __volatile__("fnstenv %0; fnclex": "=m" (FPU_ENV));
+ FPU_ENV.fcs = (FPU_ENV.swd & 0x0000ffff) | (FPU_ENV.fcs & 0xffff0000);
+ FPU_ENV.fos = FPU_ENV.twd;
+ FPU_ENV.swd &= 0xffff0000;
+ FPU_ENV.twd = 0xffffffff;
+ __asm__ __volatile__("fldenv %0"::"m" (FPU_ENV));
}
-void do_reserved(long esp, long error_code)
+void do_reserved(struct pt_regs * regs, long error_code)
{
send_sig(SIGSEGV, current, 1);
- die_if_kernel("reserved (15,17-47) error",esp,error_code);
+ die_if_kernel("reserved (15,17-47) error",regs,error_code);
}
void trap_init(void)
diff --git a/lib/malloc.c b/lib/malloc.c
index d0e42c9..925a28a 100644
--- a/lib/malloc.c
+++ b/lib/malloc.c
@@ -74,7 +74,7 @@ struct bucket_desc { /* 16 bytes */
};
struct _bucket_dir { /* 8 bytes */
- int size;
+ unsigned int size;
struct bucket_desc *chain;
};
@@ -114,19 +114,12 @@ static struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0;
/* It assumes it is called with interrupts on. and will
return that way. It also can sleep if priority != GFP_ATOMIC. */
-static inline int init_bucket_desc(int priority)
+static inline void init_bucket_desc(unsigned long page)
{
- struct bucket_desc *bdesc, *first;
+ struct bucket_desc *bdesc;
int i;
- /* this turns interrupt on, so we should be carefull. */
- first = bdesc = (struct bucket_desc *) get_free_page(priority);
- if (!bdesc)
- return 1;
-
- /* At this point it is possible that we have slept and
- free has been called etc. So we might not actually need
- this page anymore. */
+ bdesc = (struct bucket_desc *) page;
for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) {
bdesc->next = bdesc+1;
bdesc++;
@@ -135,17 +128,21 @@ static inline int init_bucket_desc(int priority)
* This is done last, to avoid race conditions in case
* get_free_page() sleeps and this routine gets called again....
*/
-
cli();
bdesc->next = free_bucket_desc;
- free_bucket_desc = first;
- sti();
- return (0);
+ free_bucket_desc = (struct bucket_desc *) page;
}
+/*
+ * Re-organized some code to give cleaner assembly output for easier
+ * verification.. LBT
+ */
void *
kmalloc(unsigned int len, int priority)
{
+ int i;
+ unsigned long flags;
+ unsigned long page;
struct _bucket_dir *bdir;
struct bucket_desc *bdesc;
void *retval;
@@ -156,96 +153,86 @@ kmalloc(unsigned int len, int priority)
*/
/* The sizes are static so there is no reentry problem here. */
- for (bdir = bucket_dir; bdir->size; bdir++)
- if (bdir->size >= len)
- break;
-
- if (!bdir->size) {
- /* This should be changed for sizes > 1 page. */
- printk("kmalloc called with impossibly large argument (%d)\n", len);
- return NULL;
+ bdir = bucket_dir;
+ for (bdir = bucket_dir ; bdir->size < len ; bdir++) {
+ if (!bdir->size)
+ goto too_large;
}
/*
* Now we search for a bucket descriptor which has free space
*/
- cli(); /* Avoid race conditions */
+ save_flags(flags);
+ cli(); /* Avoid race conditions */
for (bdesc = bdir->chain; bdesc != NULL; bdesc = bdesc->next)
- if (bdesc->freeptr != NULL || bdesc->page == NULL)
- break;
+ if (bdesc->freeptr)
+ goto found_bdesc;
/*
* If we didn't find a bucket with free space, then we'll
* allocate a new one.
*/
- if (!bdesc)
- {
- char *cp;
- int i;
-
- /* This must be a while because it is possible
- to get interrupted after init_bucket_desc
- and before cli. The interrupt could steal
- our free_desc. */
-
- while (!free_bucket_desc)
- {
- sti(); /* This might happen anyway, so we
- might as well make it explicit. */
- if (init_bucket_desc(priority))
- {
- return NULL;
- }
- cli(); /* Turn them back off. */
- }
-
- bdesc = free_bucket_desc;
- free_bucket_desc = bdesc->next;
-
- /* get_free_page will turn interrupts back
- on. So we might as well do it
- ourselves. */
+
+ /*
+ * Note that init_bucket_descriptor() does it's
+ * own cli() before returning, and guarantees that
+ * there is a bucket desc in the page.
+ */
+ if (!free_bucket_desc) {
+ restore_flags(flags);
+ page = get_free_page(priority);
+ if (!page)
+ return NULL;
+ init_bucket_desc(page);
+ }
+
+ bdesc = free_bucket_desc;
+ free_bucket_desc = bdesc->next;
+ restore_flags(flags);
- sti();
- bdesc->refcnt = 0;
- bdesc->bucket_size = bdir->size;
- bdesc->page = bdesc->freeptr =
- (void *)cp = get_free_page(priority);
-
- if (!cp)
- {
+ page = get_free_page(priority);
- /* put bdesc back on the free list. */
+ /*
+ * Out of memory? Put the bucket descriptor back on the free list
+ */
+ if (!page) {
cli();
bdesc->next = free_bucket_desc;
- free_bucket_desc = bdesc->next;
- sti();
-
+ free_bucket_desc = bdesc;
+ restore_flags(flags);
return NULL;
- }
-
- /* Set up the chain of free objects */
- for (i=PAGE_SIZE/bdir->size; i > 1; i--)
- {
- *((char **) cp) = cp + bdir->size;
- cp += bdir->size;
- }
-
- *((char **) cp) = 0;
-
- /* turn interrupts back off for putting the
- thing onto the chain. */
- cli();
- /* remember bdir is not changed. */
- bdesc->next = bdir->chain; /* OK, link it in! */
- bdir->chain = bdesc;
+ }
+
+ bdesc->refcnt = 0;
+ bdesc->bucket_size = bdir->size;
+ bdesc->page = bdesc->freeptr = (void *) page;
+
+ /* Set up the chain of free objects */
+ for (i=PAGE_SIZE/bdir->size; i > 1 ; i--) {
+ *((void **) page) = (void *)(page + bdir->size);
+ page += bdir->size;
+ }
+
+ *((void **) page) = NULL;
- }
+ /* turn interrupts back off for putting the
+ thing onto the chain. */
+ cli();
+ /* remember bdir is not changed. */
+ bdesc->next = bdir->chain; /* OK, link it in! */
+ bdir->chain = bdesc;
+found_bdesc:
retval = (void *) bdesc->freeptr;
bdesc->freeptr = *((void **) retval);
bdesc->refcnt++;
- sti(); /* OK, we're safe again */
+ restore_flags(flags); /* OK, we're safe again */
+ memset(retval, 0xf0, bdir->size);
return retval;
+
+too_large:
+ /* This should be changed for sizes > 1 page. */
+ printk("kmalloc called with impossibly large argument (%d)\n", len);
+ return NULL;
}
/*
@@ -257,20 +244,22 @@ kmalloc(unsigned int len, int priority)
*/
void kfree_s(void *obj, int size)
{
- void *page;
+ unsigned long flags;
+ void *page;
struct _bucket_dir *bdir;
struct bucket_desc *bdesc, *prev;
+ if (!obj)
+ return;
+ save_flags(flags);
/* Calculate what page this object lives in */
page = (void *) ((unsigned long) obj & 0xfffff000);
/* Now search the buckets looking for that page */
- for (bdir = bucket_dir; bdir->size; bdir++)
- {
+ for (bdir = bucket_dir; bdir->size; bdir++) {
prev = 0;
/* If size is zero then this conditional is always true */
- if (bdir->size >= size)
- {
+ if (bdir->size >= size) {
/* We have to turn off interrupts here because
we are descending the chain. If something
changes it in the middle we could suddenly
@@ -278,20 +267,21 @@ void kfree_s(void *obj, int size)
I think this would only cause a memory
leak, but better safe than sorry. */
cli(); /* To avoid race conditions */
- for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next)
- {
+ for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) {
if (bdesc->page == page)
- goto found;
+ goto found;
prev = bdesc;
- }
- }
- }
+ }
+ }
+ }
+ restore_flags(flags);
printk("Bad address passed to kernel kfree_s(%X, %d)\n",obj, size);
- sti();
+ printk("Offending eip: %08x\n",((unsigned long *) &obj)[-1]);
return;
found:
/* interrupts are off here. */
+ memset(obj, 0xf8, bdir->size);
*((void **)obj) = bdesc->freeptr;
bdesc->freeptr = obj;
bdesc->refcnt--;
@@ -316,6 +306,6 @@ found:
free_bucket_desc = bdesc;
free_page((unsigned long) bdesc->page);
}
- sti();
+ restore_flags(flags);
return;
}
diff --git a/mm/memory.c b/mm/memory.c
index 40389e0..eb17ba6 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -37,6 +37,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/ptrace.h>
unsigned long high_memory = 0;
@@ -112,17 +113,35 @@ static void free_one_table(unsigned long * page_dir)
void clear_page_tables(struct task_struct * tsk)
{
int i;
+ unsigned long pg_dir;
unsigned long * page_dir;
if (!tsk)
return;
if (tsk == task[0])
panic("task[0] (swapper) doesn't support exec()\n");
- page_dir = (unsigned long *) tsk->tss.cr3;
+ pg_dir = tsk->tss.cr3;
+ page_dir = (unsigned long *) pg_dir;
if (!page_dir || page_dir == swapper_pg_dir) {
printk("Trying to clear kernel page-directory: not good\n");
return;
}
+ if (mem_map[MAP_NR(pg_dir)] > 1) {
+ unsigned long page;
+ unsigned long * new;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page) {
+ oom(tsk);
+ return;
+ }
+ new = (unsigned long *) page;
+ for (i = 768 ; i < 1024 ; i++)
+ new[i] = page_dir[i];
+ free_page(pg_dir);
+ tsk->tss.cr3 = page;
+ return;
+ }
for (i = 0 ; i < 768 ; i++,page_dir++)
free_one_table(page_dir);
invalidate();
@@ -284,7 +303,9 @@ int unmap_page_range(unsigned long from, unsigned long size)
if ((page = *page_table) != 0) {
*page_table = 0;
if (1 & page) {
- --current->rss;
+ if (!(mem_map[MAP_NR(page)]
+ & MAP_PAGE_RESERVED))
+ --current->rss;
free_page(0xfffff000 & page);
} else
swap_free(page);
@@ -341,13 +362,13 @@ int zeromap_page_range(unsigned long from, unsigned long size, int mask)
if ((page = *page_table) != 0) {
*page_table = 0;
if (page & PAGE_PRESENT) {
- --current->rss;
+ if (!(mem_map[MAP_NR(page)]
+ & MAP_PAGE_RESERVED))
+ --current->rss;
free_page(0xfffff000 & page);
} else
swap_free(page);
}
- if (mask)
- ++current->rss;
*page_table++ = mask;
}
pcnt = (size > 1024 ? 1024 : size);
@@ -402,7 +423,9 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
if ((page = *page_table) != 0) {
*page_table = 0;
if (PAGE_PRESENT & page) {
- --current->rss;
+ if (!(mem_map[MAP_NR(page)]
+ & MAP_PAGE_RESERVED))
+ --current->rss;
free_page(0xfffff000 & page);
} else
swap_free(page);
@@ -418,10 +441,11 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
if (!mask || to >= high_memory || !mem_map[MAP_NR(to)])
*page_table++ = 0; /* not present */
else {
- ++current->rss;
*page_table++ = (to | mask);
- if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED))
+ if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) {
+ ++current->rss;
mem_map[MAP_NR(to)]++;
+ }
}
to += PAGE_SIZE;
}
@@ -440,7 +464,7 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
static unsigned long put_page(struct task_struct * tsk,unsigned long page,
unsigned long address,int prot)
{
- unsigned long tmp, *page_table;
+ unsigned long *page_table;
if ((prot & 0xfffff001) != PAGE_PRESENT)
printk("put_page: prot = %08x\n",prot);
@@ -448,11 +472,6 @@ static unsigned long put_page(struct task_struct * tsk,unsigned long page,
printk("put_page: trying to put page %p at %p\n",page,address);
return 0;
}
- tmp = mem_map[MAP_NR(page)];
- if (!(tmp & MAP_PAGE_RESERVED) && (tmp != 1)) {
- printk("put_page: mem_map disagrees with %p at %p\n",page,address);
- return 0;
- }
page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
if ((*page_table) & PAGE_PRESENT)
page_table = (unsigned long *) (0xfffff000 & *page_table);
@@ -550,6 +569,8 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
old_page &= 0xfffff000;
if (mem_map[MAP_NR(old_page)] != 1) {
if (new_page) {
+ if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
+ ++tsk->rss;
copy_page(old_page,new_page);
*(unsigned long *) pte = new_page | prot;
free_page(old_page);
@@ -841,7 +862,6 @@ void do_no_page(unsigned long error_code, unsigned long address,
++tsk->min_flt;
return;
}
- ++tsk->maj_flt;
if (!page) {
oom(current);
put_page(tsk,BAD_PAGE,address,PAGE_PRIVATE);
@@ -876,24 +896,24 @@ void do_no_page(unsigned long error_code, unsigned long address,
* and the problem, and then passes it off to one of the appropriate
* routines.
*/
-void do_page_fault(unsigned long *esp, unsigned long error_code)
+void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
unsigned long address;
unsigned long user_esp = 0;
unsigned long stack_limit;
unsigned int bit;
- extern void die_if_kernel(char *,long,long);
+ extern void die_if_kernel(char *,struct pt_regs *,long);
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
if (address < TASK_SIZE) {
if (error_code & 4) { /* user mode access? */
- if (esp[2] & VM_MASK) {
+ if (regs->eflags & VM_MASK) {
bit = (address - 0xA0000) >> PAGE_SHIFT;
if (bit < 32)
current->screen_bitmap |= 1 << bit;
} else
- user_esp = esp[3];
+ user_esp = regs->esp;
}
if (error_code & 1)
do_wp_page(error_code, address, current, user_esp);
@@ -912,7 +932,7 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
return;
}
printk("Unable to handle kernel paging request at address %08x\n",address);
- die_if_kernel("Oops",(long)esp,error_code);
+ die_if_kernel("Oops", regs, error_code);
do_exit(SIGKILL);
}
@@ -1014,14 +1034,15 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
start_mem += 4095;
start_mem &= 0xfffff000;
address = 0;
- pg_dir = swapper_pg_dir + 768; /* at virtual addr 0xC0000000 */
+ pg_dir = swapper_pg_dir;
while (address < end_mem) {
- tmp = *pg_dir;
+ tmp = *(pg_dir + 768); /* at virtual addr 0xC0000000 */
if (!tmp) {
- tmp = start_mem;
- *pg_dir = tmp | PAGE_TABLE;
+ tmp = start_mem | PAGE_TABLE;
+ *(pg_dir + 768) = tmp;
start_mem += 4096;
}
+ *pg_dir = tmp; /* also map it in at 0x0000000 for init */
pg_dir++;
pg_table = (unsigned long *) (tmp & 0xfffff000);
for (tmp = 0 ; tmp < 1024 ; tmp++,pg_table++) {
diff --git a/mm/mmap.c b/mm/mmap.c
index 79f0e0f..dfee9f8 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -78,13 +78,8 @@ int sys_mmap(unsigned long *buffer)
* that it represents a valid section of the address space. we assume
* that if PROT_EXEC is specified this should be in the code segment.
*/
- if (prot & PROT_EXEC) {
- base = get_base(current->ldt[1]); /* cs */
- limit = get_limit(0x0f); /* cs limit */
- } else {
- base = get_base(current->ldt[2]); /* ds */
- limit = get_limit(0x17); /* ds limit */
- }
+ base = 0;
+ limit = TASK_SIZE;
if (flags & MAP_FIXED) {
/*
@@ -139,8 +134,8 @@ int sys_munmap(unsigned long addr, size_t len)
{
unsigned long base, limit;
- base = get_base(current->ldt[2]); /* map into ds */
- limit = get_limit(0x17); /* ds limit */
+ base = 0;
+ limit = TASK_SIZE;
if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 ||
addr + len > limit)
diff --git a/mm/swap.c b/mm/swap.c
index 60dd311..2dc2f04 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -25,7 +25,7 @@
#define SWP_USED 1
#define SWP_WRITEOK 3
-#define SWP_TYPE(entry) (((entry) & 0xffe) >> 1)
+#define SWP_TYPE(entry) (((entry) & 0xfe) >> 1)
#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT))
@@ -43,6 +43,7 @@ static struct swap_info_struct {
} swap_info[MAX_SWAPFILES];
extern unsigned long free_page_list;
+extern int shm_swap (int);
/*
* The following are used to make sure we don't thrash too much...
@@ -95,7 +96,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
wake_up(&lock_queue);
}
-static unsigned int get_swap_page(void)
+unsigned int get_swap_page(void)
{
struct swap_info_struct * p;
unsigned int offset, type;
@@ -126,6 +127,8 @@ unsigned long swap_duplicate(unsigned long entry)
return 0;
offset = SWP_OFFSET(entry);
type = SWP_TYPE(entry);
+ if (type == SHM_SWP_TYPE)
+ return entry;
if (type >= nr_swapfiles) {
printk("Trying to duplicate nonexistent swap-page\n");
return 0;
@@ -151,6 +154,8 @@ void swap_free(unsigned long entry)
if (!entry)
return;
type = SWP_TYPE(entry);
+ if (type == SHM_SWP_TYPE)
+ return;
if (type >= nr_swapfiles) {
printk("Trying to free nonexistent swap-page\n");
return;
@@ -194,6 +199,8 @@ void swap_in(unsigned long *table_ptr)
printk("No swap page in swap_in\n");
return;
}
+ if (SWP_TYPE(entry) == SHM_SWP_TYPE)
+ return shm_no_page ((unsigned long *) table_ptr);
page = get_free_page(GFP_KERNEL);
if (!page) {
oom(current);
@@ -332,6 +339,8 @@ static int try_to_free_page(void)
while (i--) {
if (shrink_buffers(i))
return 1;
+ if (shm_swap(i))
+ return 1;
if (swap_out(i))
return 1;
}
diff --git a/net/Makefile b/net/Makefile
index 7b8672c..84c95da 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -8,14 +8,10 @@
# Note 2! The CFLAGS definition is now in the main makefile...
# only these two lines should need to be changed to remove inet sockets.
-# (and the tcp/tcpip.o in net.o)
+# (and the inet/tcpip.o in net.o)
-SUBDIRS = tcp
-
-ifdef CONFIG_TCPIP
-NET_SUBDIRS = tcp
-TCP_ARCHIVE = tcp/tcpip.a
-endif
+DRIVERS = drv
+SUBDIRS := unix inet
.c.o:
$(CC) $(CFLAGS) -c $<
@@ -24,22 +20,26 @@ endif
.c.s:
$(CC) $(CFLAGS) -S $<
-OBJS = socket.o unix.o
+OBJS = Space.o ddi.o socket.o
-net.o: $(OBJS) subdirs
- $(LD) -r -o net.o $(OBJS) $(TCP_ARCHIVE)
+all: net.o
+net.o: $(OBJS) subdirs
+ $(LD) -r -o net.o $(OBJS) $(DRIVERS)/$(DRIVERS).a network.a
-subdirs: dummy
- for i in $(NET_SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
+subdirs: dummy
+ @rm -f network.a
+ @for i in $(DRIVERS); do (cd $$i && echo $$i && $(MAKE) $$i.a) || exit; done
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) && ar rcs ../network.a $$i.o) || exit; done
+ @ranlib network.a
clean:
- rm -f core *.o *.a *.s
- @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) clean) || exit; done
+ rm -f core *.o *.a *.s .depend
+ @for i in $(DRIVERS) $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) clean) || exit; done
dep:
- $(CPP) -M *.c > .depend
- @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+ $(CPP) -M *.c > .depend
+ @for i in $(DRIVERS) $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
dummy:
diff --git a/net/Space.c b/net/Space.c
new file mode 100644
index 0000000..e3505a9
--- /dev/null
+++ b/net/Space.c
@@ -0,0 +1,83 @@
+/*
+ * Space.c Defines which protocol modules and I/O device drivers get
+ * linked into the LINUX kernel. Currently, this is only used
+ * by the NET layer of LINUX, but it eventually might move to
+ * an upper directory of the system.
+ *
+ * Version: @(#)Space.c 1.0.2 04/22/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ddi.h>
+
+
+#define CONFIG_UNIX YES /* always present... */
+
+
+/*
+ * Section A: Networking Protocol Handlers.
+ * This section defines which networking protocols get
+ * linked into the SOCKET layer of the Linux kernel.
+ * Currently, these are AF_UNIX (always) and AF_INET.
+ */
+#ifdef CONFIG_UNIX
+# include "unix/unix.h"
+#endif
+#ifdef CONFIG_INET
+# include "inet/inet.h"
+#endif
+
+struct ddi_proto protocols[] = {
+#ifdef CONFIG_UNIX
+ { "UNIX", unix_proto_init },
+#endif
+#ifdef CONFIG_INET
+ { "INET", inet_proto_init },
+#endif
+ { NULL, NULL }
+};
+
+
+/*
+ * Section B: Device Driver Modules.
+ * This section defines which network device drivers
+ * get linked into the Linux kernel. It is currently
+ * only used by the INET protocol. Any takers for the
+ * other protocols like XNS or Novell?
+ *
+ * WARNING: THIS SECTION IS NOT YET USED BY THE DRIVERS !!!!!
+ */
+#include "drv/we8003/we8003.h" /* Western Digital WD-80[01]3 */
+/*#include "drv/dp8390/dp8390.h" Donald Becker's DP8390 kit */
+/*#inclde "drv/slip/slip.h" Laurence Culhane's SLIP kit */
+
+
+struct ddi_device devices[] = {
+#if CONF_WE8003
+ { "WD80x3[EBT]",
+ "", 0, 1, we8003_init, NULL,
+ 19, 0, DDI_FCHRDEV,
+ { 0x280, 0, 15, 0, 32768, 0xD0000 } },
+#endif
+#if CONF_DP8390
+ { "DP8390/WD80x3",
+ "", 0, 1, dpwd8003_init, NULL,
+ 20, 0, DDI_FCHRDEV,
+ { 0, 0, 0, 0, 0, 0, } },
+ { "DP8390/NE-x000",
+ "", 0, 1, dpne2000_init, NULL,
+ 20, 8, DDI_FCHRDEV,
+ { 0, 0, 0, 0, 0, 0, } },
+ { "DP8390/3C50x",
+ "", 0, 1, dpec503_init, NULL,
+ 20, 16, DDI_FCHRDEV,
+ { 0, 0, 0, 0, 0, 0, } },
+#endif
+ { NULL,
+ "", 0, 0, NULL, NULL,
+ 0, 0, 0,
+ { 0, 0, 0, 0, 0, 0 } }
+};
diff --git a/net/ddi.c b/net/ddi.c
new file mode 100644
index 0000000..7ebd3bf
--- /dev/null
+++ b/net/ddi.c
@@ -0,0 +1,91 @@
+/*
+ * ddi.c Implement the Device Driver Interface (DDI) routines.
+ * Currently, this is only used by the NET layer of LINUX,
+ * but it eventually might move to an upper directory of
+ * the system.
+ *
+ * Version: @(#)ddi.c 1.0.5 04/22/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/ddi.h>
+
+
+#undef DDI_DEBUG
+#ifdef DDI_DEBUG
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x) /**/
+#endif
+
+
+extern struct ddi_device devices[]; /* device driver map */
+extern struct ddi_proto protocols[]; /* network protocols */
+
+
+/*
+ * This function gets called with an ASCII string representing the
+ * ID of some DDI driver. We loop through the DDI Devices table
+ * and return the address of the control block that has a matching
+ * "name" field. It is used by upper-level layers that want to
+ * dynamically bind some UNIX-domain "/dev/XXXX" file name to a
+ * DDI device driver. The "iflink(8)" program is an example of
+ * this behaviour.
+ */
+struct ddi_device *
+ddi_map(const char *id)
+{
+ register struct ddi_device *dev;
+
+ PRINTK (("DDI: MAP: looking for \"%s\": ", id));
+ dev = devices;
+ while (dev->title != NULL) {
+ if (strncmp(dev->name, id, DDI_MAXNAME) == 0) {
+ PRINTK (("OK at 0x%X\n", dev));
+ return(dev);
+ }
+ dev++;
+ }
+ PRINTK (("NOT FOUND\n"));
+ return(NULL);
+}
+
+
+/*
+ * This is the function that is called by a kernel routine during
+ * system startup. Its purpose is to walk trough the "devices"
+ * table (defined above), and to call all moduled defined in it.
+ */
+void
+ddi_init(void)
+{
+ struct ddi_proto *pro;
+ struct ddi_device *dev;
+
+ PRINTK (("DDI: Starting up!\n"));
+
+ /* First off, kick all configured protocols. */
+ pro = protocols;
+ while (pro->name != NULL) {
+ (*pro->init)(pro);
+ pro++;
+ }
+
+ /* Done. Now kick all configured device drivers. */
+ dev = devices;
+ while (dev->title != NULL) {
+ (*dev->init)(dev);
+ dev++;
+ }
+
+ /* We're all done... */
+}
diff --git a/net/drv/Makefile b/net/drv/Makefile
new file mode 100644
index 0000000..0ef1d71
--- /dev/null
+++ b/net/drv/Makefile
@@ -0,0 +1,47 @@
+#
+# Makefile for the linux device drivers.
+#
+# 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 definitions are now in the main makefile...
+
+OBJS =
+SUBDIRS = #we8003 slip dp8390
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+all: drv.a
+
+drv.a: $(OBJS) dummy
+ @rm -f drv.a
+ @$(AR) rcs drv.a
+ @for i in $(SUBDIRS); do [ ! -d $$i ] || \
+ (cd $$i && echo $$i && $(MAKE) && \
+ $(AR) rcs ../drv.a $$i.o) || \
+ exit; \
+ done
+
+
+clean:
+ rm -f core *.o *.a tmp_make .depend
+ for i in *.c; do rm -f `basename $$i .c`.s;done
+ for i in $(SUBDIRS); do ([ -d $$i ] && cd $$i && $(MAKE) clean); done
+
+dep:
+ > .depend
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+
+dummy:
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/net/drv/README b/net/drv/README
new file mode 100644
index 0000000..ee91361
--- /dev/null
+++ b/net/drv/README
@@ -0,0 +1,9 @@
+
+ The files and directories in this directory are under
+ heavy construction, and are intended only for device
+ driver writers to see how they should redo their code
+ to make it fit into DDI.
+
+ THESE DRIVERS ARE NOT YET OPERATIONAL !!!
+
+ Fred, 05/07/93
diff --git a/net/drv/slip/Makefile b/net/drv/slip/Makefile
new file mode 100644
index 0000000..d169e27
--- /dev/null
+++ b/net/drv/slip/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for the SLIP device driver..
+#
+# 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 definitions are now inherited from the
+# parent makes..
+#
+
+SUBDIRS =
+
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+
+slip.o: slip.c
+
+clean:
+ rm -f core *.o *.a tmp_make keyboard.s
+ for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+ $(CPP) -M *.c > .depend
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+
+dummy:
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/net/drv/slip/slip.c b/net/drv/slip/slip.c
new file mode 100644
index 0000000..f433af2
--- /dev/null
+++ b/net/drv/slip/slip.c
@@ -0,0 +1,661 @@
+/*
+ * slip.c This module implements the SLIP protocol for kernel-based
+ * devices like TTY. It interfaces between a raw TTY, and the
+ * kernel's NET protocol layers (via DDI).
+ *
+ * Version: @(#)slip.c 0.5.0 (02/11/93)
+ *
+ * Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#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 <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/slip.h>
+#include <netinet/in.h>
+
+
+#define SLIP_VERSION "0.5.0"
+#define SL_DUMP
+
+
+#define SL_DEBUG
+#ifdef SL_DEBUG
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x) /**/
+#endif
+
+
+/* Define some IP layer stuff. Not all systems have it. */
+#ifdef SL_DUMP
+# define IP_VERSION 4 /* version# of our IP software */
+# define IPF_F_OFFSET 0x1fff /* Offset field */
+# define IPF_DF 0x4000 /* Don't fragment flag */
+# define IPF_MF 0x2000 /* More Fragments flag */
+
+ typedef struct ipheader {
+ u_char v_ihl; /* Version + IP header length */
+ u_char tos; /* Type of service */
+ u_short length; /* Total length */
+ u_short id; /* Identification */
+ u_short fl_offs; /* Flags + fragment offset */
+ u_char ttl; /* Time to live */
+ u_char protocol; /* Protocol */
+ u_short checksum; /* Header checksum */
+ u_long source; /* Source address */
+ u_long dest; /* Destination address */
+ } IP;
+# define IP_OF_COPIED 0x80 /* Copied-on-fragmentation flag */
+# define IP_OF_CLASS 0x60 /* Option class */
+# define IP_OF_NUMBER 0x1f /* Option number */
+# define IPO_EOL 0 /* End of options list */
+# define IPO_NOOP 1 /* No Operation */
+# define IPO_SECURITY 2 /* Security parameters */
+# define IPO_LSROUTE 3 /* Loose Source Routing */
+# define IPO_TIMESTAMP 4 /* Internet Timestamp */
+# define IPO_RROUTE 7 /* Record Route */
+# define IPO_STREAMID 8 /* Stream ID */
+# define IPO_SSROUTE 9 /* Strict Source Routing */
+# define IP_TS_ONLY 0 /* Time stamps only */
+# define IP_TS_ADDRESS 1 /* Addresses + Time stamps */
+# define IP_TS_PRESPEC 3 /* Prespecified addresses only */
+#endif
+
+
+/* This table holds the control blocks for all SLIP channels. */
+static struct slip sl_ctrl[SL_NRUNIT];
+
+
+#ifdef SL_DUMP
+/* Dump the contents of an IP datagram. */
+static void
+ip_dump(unsigned char *ptr, int len)
+{
+ int hdr_ver, hdr_len, dta_len, dta_off;
+ IP *ip;
+ extern char *in_ntoa(long num);
+
+ ip = (IP *) ptr;
+ hdr_ver = (ip->v_ihl & 0xF0) >> 4;
+ hdr_len = (ip->v_ihl & 0x0F) * sizeof(long);
+ dta_len = ntohs(ip->length);
+ dta_off = (ntohs(ip->fl_offs) & IPF_F_OFFSET) << 3 ;
+
+ printk("\r*****\n");
+ printk("SLIP: %s->", in_ntoa(ip->source));
+ printk("%s\n", in_ntoa(ip->dest));
+ printk(" len %u ihl %u ttl %u prot %u",
+ dta_len, ip->v_ihl & 0xFF, ip->ttl & 0xFF, ip->protocol & 0xFF);
+
+ if (ip->tos != 0) printk(" tos %u", ip->tos);
+ if (dta_off != 0 || (ntohs(ip->fl_offs) & IPF_MF))
+ printk(" id %u offs %u", ntohs(ip->id), dta_off);
+
+ if (ntohs(ip->fl_offs) & IPF_DF) printk(" DF");
+ if (ntohs(ip->fl_offs) & IPF_MF) printk(" MF");
+ printk("\n*****\n");
+}
+#endif
+
+
+/*
+ * Read data from a TTY queue. This function will eventually
+ * be moved into the TTY layer itself, making it available for
+ * other layers, too.
+ */
+int tty_read_data(struct tty_struct *tty, unsigned char *buf, int max)
+{
+ register int count;
+ register unsigned char c;
+
+ /* Keep fetching characters from TTY until done or full. */
+ count = 0;
+ PRINTK (("SLIP: tty_read:"));
+ while (max-- > 0) {
+ if (EMPTY(&tty->read_q)) break;
+ c = (get_tty_queue(&tty->read_q) & 0377);
+ *buf++ = c;
+ PRINTK ((" %02x", (int) (c & 255)));
+ count++;
+ }
+ PRINTK (("\r\nSLIP: tty_read: read %d bytes\r\n", count));
+ return(count);
+}
+
+
+/*
+ * Write data to a TTY queue. This function will eventually
+ * be moved into the TTY layer itself, making it available for
+ * other layers, too.
+ */
+void tty_write_data(struct tty_struct *tty, char *buf, int count)
+{
+ /* PRINTK (("SLIP: tty_write: writing %d bytes\r\n", count)); */
+ while(count--) {
+ put_tty_queue(*buf++, &tty->write_q);
+ }
+}
+
+
+/*
+ * Flush a TTY write queue by calling the TTY layer. This
+ * function will eventually be moved into the TTY layer itself,
+ * making it available for other layers, too.
+ */
+void tty_flush(struct tty_struct *tty)
+{
+ /* PRINTK (("SLIP: tty_flush: flusing the toilet...\r\n")); */
+ /*
+ * This should also tell TTY which function to call-back
+ * when the work is done, allowing us to clean up and
+ * possibly start another output...
+ */
+ tty_write_flush(tty);
+}
+
+
+/* Find a SLIP channel from its `tty' link. */
+static struct slip *
+sl_find(struct tty_struct *tty)
+{
+ int i;
+ struct slip *sl;
+
+ if (tty == NULL) return(NULL);
+ for (i = 0; i < SL_NRUNIT; i++) {
+ sl = &sl_ctrl[i];
+ if (sl->tty == tty) return(sl);
+ }
+ return(NULL);
+}
+
+
+/* Find a free SLIP channel, and link in this `tty' line. */
+static inline struct slip *
+sl_alloc(void)
+{
+ int i;
+ struct slip *sl;
+ unsigned long flags;
+
+ for (i = 0; i < SL_NRUNIT; i++) {
+ sl = &sl_ctrl[i];
+ if (sl->inuse == 0) {
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->inuse++;
+ sl->tty = NULL;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ return(sl);
+ }
+ }
+ return(NULL);
+}
+
+
+/* Free a SLIP channel. */
+static inline void
+sl_free(struct slip *sl)
+{
+ unsigned long flags;
+
+ if (sl->inuse == 1) {
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->inuse--;
+ sl->tty = NULL;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ }
+}
+
+
+/* Stuff one byte into a SLIP queue. */
+static inline void
+put_sl_queue(struct sl_queue * queue, char c)
+{
+ int head;
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ head = (queue->head + 1) & (SL_BUF_SIZE-1);
+ if (head != queue->tail) {
+ queue->buf[queue->head] = c;
+ queue->head = head;
+ }
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Release 'i' bytes from a SLIP queue. */
+static inline void
+eat_sl_queue(struct sl_queue * queue, int i)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ if (queue->tail != queue->head)
+ queue->tail = (queue->tail + i) & (SL_BUF_SIZE-1);
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Set the "sending" flag. This must be atomic, hence the ASM. */
+static inline void
+sl_lock(struct slip *sl)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->sending = 1;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Clear the "sending" flag. This must be atomic, hence the ASM. */
+static inline void
+sl_unlock(struct slip *sl)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->sending = 0;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+static void
+sl_recv(struct slip *sl, int len)
+{
+#if 0
+ struct device *dev;
+#endif
+ register unsigned char *p;
+ int done;
+
+ PRINTK (("SLIP: sending one dgram to IP (len=%d)\r\n", len));
+#ifdef SL_DUMP
+ printk("<< iface \"sl%d\" recv:\r\n", sl->line);
+ ip_dump((unsigned char *) &sl->rcv_queue.buf[sl->rcv_queue.tail], len);
+#endif
+
+ /* Bump the datagram to the upper layers... */
+#if 0
+ dev = sl->dev;
+ p = (unsigned char *) &sl->rcv_queue.buf[sl->rcv_queue.tail];
+ do {
+ done = dev_rint(p, len, 0, dev);
+ if (done == 1) break;
+ } while(1);
+#endif
+ eat_sl_queue(&sl->rcv_queue, len);
+ sl->rcvd++;
+}
+
+
+/* Encapsulate one IP datagram and stuff into a TTY queue. */
+static void
+sl_send(struct slip *sl, unsigned char *p, int len)
+{
+ register unsigned char *bp;
+ register int count;
+
+ /* PRINTK (("SLIP: sl_send(0x%X, %d) called\n", p, len)); */
+ bp = (unsigned char *)sl->xbuff;
+#ifdef SL_DUMP
+ printk(">> iface \"sl%d\" sent:\r\n", sl->line);
+ ip_dump(p, len);
+#endif
+ count = 0;
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+ *bp++ = END;
+ count++;
+
+ /*
+ * For each byte in the packet, send the appropriate
+ * character sequence, according to the SLIP protocol.
+ * FIXME: change this to copy blocks of characters between
+ * special characters to improve speed.
+ */
+ while(len--) {
+ switch(*p) {
+ case END:
+ *bp++ = ESC;
+ *bp++ = ESC_END;
+ count += 2;
+ break;
+ case ESC:
+ *bp++ = ESC;
+ *bp++ = ESC_ESC;
+ count += 2;
+ break;
+ default:
+ *bp++ = *p;
+ count++;
+ }
+ p++;
+ }
+ *bp++ = END;
+ count++;
+ sl->sent++;
+ tty_write_data(sl->tty, sl->xbuff, count); /* stuff into TTY */
+}
+
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+static int
+sl_start_xmit(void /*struct sk_buff*/ *skb, void /*struct device*/ *dev)
+{
+ struct slip *sl;
+ struct tty_struct *tty;
+
+#if 0
+ /* Find the correct SLIP channel to use. */
+ sl = &sl_ctrl[dev->base_addr];
+ tty = sl->tty;
+ /* PRINTK (("SLIP: sl_start_xmit(\"%s\") skb=0x%X busy=%d\n",
+ dev->name, skb, sl->sending)); */
+
+ /*
+ * If we are busy already- too bad. We ought to be able
+ * to queue things at this point, to allow for a little
+ * frame buffer. Oh well...
+ */
+ if (sl->sending) {
+ PRINTK (("SLIP: sl_start_xmit: BUSY\r\n"));
+ return(1);
+ }
+
+ /* We were not, so we are now... :-) */
+ sti();
+ sl_lock(sl);
+
+ if (skb != NULL) {
+ /* PRINTK (("SLIP: sl_start_xmit: encaps(0x%X, %d)\r\n",
+ (unsigned) skb, skb->len)); */
+ sl_send(sl, (unsigned char *) (skb + 1), skb->len);
+ }
+
+ /* PRINTK (("SLIP: sl_start_xmit: kicking TTY!\n")); */
+ tty_flush(tty); /* kick TTY in the butt */
+ sl_unlock(sl);
+#endif
+ return(0);
+}
+
+
+/*
+ * Return the frame type ID. Shouldn't we pick this up from the
+ * frame on which we have to operate, like in 'eth' ? - FvK
+ */
+static unsigned short
+sl_type_trans (void /*struct sk_buff*/ *skb, void /*struct device*/ *dev)
+{
+#ifdef notdef
+ struct slip *sl;
+
+ sl = sl_ctrl[dev->base_addr];
+ return(sl->type);
+#else
+ return(NET16(ETHERTYPE_IP));
+#endif
+}
+
+
+/* Open the low-level part of the SLIP channel. Easy! */
+static int
+sl_open(void /*struct device*/ *dev)
+{
+ struct slip *sl;
+
+#if 0
+ sl = &sl_ctrl[dev->base_addr];
+ if (sl->tty == NULL) {
+ PRINTK (("SLIP: channel sl%d not connected!\n", sl->line));
+ return(-ENXIO);
+ }
+
+ sl->escape = 0; /* SLIP state machine */
+ sl->received = 0; /* SLIP receiver count */
+ PRINTK (("SLIP: channel sl%d opened.\n", sl->line));
+#endif
+ return(0);
+}
+
+
+/* Close the low-level part of the SLIP channel. Easy! */
+static int
+sl_close(void /*struct device*/ *dev)
+{
+ struct slip *sl;
+
+#if 0
+ sl = &sl_ctrl[dev->base_addr];
+ if (sl->tty == NULL) {
+ PRINTK (("SLIP: channel sl%d not connected!\n", sl->line));
+ return(-EBUSY);
+ }
+ sl_free(sl);
+
+ /*
+ * The next two lines should be handled by a "dev_down()"
+ * function, which takes care of shutting down an inter-
+ * face. It would also be called by the "ip" module when
+ * an interface is brought down manually.
+ */
+ del_devroute(dev);
+ dev->up = 0;
+ PRINTK (("SLIP: channel sl%d closed.\n", sl->line));
+#endif
+ return(0);
+}
+
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLIP data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void
+slip_recv(struct tty_struct *tty)
+{
+ unsigned char buff[SL_MTU * 2];
+ register unsigned char *p;
+ register int count;
+ struct slip *sl;
+ unsigned char c;
+
+#if 0
+PRINTK (("SLIP: slip_recv(%d) called\n", tty->line));
+ if ((sl = sl_find(tty)) == NULL) return; /* not connected */
+
+ if (SL_FULL(&sl->rcv_queue)) {
+ PRINTK (("SLIP: recv queue full\r\n"));
+ return;
+ }
+
+ while((count = tty_read_data(tty, buff, (SL_MTU * 2))) > 0) {
+ p = buff;
+ while(count-- > 0) {
+ c = *p++;
+ switch(c) {
+ case ESC:
+ sl->escape = 1;
+ break;
+ case ESC_ESC:
+ if (sl->escape) c = ESC;
+ put_sl_queue(&sl->rcv_queue, c);
+ sl->escape = 0;
+ sl->received++;
+ break;
+ case ESC_END:
+ if (sl->escape) c = END;
+ put_sl_queue(&sl->rcv_queue, c);
+ sl->escape = 0;
+ sl->received++;
+ break;
+ case END:
+ sl->escape = 0;
+ if (sl->received < 3) {
+ if (sl->received)
+ eat_sl_queue(&sl->rcv_queue,
+ sl->received);
+ sl->received = 0;
+ } else {
+ PRINTK (("SLIP: full frame received!\r\n"));
+ sl_recv(sl, sl->received);
+ sl->received = 0;
+ }
+ break;
+ default:
+ put_sl_queue(&sl->rcv_queue, c);
+ sl->escape = 0;
+ sl->received++;
+ }
+ }
+ }
+#endif
+}
+
+
+/* Return the channel number of a SLIP connection. */
+static int
+slip_chan(struct tty_struct *tty)
+{
+ struct slip *sl;
+
+ if ((sl = sl_find(tty)) == NULL) return(-ENXIO); /* not connected */
+ return(sl->line);
+}
+
+
+/*
+ * Open the high-level part of the SLIP channel.
+ * This function is called by the TTY module when the
+ * SLIP line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLIP channel...
+ */
+static int
+slip_open(struct tty_struct *tty)
+{
+ struct slip *sl;
+
+ /* First make sure we're not already connected. */
+ if ((sl = sl_find(tty)) != NULL) {
+ PRINTK (("SLIP: TTY %d already connected to sl%d !\n",
+ tty->line, sl->line));
+ return(-EEXIST);
+ }
+
+ /* OK. Find a free SLIP channel to use. */
+ if ((sl = sl_alloc()) == NULL) {
+ PRINTK (("SLIP: TTY %d not connected: all channels in use!\n",
+ tty->line));
+ return(-ENFILE);
+ }
+ sl->tty = tty;
+
+ /* Link the TTY line to this channel. */
+ (void) sl_open(sl->dev);
+ PRINTK (("SLIP: TTY %d connected to sl%d.\n", tty->line, sl->line));
+
+ /* Done. We have linked the TTY line to a channel. */
+ return(sl->line);
+}
+
+
+/*
+ * Close down a SLIP channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to SLIP
+ * (which usually is TTY again).
+ */
+static void
+slip_close(struct tty_struct *tty)
+{
+ struct slip *sl;
+
+ /* First make sure we're connected. */
+ if ((sl = sl_find(tty)) == NULL) {
+ PRINTK (("SLIP: TTY %d not connected !\n", tty->line));
+ return;
+ }
+
+ (void) sl_close(sl->dev);
+ PRINTK (("SLIP: TTY %d disconnected from sl%d.\n", tty->line, sl->line));
+}
+
+
+/* Initialize the SLIP driver. Called by DDI. */
+int
+slip_init(struct ddi *dev)
+{
+ int i;
+ struct slip *sl;
+
+#if 1
+ PRINTK(("SLIP/DDI: version %s (%d channels, buffer=0x%X:%d)\n",
+ ddi->ioaddr, ddi->memaddr, ddi->memsize));
+#else
+ sl = &sl_ctrl[dev->base_addr];
+
+ if (already++ == 0) {
+ printk("SLIP: version %s (%d channels): ",
+ SLIP_VERSION, SL_NRUNIT);
+ if ((i = tty_set_ldisc(N_SLIP, slip_open, slip_close,
+ slip_chan, slip_recv)) == 0) printk("OK\n");
+ else printk("ERROR: %d\n", i);
+ }
+
+ /* Set up the "SLIP Control Block". */
+ sl->inuse = 0; /* not allocated now */
+ sl->line = dev->base_addr; /* SLIP channel number */
+ sl->tty = NULL; /* pointer to TTY line */
+ sl->dev = dev; /* pointer to DEVICE */
+ sl->sending = 0; /* locked on output */
+ sl->rcv_queue.head = 0; /* ptr to RECV queue */
+ sl->rcv_queue.tail = 0; /* ptr to RECV queue */
+ sl->escape = 0; /* SLIP state machine */
+ sl->received = 0; /* SLIP receiver count */
+ sl->sent = 0; /* #frames sent out */
+ sl->rcvd = 0; /* #frames received */
+ sl->errors = 0; /* not used at present */
+
+ /* Finish setting up the DEVICE info. */
+ dev->mtu = SL_MTU;
+ dev->rmem_end = (unsigned long)&sl->rcv_queue.buf[SL_BUF_SIZE-1];
+ dev->rmem_start = (unsigned long)&sl->rcv_queue.buf[0];
+ dev->mem_end = (unsigned long)&sl->xbuff[(SL_MTU * 2) -1];
+ dev->mem_start = (unsigned long)&sl->xbuff[0];
+ dev->hard_start_xmit = sl_start_xmit;
+ dev->open = sl_open;
+ dev->stop = sl_close;
+ dev->hard_header = sl_hard_header;
+ dev->add_arp = sl_add_arp;
+ dev->type_trans = sl_type_trans;
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->type = 0; /* FIXME: ??? */
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = sl_rebuild_header;
+ for (i = 0; i < DEV_NUMBUFFS; i++) dev->buffs[i] = NULL;
+
+#endif
+ return(0);
+}
diff --git a/net/drv/slip/slip.h b/net/drv/slip/slip.h
new file mode 100644
index 0000000..6c2e871
--- /dev/null
+++ b/net/drv/slip/slip.h
@@ -0,0 +1,64 @@
+/*
+ * slip.h Define the SLIP device driver interface and constants.
+ *
+ * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY
+ * AS SOON AS POSSIBLE!
+ *
+ * Version: @(#)slip.h 1.2.0 (02/11/93)
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_SLIP_H
+#define _LINUX_SLIP_H
+
+/* SLIP configuration. */
+#define SL_NRUNIT 4 /* number of SLIP channels */
+#define SL_MTU 296 /* 296; I am used to 600- FvK */
+#define SL_BUF_SIZE 8192 /* same as TTY for now */
+#ifdef not_any_more
+#define SL_RCV_SIZE 2048
+#endif
+
+/* SLIP protocol characters. */
+#define END 0300 /* indicates end of frame */
+#define ESC 0333 /* indicates byte stuffing */
+#define ESC_END 0334 /* ESC ESC_END means END 'data' */
+#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
+
+struct sl_queue {
+ unsigned long data;
+ unsigned long head;
+ unsigned long tail;
+ struct wait_queue *proc_list;
+ unsigned char buf[SL_BUF_SIZE];
+};
+
+struct slip {
+ int inuse; /* are we allocated? */
+ int line; /* SLIP channel number */
+ struct tty_struct *tty; /* ptr to TTY structure */
+#if 0
+ struct device *dev; /* easy for intr handling */
+#endif
+ unsigned int sending; /* "channel busy" indicator */
+ struct sl_queue rcv_queue;
+ char snd_buf[(SL_MTU*2)+4];
+ unsigned char xbuff[(SL_MTU * 2)];
+ int escape; /* SLIP state machine */
+ int received; /* SLIP receive counter */
+ unsigned long sent; /* #frames sent */
+ unsigned long rcvd; /* #frames rcvd */
+ unsigned long errors; /* error count */
+};
+
+#define SL_INC(a) ((a) = ((a)+1) & (SL_BUF_SIZE-1))
+#define SL_DEC(a) ((a) = ((a)-1) & (SL_BUF_SIZE-1))
+#define SL_EMPTY(a) ((a)->head == (a)->tail)
+#define SL_LEFT(a) (((a)->tail-(a)->head-1)&(SL_BUF_SIZE-1))
+#define SL_LAST(a) ((a)->buf[(SL_BUF_SIZE-1)&((a)->head-1)])
+#define SL_FULL(a) (!SL_LEFT(a))
+#define SL_CHARS(a) (((a)->head-(a)->tail)&(SL_BUF_SIZE-1))
+
+extern int slip_init(struct ddi *dev);
+
+#endif /* _LINUX_SLIP.H */
diff --git a/net/drv/we8003/Makefile b/net/drv/we8003/Makefile
new file mode 100644
index 0000000..3a5bafb
--- /dev/null
+++ b/net/drv/we8003/Makefile
@@ -0,0 +1,42 @@
+#
+# Makefile for the WE8003 device driver..
+#
+# 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 definitions are now inherited from the
+# parent makes..
+#
+
+SUBDIRS =
+
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+OBJS = main.o
+
+
+we8003.o: $(OBJS)
+ ld -r -o we8003.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a tmp_make .depend
+ for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+ $(CPP) -M *.c > .depend
+ @for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE) dep) || exit; done
+
+dummy:
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/net/drv/we8003/dp8390.h b/net/drv/we8003/dp8390.h
new file mode 100644
index 0000000..5372a38
--- /dev/null
+++ b/net/drv/we8003/dp8390.h
@@ -0,0 +1,202 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the DP8390 Network Interface Controller.
+ *
+ * Version: $Id: dp8390.h,v 0.8.4.1 1992/11/10 00:17:18 waltje Exp $
+ *
+ * Authors: Original taken from the 386BSD operating system.
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* format of status
+ bit
+ 0 packet ok
+ 1 crc error
+ 2 frame alignment error
+ 3 fifo overrun
+*/
+#define STRECVD 0xf1
+struct wd_ring {
+ unsigned char status; /* status */
+ unsigned char next; /* pointer to next packet */
+ unsigned short count; /* packet length in bytes + 4 */
+};
+
+/* 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
+
+/* Page 0 */
+#define CR (WD_BASE+WD_NIC+0) /* RW - Command */
+#define CLDA0 (WD_BASE+WD_NIC+1) /* R - CurrentLocalDMA Addr 0 */
+#define PSTART (WD_BASE+WD_NIC+1) /* W - Page Start Register */
+#define CLDA1 (WD_BASE+WD_NIC+2) /* R - Current Local DMA Addr 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 Reg */
+#define CRDA0 (WD_BASE+WD_NIC+8) /* R - Current Remote DMA Add 0 */
+#define RSAR0 (WD_BASE+WD_NIC+8) /* W - Remote Start Address 0 */
+#define CRDA1 (WD_BASE+WD_NIC+9) /* R - CurrentRemote DMA Addr 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, 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 intr */
+
+/* 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=ext1, 11=ext2 */
+#define ATD 8 /* Auto Transmit Enable=1 tx inh enb */
+#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 /* Xmit, check NCR for count */
+#define ABT 8 /* Xmit 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 */
diff --git a/net/tcp/we.c b/net/drv/we8003/handler.c
index a074838..2d1670f 100644
--- a/net/tcp/we.c
+++ b/net/drv/we8003/handler.c
@@ -1,86 +1,22 @@
-/* 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 */
-
-/* $Id: we.c,v 0.8.4.10 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: we.c,v $
- * Revision 0.8.4.10 1993/01/23 18:00:11 bir7
- * Added volatile keyword and converted entry points.
- *
- * Revision 0.8.4.9 1993/01/22 22:58:08 bir7
- * Check in for merge with previous .99 pl 4.
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
*
- * Revision 0.8.4.8 1992/12/12 19:25:04 bir7
- * cleaned up Log messages.
+ * WE - A simple WD8003, WD8013 and SMC Elite-16 driver.
*
- * Revision 0.8.4.7 1992/12/12 01:50:49 bir7
- * made ring buffer volatile.
+ * Version: $Id: handler.c,v 1.0.0 1993/02/15 00:00:00 waltje Exp $
*
- * Revision 0.8.4.6 1992/12/06 11:31:47 bir7
- * Added missing braces in if statement.
+ * Authors: Original taken from the 386BSD operating system.
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Bob Harris, <rth@sparta.com>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
*
- * Revision 0.8.4.5 1992/12/05 21:35:53 bir7
- * Added check for bad hardware returning runt packets.
- *
- * Revision 0.8.4.4 1992/12/03 19:52:20 bir7
- * Added better queue checking.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Put more checking in start_xmit to make sure packet doesn't disapear
- * out from under us.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.4 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -96,16 +32,9 @@
#include <linux/fcntl.h>
#include <netinet/in.h>
#include <linux/interrupt.h>
+#include "dp8390.h"
+#include "we8003.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;
@@ -222,7 +151,6 @@ wd8003_start_xmit(struct sk_buff *skb, struct device *dev)
/* put in a time out. */
if (jiffies - dev->trans_start < 30)
{
- sti();
return (1);
}
@@ -432,23 +360,27 @@ wd_rcv( struct device *dev )
}
}
-/* This routine handles the interrupt case of receiver overruns */
+/* Handle the "receiver overrun" interrupt. */
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 */
+ unsigned char cmd, dummy;
+ register int io;
+
+ /*
+ * 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 */
@@ -635,102 +567,115 @@ static struct sigaction wd8003_sigaction =
NULL
};
-int
-wd8003_init(struct device *dev)
+
+/* Probe for a WD80x3 board. */
+static int
+we8003_probe(struct ddconf *conf)
{
unsigned char csum;
+ register int io;
int i;
+
+ io = conf->ioaddr;
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);
+ for (i = 0; i < 8; i++) {
+ csum += inb_p(io + WD_ROM + i);
+ }
+ if (csum != WD_CHECK) {
+ PRINTK (("%s: Warning: board not found at IO=0x%X.\n", io));
+ return(-ENODEV);
+ }
+ return(0);
+}
- /* make sure no one can attempt to open the device. */
- return (1);
- }
- 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 */
+
+/* Check for a 16-bit ISA controller. Set a flag if found. */
+static void
+we8003_8bit(struct ddi *dev)
+{
+#if !FORCE_8BIT
+ unsigned char csum;
+ register int io;
+ int i;
+
+ io = dev->config.ioaddr;
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! */
+ if (inb_p(io + EN_SAPROM +i) != inb_p(io + EN_CMD + i)) {
+ /* Fiddle with 16-bit bit. */
+ csum = inb_p(io + EN_REG1);
+
+ /* Attempt to clear 16-bit bit. */
+ outb(csum ^ BUS16, io + EN_REG1);
+ if ((csum & BUS16) == (inb_p(io + EN_REG1) & BUS16)) {
+ outb_p(LAN16ENABLE|MEMMASK, io + EN_REG5);
+ outb(csum, io + EN_REG1);
+ dev->flags |= DDI_FBUS16;
+ break;
}
- outb( csum , EN_REG1 );
+ outb(csum, io + EN_REG1);
}
- }
-#endif /* FORCE_8BIT */
+ }
+#endif
+}
- /* 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
- return (1);
- }
- }
- /* Calculate how many pages of memory on board */
- max_pages = ( i - dev->mem_start )/256;
+/* Setup and clear the on-board memory. */
+static void
+we8003_cmem(struct ddi *dev)
+{
+ register int io;
- /* need to set up the dev->mem_end and dev->rmem_end */
- dev->rmem_end = i;
- dev->mem_end = i;
+ io = dev->config.ioaddr;
+ outb_p(WD_IMEM, io + WD_CTL); /* mapin the interface memory */
+
+ /* FIXME: clear the interface memory here. */
+}
- /* 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]);
- }
+
+/* Fetch and record the interface's hardware address. */
+static void
+we8003_geth(struct ddi *dev)
+{
+ register int io;
+ register int i;
+
+ io = dev->config.ioaddr;
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ dev->dev_addr[i] = inb_p(io + WD_ROM + i);
+ dev->broadcast[i] = 0xff;
+ }
+}
+
+
+/* Initialize the WD8003 hardware. */
+int
+we8003_conf(struct ddi *dev)
+{
+ /*
+ * Check for any boards present. We must change this one
+ * time to include complete AutoProbe, but for now, we just
+ * see if we need to initialize a board at all. - FvK
+ */
+ if (dev->config.ioaddr == 0) return(0); /* fake it's OK */
+ if (we8003_probe(&dev->config) < 0) return(1);
+
+ /* Check for 16-bit board- it doesn't have register 0/8 aliasing. */
+ we8003_8bit(dev);
+
+ /* Set up and clear the shared memory. */
+ we8003_cmem(dev);
+
+ /* Fetch and record the hardware address. */
+ we8003_geth(dev);
/* Clear the statistics */
- for( i = 0; i < sizeof( struct enet_statistics ); i++ )
- ((char *)&stats)[i] = 0;
+ memset((char *) &stats, 0, sizeof(struct enet_statistics);
- printk ("\n");
dev->tbusy = 0;
dev->interrupt = 0;
-
- if (irqaction (dev->irq, &wd8003_sigaction))
- {
- printk ("Unable to get IRQ%d for wd8013 board\n", dev->irq);
- return (1);
- }
- return (0);
+ if (irqaction (dev->irq, &we8003_sigaction)) {
+ printk("%s: unable to get IRQ%d\n", dev->name, dev->irq);
+ return(1);
+ }
+ return(0);
}
diff --git a/net/drv/we8003/main.c b/net/drv/we8003/main.c
new file mode 100644
index 0000000..8a2f671
--- /dev/null
+++ b/net/drv/we8003/main.c
@@ -0,0 +1,179 @@
+/*
+ * we8003.c A generic WD8003 driver for LINUX.
+ *
+ * Version: @(#)we8003.c 1.0.0 04/22/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#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 <linux/string.h>
+#include <linux/ddi.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <linux/fcntl.h>
+#include <netinet/in.h>
+#include "we8003.h"
+
+
+#define VERSION "1.0.0" /* current version ID */
+
+
+struct ddi_device *we_ptrs[NR_WE8003]; /* pointers to DDI blocks */
+
+
+static int
+we_getconf(struct ddi_device *dev, struct ddconf *cp)
+{
+ cp->ioaddr = dev->config.ioaddr; /* I/O base address */
+ cp->ioaux = 0; /* not used */
+ cp->irq = dev->config.irq; /* IRQ channel */
+ cp->dma = 0; /* not used */
+ cp->memaddr = dev->config.memaddr; /* RAM base address */
+ cp->memsize = dev->config.memsize; /* RAM size */
+ return(0);
+}
+
+
+static int
+we_setconf(struct ddi_device *dev, struct ddconf *cp)
+{
+ dev->config.ioaddr = cp->ioaddr; /* I/O base address */
+ dev->config.irq = cp->irq; /* IRQ channel */
+ dev->config.memaddr = cp->memaddr; /* RAM base address */
+ dev->config.memsize = cp->memsize; /* RAM size */
+ PRINTK (("%s: IO=0x%X IRQ=%d MEM=0x%X(%d)\n",
+ dev->name, dev->config.ioaddr, dev->config.irq,
+ dev->config.memaddr, dev->config.memsize));
+
+ /* FIXME: request the IRQ line and initialize HW here! */
+
+ return(0);
+}
+
+
+static int
+we_open(struct inode * inode, struct file * file)
+{
+ int minor;
+ struct ddi_device *dev;
+
+ minor = MINOR(inode->i_rdev);
+ if (minor < 0 || minor >= NR_WE8003) return(-ENODEV);
+ dev = we_ptrs[minor];
+ if (dev == NULL || (dev->flags & DDI_FREADY) == 0) return(-ENODEV);
+
+ return(0);
+}
+
+
+static void
+we_close(struct inode * inode, struct file * file)
+{
+ int minor;
+ struct ddi_device *dev;
+
+ minor = MINOR(inode->i_rdev);
+ if (minor < 0 || minor >= NR_WE8003) return;
+ dev = we_ptrs[minor];
+ if (dev == NULL || (dev->flags & DDI_FREADY) == 0) return;
+}
+
+
+static int
+we_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int minor, ret;
+ struct ddi_device *dev;
+ struct ddconf conf;
+
+ minor = MINOR(inode->i_rdev);
+ if (minor < 0 || minor >= NR_WE8003) return(-ENODEV);
+ dev = we_ptrs[minor];
+ if (dev == NULL || (dev->flags & DDI_FREADY) == 0) return(-ENODEV);
+
+ ret = -EINVAL;
+ switch(cmd) {
+ case DDIOCGNAME:
+ memcpy_tofs((void *)arg, dev->name, DDI_MAXNAME);
+ ret = 0;
+ break;
+ case DDIOCGCONF:
+ ret = we_getconf(dev, &conf);
+ memcpy_tofs((void *)arg, &conf, sizeof(conf));
+ break;
+ case DDIOCSCONF:
+ memcpy_fromfs(&conf, (void *)arg, sizeof(conf));
+ ret = we_setconf(dev, &conf);
+ break;
+ default:
+ break;
+ }
+ return(ret);
+}
+
+
+static struct file_operations we_fops = {
+ NULL, /* LSEEK */
+ NULL, /* READ */
+ NULL, /* WRITE */
+ NULL, /* READDIR */
+ NULL, /* SELECT */
+ we_ioctl, /* IOCTL */
+ NULL, /* MMAP */
+ we_open, /* OPEN */
+ we_close /* CLOSE */
+};
+
+
+/* This is the main entry point of this driver. */
+int
+we8003_init(struct ddi_device *dev)
+{
+ static int unit_nr = 0;
+ int i;
+
+ /* Initialize the driver if this is the first call. */
+ if (unit_nr == 0) {
+ for(i = 0; i < NR_WE8003; i++) we_ptrs[i] = NULL;
+ }
+
+ /* Initialize the local control block pointer. */
+ we_ptrs[unit_nr] = dev;
+ dev->unit = unit_nr++;
+ sprintf(dev->name, WE_NAME, dev->unit);
+ dev->flags |= DDI_FREADY;
+
+ /* Say hello to our viewers. */
+ PRINTK (("%s: version %s: ", dev->title, VERSION));
+ (void) we_setconf(dev, &dev->config);
+
+ /* First of all, setup a VFS major device handler if needed. */
+ if (dev->major != 0) {
+ if (dev->flags & DDI_FBLKDEV) {
+ if (register_blkdev(dev->major, "WE8003", &we_fops) < 0) {
+ printk("%s: cannot register block device %d!\n",
+ dev->name, dev->major);
+ return(-EINVAL);
+ }
+ }
+ if (dev->flags & DDI_FCHRDEV) {
+ if (register_chrdev(dev->major, "WE8003", &we_fops) < 0) {
+ printk("%s: cannot register character device %d!\n",
+ dev->name, dev->major);
+ return(-EINVAL);
+ }
+ }
+ }
+
+ /* All done... */
+ return(0);
+}
diff --git a/net/drv/we8003/we8003.h b/net/drv/we8003/we8003.h
new file mode 100644
index 0000000..e853622
--- /dev/null
+++ b/net/drv/we8003/we8003.h
@@ -0,0 +1,33 @@
+/*
+ * we8003.h Define the interface of the WE8003 Ethernet driver.
+ *
+ * Version: @(#)we8003.h 1.0.0 (02/11/93)
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_WE8003_H
+#define _LINUX_WE8003_H
+
+
+#define CONF_WE8003 0 /* add this driver to kernel */
+#define FORCE_8BIT 0 /* for forcing the WD8003 mode */
+
+
+#define WE_DEBUG
+#ifdef WE_DEBUG
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x) /**/
+#endif
+
+
+#define NR_WE8003 4 /* max number of units */
+#define WE_NAME "WE8003.%d" /* our DDI ID string */
+
+
+extern struct ddi_device *we_ptrs[NR_WE8003]; /* pointers to DDI blocks */
+
+
+extern int we8003_init(struct ddi_device *dev);
+
+#endif /* _LINUX_WE8003_H */
diff --git a/net/inet/8390.c b/net/inet/8390.c
new file mode 100644
index 0000000..2ba80b8
--- /dev/null
+++ b/net/inet/8390.c
@@ -0,0 +1,734 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992,1993 by Donald Becker. This is alpha test code.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This driver should work with many 8390-based ethernet adaptors.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version =
+ "8390.c:v0.99-10 5/28/93 for 0.99.6+ Donald Becker (becker@super.org)\n";
+#include <linux/config.h>
+#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
+/* They don't know what they want -- give it all to them! */
+#define EL2
+#define NE2000
+#define WD80x3
+#define HPLAN
+#endif
+
+/*
+ Braindamage remaining:
+
+ Ethernet devices should use a chr_drv device interface, with ioctl()s to
+ configure the card, bring the interface up or down, allow access to
+ statistics, and maybe read() and write() access to raw packets.
+ This won't be done until after Linux 1.00.
+
+ This driver should support multiple, diverse boards simultaneousely.
+ This won't be done until after Linux 1.00.
+
+Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+ The NE* programming info came from the Crynwr packet driver, and figuring
+ out that the those boards are similar to the NatSemi evaluation board
+ described in AN-729. Thanks NS, no thanks to Novell/Eagle.
+ Cabletron provided only info I had already gotten from other sources -- hiss.
+ */
+
+#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 <linux/string.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+#include "8390.h"
+
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef EI_DEBUG
+int ei_debug = EI_DEBUG;
+#else
+int ei_debug = 2;
+#endif
+
+struct device *irq2dev_map[16] = {0,0,0, /* zeroed...*/};
+
+/* Max number of packets received at one Intr. */
+/*static int high_water_mark = 0;*/
+
+/* Index to functions. */
+/* Put in the device structure. */
+static int ei_open(struct device *dev);
+/* Dispatch from interrupts. */
+void ei_interrupt(int reg_ptr);
+static void ei_tx_intr(struct device *dev);
+static void ei_receive(struct device *dev);
+static void ei_rx_overrun(struct device *dev);
+
+int ethdev_init(struct device *dev);
+/* Routines generic to NS8390-based boards. */
+void NS8390_init(struct device *dev, int startp);
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page);
+
+extern int el2autoprobe(int ioaddr, struct device *dev);
+extern int el2probe(int ioaddr, struct device *dev);
+extern int neprobe(int ioaddr, struct device *dev);
+extern int wdprobe(int ioaddr, struct device *dev);
+extern int hpprobe(int ioaddr, struct device *dev);
+
+struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
+
+/* Open/initialize the board. This routine goes all-out, setting everything
+ up anew at each open, even though many of these registers should only
+ need to be set once at boot.
+ */
+static int
+ei_open(struct device *dev)
+{
+ struct ei_device *ei_local = dev->private;
+
+ if ( ! ei_local) {
+ printk("%s: Opening a non-existent physical device\n", dev->name);
+ return 1; /* ENXIO would be more accurate. */
+ }
+
+ irq2dev_map[dev->irq] = dev;
+ NS8390_init(dev, 1);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ /* The old local flags... */
+ ei_local->txing = 0;
+ /* ... are now global. */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+static int
+ei_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = dev->private;
+ int length, send_length;
+ int tmp_tbusy; /* we must lock dev_tint in dev.c with dev->t_busy =1 */
+ /* because on a slow pc a quasi endless loop can appear */
+
+ if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
+ int txsr = inb(e8390_base+EN0_TSR), isr;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
+ return 1;
+ }
+ isr = inb(e8390_base+EN0_ISR);
+ printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
+ dev->name, txsr, isr);
+ /* It's possible to check for an IRQ conflict here.
+ I may have to do that someday. */
+ if (isr)
+ printk("%s: Possible IRQ conflict on IRQ%d?", dev->name, dev->irq);
+ else
+ printk("%s: Possible network cable problem?\n", dev->name);
+ /* It futile, but try to restart it anyway. */
+ ei_reset_8390(dev);
+ NS8390_init(dev, 1);
+ printk("\n");
+ }
+
+ /* This is new: it means some higher layer thinks we've missed an
+ tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+ /* Fill in the ethernet header. */
+ if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
+ skb->dev = dev;
+ arp_queue (skb);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+ length = skb->len;
+ send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+ /* Turn off interrupts so that we can put the packet out safely. */
+ cli();
+ if (dev->interrupt || ei_local->irqlock) {
+ /* We should never get here during an interrupt after 0.99.4. */
+ sti();
+ if (ei_debug > 2)
+ printk("%s: Attempt to reenter critical zone%s.\n",
+ dev->name, ei_local->irqlock ? " during interrupt" : "");
+ return 1;
+ }
+ outb(0x00, e8390_base + EN0_IMR);
+ tmp_tbusy=dev->tbusy;
+ dev->tbusy = 1; /* lock dev_tint() in dev.c */
+ ei_local->irqlock = 1;
+ sti();
+ if (ei_local->pingpong) {
+ int output_page;
+ if (ei_local->tx1 == 0) {
+ output_page = ei_local->tx_start_page;
+ ei_local->tx1 = send_length;
+ if (ei_debug && ei_local->tx2 > 0)
+ printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx2, ei_local->lasttx,
+ ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
+ output_page = ei_local->tx_start_page + 6;
+ ei_local->tx2 = send_length;
+ if (ei_debug && ei_local->tx1 > 0)
+ printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx1, ei_local->lasttx,
+ ei_local->txing);
+ } else {
+ /* We can get to here if we get an rx interrupt and queued
+ a tx packet just before masking 8390 irqs above. */
+ if (ei_debug > 2)
+ printk("%s: No packet buffer space for ping-pong use.\n",
+ dev->name);
+ cli();
+ ei_local->irqlock = 0;
+ dev->tbusy = tmp_tbusy;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ sti();
+ return 1;
+ }
+ dev->trans_start = jiffies;
+ ei_block_output(dev, length, (void*)(skb+1), output_page);
+ if (! ei_local->txing) {
+ NS8390_trigger_send(dev, send_length, output_page);
+ if (output_page == ei_local->tx_start_page)
+ ei_local->tx1 = -1, ei_local->lasttx = -1;
+ else
+ ei_local->tx2 = -1, ei_local->lasttx = -2;
+ ei_local->txing = 1;
+ } else
+ ei_local->txqueue++;
+ if (ei_local->tx1 && ei_local->tx2)
+ tmp_tbusy = 1;
+ } else {
+ dev->trans_start = jiffies;
+ ei_block_output(dev, length, (void*)(skb+1), ei_local->tx_start_page);
+ NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
+ tmp_tbusy = 1;
+ } /* PINGPONG */
+
+ if (skb->free)
+ kfree_skb (skb, FREE_WRITE);
+
+ /* Turn 8390 interrupts back on. */
+ cli();
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ ei_local->irqlock = 0;
+ dev->tbusy=tmp_tbusy;
+ sti();
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the ether interface interrupts. */
+void
+ei_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int e8390_base;
+ int interrupts, boguscount = 0;
+ struct ei_device *ei_local;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ e8390_base = dev->base_addr;
+ ei_local = dev->private;
+ if (dev->interrupt || ei_local->irqlock) {
+ /* The "irqlock" check is only for testing. */
+ sti();
+ printk(ei_local->irqlock
+ ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+ : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+ dev->name, inb_p(e8390_base + EN0_ISR),
+ inb_p(e8390_base + EN0_IMR));
+ return;
+ }
+
+ dev->interrupt = 1;
+ sti(); /* Allow other interrupts. */
+
+ /* Change to page 0 and read the intr status reg. */
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ if (ei_debug > 3)
+ printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
+ inb_p(e8390_base + EN0_ISR));
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
+ && ++boguscount < 20) {
+ if (interrupts & ENISR_RDC) {
+ /* Ack meaningless DMA complete. */
+ outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ }
+ if (interrupts & ENISR_OVER) {
+ ei_rx_overrun(dev);
+ } else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ /* Got a good (?) packet. */
+ ei_receive(dev);
+ }
+ /* Push the next to-transmit packet through. */
+ if (interrupts & ENISR_TX) {
+ ei_tx_intr(dev);
+ } else if (interrupts & ENISR_COUNTERS) {
+ struct ei_device *ei_local = dev->private;
+ ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER0);
+ ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER1);
+ ei_local->missed_packets += inb_p(e8390_base + EN0_COUNTER2);
+ outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore the transmit errs and reset intr for now. */
+ if (interrupts & ENISR_TX_ERR) {
+ outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ }
+
+ if (interrupts && ei_debug) {
+ printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have finished a transmit: check for errors and then trigger the next
+ packet to be sent. */
+static void
+ei_tx_intr(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int status = inb(e8390_base + EN0_TSR);
+ struct ei_device *ei_local = dev->private;
+
+ outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+ if ((status & ENTSR_PTX) == 0)
+ ei_local->tx_errors++;
+ else
+ ei_local->tx_packets++;
+
+ if (ei_local->pingpong) {
+ ei_local->txqueue--;
+ if (ei_local->tx1 < 0) {
+ if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+ printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
+ ei_local->tx1 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx2 > 0) {
+ NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+ dev->trans_start = jiffies;
+ ei_local->txing = 1;
+ ei_local->tx2 = -1,
+ ei_local->lasttx = 2;
+ } else
+ ei_local->lasttx = 20, ei_local->txing = 0;
+ } else if (ei_local->tx2 < 0) {
+ if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
+ printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
+ ei_local->tx2 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx1 > 0) {
+ NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ ei_local->txing = 1;
+ ei_local->tx1 = -1;
+ ei_local->lasttx = 1;
+ } else
+ ei_local->lasttx = 10, ei_local->txing = 0;
+ } else
+ printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+ dev->name, ei_local->lasttx);
+ } else {
+ ei_local->txing = 0;
+ dev->tbusy = 0;
+ }
+ mark_bh (INET_BH);
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+
+static void
+ei_receive(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = dev->private;
+ int rxing_page, this_frame, next_frame, current_offset;
+ int boguscount = 0;
+ struct e8390_pkt_hdr rx_frame;
+ int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+ while (++boguscount < 10) {
+ int size;
+
+ /* Get the rx page (incoming packet pointer). */
+ outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ rxing_page = inb_p(e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+ /* Remove one frame from the ring. Boundary is alway a page behind. */
+ this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ if (this_frame >= ei_local->stop_page)
+ this_frame = ei_local->rx_start_page;
+
+ /* Someday we'll omit the previous step, iff we never get this message.*/
+ if (ei_debug > 0 && this_frame != ei_local->current_page)
+ printk("%s: mismatched read page pointers %2x vs %2x.\n",
+ dev->name, this_frame, ei_local->current_page);
+
+ if (this_frame == rxing_page) /* Read all the frames? */
+ break; /* Done for now */
+
+ current_offset = this_frame << 8;
+ ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
+ current_offset);
+
+ size = rx_frame.count - sizeof(rx_frame);
+
+ next_frame = this_frame + 1 + ((size+4)>>8);
+
+ /* Check for bogosity warned by 3c503 book: the status byte is never
+ written. This happened a lot during testing! This code should be
+ cleaned up someday, and the printk()s should be PRINTK()s. */
+ if ( rx_frame.next != next_frame
+ && rx_frame.next != next_frame + 1
+ && rx_frame.next != next_frame - num_rx_pages
+ && rx_frame.next != next_frame + 1 - num_rx_pages) {
+#ifndef EI_DEBUG
+ ei_local->current_page = rxing_page;
+ outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ continue;
+#else
+ static int last_rx_bogosity = -1;
+ printk("%s: bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
+ dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
+ current_offset);
+
+ if (ei_local->rx_packets != last_rx_bogosity) {
+ /* Maybe we can avoid resetting the chip... empty the packet ring. */
+ ei_local->current_page = rxing_page;
+ printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
+ dev->name, ei_local->current_page, next_frame,
+ rx_frame.next, rx_frame.status);
+ last_rx_bogosity = ei_local->rx_packets;
+ outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ continue;
+ } else {
+ /* Oh no Mr Bill! Last ditch error recovery. */
+ printk("%s: recovery failed, resetting at packet #%d..",
+ dev->name, ei_local->rx_packets);
+ sti();
+ ei_reset_8390(dev);
+ NS8390_init(dev, 1);
+ printk("restarting.\n");
+ return;
+ }
+#endif /* EI8390_NOCHECK */
+ }
+
+ if ((size < 32 || size > 1535) && ei_debug)
+ printk("%s: bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
+ dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
+ if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
+ int sksize = sizeof(struct sk_buff) + size;
+ struct sk_buff *skb;
+ skb = kmalloc(sksize, GFP_ATOMIC);
+ if (skb != NULL) {
+ skb->lock = 0;
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ /* 'skb+1' points to the start of sk_buff data area. */
+ ei_block_input(dev, size, (void *)(skb+1),
+ current_offset + sizeof(rx_frame));
+ if (dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
+ printk("%s: receive buffers full.\n", dev->name);
+ break;
+ }
+ } else if (ei_debug) {
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize);
+ break;
+ }
+ ei_local->rx_packets++;
+ } else {
+ if (ei_debug)
+ printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n",
+ dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
+ ei_local->soft_rx_err_bits |= rx_frame.status,
+ ei_local->soft_rx_errors++;
+ }
+ next_frame = rx_frame.next;
+
+ /* This should never happen, it's here for debugging. */
+ if (next_frame >= ei_local->stop_page) {
+ printk("%s: next frame inconsistency, %#2x..", dev->name, next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page += 1 + ((size+4)>>8);
+ ei_local->current_page = next_frame;
+ outb(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+
+ /* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
+ outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
+ return;
+}
+
+/* We have a receiver overrun: we have to kick the 8390 to get it started
+ again.*/
+static void
+ei_rx_overrun(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int reset_start_time = jiffies;
+ struct ei_device *ei_local = dev->private;
+
+ /* We should already be stopped and in page0. Remove after testing. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ if (ei_debug)
+ printk("%s: Receiver overrun.\n", dev->name);
+ ei_local->rx_overruns++;
+
+ /* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
+ It might mean something -- magic to speed up a reset? A 8390 bug?*/
+
+ /* Wait for reset in case the NIC is doing a tx or rx. This could take up to
+ 1.5msec, but we have no way of timing something in that range. The 'jiffies'
+ are just a sanity check. */
+ while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 1) {
+ printk("%s: reset did not complete at ei_rx_overrun.\n",
+ dev->name);
+ NS8390_init(dev, 1);
+ return;
+ };
+
+ {
+ int old_rx_packets = ei_local->rx_packets;
+ /* Remove packets right away. */
+ ei_receive(dev);
+ ei_local->rx_overrun_packets +=
+ (ei_local->rx_packets - old_rx_packets);
+ }
+ outb_p(0xff, e8390_base+EN0_ISR);
+ /* Generic 8390 insns to start up again, same as in open_8390(). */
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+}
+
+int
+ethif_init(struct device *dev)
+{
+ struct ei_device *ei_local;
+
+ if (ei_debug > 1)
+ printk(version);
+
+ /* The open call may be overridden by the card-specific code. */
+ dev->open = &ei_open;
+
+ /* Make up a ei_local structure. */
+ dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ memset(dev->private, 0, sizeof(struct ei_device));
+ ei_local = (struct ei_device *)dev->private;
+#ifndef NO_PINGPONG
+ ei_local->pingpong = 1;
+#endif
+
+ if (1
+#ifdef WD80x3
+ && ! wdprobe(dev->base_addr, dev)
+#endif
+#ifdef EL2
+ && ! el2autoprobe(dev->base_addr, dev)
+#endif
+#ifdef NE2000
+ && ! neprobe(dev->base_addr, dev)
+#endif
+#ifdef HPLAN
+ && ! hpprobe(dev->base_addr, dev)
+#endif
+ && 1 ) {
+ printk("No ethernet device found.\n");
+ kfree(dev->private);
+ dev->private = NULL;
+ return 1; /* ENODEV or EAGAIN would be more accurate. */
+ }
+
+ return ethdev_init(dev);
+}
+
+/* Initialize the rest of the device structure. */
+int
+ethdev_init(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ dev->hard_header = eth_header;
+ dev->add_arp = eth_add_arp;
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+
+ if (dev->private == NULL) {
+ dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ memset(dev->private, 0, sizeof(struct ei_device));
+ }
+
+ dev->hard_start_xmit = &ei_start_xmit;
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ for (i = 0; i < dev->addr_len; i++) {
+ dev->broadcast[i]=0xff;
+ }
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return 0;
+}
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+void NS8390_init(struct device *dev, int startp)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = dev->private;
+ int i;
+ int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
+ outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ outb_p(0x00, e8390_base + EN0_RCNTLO);
+ outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
+ outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ outb_p(0xFF, e8390_base + EN0_ISR);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers,
+ and set the multicast hash bitmap to receive all multicasts. */
+ cli();
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
+ for(i = 0; i < 6; i++) {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
+ }
+ for(i = 0; i < 8; i++)
+ outb_p(0xff, e8390_base + EN1_MULT + i);
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
+ sti();
+ if (startp) {
+ outb_p(0xff, e8390_base + EN0_ISR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
+ }
+ return;
+}
+
+/* Trigger a transmit start, assuming the length is valid. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page)
+{
+ int e8390_base = dev->base_addr;
+
+ ei_status.txing = 1;
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
+
+ if (inb_p(e8390_base) & E8390_TRANS) {
+ printk("%s: trigger_send() called with the transmitter busy.\n",
+ dev->name);
+ return;
+ }
+ outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ outb_p(start_page, e8390_base + EN0_TPSR);
+ outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
+ return;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -I/usr/src/linux/net/tcp -c 8390.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/8390.h b/net/inet/8390.h
new file mode 100644
index 0000000..534b9f4
--- /dev/null
+++ b/net/inet/8390.h
@@ -0,0 +1,150 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+ under the same license.
+ Some of these names and comments are from the Crynwr packet drivers. */
+
+#ifndef e8390_h
+#define e8390_h
+
+#define TX_2X_PAGES 12
+#define TX_1X_PAGES 6
+#define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+
+#define ETHER_ADDR_LEN 6
+
+/* From 8390.c */
+void ei_interrupt(int reg_ptr);
+/* From auto_irq.c */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+
+/* Most of these entries should be in 'struct device' (or most of the
+ things in there should be here!) */
+/* You have one of these per-board */
+struct ei_device {
+ char *name;
+ void (*reset_8390)(struct device *);
+ void (*block_output)(struct device *, int, const unsigned char *, int);
+ int (*block_input)(struct device *, int, char *, int);
+ int open:1;
+ int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
+ int txing:1; /* Transmit Active */
+ int dmaing:2; /* Remote DMA Active */
+ int irqlock:1; /* 8390's intrs disabled when '1'. */
+ int pingpong:1; /* Using the ping-pong driver */
+ unsigned char tx_start_page, rx_start_page, stop_page;
+ unsigned char current_page; /* Read pointer in buffer */
+ unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
+ unsigned char txqueue; /* Tx Packet buffer queue length. */
+ unsigned char in_interrupt;
+ short tx1, tx2; /* Packet lengths for ping-pong tx. */
+ short lasttx; /* Alpha version consistency check. */
+ /* The statistics: these are returned from the ioctl() as a block. */
+ int tx_packets;
+ int tx_errors;
+ int rx_packets;
+ int soft_rx_errors;
+ int soft_rx_err_bits;
+ int missed_packets;
+ int rx_overruns;
+ int rx_overrun_packets;
+};
+
+#define ei_status (*(struct ei_device *)(dev->private))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS 0x01 /* word transfer mode selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
+#define EN1_CURPAG 0x07 /* Current memory page RD WR */
+#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicase address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+/* The other bits in the TX status register mean:
+ 0x02 The transmit wasn't deferred.
+ 0x04 The transmit collided at least once.
+ 0x08 The transmit collided 16 times, and was deferred.
+ 0x10 The carrier sense was lost (from the ethernet transceiver)
+ 0x20 A "FIFO underrun" (internal error) occured during transmit.
+ 0x40 The collision detect "heartbeat" signal was lost.
+ 0x80 There was an out-of-window collision.
+ */
+
+/* The per-packet-header format. */
+struct e8390_pkt_hdr {
+ unsigned char status; /* status */
+ unsigned char next; /* pointer to next packet. */
+ unsigned short count; /* header + packet lenght in bytes */
+};
+#endif /* e8390_h */
diff --git a/net/inet/CONFIG b/net/inet/CONFIG
new file mode 100644
index 0000000..238df51
--- /dev/null
+++ b/net/inet/CONFIG
@@ -0,0 +1,43 @@
+#
+# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
+# if you set the address or IRQ to zero, so we do that by default.
+# Cards supportted:
+#
+# WD80x3 The Western Digital (SMC) WD80x3 driver
+# WD_SHMEM=xxx Forces the address of the shared memory
+# FORCE_8BIT Force card into 8-bit mode (WD8003)
+# NE2000 The Novell NE-2000 driver
+# HPLAN The HP-LAN driver
+# EL1 The 3c501 EtherLink I driver (source missing?)
+# EL2 The 3c503 EtherLink II driver
+# EL2_AUI Selects the AUI port instead of the BNC port
+# PLIP The Crynwe PL/IP driver
+# SLIP The MicroWalt SLIP driver
+# SL_DUMP Uses the "dump frame" debug code
+# D_LINK The D-Link DE-600 Portable Ethernet Adaptor.
+# D_LINK_IO The D-Link I/O address (0x378 == default)
+# D_LINK_IRQ The D-Link IRQ number to use (IRQ7 == default)
+# D_LINK_DEBUG Enable or disable D-Link debugging
+#
+# Note: for most WD (SMC) cards, the AutoProbe doesn't work. You have
+# to force those cards into operation, by specifying the I/O add-
+# ress (EI8390=xxx), the IRQ (EI8390_IRQ=xxx) and the address of
+# the shared memory (WD_SHMEM=xxxx). All other supported cards
+# behave like they should, you can leave the values to 0. -FvK
+#
+CARDS = -DSLIP -DPLIP -DWD80x3 -DNE2000 -DHPLAN -DEL2 -DD_LINK
+
+# For WD and SMC cards:
+OPTS = -DEI8390=0x280 -DEI8390_IRQ=15
+WD_OPTS = -DWD_SHMEM=0xCC000 -UFORCE_8BIT
+
+# For all other cards:
+#OPTS = -DEI8390=0 -DEI8390_IRQ=0
+#WD_OPTS = -DUD_SHMEM=0xCC000 -UFORCE_8BIT
+
+EL_OPTS = -UEL2_AUI
+NE_OPTS =
+HP_OPTS =
+PLIP_OPTS =
+SLIP_OPTS = -DSL_DUMP
+DL_OPTS = -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
diff --git a/net/inet/INTRO.8390 b/net/inet/INTRO.8390
new file mode 100644
index 0000000..714f69a
--- /dev/null
+++ b/net/inet/INTRO.8390
@@ -0,0 +1,62 @@
+
+Subject: Enhanced Ethercard driver available for alpha test.
+
+My "8390" Linux ethercard drivers are now available from usra.edu and
+super.org in ~ftp/pub/linux/ethercards/*. They'll be at tsx-11 and
+sunsite RSN.
+
+These drivers support all common 8390-based ethernet boards. Currently
+"common" is defined as:
+
+ 3Com Products:
+* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!)
+ 3Com 3c503/16 and excellent documentation provided by 3Com.
+
+ Clones-n-things
+ NE1000 Novell and Eagle are useless for documentation,
+* NE2000 but copied the designs directly from NatSemi;->.
+
+ WD/SMC products
+ WD8003
+* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks!
+
+* I've seen it work myself!
+
+There is support for the following boards, but since I've only been
+able to borrow a thinnet of an HP ethercard I haven't been able to test it:
+
+ HP LAN adaptors
+** HP27245
+** HP27247
+** HP27250
+
+Thanks are due to the dozens of alpha testers, and special thanks to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for loaning me ethercards.
+
+The following addresses are autoprobed, in this order:
+wd.c: 0x300, 0x280, 0x380, 0x240
+3c503: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0
+ne.c: 0x300, 0x280, 0x320, 0x340, 0x360
+hp.c: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240
+
+80x3 clones that are reported to work:
+ LANNET LEC-45
+
+"NE2000" clones that are reported to work:
+ Alta Combo(NE2000 clone)
+ Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory)
+ Asante Etherpak 2001/2003
+ D-Link Ethernet II
+ LTC E-NET/16 P/N: 8300-200-002 (lipka@lip.hanse.de)
+ Network Solutions HE-203
+ SVEC 4 Dimension Ethernet
+ 4-Dimension FD0490 EtherBoard16
+ Cabletron products:
+ I've had a really bad time with Cabletron -- they strung
+ me along for months before telling me all information was
+ proprietary. The following boards work, but there probably
+ won't be drivers for other versions. Complain to pkelly@ctron.com.
+ E1010 No ID PROM and sketchy info from Ctron means you'll
+ E1010-x have to compile-in information about your board.
+ E2010
+ E2010-x
+
diff --git a/net/inet/LICENSE.8390 b/net/inet/LICENSE.8390
new file mode 100644
index 0000000..500940a
--- /dev/null
+++ b/net/inet/LICENSE.8390
@@ -0,0 +1,15 @@
+Code in this directory written at the IDA Supercomputing Research Center
+carries the following copyright and license.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used
+ and distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ In addition to the disclaimers in the GPL, SRC expressly disclaims any
+ and all warranties, expressed or implied, concerning the enclosed software.
+ This software was developed at SRC for use in internal research, and the
+ intent in sharing this software is to promote the productive interchange
+ of ideas throughout the research community. All software is furnished
+ on an "as-is" basis. No further updates to this software should be
+ expected. Although updates may occur, no commitment exists.
diff --git a/net/inet/Makefile b/net/inet/Makefile
new file mode 100644
index 0000000..9e984aa
--- /dev/null
+++ b/net/inet/Makefile
@@ -0,0 +1,85 @@
+#
+# Makefile for the Linix TCP/IP (INET) layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+.c.o:
+ $(CC) $(CFLAGS) \
+ -c -o $*.o $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.s:
+ $(CC) $(CFLAGS) \
+ -S -o $*.s $<
+
+
+OBJS = Space.o sock.o utils.o route.o proc.o timer.o protocol.o loopback.o \
+ eth.o packet.o arp.o dev.o 8390.o wd.o ne.o el.o hp.o plip.o \
+ slip.o d_link.o auto_irq.o ip.o raw.o icmp.o tcp.o udp.o
+
+ifdef CONFIG_INET
+
+inet.o: $(OBJS)
+ $(LD) -r -o inet.o $(OBJS)
+
+else
+
+inet.o:
+ echo | $(AS) -o inet.o
+
+endif
+
+include CONFIG
+
+
+Space.o: CONFIG Space.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(OPTS) $(CARDS) $(DL_OPTS) \
+ -c Space.c -o $@
+
+8390.o: CONFIG 8390.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(CARDS) -c 8390.c -o $@
+
+wd.o: CONFIG wd.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c wd.c -o $@
+
+el.o: CONFIG el.c elreg.h Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) -UEL2_AUI -c el.c -o $@
+
+ne.o: CONFIG ne.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c ne.c -o $@
+
+hp.o: CONFIG hp.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c hp.c -o $@
+
+plip.o: CONFIG plip.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c plip.c -o $@
+
+slip.o: CONFIG slip.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(SLIP_OPTS) -c slip.c -o $@
+
+d_link.o: CONFIG d_link.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c d_link.c -o $@
+
+subdirs: dummy
+ for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
+
+
+clean:
+ rm -f core *.o *.a *.s
+
+dep:
+ $(CPP) -M *.c > .depend
+
+tar:
+ tar -cvf /dev/f1 .
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/net/inet/README.8390 b/net/inet/README.8390
new file mode 100644
index 0000000..0bb97df
--- /dev/null
+++ b/net/inet/README.8390
@@ -0,0 +1,73 @@
+Installation Directions:
+
+EMail me (becker@super.org) telling me which version you have gotten.
+I need to know how many people have tried, succeeded and
+failed before this is released as part of the official Linux.
+
+Use Linux 0.99.5 or later. Make certain you can make a working kernel
+_before_ you install the ethercard driver.
+
+Put the all of the files into linux/net/tcp/. You'll need all of the
+files in this directory.
+GNUmakefile 8390.c 8390.h auto_irq.c Space.c wd.c ne.c 3c503.[ch] and hp.c.
+Space.c is the only tricky one -- it overwrites the old Space.c.
+Stock versions of Space.c leave the "we" driver enabled and will not work.
+
+Change the GNUmakefile to reflect your configuration. Use the guide at
+the end of these instructions and in the README file. Note that the
+'GNUmakefile' name is magic; it is loaded in preference to 'Makefile'.
+
+Make and install your new kernel.
+
+To actually use this driver you must get the TCP/IP package and edit
+your /usr/etc/inet/rc.net file to config whatever you named your
+ethernet device. (You can edit the GNUmakefile to use something
+besides the "eth0" name. Note that the default name has changed to
+the now-standard "eth0".)
+
+If you try to 'config' an interface that doesn't exist your kernel
+will report "invalid ioctl()" for anthing that tries to use the card.
+Note that the ethercard devices aren't (yet) '/dev/eth0' devices --
+they only exist in the socket namespace and thus you don't need to
+'mknode' them.
+
+________________
+Important defines
+
+For Space.c
+#define EI8390 0 /* The base address of your ethercard. */
+#define EI8390_IRQ 0 /* and the interrupt you want to use. */
+ /* '0' means autoconfigure */
+For 8390.c
+#define EI_DEBUG 2 /* Use '0' for no messages. */
+#define EL2 /* For the 3c503 driver. */
+#define NE2000 /* For the NE1000/NE2000/Ctron driver. */
+#define WD80x3 /* For the WD8003/WD8013 driver. */
+#define HPLAN /* For the HP27xxx driver. */
+
+For the individual drivers
+
+EI8390 Define (probably in autoconf.h or config.site.h) this to the base
+ address of your ethernet card.
+EI8390_IRQ Define (probably in autoconf.h or config.site.h) this to the
+ IRQ line of your ethernet card. Most drivers convert a IRQ2 to an
+ IRQ9 for you, so don't be surprised.
+EI_DEBUG Set to the desired numeric debugging level. Use 3 or
+ greater when actively debugging a problem, '1' for a
+ casual interest in what's going on, and '0' for normal
+ use.
+NO_PINGPONG
+ Define this if you don't want ping-pong transmit buffers.
+EL2_AUI
+ Define for this if you are using the 3c503 and use the AUI/DIX
+ connector rather than the built-in thin-net transceiver.
+WD_SHMEM
+ Define this to override the shared memory address used by the
+ WD driver. This should only be necessary for jumpered ethercards.
+
+If you have a Cabletron ethercard you might want to look at ne.c:neprobe()
+for info on how to enable more packet buffer space.
+
+ETHERLINK1_IRQ
+ETHERLINK1 Define these to the base address and IRQ of a 3c501 (NOT 3c503)
+ card. Refer to net/tcp/Space.c.
diff --git a/net/inet/README.DLINK b/net/inet/README.DLINK
new file mode 100644
index 0000000..cd69879
--- /dev/null
+++ b/net/inet/README.DLINK
@@ -0,0 +1,258 @@
+From: bj0rn@blox.se (Bjorn Ekwall)
+To: waltje@nic.NL.Mugnet.ORG
+Subject: Re: NET-2 and the D-Link Ethernet driver
+X-Mailer: Mail User's Shell (7.2.4 2/2/92)
+Message-ID: <m0nvKUE-0001OLC@blox.se>
+Date: Tue, 18 May 1993 07:45:13 +0000
+
+
+Hello Fred!
+
+> My collegue put a copy of your article regarding the Dlink 0.20
+> driver on my desk. I wasn't aware such a driver already existed...
+>
+> Anyway. I'd like to get a copy of your driver to add it to the
+> current "new" kernel code (called NET-2). Many people are waiting
+> for it, and now is the time to add it... right?
+
+That's true, I'm waiting too :-)
+
+Enclosed is the most current release that has been shipped so far (== v 0.20+).
+Note that there are some things that still needs some attention:
+
+ - There is a not yet fully tested part of the code for handling
+ alternations of transmitter pages. Enable with "-DD_LINK_FIFO".
+
+ - I am only 99% sure that I have not missed any valid
+ interrupt-code from the adapter.
+
+ - There is always(?) a spurious IRQ 7 interrupt at boot. This might
+ come from the port being initialized by lp_init(), but I haven't
+ been able to stomp it out. Kludge found in d_link_interrupt().
+
+Anyway, there seems to be some people out there using the driver,
+and I haven't received any bugreports yet on this version
+(hope that Murphy doesn't read email :-))
+
+
+Good luck with the code,
+
+Bjorn Ekwall == bj0rn@blox.se
+
+
+= = = = = = Release shar-file follows = = = = = = = = = = = = = = = = = = =
+Now, here it is!
+
+A driver for the D-Link Ethernet pocket adapter in the parallel port,
+to use with Linux on a laptop PC.
+
+It has been tested with mount, rsh, ftp, telnet and remote X-clients.
+TCP/IP throughput can be expected to be around 70-80 kbytes/second.
+
+It now "runs" on:
+ - linux 0.99pl5, (with supplied patch for linux/net/tcp/dev.c)
+ - linux 0.99pl6, and later
+(using a 386-25 Taiwan-made with 5Mb DRAM + 120Mb disk (60Mb == linux)).
+
+Since this still is an alpha-release, some bugs might still be lurking...
+
+All comments/requests/fixes to Bjorn Ekwall == bj0rn@blox.se
+
+*** If you read nothing else, at least check around line 20 in the source! ***
+
+(You may want to unshar this in /linux/net/tcp)
+
+This is version 0.20
+---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ---- 8< ----
+= = = = = = Release shar-file ends = = = = = = = = = = = = = = = = = = =
+
+
+
+ CONTENTS:
+
+ 1. Introduction.
+ 2. License.
+ 3. Files in this release.
+ 4. Installation.
+ 5. Known problems and some solutions.
+ 6. Acknowledgments.
+
+
+ 1. INTRODUCTION.
+
+ This is an Ethernet driver for the D-Link Ethernet pocket
+ adapter for the parallel port, used with Linux on a laptop.
+
+ This is an ALPHA release, i.e. it might not work flawlessly! (:-)
+ It now runs on SLS (beg. of February) linux 0.99pl5, patched
+ in linux/net/tcp/dev.c, and 0.99pl6 (and later), in a 386-25
+ Taiwan-made with 5Mb DRAM + 120Mb disk (60Mb == linux).
+
+ I have used this driver for ftp, telnet and X-clients on
+ remote machines. Transmissions with ftp seems to work as
+ good as can be expected (i.e. about 80k bytes/sec) from a
+ parallel port...:-)
+
+ All comments/fixes to Bjorn Ekwall (bj0rn@blox.se).
+
+
+ 2. LICENSE.
+
+ 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.
+
+
+ 3. FILES IN THIS RELEASE.
+
+ README.d_link This file.
+ d_link.c The Source (,may it be with You :-).
+ Makefile.new Replaced "we.o" with "d_link.o"
+ Space.c.new An entry for "d_link" instead of "wdxxx"
+
+
+ 4. INSTALLATION.
+
+ o Replace (or edit) /linux/net/tcp/Makefile with
+ Makefile.new (save the original!). Actually, you only
+ have to make sure that "d_link.o" is included in the list
+ at "OBJS =".
+
+ o Replace /linux/net/tcp/Space.c with Space.c.new (save the
+ original!). If your kernel source is later than 0.99p5,
+ check your original for any greater differences
+ (shouldn't really be any).
+
+ o Copy d_link.c to /linux/net/tcp if you unshared somewhere
+ else.
+
+ o If you have linux 0.99p5, then add two lines close to the
+ end of /linux/net/tcp/dev.c (thanks Ross Biro, for the
+ information!):
+ ...
+ }
+ }
+ skb->next = NULL;
+ skb->prev = NULL;
+ sti();
+ /* this will send it through the process again. */
+ dev->queue_xmit (skb, dev, -i-1);
+ + if (dev->tbusy)
+ + return;
+ }
+ }
+
+ ...
+
+ o Read the NET_FAQ and the relevant files in /etc/inet
+ (hint: "hosts")
+
+ o Make sure that TCP/IP is included in your config, and
+ then do:
+ # cd /linux
+ # make clean
+ # make depend
+ # make Image (or whatever magic you usually do)
+
+ o I use lilo to boot multiple kernels, so that I at least
+ can have one working kernel :-). If you do too, append
+ these lines to /etc/lilo/config:
+ ...
+ image = /usr/src/linux/Image
+ label = newlinux
+ root = /dev/hda2 (or whatever YOU have...)
+ ...
+
+ # /etc/lilo/install
+
+ o Do "sync" and reboot the new kernel with a D-Link pocket
+ adapter connected.
+
+ Now, watch for any fireworks (try to ignore (or live with)
+ the smoke... :-) or:
+
+ do
+ read NET-FAQ and all info in /etc/inet
+ if fix in code needed...
+ vi /linux/net/tcp/d_link.c
+ cd /linux
+ make Image
+ /etc/lilo/install
+ sync
+ reboot
+ endif
+ try it...
+ until satisfied
+
+
+ 5. KNOWN "PROBLEMS" AND SOME SOLUTIONS.
+
+ o Some machines have trouble handling the parallel port and
+ the adapter at high speed. If you experience problems
+ like "Strange interrupt...", try to uncomment the
+ "#define REALLY_SLOW_IO" near line 20 in d_link.c
+
+ o A frequent number of "eth0: transmit timed out..." might
+ indicate a heavy load on your network or some strange
+ internal driver bug. You can decrease the number of
+ messages by changing about 25 lines into the function
+ "d_link_start_xmit()", where it looks like:
+ if (tickssofar < 5)
+ Change the "5" to something greater, like 10, 20 or
+ whatever... Every "tick" equals 10 milliseconds.
+
+ o There has been some "hangs" when communicating with other
+ systems. As of now I'm not clear of where the trouble
+ might come from. It seems that reception of large
+ amounts of information can trigger this. Try to be
+ patient and see if the machines can clear up the mess
+ without any help (can happen!). If not, try to switch to
+ another virtual console and call the other system with
+ telnet and see what happens. If your machine seems
+ completely locked, there might be a race of transmit and
+ receive interrupts emanating from inet_bh() in dev.c .
+ The reason why this driver can trigger this is not quite
+ clear yet.
+
+ o There will always(?) be a spurious IRQ 7 interrupt at
+ boot. This might come from the port being initialized by
+ lp_init(), but I haven't been able to stomp it out.
+ Kludge can be found in d_link_interrupt().
+
+ o The code inside d_link_interrupt() is somewhat of a
+ guesswork since I'm not quite clear about how the adapter
+ REALLY tells us about its internal status. Since my only
+ source of information is the asm-source (:-), I'm not
+ sure of how to improve on it. Some more turns around the
+ edit_a_hack-compile-boot-tryout loop...
+
+ o There is some rudimentary support for debugging, see
+ the source. Use "-DD_LINK_DEBUG=3" when compiling.
+
+
+ 6. ACKNOWLEDGMENTS.
+
+ This driver wouldn't have been done without the base
+ (and support) from Ross Biro (bir7@leland.stanford.edu).
+ The driver also relies upon GPL-ed source from D-Link Inc.
+ and from Russel Nelson at Crynwr Software (nelson@crynwr.com).
+ Additional input also from Donald Becker (becker@super.org).
+ Alpha release primary victim^H^H^H^H^H^Htester:
+ Erik Proper (erikp@cs.kun.nl).
+
+
+ Happy hacking!
+
+ Bjorn Ekwall == bj0rn@blox.se
diff --git a/net/inet/README.DRIVERS b/net/inet/README.DRIVERS
new file mode 100644
index 0000000..0e19811
--- /dev/null
+++ b/net/inet/README.DRIVERS
@@ -0,0 +1,425 @@
+This file contains some info on writing device driver for the
+NET-2 networking suite of the Linux operating system.
+
+Basically, have a look at the "loopback.c" and "slip.c" drivers
+to get a feel of what is going on, and then use the code below
+as a skeleton of the new driver you are writing.
+
+=============================================================================
+/* network.c: A sample network driver core for linux. */
+/*
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorported herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version =
+ "network.c:v0.02 Donald Becker (becker@super.org)\n";
+
+/* Always include 'config.h' first in case the user wants to turn on
+ or override something. */
+#include <linux/config.h>
+
+/*
+ Sources:
+ List your sources of programming information to document that
+ the driver is your own creation, and give due credit to others
+ that contributed to the work. Remember that GNU project code
+ cannot use proprietary or trade secret information. Interface
+ definitions are generally considered non-copyrightable to the
+ extent that the same names and structures must be used to be
+ compatible. */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/in.h>
+
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The map from IRQ number (as passed to the interrupt handler) to
+ 'struct device'. */
+extern struct device *irq2dev_map[16];
+
+/* Common network statistics -- these will be in *.h someday. */
+struct netstats {
+ int tx_packets;
+ int rx_packets;
+ int tx_errors;
+ int rx_errors;
+ int missed_packets;
+ int soft_tx_errors;
+ int soft_rx_errors;
+ int soft_trx_err_bits;
+};
+static struct netstats *localstats;
+
+/* Index to functions, as function prototypes. */
+/* Put in the device structure. */
+static int net_open(struct device *dev);
+static void net_send_packet(struct sk_buff *skb, struct device *dev);
+/* Dispatch from interrupts. */
+static void net_interrupt(int reg_ptr);
+static void net_tx_intr(struct device *dev);
+static void net_rx_intr(struct device *dev);
+/* Routines used internally. */
+static void chipset_init(struct device *dev, int startp);
+static void trigger_send(struct device *dev, unsigned int length,
+ int start_page);
+extern int netcard_probe(int ioaddr, struct device *dev);
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'config <dev->name>' program is
+ run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int
+net_open(struct device *dev)
+{
+ if ( ! net_status.exists) {
+ printk("%s: Opening a non-existent physical device\n",
+ dev ? dev->name : "(null)");
+ return ENXIO; /* Anything non-zero will do. */
+ }
+
+ chipset_init(dev, 1);
+
+ /* If the IRQ line can be software selected find a free line to use. */
+ if (dev->irq < 2) {
+ int irq_list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+ int *irq = irq_list;
+ for (; *irq; irq++) {
+ if (request_irq(dev->irq = *irq, &net_interrupt) == 0)
+ break;
+ }
+ if (*irq == 0) {
+ printk (" unable to get an IRQ.\n");
+ return EAGAIN;
+ }
+ card_set_irq(dev, *irq);
+ }
+
+ irq2dev_map[irq] = dev;
+ dev->tbusy = 0; /* Transmit busy... */
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+/* The inverse routine to net_open(). */
+static int
+net_close(struct device *dev)
+{
+ dev->start = 0;
+ card_flush_tx(dev);
+ card_disable_reciever(dev);
+ irq2dev_map[dev->irq] = NULL;
+ /* If not IRQ jumpered, free up the interrupt line. */
+ card_set_irq(0);
+ free_irq(dev->irq);
+ return 0;
+}
+
+static int
+net_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+
+ if (dev->tbusy) { /* Do timeouts, to avoid hangs. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ conflict" : "network cable problem");
+ /* Try to restart the adaptor. */
+ chipset_init(dev, 1);
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* For ethernet, fill in the header. */
+ if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
+ skb->dev = dev;
+ arp_queue (skb);
+ return 0;
+ }
+
+ dev->trans_start = jiffies;
+ cli();
+ net_send_packet(skb, dev);
+ sti();
+ if (skb->free)
+ kfree_skb (skb, FREE_WRITE);
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int interrupts, boguscount = 0;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+ sti(); /* Allow other interrupts. */
+
+ if (net_debug >= 4)
+ printk("%s: interrupt.\n", dev->name);
+
+ localstats = (struct netstats*) dev->private;
+
+ do {
+ interrupts = chip_read_status(dev);
+ if (interrupts & OVERRUN_INTR) {
+ localstats->missed_packets++;
+ net_rx_overrun(dev);
+ } else if (interrupts & RX_INTR) {
+ /* Got a good (?) packet. */
+ net_receive(dev);
+ localstats->rx_packets++;
+ }
+ if (interrupts & TX_INTR) {
+ /* Push the next to-transmit packet through. */
+ net_tx_intr(dev);
+ } else if (interrupts & COUNTERS_INTR) {
+ /* Increment the appropriate 'localstats' field. */
+ }
+ } while (++boguscount < 20) ;
+
+ if (interrupts && net_debug) {
+ printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ }
+ return;
+}
+
+/* We have finished a transmit: check for errors and then mark that
+ the bottom half has some work to do. */
+static void
+net_tx_intr(struct device *dev)
+{
+ int status = card_get_status(dev);
+ if (status & TX_OK)
+ tx_packets++;
+ else {
+ tx_errors++;
+ card_reset(dev);
+ }
+ dev->tbusy = 0;
+ mark_bh (INET_BH);
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_receive(struct device *dev)
+{
+ int boguscount = 0;
+
+ do {
+ int size = chip_get_rx_size(dev);
+ int sksize;
+ struct sk_buff *skb;
+
+ if (size == 0) /* Read all the frames? */
+ break; /* Done for now */
+
+ if ((size < 32 || size > dev->mtu) && net_debug)
+ printk("%s: Bogus packet size %d.\n", dev->name, size);
+
+ sksize = sizeof(struct sk_buff) + size;
+ skb = kmalloc(sksize, GFP_ATOMIC);
+ if (skb != NULL) {
+ skb->lock = 0;
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ /* 'skb+1' points to the start of sk_buff data area. */
+ card_get_packet(dev, size, (void *)(skb+1));
+ if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
+ printk("%s: receive buffers full.\n", dev->name);
+ break;
+ }
+ } else if (net_debug) {
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize);
+ break;
+ }
+ rx_packets++;
+ } while (++boguscount < 10);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* We have a receiver overrun: do whatever this chipset needs
+ to recover. */
+static void
+net_rx_overrun(struct device *dev)
+{
+ int reset_start_time = jiffies;
+ if (net_debug)
+ printk("%s: Receiver overrun.\n", dev->name);
+ return;
+}
+
+int
+netdev_init(struct device *dev)
+{
+ int i, found = 0;
+ int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+ /* Alpha testers must have the version number to report bugs. */
+ if (net_debug > 1)
+ printk(version);
+
+ /* Probe for an adaptor at a likely set of locations. */
+ if (dev->base_addr > 0x100) {
+ if (card_probe_addr(dev, ioaddr) == 0) {
+ printk("%s: probe failed at %#3x.\n", dev->name, ioaddr);
+ return ENODEV;
+ }
+ } else {
+ for (port = &ports[0]; *port; port++)
+ if (card_probe_addr(dev, *port))
+ break;
+ if (*port == 0) {
+ dev->open = NULL;
+ printk("No network device found.\n");
+ return ENODEV;
+ }
+ }
+
+ /* Initialize the device structure. */
+ dev->private = kmalloc(sizeof(struct netstats), GFP_KERNEL);
+ memset(dev->private, 0, sizeof(struct netstats));
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ dev->type = ETHER_TYPE;
+ for (i = 0; i < dev->addr_len; i++) {
+ dev->broadcast[i]=0xff;
+ }
+ card_get_station_address(dev->dev_addr);
+
+ /* Finish setting up the DEVICE info. */
+ dev->queue_xmit = dev_queue_xmit;
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit= net_start_xmit;
+
+ /* These are ethernet specific. */
+ dev->type = ARPHRD_ETHER;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->hard_header = eth_header;
+ dev->add_arp = eth_add_arp;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+ dev->hard_header_len= ETH_HLEN;
+ dev->addr_len = ETH_ALEN;
+
+ /* New-style flags. */
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+#ifdef jumpered_interrupts
+ /* If this board has jumpered interrupts, snarf the interrupt vector
+ now. There is no point in waiting since no other device can use
+ the interrupt, and this marks the 'irqaction' as busy. */
+
+ if (dev->irq == -1)
+ ; /* Do nothing: a user-level program will set it. */
+ else if (dev->irq < 2) { /* "Auto-IRQ" */
+ autoirq_setup(0);
+ /* Trigger an interrupt here. */
+ dev->irq = autoirq_report(0);
+ if (net_debug >= 2)
+ printk(" autoirq is %d", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ { int irqval = request_irq(dev->irq, &net_interrupt);
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, irqval);
+ return 0;
+ }
+ }
+#endif /* jumpered interrupt */
+ printk("%s: %s found at %#3x, IRQ %d.\n", dev->name,
+ "network card", dev->base_addr, dev->irq);
+
+ return 0;
+}
+
+/* Check IOADDR for a network adaptor of this type, and return
+ a non-zero value iff one exists. */
+static int
+card_probe_addr(struct device *dev, int ioaddr)
+{
+ if (inb_p(ioaddr) == 0xFF) { /* Trivial check. */
+ return 0;
+ }
+
+ /* Check for the card here. You should minimize outb()s, and
+ restore the previous value if possible. */
+ if (/*probe result */1 == 0)
+ return 0;
+
+ return dev->base_addr = ioaddr;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c network.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
+===============================================================================
diff --git a/net/inet/README.TODO b/net/inet/README.TODO
new file mode 100644
index 0000000..ce2d19e
--- /dev/null
+++ b/net/inet/README.TODO
@@ -0,0 +1,32 @@
+THINGS TO DO:
+
++ All device drivers must adhere to the DDI scheme of being able
+ to get configured dynamically (i.e. at run time). This requires
+ lots of work in the drivers, and in the way the "dev" structures
+ are handled. Before Linux 1.0 if possible.
+
++ Replace the ugly 'sk_buff' structure with the 'mbuf' structure,
+ and redo all code accordingly.
+
++ Move all device drivers to the 'drv' layer.
+
++ Impose a much stricter layering between the protocol modules.
+
++ Implement IP fragmentation.
+
++ Add support for the PPP and AX.25 protocols, possibly via the
+ external 'pkt' driver. This is NOT the current 'packet.c' module,
+ but a scheme in which we can implement external (user-space) drivers
+ for networking protocols, like SLIP, PPP and AX.25.
+
++ Add support for the Novell IPX/SPX protocols.
+
+REMAINING KNOWN BUGS AND PROBLEMS:
+
++ Local-port weirdness when using the 'r' utilities?
+ All connections seem to be using the same local (privileged) TCP
+ port (1023), and this cannot be the way God wanted it to be...
+
++ Sudden lockups when overloading a socket?
+ This seems to occur with X11 sessions (as per Linus Torvalds, 05/28/93)
+ and has its origin in the new timer.c code...
diff --git a/net/inet/README1.PLIP b/net/inet/README1.PLIP
new file mode 100644
index 0000000..d68745b
--- /dev/null
+++ b/net/inet/README1.PLIP
@@ -0,0 +1,113 @@
+\title{PLIP: The Parallel Line Internet Protocol Device}
+
+\author{ Donald Becker (becker@super.org)}
+\affiliation{I.D.A. Supercomputing Research Center, Bowie MD 20715}
+
+%% At some point T. Thorn will probably contribute text,
+%% \author{ Tommy Thorn (tthorn@daimi.aau.dk)}
+
+\section{PLIP Introduction}
+This document describes the parallel port packet pusher for Net/LGX.
+This device interface allows a point-to-point connection between two
+parallel ports to appear as a IP network interface.
+
+\chapter{PLIP hardware interconnection}
+PLIP uses several different data transfer methods. The first (and the
+only one implemented in the early version of the code) uses a standard
+printer "null" cable to transfers data four bits at a time using
+data bit outputs connected to status bit inputs.
+
+The second data transfer method relies on both machines having
+bi-directional parallel ports, rather than output-only ``printer''
+ports. This allows byte-wide transfers and avoids reconstructing
+nibbles into bytes, leading to much faster transfers.
+
+\section{Parallel Transfer Mode 0 Cable}
+The cable for the first transfer mode is a standard
+printer "null" cable which transfers data four bits at a time using
+data bit outputs of the first port (machine T) connected to the
+status bit inputs of the second port (machine R). There are five
+status inputs, and they are used as four data inputs and a clock (data
+strobe) input, arranged so that the data input bits appear as contiguous
+bits with standard status register implementation.
+
+A cable that implements this protocol is available commercially as a
+"Null Printer" or "Turbo Laplink" cable. It can be constructed with
+two DB-25 male connectors symmetrically connected as follows:
+
+ STROBE output 1*
+ D0->ERROR 2 - 15 15 - 2
+ D1->SLCT 3 - 13 13 - 3
+ D2->PAPOUT 4 - 12 12 - 4
+ D3->ACK 5 - 10 10 - 5
+ D4->BUSY 6 - 11 11 - 6
+ D5,D6,D7 are 7*, 8*, 9*
+ AUTOFD output 14*
+ INIT output 16*
+ SLCTIN 17 - 17
+ extra grounds are 18*,19*,20*,21*,22*,23*,24*
+ GROUND 25 - 25
+* Do not connect these pins on either end
+
+If the cable you are using has a metallic shield it should be
+connected to the metallic DB-25 shell at one end only.
+
+\section{Parallel Transfer Mode 1}
+The second data transfer method relies on both machines having
+bi-directional parallel ports, rather than output-only ``printer''
+ports. This allows byte-wide transfers, and avoids reconstructing
+nibbles into bytes. This cable should not be used on unidirectional
+``printer'' (as opposed to ``parallel'') ports or when the machine
+isn't configured for PLIP, as it will result in output driver
+conflicts and the (unlikely) possibility of damage.
+
+The cable for this tranfer mode should be constructed as follows:
+
+ STROBE->BUSY 1 - 11
+ D0->D0 2 - 2
+ D1->D1 3 - 3
+ D2->D2 4 - 4
+ D3->D3 5 - 5
+ D4->D4 6 - 6
+ D5->D5 7 - 7
+ D6->D6 8 - 8
+ D7->D7 9 - 9
+ INIT -> ACK 16 - 10
+ AUTOFD->PAPOUT 14 - 12
+ SLCT->SLCTIN 13 - 17
+ GND->ERROR 18 - 15
+ extra grounds are 19*,20*,21*,22*,23*,24*
+ GROUND 25 - 25
+* Do not connect these pins on either end
+
+Once again, if the cable you are using has a metallic shield it should
+be connected to the metallic DB-25 shell at one end only.
+
+\section{PLIP Mode 0 tranfer protocol}
+The PLIP driver is compatible with the "Crynwr" parallel port transfer
+standard in Mode 0. That standard specifies the following protocol:
+
+ send header nibble '8'
+ count-low octet
+ count-high octet
+ ... data octets
+ checksum octet
+
+Each octet is sent as
+ <wait for rx. '1'> <send 0x10+(octet&0x0F)>
+ <wait for rx. '0'> <send 0x00+((octet>>4)&0x0F)>
+
+To start a transfer the transmitting machine outputs a nibble 0x08.
+The raises the ACK line, triggering an interrupt in the receiving
+machine. The receiving machine disables
+
+Restated:
+
+(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise)
+Send_Byte:
+ OUT := low nibble, OUT.4 := 1
+ WAIT FOR IN.4 = 1
+ OUT := high nibble, OUT.4 := 0
+ WAIT FOR IN.4 = 0
+
+
diff --git a/net/inet/README2.PLIP b/net/inet/README2.PLIP
new file mode 100644
index 0000000..63b4754
--- /dev/null
+++ b/net/inet/README2.PLIP
@@ -0,0 +1,78 @@
+
+(2nd attempt. 1st bounced.)
+Hi again
+
+About my previous mail: I've looked into parallel.asm, and I'm
+rather confused. Looks like the code agrees with you, but not
+the protocol description preceeding it?? I got to look more
+careful, but it wont be for a while (approx a week).
+
+>From plip.c (v0.04):
+
+>make one yourself. The wiring is:
+> INIT 16 - 16 SLCTIN 17 - 17
+> GROUND 25 - 25
+> D0->ERROR 2 - 15 15 - 2
+
+I saw you removed 1 and 14 from the cable description, but not
+16 and 17. Why is that?
+
+Have been succesful in getting parallel.com working (the Messy-Loss
+software). Using the pksend on the sender and pkall/pkwatch/whatnot
+gives me a hung receiver. (The cable works, I've tried unet11, a DOS
+cheap-net prog.)
+
+Using PLIP v0.03 and trying to ping the other end gives
+ 88 timeout 88 timeout....(more) 2386 bogous packet size, dropped
+on the receiver, and on the sender lots of timeout, but of
+course I don't know how much is supposed to work.
+
+The following to something I wrote when I should have gone to bed a
+long time ago. Use it for whatever you like, or dump it in the bin. ;^)
+
+/Tommy
+-----
+Becker [& Co] provdly presents PLIP
+
+What is PLIP?
+=============
+
+PLIP is Parallel Line IP, that is, the transportation of IP packages
+over a parallel port. In the case of a PC, the obvious choice is the
+printer port. PLIP is a non-standard, but [can use] uses the standard
+LapLink null-printer cable [can also work in turbo mode, with a PLIP
+cable]. [The protocol used to pack IP packages, is a simple one
+initiated by Crynwr.]
+
+Advantages of PLIP
+==================
+
+It's cheap, it's availble everywhere, and it's easy.
+
+The PLIP cable is all that's needed to connect two Linux boxes, and it
+can be build for very bucks.
+
+Connecting two Linux boxes takes only a seconds decision and a few
+minutes work, no need to search for a [supported] netcard. This might
+even be especially important in the case of notebooks, where netcard
+are not easily availble.
+
+Not requiring a netcard also means that apart from connecting the
+cables, everything else is software configuration [which in principle
+could be made very easy.]
+
+Disadvantages of PLIP
+=====================
+
+Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m.
+Can only be used to connect three (?) Linux boxes. Doesn't connect to
+an exiting ethernet. Isn't standard (not even de facto standard, like
+SLIP).
+
+Performens
+==========
+
+PLIP easily outperforms ethernet cards....(ups, I was dreaming, but
+it *is* getting late. EOB)
+
+
diff --git a/net/inet/Space.c b/net/inet/Space.c
new file mode 100644
index 0000000..d7cbc05
--- /dev/null
+++ b/net/inet/Space.c
@@ -0,0 +1,220 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Holds initial configuration information for devices.
+ *
+ * NOTE: This file is a nice idea, but its current format does not work
+ * well for drivers that support multiple units, like the SLIP
+ * driver. We should actually have only one pointer to a driver
+ * here, with the driver knowing how many units it supports.
+ * Currently, the SLIP driver abuses the "base_addr" integer
+ * field of the 'device' structure to store the unit number...
+ * -FvK
+ *
+ * Version: @(#)Space.c 1.0.7 05/28/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald J. Becker, <becker@super.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/ddi.h>
+#include "dev.h"
+
+
+#define LOOPBACK /* always present, right? */
+
+
+#define NEXT_DEV NULL
+
+
+#ifdef D_LINK
+ extern int d_link_init(struct device *);
+ static struct device d_link_dev = {
+ "dl0",
+ 0,
+ 0,
+ 0,
+ 0,
+ D_LINK_IO,
+ D_LINK_IRQ,
+ 0, 0, 0,
+ NEXT_DEV,
+ d_link_init
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&d_link_dev)
+#endif
+
+
+#ifdef EL1
+# ifndef EL1_IRQ
+# define EL1_IRQ 9
+# endif
+ extern int el1_init(struct device *);
+ static struct device el1_dev = {
+ "el0",
+ 0,
+ 0,
+ 0,
+ 0,
+ EL1,
+ EL1_IRQ,
+ 0, 0, 0,
+ NEXT_DEV,
+ el1_init
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&el1_dev)
+#endif /* EL1 */
+
+#if defined(EI8390) || defined(EL2) || defined(NE2000) \
+ || defined(WD80x3) || defined(HPLAN)
+# ifndef EI8390
+# define EI8390 0
+# endif
+# ifndef EI8390_IRQ
+# define EI8390_IRQ 0
+# endif
+ extern int ethif_init(struct device *);
+ static struct device ei8390_dev = {
+ "eth0",
+ 0, /* auto-config */
+ 0,
+ 0,
+ 0,
+ EI8390,
+ EI8390_IRQ,
+ 0, 0, 0,
+ NEXT_DEV,
+ ethif_init
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ei8390_dev)
+#endif /* The EI8390 drivers. */
+
+#ifdef PLIP
+ extern int plip_init(struct device *);
+ static struct device plip2_dev = {
+ "plip2",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x278,
+ 2,
+ 0, 0, 0,
+ NEXT_DEV,
+ plip_init
+ };
+ static struct device plip1_dev = {
+ "plip1",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x378,
+ 7,
+ 0, 0, 0,
+ &plip2_dev,
+ plip_init
+ };
+ static struct device plip0_dev = {
+ "plip0",
+ 0,
+ 0,
+ 0,
+ 0,
+ 0x3BC,
+ 5,
+ 0, 0, 0,
+ &plip1_dev,
+ plip_init
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&plip0_dev)
+#endif /* PLIP */
+
+
+#ifdef SLIP
+ extern int slip_init(struct device *);
+ static struct device slip3_dev = {
+ "sl3", /* Internal SLIP driver, channel 3 */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x3, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ slip_init /* slip_init should set up the rest */
+ };
+ static struct device slip2_dev = {
+ "sl2", /* Internal SLIP driver, channel 2 */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x2, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &slip3_dev, /* next device */
+ slip_init /* slip_init should set up the rest */
+ };
+ static struct device slip1_dev = {
+ "sl1", /* Internal SLIP driver, channel 1 */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x1, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &slip2_dev, /* next device */
+ slip_init /* slip_init should set up the rest */
+ };
+ static struct device slip0_dev = {
+ "sl0", /* Internal SLIP driver, channel 0 */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &slip1_dev, /* next device */
+ slip_init /* slip_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&slip0_dev)
+#endif /* SLIP */
+
+
+#ifdef LOOPBACK
+ extern int loopback_init(struct device *dev);
+ static struct device loopback_dev = {
+ "lo", /* Software Loopback interface */
+ -1, /* recv memory end */
+ 0x0, /* recv memory start */
+ -1, /* memory end */
+ 0, /* memory start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ loopback_init /* loopback_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&loopback_dev)
+#endif
+
+
+struct device *dev_base = NEXT_DEV;
diff --git a/net/inet/arp.c b/net/inet/arp.c
new file mode 100644
index 0000000..b835d8e
--- /dev/null
+++ b/net/inet/arp.c
@@ -0,0 +1,861 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * This file implements the Address Resolution Protocol (ARP),
+ * which is used by TCP/IP to map the IP addresses from a host
+ * to a low-level hardware address (like an Ethernet address)
+ * which it can use to talk to that host.
+ *
+ * NOTE: This module will be rewritten completely in the near future,
+ * because I want it to become a multi-address-family address
+ * resolver, like it should be. It will be put in a separate
+ * directory under 'net', being a protocol of its own. -FvK
+ *
+ * Version: @(#)arp.c 1.0.15 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Stephen A. Wood, <saw@hallc1.cebaf.gov>
+ * Arnt Gulbrandsen, <agulbra@pvv.unit.no>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "eth.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+#define ARP_MAX_TRIES 3
+
+
+static char *unk_print(unsigned char *, int);
+static char *eth_aprint(unsigned char *, int);
+
+
+static char *arp_cmds[] = {
+ "0x%04X",
+ "REQUEST",
+ "REPLY",
+ "REVERSE REQUEST",
+ "REVERSE REPLY",
+ NULL
+};
+#define ARP_MAX_CMDS (sizeof(arp_cmds) / sizeof(arp_cmds[0]))
+
+static struct {
+ char *name;
+ char *(*print)(unsigned char *ptr, int len);
+} arp_types[] = {
+ { "0x%04X", unk_print },
+ { "10 Mbps Ethernet", eth_aprint },
+ { "3 Mbps Ethernet", eth_aprint },
+ { "AX.25", unk_print },
+ { "Pronet", unk_print },
+ { "Chaos", unk_print },
+ { "IEEE 802.2 Ethernet (?)", eth_aprint },
+ { "Arcnet", unk_print },
+ { "AppleTalk", unk_print },
+ { NULL, NULL }
+};
+#define ARP_MAX_TYPE (sizeof(arp_types) / sizeof(arp_types[0]))
+
+
+struct arp_table *arp_table[ARP_TABLE_SIZE] = {
+ NULL,
+};
+struct sk_buff *arp_q = NULL;
+
+
+/* Dump the ADDRESS bytes of an unknown hardware type. */
+static char *
+unk_print(unsigned char *ptr, int len)
+{
+ static char buff[32];
+ char *bufp = buff;
+ int i;
+
+ for (i = 0; i < len; i++)
+ bufp += sprintf(bufp, "%02X ", (*ptr++ & 0377));
+ return(buff);
+}
+
+
+/* Dump the ADDRESS bytes of an Ethernet hardware type. */
+static char *
+eth_aprint(unsigned char *ptr, int len)
+{
+ if (len != ETH_ALEN) return("");
+ return(eth_print(ptr));
+}
+
+
+/* Dump an ARP packet. Not complete yet for non-Ethernet packets. */
+static void
+arp_print(struct arphdr *arp)
+{
+ int len, idx;
+ unsigned char *ptr;
+
+ if (inet_debug != DBG_ARP) return;
+
+ printk("ARP: ");
+ if (arp == NULL) {
+ printk("(null)\n");
+ return;
+ }
+
+ /* Print the opcode name. */
+ len = htons(arp->ar_op);
+ if (len < ARP_MAX_CMDS) idx = len;
+ else idx = 0;
+ printk("op ");
+ printk(arp_cmds[idx], len);
+
+ /* Print the ARP header. */
+ len = htons(arp->ar_hrd);
+ if (len < ARP_MAX_TYPE) idx = len;
+ else idx = 0;
+ printk(" hrd = "); printk(arp_types[idx].name, len);
+ printk(" pro = 0x%04X\n", htons(arp->ar_pro));
+ printk(" hlen = %d plen = %d\n", arp->ar_hln, arp->ar_pln);
+
+ /*
+ * Print the variable data.
+ * When ARP gets redone (after the formal introduction of NET-2),
+ * this part will be redone. ARP will then be a multi-family address
+ * resolver, and the code below will be made more general. -FvK
+ */
+ ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
+ printk(" sender HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
+ ptr += arp->ar_hln;
+ printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
+ ptr += arp->ar_pln;
+ printk(" target HA = %s ", arp_types[idx].print(ptr, arp->ar_hln));
+ ptr += arp->ar_hln;
+ printk(" PA = %s\n", in_ntoa(*(unsigned long *) ptr));
+}
+
+
+/* This will try to retransmit everything on the queue. */
+static void
+arp_send_q(void)
+{
+ struct sk_buff *skb;
+ struct sk_buff *next;
+
+ cli();
+ next = arp_q;
+ arp_q = NULL;
+ sti();
+ while ((skb = next) != NULL) {
+ if (skb->magic != ARP_QUEUE_MAGIC) {
+ printk("ARP: *** Bug: skb with bad magic %X: squashing queue\n",
+ skb->magic);
+ return;
+ }
+
+ /* Extra consistency check. */
+ if (skb->next == NULL
+#ifdef CONFIG_MAX_16M
+ || ((unsigned long)(skb->next) > 16*1024*1024)
+#endif
+ ) {
+ printk("ARP: *** Bug: bad skb->next, squashing queue\n");
+ return;
+ }
+
+ /* First remove skb from the queue. */
+ next = skb->next;
+ if (next != skb) {
+ skb->prev->next = next;
+ next->prev = skb->prev;
+ } else {
+ next = NULL;
+ }
+
+ skb->magic = 0;
+ skb->next = NULL;
+ skb->prev = NULL;
+
+ /* Decrement the 'tries' counter. */
+ cli();
+ skb->tries--;
+ if (skb->tries == 0) {
+ /*
+ * Grmpf.
+ * We have tried ARP_MAX_TRIES to resolve the IP address
+ * from this datagram. This means that the machine does
+ * not listen to our ARP requests. Perhaps someone tur-
+ * ned off the thing?
+ * In any case, trying further is useless. So, we kill
+ * this packet from the queue. (grinnik) -FvK
+ */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+
+ sti();
+ continue;
+ }
+
+ /* Can we now complete this packet? */
+ sti();
+ if (!skb->dev->rebuild_header(skb+1, skb->dev)) {
+ /* Yes, so send it out. */
+ skb->next = NULL;
+ skb->prev = NULL;
+ skb->arp = 1;
+ skb->dev->queue_xmit(skb, skb->dev, 0);
+ } else {
+ /* Alas. Re-queue it... */
+ cli();
+ skb->magic = ARP_QUEUE_MAGIC;
+ if (arp_q == NULL) {
+ skb->next = skb;
+ skb->prev = skb;
+ arp_q = skb;
+ } else {
+ skb->next = arp_q;
+ skb->prev = arp_q->prev;
+ arp_q->prev->next = skb;
+ arp_q->prev = skb;
+ }
+ sti();
+ }
+ }
+}
+
+
+/* Create and send our response to an ARP request. */
+static int
+arp_response(struct arphdr *arp1, struct device *dev)
+{
+ struct arphdr *arp2;
+ struct sk_buff *skb;
+ unsigned long src, dst;
+ unsigned char *ptr1, *ptr2;
+ int hlen;
+
+ /* Get some mem and initialize it for the return trip. */
+ skb = kmalloc(sizeof(struct sk_buff) + sizeof(struct arphdr) +
+ (2 * arp1->ar_hln) + (2 * arp1->ar_pln) +
+ dev->hard_header_len, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("ARP: no memory available for ARP REPLY!\n");
+ return(1);
+ }
+
+ /* Decode the source (REQUEST) message. */
+ ptr1 = ((unsigned char *) &arp1->ar_op) + sizeof(u_short);
+ src = *((unsigned long *) (ptr1 + arp1->ar_hln));
+ dst = *((unsigned long *) (ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln));
+
+ skb->lock = 0;
+ skb->mem_addr = skb;
+ skb->len = sizeof(struct arphdr) + (2 * arp1->ar_hln) +
+ (2 * arp1->ar_pln) + dev->hard_header_len;
+ skb->mem_len = sizeof(struct sk_buff) + skb->len;
+ hlen = dev->hard_header((unsigned char *)(skb+1), dev,
+ ETH_P_ARP, src, dst, skb->len);
+ if (hlen < 0) {
+ printk("ARP: cannot create HW frame header for REPLY !\n");
+ return(1);
+ }
+
+ /*
+ * Fill in the ARP REPLY packet.
+ * This looks ugly, but we have to deal with the variable-length
+ * ARP packets and such. It is not as bad as it looks- FvK
+ */
+ arp2 = (struct arphdr *) ((unsigned char *) (skb+1) + hlen);
+ ptr2 = ((unsigned char *) &arp2->ar_op) + sizeof(u_short);
+ arp2->ar_hrd = arp1->ar_hrd;
+ arp2->ar_pro = arp1->ar_pro;
+ arp2->ar_hln = arp1->ar_hln;
+ arp2->ar_pln = arp1->ar_pln;
+ arp2->ar_op = htons(ARPOP_REPLY);
+ memcpy(ptr2, dev->dev_addr, arp2->ar_hln);
+ ptr2 += arp2->ar_hln;
+ memcpy(ptr2, ptr1 + (arp1->ar_hln * 2) + arp1->ar_pln, arp2->ar_pln);
+ ptr2 += arp2->ar_pln;
+ memcpy(ptr2, ptr1, arp2->ar_hln);
+ ptr2 += arp2->ar_hln;
+ memcpy(ptr2, ptr1 + arp1->ar_hln, arp2->ar_pln);
+
+ skb->free = 1;
+ skb->arp = 1;
+ skb->sk = NULL;
+ skb->next = NULL;
+
+ DPRINTF((DBG_ARP, ">>"));
+ arp_print(arp2);
+
+ /* Queue the packet for transmission. */
+ 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)
+{
+ struct arp_table *apt;
+ unsigned long hash;
+
+ DPRINTF((DBG_ARP, "ARP: lookup(%s)\n", in_ntoa(paddr)));
+
+ /* We don't want to ARP ourselves. */
+ if (chk_addr(paddr) == IS_MYADDR) {
+ printk("ARP: ARPing my own IP address %s !\n", in_ntoa(paddr));
+ return(NULL);
+ }
+
+ /* Loop through the table for the desired address. */
+ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
+ cli();
+ apt = arp_table[hash];
+ while(apt != NULL) {
+ if (apt->ip == paddr) {
+ sti();
+ return(apt);
+ }
+ apt = apt->next;
+ }
+ sti();
+ return(NULL);
+}
+
+
+/* Delete an ARP mapping entry in the cache. */
+void
+arp_destroy(unsigned long paddr)
+{
+ struct arp_table *apt;
+ struct arp_table **lapt;
+ unsigned long hash;
+
+ DPRINTF((DBG_ARP, "ARP: destroy(%s)\n", in_ntoa(paddr)));
+
+ /* We cannot destroy our own ARP entry. */
+ if (chk_addr(paddr) == IS_MYADDR) {
+ DPRINTF((DBG_ARP, "ARP: Destroying my own IP address %s !\n",
+ in_ntoa(paddr)));
+ return;
+ }
+ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
+
+ cli();
+ lapt = &arp_table[hash];
+ while ((apt = *lapt) != NULL) {
+ if (apt->ip == paddr) {
+ *lapt = apt->next;
+ kfree_s(apt, sizeof(struct arp_table));
+ sti();
+ return;
+ }
+ lapt = &apt->next;
+ }
+ sti();
+}
+
+
+/* Create an ARP entry. The caller should check for duplicates! */
+static struct arp_table *
+arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype)
+{
+ struct arp_table *apt;
+ unsigned long hash;
+
+ DPRINTF((DBG_ARP, "ARP: create(%s, ", in_ntoa(paddr)));
+ DPRINTF((DBG_ARP, "%s, ", eth_print(addr)));
+ DPRINTF((DBG_ARP, "%d, %d)\n", hlen, htype));
+
+ apt = kmalloc(sizeof(struct arp_table), GFP_ATOMIC);
+ if (apt == NULL) {
+ printk("ARP: no memory available for new ARP entry!\n");
+ return(NULL);
+ }
+
+ /* Fill in the allocated ARP cache entry. */
+ hash = htonl(paddr) & (ARP_TABLE_SIZE - 1);
+ apt->ip = paddr;
+ apt->hlen = hlen;
+ apt->htype = htype;
+ apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */
+ memcpy(apt->ha, addr, hlen);
+ apt->last_used = timer_seq;
+ cli();
+ apt->next = arp_table[hash];
+ arp_table[hash] = apt;
+ sti();
+ return(apt);
+}
+
+
+/*
+ * An ARP REQUEST packet has arrived.
+ * We try to be smart here, and fetch the data of the sender of the
+ * packet- we might need it later, so fetching it now can save us a
+ * broadcast later.
+ * Then, if the packet was meant for us (i.e. the TARGET address was
+ * one of our own IP addresses), we set up and send out an ARP REPLY
+ * packet to the sender.
+ */
+int
+arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct arphdr *arp;
+ struct arp_table *tbl;
+ unsigned long src, dst;
+ unsigned char *ptr;
+ int ret;
+
+ DPRINTF((DBG_ARP, "<<\n"));
+ arp = skb->h.arp;
+ arp_print(arp);
+
+ /* If this test doesn't pass, something fishy is going on. */
+ if (arp->ar_hln != dev->addr_len || dev->type != NET16(arp->ar_hrd)) {
+ printk("ARP: Bad packet received on device \"%s\" !\n", dev->name);
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+
+ /* For now we will only deal with IP addresses. */
+ if (arp->ar_pro != NET16(ETH_P_IP) || arp->ar_pln != 4) {
+ if (arp->ar_op != NET16(ARPOP_REQUEST))
+ printk("ARP: Non-IP request on device \"%s\" !\n", dev->name);
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+
+ /*
+ * As said before, we try to be smart by using the
+ * info already present in the packet: the sender's
+ * IP and hardware address.
+ */
+ ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
+ memcpy(&src, ptr + arp->ar_hln, arp->ar_pln);
+ tbl = arp_lookup(src);
+ if (tbl != NULL) {
+ DPRINTF((DBG_ARP, "ARP: udating entry for %s\n", in_ntoa(src)));
+ memcpy(tbl->ha, ptr, arp->ar_hln);
+ tbl->hlen = arp->ar_hln;
+ tbl->flags |= ATF_COM;
+ tbl->last_used = timer_seq;
+ } else {
+ memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
+ if (chk_addr(dst) != IS_MYADDR) {
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ } else {
+ tbl = arp_create(src, ptr, arp->ar_hln, arp->ar_hrd);
+ if (tbl == NULL) {
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+ }
+ }
+
+ /*
+ * Since we updated the ARP cache, we might have enough
+ * information to send out some previously queued IP
+ * datagrams....
+ */
+ arp_send_q();
+
+ /*
+ * OK, we used that part of the info. Now check if the
+ * request was an ARP REQUEST for one of our own addresses...
+ */
+ if (arp->ar_op != NET16(ARPOP_REQUEST)) {
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+ memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
+ if (chk_addr(dst) != IS_MYADDR) {
+ DPRINTF((DBG_ARP, "ARP: request was not for me!\n"));
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+
+ /*
+ * Yes, it is for us.
+ * Allocate, fill in and send an ARP REPLY packet.
+ */
+ ret = arp_response(arp, dev);
+ kfree_skb(skb, FREE_READ);
+ return(ret);
+}
+
+
+/* Create and send an ARP REQUEST packet. */
+void
+arp_send(unsigned long paddr, struct device *dev, unsigned long saddr)
+{
+ struct sk_buff *skb;
+ struct arphdr *arp;
+ unsigned char *ptr;
+ int tmp;
+
+ DPRINTF((DBG_ARP, "ARP: send(paddr=%s, ", in_ntoa(paddr)));
+ DPRINTF((DBG_ARP, "dev=%s, ", dev->name));
+ DPRINTF((DBG_ARP, "saddr=%s)\n", in_ntoa(saddr)));
+
+ skb = kmalloc(sizeof(struct sk_buff) + sizeof(struct arphdr) +
+ (2 * dev->addr_len) + (2 * 4 /* arp->plen */), GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr));
+ return;
+ }
+
+ /* Fill in the request. */
+ skb->lock = 0;
+ skb->sk = NULL;
+ skb->mem_addr = skb;
+ skb->len = sizeof(struct arphdr) +
+ dev->hard_header_len + (2 * dev->addr_len) + 8;
+ skb->mem_len = sizeof(struct sk_buff) + skb->len;
+ skb->arp = 1;
+ skb->dev = dev;
+ skb->next = NULL;
+ tmp = dev->hard_header((unsigned char *)(skb+1), dev,
+ ETH_P_ARP, 0, saddr, skb->len);
+ if (tmp < 0) {
+ kfree_s(skb->mem_addr, skb->mem_len);
+ return;
+ }
+ arp = (struct arphdr *) ((unsigned char *) (skb+1) + tmp);
+ arp->ar_hrd = htons(dev->type);
+ arp->ar_pro = htons(ETH_P_IP);
+ arp->ar_hln = dev->addr_len;
+ arp->ar_pln = 4;
+ arp->ar_op = htons(ARPOP_REQUEST);
+
+ ptr = ((unsigned char *) &arp->ar_op) + sizeof(u_short);
+ memcpy(ptr, dev->dev_addr, arp->ar_hln);
+ ptr += arp->ar_hln;
+ memcpy(ptr, &saddr, arp->ar_pln);
+ ptr += arp->ar_pln;
+ memcpy(ptr, dev->broadcast, arp->ar_hln);
+ ptr += arp->ar_hln;
+ memcpy(ptr, &paddr, arp->ar_pln);
+
+ DPRINTF((DBG_ARP, ">>\n"));
+ arp_print(arp);
+ dev->queue_xmit(skb, dev, 0);
+}
+
+
+/* Find an ARP mapping in the cache. If not found, post a REQUEST. */
+int
+arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
+ unsigned long saddr)
+{
+ struct arp_table *apt;
+
+ DPRINTF((DBG_ARP, "ARP: find(haddr=%s, ", eth_print(haddr)));
+ DPRINTF((DBG_ARP, "paddr=%s, ", in_ntoa(paddr)));
+ DPRINTF((DBG_ARP, "dev=%s, saddr=%s)\n", dev->name, in_ntoa(saddr)));
+
+ switch(chk_addr(paddr)) {
+ case IS_MYADDR:
+ memcpy(haddr, dev->dev_addr, dev->addr_len);
+ return(0);
+ case IS_BROADCAST:
+ memcpy(haddr, dev->broadcast, 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_send will
+ * verify the address for us.
+ */
+ if ((!(apt->flags & ATF_PERM)) ||
+ (!before(apt->last_used, timer_seq+ARP_TIMEOUT) && apt->hlen != 0)) {
+ apt->last_used = timer_seq;
+ memcpy(haddr, apt->ha, dev->addr_len);
+ return(0);
+ } else {
+ DPRINTF((DBG_ARP, "ARP: find: found expired entry for %s\n",
+ in_ntoa(apt->ip)));
+ }
+ }
+
+ /*
+ * This assume haddr are at least 4 bytes.
+ * If this isn't true we can use a lookup table, one for every dev.
+ * NOTE: this bit of code still looks fishy to me- FvK
+ */
+ *(unsigned long *)haddr = paddr;
+
+ /* If we didn't find an entry, we will try to send an ARP packet. */
+ arp_send(paddr, dev, saddr);
+
+ return(1);
+}
+
+
+/* Add an entry to the ARP cache. Check for dupes! */
+void
+arp_add(unsigned long addr, unsigned char *haddr, struct device *dev)
+{
+ struct arp_table *apt;
+
+ DPRINTF((DBG_ARP, "ARP: add(%s, ", in_ntoa(addr)));
+ DPRINTF((DBG_ARP, "%s, ", eth_print(haddr)));
+ DPRINTF((DBG_ARP, "%d, %d)\n", dev->hard_header_len, dev->type));
+
+ /* This is probably a good check... */
+ if (addr == 0) {
+ printk("ARP: add: will not add entry for 0.0.0.0 !\n");
+ return;
+ }
+
+ /* First see if the address is already in the table. */
+ apt = arp_lookup(addr);
+ if (apt != NULL) {
+ DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr)));
+ apt->last_used = timer_seq;
+ memcpy(apt->ha, haddr , dev->addr_len);
+ return;
+ }
+ arp_create(addr, haddr, dev->addr_len, dev->type);
+}
+
+
+/* Create an ARP entry for a device's broadcast address. */
+void
+arp_add_broad(unsigned long addr, struct device *dev)
+{
+ struct arp_table *apt;
+
+ arp_add(addr, dev->broadcast, dev);
+ apt = arp_lookup(addr);
+ if (apt != NULL) {
+ apt->flags |= ATF_PERM;
+ }
+}
+
+
+/* Queue an IP packet, while waiting for the ARP reply packet. */
+void
+arp_queue(struct sk_buff *skb)
+{
+ cli();
+ skb->tries = ARP_MAX_TRIES;
+
+ if (skb->next != NULL) {
+ sti();
+ printk("ARP: arp_queue skb already on queue magic=%X.\n", skb->magic);
+ return;
+ }
+ 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;
+ }
+ skb->magic = ARP_QUEUE_MAGIC;
+ sti();
+}
+
+
+/*
+ * Write the contents of the ARP cache to a PROCfs file.
+ * This is not by long perfect, as the internal ARP table doesn't
+ * have all the info we would like to have. Oh well, it works for
+ * now, eh? - FvK
+ * Also note, that due to space limits, we cannot generate more than
+ * 4Kbyte worth of data. This usually is enough, but I have seen
+ * machines die from under me because of a *very* large ARP cache.
+ * This can be simply tested by doing:
+ *
+ * # ping 255.255.255.255
+ * # arp -a
+ *
+ * Perhaps we should redo PROCfs to handle larger buffers? Michael?
+ */
+int
+arp_get_info(char *buffer)
+{
+ struct arpreq *req;
+ struct arp_table *apt;
+ int i;
+ char *pos;
+
+ /* Loop over the ARP table and copy structures to the buffer. */
+ pos = buffer;
+ i = 0;
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ cli();
+ apt = arp_table[i];
+ sti();
+ while (apt != NULL) {
+ if (pos < (buffer + 4000)) {
+ req = (struct arpreq *) pos;
+ memset((char *) req, 0, sizeof(struct arpreq));
+ req->arp_pa.sa_family = AF_INET;
+ memcpy((char *) req->arp_pa.sa_data, (char *) &apt->ip, 4);
+ req->arp_ha.sa_family = apt->htype;
+ memcpy((char *) req->arp_ha.sa_data,
+ (char *) &apt->ha, apt->hlen);
+ }
+ pos += sizeof(struct arpreq);
+ cli();
+ apt = apt->next;
+ sti();
+ }
+ }
+ return(pos - buffer);
+}
+
+
+/* Set (create) an ARP cache entry. */
+static int
+arp_req_set(struct arpreq *req)
+{
+ struct arpreq r;
+ struct arp_table *apt;
+ struct sockaddr_in *si;
+ int htype, hlen;
+
+ /* We only understand about IP addresses... */
+ memcpy_fromfs(&r, req, sizeof(r));
+ if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
+
+ /*
+ * Find out about the hardware type.
+ * We have to be compatible with BSD UNIX, so we have to
+ * assume that a "not set" value (i.e. 0) means Ethernet.
+ */
+ si = (struct sockaddr_in *) &r.arp_pa;
+ switch(r.arp_ha.sa_family) {
+ case 0:
+ case ARPHRD_ETHER:
+ htype = ARPHRD_ETHER;
+ hlen = ETH_ALEN;
+ break;
+ default:
+ return(-EPFNOSUPPORT);
+ }
+
+ /* Is there an existing entry for this address? */
+ if (si->sin_addr.s_addr == 0) {
+ printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
+ return(-EINVAL);
+ }
+ apt = arp_lookup(si->sin_addr.s_addr);
+ if (apt == NULL) {
+ apt = arp_create(si->sin_addr.s_addr, r.arp_ha.sa_data, hlen, htype);
+ if (apt == NULL) return(-ENOMEM);
+ }
+
+ /* We now have a pointer to an ARP entry. Update it! */
+ memcpy((char *) &apt->ha, (char *) &r.arp_ha.sa_data, hlen);
+ apt->last_used = timer_seq;
+ apt->flags = r.arp_flags;
+
+ return(0);
+}
+
+
+/* Get an ARP cache entry. */
+static int
+arp_req_get(struct arpreq *req)
+{
+ struct arpreq r;
+ struct arp_table *apt;
+ struct sockaddr_in *si;
+
+ /* We only understand about IP addresses... */
+ memcpy_fromfs(&r, req, sizeof(r));
+ if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
+
+ /* Is there an existing entry for this address? */
+ si = (struct sockaddr_in *) &r.arp_pa;
+ apt = arp_lookup(si->sin_addr.s_addr);
+ if (apt == NULL) return(-ENXIO);
+
+ /* We found it; copy into structure. */
+ memcpy((char *) r.arp_ha.sa_data, (char *) &apt->ha, apt->hlen);
+ r.arp_ha.sa_family = apt->htype;
+
+ /* Copy the information back */
+ memcpy_tofs(req, &r, sizeof(r));
+ return(0);
+}
+
+
+/* Delete an ARP cache entry. */
+static int
+arp_req_del(struct arpreq *req)
+{
+ struct arpreq r;
+ struct sockaddr_in *si;
+
+ /* We only understand about IP addresses... */
+ memcpy_fromfs(&r, req, sizeof(r));
+ if (r.arp_pa.sa_family != AF_INET) return(-EPFNOSUPPORT);
+
+ si = (struct sockaddr_in *) &r.arp_pa;
+ arp_destroy(si->sin_addr.s_addr);
+
+ return(0);
+}
+
+
+/* Handle an ARP layer I/O control request. */
+int
+arp_ioctl(unsigned int cmd, void *arg)
+{
+ switch(cmd) {
+ case DDIOCSDBG:
+ return(dbg_ioctl(arg, DBG_ARP));
+ case SIOCDARP:
+ if (!suser()) return(-EPERM);
+ return(arp_req_del((struct arpreq *)arg));
+ case SIOCGARP:
+ return(arp_req_get((struct arpreq *)arg));
+ case SIOCSARP:
+ if (!suser()) return(-EPERM);
+ return(arp_req_set((struct arpreq *)arg));
+ default:
+ return(-EINVAL);
+ }
+ /*NOTREACHED*/
+ return(0);
+}
diff --git a/net/inet/arp.h b/net/inet/arp.h
new file mode 100644
index 0000000..c75c6cf
--- /dev/null
+++ b/net/inet/arp.h
@@ -0,0 +1,63 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ARP protocol module.
+ *
+ * Version: @(#)arp.h 1.0.6 05/21/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ARP_H
+#define _ARP_H
+
+#define ARP_TABLE_SIZE 16 /* size of ARP table */
+#define ARP_TIMEOUT 30000 /* five minutes */
+#define ARP_RES_TIME 250 /* 2.5 seconds */
+
+#define ARP_MAX_TRIES 3 /* max # of tries to send ARP */
+#define ARP_QUEUE_MAGIC 0x0432447A /* magic # for queues */
+
+
+/* This structure defines the ARP mapping cache. */
+struct arp_table {
+ struct arp_table *next;
+ volatile unsigned long last_used;
+ unsigned int flags;
+#if 1
+ unsigned long ip;
+#else
+ unsigned char pa[MAX_ADDR_LEN];
+ unsigned char plen;
+ unsigned char ptype;
+#endif
+ unsigned char ha[MAX_ADDR_LEN];
+ unsigned char hlen;
+ unsigned char htype;
+};
+
+
+/* This is also used in "sock.c" and "tcp.c" - YUCK! - FvK */
+extern struct sk_buff *arp_q;
+
+
+extern void arp_destroy(unsigned long paddr);
+extern int arp_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int arp_find(unsigned char *haddr, unsigned long paddr,
+ struct device *dev, unsigned long saddr);
+extern void arp_add(unsigned long addr, unsigned char *haddr,
+ struct device *dev);
+extern void arp_add_broad(unsigned long addr, struct device *dev);
+extern void arp_queue(struct sk_buff *skb);
+extern int arp_get_info(char *buffer);
+extern int arp_ioctl(unsigned int cmd, void *arg);
+
+#endif /* _ARP_H */
diff --git a/net/inet/auto_irq.c b/net/inet/auto_irq.c
new file mode 100644
index 0000000..b053354
--- /dev/null
+++ b/net/inet/auto_irq.c
@@ -0,0 +1,110 @@
+/* auto_irq.c: Auto-configure IRQ lines for linux. */
+/*
+ Written 1993 by Donald Becker.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ This code is a general-purpose IRQ line detector for devices with
+ jumpered IRQ lines. If you can make the device raise an IRQ (and
+ that IRQ line isn't already being used), these routines will tell
+ you what IRQ line it's using -- perfect for those oh-so-cool boot-time
+ device probes!
+
+ To use this, first call autoirq_setup(timeout). TIMEOUT is how many
+ 'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
+ and can usually be zero at boot. 'autoirq_setup()' returns the bit
+ vector of nominally-available IRQ lines (lines may be physically in-use,
+ but not yet registered to a device).
+ Next, set up your device to trigger an interrupt.
+ Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
+ most recently active. The TIMEOUT should usually be zero, but may
+ be set to the number of jiffies to wait for a slow device to raise an IRQ.
+
+ The idea of using the setup timeout to filter out bogus IRQs came from
+ the serial driver.
+*/
+
+
+#ifdef version
+static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
+#endif
+
+/*#include <linux/config.h>*/
+/*#include <linux/kernel.h>*/
+#include <linux/sched.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+/*#include <asm/system.h>*/
+
+int irqs_busy = 0x01; /* The set of fixed IRQs always enabled */
+int irqs_used = 0x01; /* The set of fixed IRQs sometimes enabled. */
+int irqs_reserved = 0x00; /* An advisory "reserved" table. */
+int irqs_shared = 0x00; /* IRQ lines "shared" among conforming cards.*/
+
+static volatile int irq_number; /* The latest irq number we actually found. */
+static volatile int irq_bitmap; /* The irqs we actually found. */
+static int irq_handled; /* The irq lines we have a handler on. */
+
+static void autoirq_probe(int irq)
+{
+ irq_number = irq;
+ set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
+ return;
+}
+struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
+
+int autoirq_setup(int waittime)
+{
+ int i, mask;
+ int timeout = jiffies+waittime;
+
+ irq_number = 0;
+ irq_bitmap = 0;
+ irq_handled = 0;
+ for (i = 0; i < 16; i++) {
+ if (!irqaction(i, &autoirq_sigaction))
+ set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
+ }
+ /* Update our USED lists. */
+ irqs_used |= ~irq_handled;
+
+ /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
+ while (timeout >= jiffies)
+ ;
+
+ for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
+ if (irq_bitmap & irq_handled & mask) {
+ irq_handled &= ~mask;
+ printk(" Spurious interrupt on IRQ %d\n", i);
+ free_irq(i);
+ }
+ }
+ return irq_handled;
+}
+
+int autoirq_report(int waittime)
+{
+ int i;
+ int timeout = jiffies+waittime;
+
+ /* Hang out at least <waittime> jiffies waiting for the IRQ. */
+ while (timeout >= jiffies)
+ if (irq_number)
+ break;
+
+ /* Retract the irq handlers that we installed. */
+ for (i = 0; i < 16; i++) {
+ if (test_bit(i, (void *)&irq_handled))
+ free_irq(i);
+ }
+ return irq_number;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/d_link.c b/net/inet/d_link.c
new file mode 100644
index 0000000..711f861
--- /dev/null
+++ b/net/inet/d_link.c
@@ -0,0 +1,786 @@
+/*
+ * d_link.c
+ *
+ * Portions (C) Copyright 1993 by Bjorn Ekwall
+ * The Author may be reached as bj0rn@blox.se
+ *
+ * Linux driver for the D-Link Ethernet pocket adapter.
+ * Based on sources from linux 0.99pl5
+ * and on a sample network driver core for linux.
+ *
+ * Sample driver written 1993 by Donald Becker <becker@super.org>
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ *
+ * compile-command:
+ * "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -c d_link.c"
+ */
+/*************************************************************
+ * If you have trouble reading/writing to the adapter,
+ * uncomment the following "#define":
+#define REALLY_SLOW_IO
+ */
+
+/* $Id: d_link.c,v 0.21 1993/06/02 19:41:00 waltje Exp $ */
+/* $Log: d_link.c,v $
+ * Revision 0.21 1993/06/02 19:41:00 waltje
+ * Applied multi-IRQ fix from Bjorn Ekwall and increaded version
+ * number to 0.21.
+ *
+ * Revision 0.20 1993/03/26 11:43:53 root
+ * Changed version number to indicate "alpha+" (almost beta :-)
+ *
+ * Revision 0.16 1993/03/26 11:26:46 root
+ * Last ALPHA-minus version.
+ * REALLY_SLOW_IO choice included (at line 20)
+ * SLOW_DOWN_IO added anyway in convenience macros/functions
+ * Test of D_LINK_FIFO included (not completely debugged)
+ *
+ * Revision 0.15 1993/03/24 14:00:49 root
+ * Modified the interrupt handling considerably.
+ * (The .asm source had me fooled in how it _really_ works :-)
+ *
+ * Revision 0.14 1993/03/21 01:57:25 root
+ * Modified the interrupthandler for more robustness (hopefully :-)
+ *
+ * Revision 0.13 1993/03/19 11:45:09 root
+ * Re-write of ALPHA release using Don Beckers skeleton (still works, kind of ...:-)
+ *
+ * Revision 0.12 1993/03/16 13:22:21 root
+ * working ALPHA-release
+ *
+ */
+
+static char *version =
+ "d_link.c: $Revision: 0.21 $, Bjorn Ekwall (bj0rn@blox.se)\n";
+
+/*
+ * Based on adapter information gathered from DE600.ASM by D-Link Inc.,
+ * as included on disk C in the v.2.11 of PC/TCP from FTP Software.
+ *
+ * For DE600.asm:
+ * Portions (C) Copyright 1990 D-Link, Inc.
+ * Copyright, 1988-1992, Russell Nelson, Crynwr Software
+ *
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <netinet/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <errno.h>
+
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef D_LINK_DEBUG
+#define D_LINK_DEBUG 0
+#endif
+static unsigned int d_link_debug = D_LINK_DEBUG;
+
+#ifdef D_LINK_DEBUG
+#define PRINTK(x) if (d_link_debug >= 2) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+
+/**************************************************
+ * *
+ * Definition of D-Link Ethernet Pocket adapter *
+ * *
+ **************************************************/
+/*
+ * D-Link Ethernet pocket adapter ports
+ */
+#define DATA_PORT (dev->base_addr + 0)
+#define STATUS_PORT (dev->base_addr + 1)
+#define COMMAND_PORT (dev->base_addr + 2)
+
+/*
+ * D-Link COMMAND_PORT commands
+ */
+#define SELECT_NIC 0x04 /* select Network Interface Card */
+#define SELECT_PRN 0x1c /* select Printer */
+#define NML_PRN 0xec /* normal Printer situation */
+#define IRQEN 0x10 /* enable IRQ line */
+
+/*
+ * D-Link STATUS_PORT
+ */
+#define TX_INTR 0x88
+#define RX_INTR 0x40
+
+#define OTHER_INTR 0x00 /* dummy, always false */
+
+/*
+ * D-Link DATA_PORT commands
+ * command in low 4 bits
+ * data in high 4 bits
+ * select current data nibble with HI_NIBBLE bit
+ */
+#define WRITE_DATA 0x00 /* write memory */
+#define READ_DATA 0x01 /* read memory */
+#define STATUS 0x02 /* read status register */
+#define COMMAND 0x03 /* write command register (see COMMAND below) */
+#define NULL_COMMAND 0x04 /* null command */
+#define RX_LEN 0x05 /* read received packet length */
+#define TX_ADDR 0x06 /* set adapter transmit memory address */
+#define RW_ADDR 0x07 /* set adapter read/write memory address */
+#define HI_NIBBLE 0x08 /* read/write the high nibble of data,
+ or-ed with rest of command */
+
+/*
+ * command register, (I don't know all about these bits...)
+ * accessed through DATA_PORT with low bits = COMMAND
+ */
+#define RX_ALL 0x01 /* bit 0,1 = 01 */
+#define RX_BP 0x02 /* bit 0,1 = 10 */
+#define RX_MBP 0x03 /* bit 0,1 = 11 */
+
+#define TX_ENABLE 0x04 /* bit 2 */
+#define RX_ENABLE 0x08 /* bit 3 */
+
+#define RESET 0x80 /* set bit 7 high */
+#define STOP_RESET 0x00 /* set bit 7 low */
+
+/*
+ * data to command register
+ * (high 4 bits in write to DATA_PORT)
+ */
+#define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */
+#define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */
+#define FLIP_IRQ 0x40 /* bit 6 */
+
+/* Convenience definition, transmitter page 2 */
+#define TX_PAGE2_SELECT 0x02
+
+/*
+ * D-Link adapter internal memory:
+ *
+ * 0-2K 1:st transmit page (send from pointer up to 2K)
+ * 2-4K 2:nd transmit page (send from pointer up to 4K)
+ *
+ * 4-6K 1:st receive page (data from 4K upwards)
+ * 6-8K 2:nd receive page (data from 6K upwards)
+ *
+ * 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
+ */
+#define MEM_2K 0x0800 /* 2048 */
+#define MEM_4K 0x1000 /* 4096 */
+#define NODE_ADDRESS 0x2000 /* 8192 */
+
+#define RUNT 64 /*56*/ /* Too small Ethernet packet */
+
+/**************************************************
+ * *
+ * End of definition *
+ * *
+ **************************************************/
+
+/* Common network statistics -- these will be in *.h someday. */
+struct netstats {
+ int tx_packets;
+ int rx_packets;
+ int tx_errors;
+ int rx_errors;
+ int missed_packets;
+ int soft_tx_errors;
+ int soft_rx_errors;
+ int soft_trx_err_bits;
+};
+static struct netstats *localstats;
+
+/*
+ * Index to functions, as function prototypes.
+ */
+
+/* Routines used internally. (See "convenience macros" also) */
+static int d_link_read_status(struct device *dev);
+static unsigned char d_link_read_byte(int type, struct device *dev);
+
+/* Put in the device structure. */
+static int d_link_open(struct device *dev);
+static int d_link_close(struct device *dev);
+static int d_link_start_xmit(struct sk_buff *skb, struct device *dev);
+
+/* Dispatch from interrupts. */
+static void d_link_interrupt(int reg_ptr);
+static void d_link_tx_intr(struct device *dev);
+static void d_link_rx_intr(struct device *dev);
+
+/* Initialization */
+int d_link_init(struct device *dev);
+static void adapter_init(struct device *dev, int startp);
+
+/* Passed to sigaction() to register the interrupt handler. */
+static struct sigaction d_link_sigaction = {
+ &d_link_interrupt,
+ 0,
+ 0,
+ NULL,
+ };
+
+/*
+ * D-Link driver variables:
+ */
+static volatile int rx_page = 0;
+static struct device *realdev;
+#ifdef D_LINK_FIFO
+static volatile int free_tx_page = 0x03; /* 2 pages = 0000 0011 */
+static volatile unsigned int busy_tx_page = 0x00; /* 2 pages = 0000 0000 */
+static volatile int transmit_next_from;
+#endif
+
+/*
+ * Convenience macros/functions for D-Link adapter
+ *
+ * If you are having trouble reading/writing correctly,
+ * try to uncomment the line "#define REALLY_SLOW_IO" (near line 20)
+ */
+
+#define select_prn() \
+ outb_p(SELECT_PRN, COMMAND_PORT)
+
+#define select_nic() \
+ outb_p(SELECT_NIC, COMMAND_PORT)
+
+#define d_link_put_byte(data) \
+ outb_p(((data) << 4) | WRITE_DATA , DATA_PORT); \
+ outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT)
+
+/*
+ * The first two outb_p()'s below could perhaps be deleted if there
+ * would be more delay in the last two. Not certain about it yet...
+ */
+#define d_link_put_command(cmd) \
+ outb_p(( rx_page << 4) | COMMAND , DATA_PORT); \
+ outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT); \
+ outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT); \
+ outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT)
+
+#define d_link_setup_address(addr,type) \
+ outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT); \
+ outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT); \
+ outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT); \
+ outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT)
+
+static int
+d_link_read_status(struct device *dev)
+{
+ int status;
+
+ select_nic();
+ outb_p(STATUS, DATA_PORT);
+ SLOW_DOWN_IO; /* See comment line 20 */
+ status = inb_p(STATUS_PORT);
+ outb_p(NULL_COMMAND, DATA_PORT);
+ outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
+
+ return status;
+}
+
+static unsigned char
+d_link_read_byte(int type, struct device *dev) { /* dev used by macros */
+ unsigned char lo;
+
+ outb_p((type), DATA_PORT);
+ SLOW_DOWN_IO; /* See comment line 20 */
+ lo = inb_p(STATUS_PORT);
+ outb_p((type) | HI_NIBBLE, DATA_PORT);
+ SLOW_DOWN_IO; /* See comment line 20 */
+ return ((lo & 0xf0) >> 4) | (inb_p(STATUS_PORT) & 0xf0);
+}
+
+#ifdef D_LINK_FIFO
+ /* Handle a "fifo" in an int (= busy_tx_page) */
+# define AT_FIFO_OUTPUT (busy_tx_page & 0x0f)
+# define ANY_QUEUED_IN_FIFO (busy_tx_page & 0xf0)
+
+# define PULL_FROM_FIFO { busy_tx_page >>= 4;}
+# define PUSH_INTO_FIFO(page) { \
+ if (busy_tx_page) /* there already is a transmit in progress */ \
+ busy_tx_page |= (page << 4); \
+ else \
+ busy_tx_page = page; \
+ }
+#endif
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'config <dev->name>' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ */
+static int
+d_link_open(struct device *dev)
+{
+ adapter_init(dev, 1);
+ dev->tbusy = 0; /* Transmit busy... */
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+/*
+ * The inverse routine to d_link_open().
+ */
+static int
+d_link_close(struct device *dev)
+{
+ dev->start = 0;
+
+ adapter_init(dev, 0);
+
+ irqaction(dev->irq, NULL);
+
+ return 0;
+}
+
+/*
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+d_link_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ static int tx_page = 0;
+ int transmit_from;
+ int len;
+ int tickssofar;
+ unsigned char *buffer = (unsigned char *)(skb + 1);
+
+ /*
+ * If some higher layer thinks we've missed a
+ * tx-done interrupt * we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* For ethernet, fill in the header (hardware addresses) with an arp. */
+ if (!skb->arp && dev->rebuild_header(skb + 1, dev)) {
+ skb->dev = dev;
+ arp_queue (skb);
+ return 0;
+ }
+
+#ifdef D_LINK_FIFO
+ if (free_tx_page == 0) { /* Do timeouts, to avoid hangs. */
+#else
+ if (dev->tbusy) { /* Do timeouts, to avoid hangs. */
+#endif
+ tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 5) {
+ return 1;
+ }
+
+ /* else */
+ printk("%s: transmit timed out (%d), %s?\n",
+ dev->name,
+ tickssofar,
+ "network cable problem"
+ );
+ /* Try to restart the adapter. */
+ /* Maybe in next release... :-)
+ adapter_init(dev, 1);
+ */
+ }
+
+ /* Start real output */
+ PRINTK(("d_link_start_xmit:len=%d\n", skb->len));
+ cli();
+ select_nic();
+
+#ifdef D_LINK_FIFO
+ /* magic code selects the least significant bit in free_tx_page */
+ tx_page = free_tx_page & (-free_tx_page);
+
+ free_tx_page &= ~tx_page;
+ dev->tbusy = !free_tx_page; /* any more free pages? */
+
+ PUSH_INTO_FIFO(tx_page);
+#else
+ tx_page ^= TX_PAGE2_SELECT; /* Flip page, only 2 pages */
+#endif
+
+ if ((len = skb->len) < RUNT) /*&& Hmm...? */
+ len = RUNT;
+
+ if (tx_page & TX_PAGE2_SELECT)
+ transmit_from = MEM_4K - len;
+ else
+ transmit_from = MEM_2K - len;
+
+ d_link_setup_address(transmit_from, RW_ADDR);
+
+ for ( ; len > 0; --len, ++buffer) {
+ d_link_put_byte(*buffer); /* macro! watch out for side effects! */
+ }
+
+#ifdef D_LINK_FIFO
+ if (ANY_QUEUED_IN_FIFO == 0) { /* there is no transmit in progress */
+ d_link_setup_address(transmit_from, TX_ADDR);
+ d_link_put_command(TX_ENABLE);
+ dev->trans_start = jiffies;
+ } else {
+ transmit_next_from = transmit_from;
+ }
+#else
+ d_link_setup_address(transmit_from, TX_ADDR);
+ d_link_put_command(TX_ENABLE);
+ dev->trans_start = jiffies;
+#endif
+
+ sti(); /* interrupts back on */
+
+ if (skb->free) {
+ kfree_skb (skb, FREE_WRITE);
+ }
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void
+d_link_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = realdev;
+ int interrupts;
+
+ /* Get corresponding device */
+/*
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->irq == irq)
+ break;
+ }
+*/
+ if (dev == NULL) {
+ printk ("d_link_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->start == 0) {
+ return; /*&& bogus interrupt at boot!?!?!? */
+ }
+
+ cli();
+ dev->interrupt = 1;
+ sti(); /* Allow other interrupts. */
+
+ localstats = (struct netstats*) dev->private;
+
+ interrupts = d_link_read_status(dev);
+
+ PRINTK(("d_link_interrupt (%2.2X)\n", interrupts));
+
+ /*
+ * Interrupts have been observed to be:
+ *
+ * Value My interpretation
+ * ----- -----------------
+ * 0x47 Normal receive interrupt
+ * 0x4F Receive AND transmit interrupt simultaneously
+ * 0x87 Normal transmit interrupt (? I treat it as such...)
+ * 0x8F Normal transmit interrupt
+ */
+
+ /*
+ * Take care of TX interrupts first, in case there is an extra
+ * page to transmit (keep the adapter busy while we work).
+ */
+ if (interrupts & TX_INTR) { /* 1xxx 1xxx */
+ d_link_tx_intr(dev);
+ }
+
+ if (interrupts & RX_INTR) { /* x1xx xxxx */
+ d_link_rx_intr(dev);
+ }
+
+ /* I'm not sure if there are any other interrupts from D-Link... */
+ if (d_link_debug && (interrupts & OTHER_INTR)) {
+ printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ }
+
+ /* Check comment near line 20! */
+ if ( (interrupts != 0x47) &&
+ (interrupts != 0x87) &&
+ (interrupts != 0x4F) &&
+ (interrupts != 0x8F)
+ )
+ printk("Strange d_link_interrupt: <%2.2X>\n", interrupts);
+
+ cli();
+ dev->interrupt = 0;
+
+ /* Enable adapter interrupts */
+ select_prn();
+ return;
+}
+
+/*
+ * Do internal handshake: Transmitter done (of this page).
+ * Also handle the case of a pending transmit page.
+ */
+static void
+d_link_tx_intr(struct device *dev)
+{
+ localstats->tx_packets++;
+
+ cli();
+ dev->tbusy = 0;
+
+#ifdef D_LINK_FIFO
+ free_tx_page |= AT_FIFO_OUTPUT;
+ PULL_FROM_FIFO;
+
+ if (AT_FIFO_OUTPUT != 0) { /* more in queue! */
+ d_link_setup_address(transmit_next_from, TX_ADDR);
+ d_link_put_command(TX_ENABLE);
+ dev->trans_start = jiffies;
+ }
+#endif
+
+ mark_bh(INET_BH);
+ sti();
+}
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ */
+static void
+d_link_rx_intr(struct device *dev)
+{
+ struct sk_buff *skb;
+ int i;
+ int size;
+ int sksize;
+ unsigned char *buffer;
+
+ /* Get size of received packet */
+ /* Ignore trailing 4 CRC-bytes */
+ size = d_link_read_byte(RX_LEN, dev); /* low byte */
+ size = size + (d_link_read_byte(RX_LEN, dev) << 8) - 4;
+
+ /* Tell adapter where to store next incoming packet, enable receiver */
+ rx_page ^= RX_PAGE2_SELECT; /* Flip bit, only 2 pages */
+ d_link_put_command(RX_ENABLE);
+
+ if (size == 0) /* Read all the frames? */
+ return; /* Done for now */
+
+ if ((size < 32 || size > 1535) && d_link_debug)
+ printk("%s: Bogus packet size %d.\n", dev->name, size);
+
+ sksize = sizeof(struct sk_buff) + size;
+ if ((skb = kmalloc(sksize, GFP_ATOMIC)) == NULL) {
+ if (d_link_debug) {
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize);
+ }
+ return;
+ }
+ /* else */
+
+ skb->lock = 0;
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ /* 'skb + 1' points to the start of sk_buff data area. */
+ buffer = (unsigned char *)(skb + 1);
+
+ /* Get packet */
+
+ /* Tell adapter from where we want to read this packet */
+ if (rx_page & RX_PAGE2_SELECT) {
+ d_link_setup_address(MEM_4K, RW_ADDR);
+ } else {
+ d_link_setup_address(MEM_4K + MEM_2K, RW_ADDR);
+ }
+
+ /* copy the packet into the buffer */
+ for (i = size; i > 0; --i) {
+ *buffer++ = d_link_read_byte(READ_DATA, dev);
+ }
+
+ localstats->rx_packets++; /* count all receives */
+
+ if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
+ printk("%s: receive buffers full.\n", dev->name);
+ return;
+ }
+
+ /*
+ * If any worth-while packets have been received, dev_rint()
+ * has done a mark_bh(INET_BH) for us and will work on them
+ * when we get to the bottom-half routine.
+ */
+ return;
+}
+
+int
+d_link_init(struct device *dev)
+{
+ int i;
+
+ printk("%s: D-Link pocket adapter", dev->name);
+ /* Alpha testers must have the version number to report bugs. */
+ if (d_link_debug > 1)
+ printk(version);
+
+ /* probe for adapter */
+ rx_page = 0;
+ (void)d_link_read_status(dev);
+ d_link_put_command(RESET);
+ d_link_put_command(STOP_RESET);
+ if (d_link_read_status(dev) & 0xf0) {
+ printk(": probe failed at %#3x.\n", dev->base_addr);
+ return ENODEV;
+ }
+
+ /*
+ * Maybe we found one,
+ * have to check if it is a D-Link adapter...
+ */
+
+ /* Get the adapter ethernet address from the ROM */
+ d_link_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = d_link_read_byte(READ_DATA, dev);
+ dev->broadcast[i] = 0xff;
+ }
+
+ /* Check magic code */
+ if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
+ /* OK, install real address */
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x80;
+ dev->dev_addr[2] = 0xc8;
+ dev->dev_addr[3] &= 0x0f;
+ dev->dev_addr[3] |= 0x70;
+ } else {
+ printk(", not found in printer port!\n");
+ return ENODEV;
+ }
+
+ /* Initialize the device structure. */
+ dev->private = kmalloc(sizeof(struct netstats), GFP_KERNEL);
+ memset(dev->private, 0, sizeof(struct netstats));
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ dev->hard_header = eth_header;
+ dev->add_arp = eth_add_arp;
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+
+ dev->open = &d_link_open;
+ dev->stop = &d_link_close;
+ dev->hard_start_xmit = &d_link_start_xmit;
+
+ /* These are ethernet specific. */
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ if (irqaction (dev->irq, &d_link_sigaction)) {
+ printk (": unable to get IRQ %d\n", dev->irq);
+ return 0;
+ }
+
+ printk(", Ethernet Address: %2.2X", dev->dev_addr[0]);
+ for (i = 1; i < ETH_ALEN; i++)
+ printk(":%2.2X",dev->dev_addr[i]);
+ printk("\n");
+
+ return 0;
+}
+
+static void
+adapter_init(struct device *dev, int startp)
+{
+ int i;
+
+ cli(); /* no interrupts yet, please */
+
+ select_nic();
+ rx_page = 0;
+ d_link_put_command(RESET);
+ d_link_put_command(STOP_RESET);
+
+ if (startp) {
+ irqaction (dev->irq, &d_link_sigaction);
+ realdev = dev;
+#ifdef D_LINK_FIFO
+ free_tx_page = 0x03; /* 2 pages = 0000 0011 */
+ busy_tx_page = 0x00; /* 2 pages = 0000 0000 */
+#endif
+ /* set the ether address. */
+ d_link_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++) {
+ d_link_put_byte(dev->dev_addr[i]);
+ }
+
+ /* where to start saving incoming packets */
+ rx_page = 0 | RX_BP | RX_BASE_PAGE;
+ d_link_setup_address(MEM_4K, RW_ADDR);
+ /* Enable receiver */
+ d_link_put_command(RX_ENABLE);
+ }
+ else
+ d_link_put_command(0);
+
+ select_prn();
+
+ sti();
+}
diff --git a/net/inet/dev.c b/net/inet/dev.c
new file mode 100644
index 0000000..7543108
--- /dev/null
+++ b/net/inet/dev.c
@@ -0,0 +1,902 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Interface (streams) handling functions.
+ *
+ * Version: @(#)dev.c 1.0.19 05/31/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/config.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 <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+static struct packet_type arp_packet_type = {
+ NET16(ETH_P_ARP),
+ 0, /* copy */
+ arp_rcv,
+ NULL,
+ NULL /* next */
+};
+
+
+static struct packet_type ip_packet_type = {
+ NET16(ETH_P_IP),
+ 0, /* copy */
+ ip_rcv,
+ NULL,
+ &arp_packet_type
+};
+
+
+struct packet_type *ptype_base = &ip_packet_type;
+static struct sk_buff *volatile backlog = NULL;
+static unsigned long ip_bcast = 0;
+
+
+/* Return the lesser of the two values. */
+static unsigned long
+min(unsigned long a, unsigned long b)
+{
+ if (a < b) return(a);
+ return(b);
+}
+
+
+/* Determine a default network mask, based on the IP address. */
+static unsigned long
+get_mask(unsigned long addr)
+{
+ unsigned long dst;
+
+ if (addr == 0) return(0); /* special case */
+
+ dst = ntohl(addr);
+ if (IN_CLASSA(dst)) return(htonl(IN_CLASSA_NET));
+ if (IN_CLASSB(dst)) return(htonl(IN_CLASSB_NET));
+ if (IN_CLASSC(dst)) return(htonl(IN_CLASSC_NET));
+
+ /* Something else, probably a subnet. */
+ return(0);
+}
+
+
+int
+ip_addr_match(unsigned long me, unsigned long him)
+{
+ int i;
+
+ DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me)));
+ DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him)));
+
+ if (me == him) return(1);
+ for (i = 0; i < 4; i++, me >>= 8, him >>= 8) {
+ if ((me & 0xFF) != (him & 0xFF)) {
+ /*
+ * The only way this could be a match is for
+ * the rest of addr1 to be 0 or 255.
+ */
+ if (me != 0 && me != 255) return(0);
+ return(1);
+ }
+ }
+ return(1);
+}
+
+
+/* Check the address for our address, broadcasts, etc. */
+int
+chk_addr(unsigned long addr)
+{
+ struct device *dev;
+ unsigned long dst;
+
+ DPRINTF((DBG_DEV, "chk_addr(%s) --> ", in_ntoa(addr)));
+ dst = ntohl(addr);
+
+ /* Accept both `all ones' and `all zeros' as BROADCAST. */
+ if (dst == INADDR_ANY || dst == INADDR_BROADCAST) {
+ DPRINTF((DBG_DEV, "BROADCAST\n"));
+ return(IS_BROADCAST);
+ }
+
+ /* Accept all of the `loopback' class A net. */
+ if ((dst & IN_CLASSA_NET) == 0x7F000000) {
+ DPRINTF((DBG_DEV, "LOOPBACK\n"));
+
+ /*
+ * We force `loopback' to be equal to MY_ADDR.
+ */
+ return(IS_MYADDR);
+ /* return(IS_LOOPBACK); */
+ }
+
+ /* OK, now check the interface addresses. */
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->pa_addr == 0) continue;
+
+ /* Is it the exact IP address? */
+ if (addr == dev->pa_addr) {
+ DPRINTF((DBG_DEV, "MYADDR\n"));
+ return(IS_MYADDR);
+ }
+
+ /* Nope. Check for a subnetwork broadcast. */
+ if ((addr & dev->pa_mask) == (dev->pa_addr & dev->pa_mask)) {
+ if ((addr & ~dev->pa_mask) == 0) {
+ DPRINTF((DBG_DEV, "SUBBROADCAST-0\n"));
+ return(IS_BROADCAST);
+ }
+ if (((addr & ~dev->pa_mask) | dev->pa_mask)
+ == INADDR_BROADCAST) {
+ DPRINTF((DBG_DEV, "SUBBROADCAST-1\n"));
+ return(IS_BROADCAST);
+ }
+ }
+ }
+
+ DPRINTF((DBG_DEV, "NONE\n"));
+ return(0); /* no match at all */
+}
+
+
+/*
+ * Retrieve our own address.
+ * Because the loopback address (127.0.0.1) is already recognized
+ * automatically, we can use the loopback interface's address as
+ * our "primary" interface. This is the addressed used by IP et
+ * al when it doesn't know which address to use (i.e. it does not
+ * yet know from or to which interface to go...).
+ */
+unsigned long
+my_addr(void)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->flags & IFF_LOOPBACK) return(dev->pa_addr);
+ }
+ return(0);
+}
+
+
+/* Add a protocol ID to the list. This will change soon. */
+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;
+}
+
+
+/* Remove a protocol ID from the list. This will change soon. */
+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;
+ }
+ }
+}
+
+
+/* Find an interface in the list. This will change soon. */
+struct device *
+dev_get(char *name)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (strcmp(dev->name, name) == 0) return(dev);
+ }
+ return(NULL);
+}
+
+
+/* Find an interface that can handle addresses for a certain address. */
+struct device *
+dev_check(int which, unsigned long addr)
+{
+ struct device *dev;
+
+ for(dev = dev_base; dev != NULL; dev = dev->next) {
+ if ((dev->flags & IFF_UP) == 0) continue;
+ switch(which) {
+ case 0: /* local address */
+ if (ip_addr_match(addr & dev->pa_mask, dev->pa_addr))
+ return(dev);
+ break;
+ case 1: /* p-p destination address */
+ if ((dev->flags & IFF_POINTOPOINT) &&
+ ip_addr_match(addr & dev->pa_mask, dev->pa_dstaddr))
+ return(dev);
+ break;
+ case 2: /* broadcast address */
+ if ((dev->flags & IFF_BROADCAST) &&
+ ip_addr_match(addr, dev->pa_brdaddr))
+ return(dev);
+ break;
+ }
+ }
+ return(NULL);
+}
+
+
+/* Prepare an interface for use. */
+int
+dev_open(struct device *dev)
+{
+ int ret = 0;
+
+ if (dev->open) ret = dev->open(dev);
+ if (ret == 0) dev->flags |= (IFF_UP | IFF_RUNNING);
+
+ return(ret);
+}
+
+
+/* Completely shutdown an interface. */
+int
+dev_close(struct device *dev)
+{
+ if (dev->flags != 0) {
+ dev->flags = 0;
+ if (dev->stop) dev->stop(dev);
+ rt_flush(dev);
+ dev->pa_addr = 0;
+ dev->pa_dstaddr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ }
+
+ return(0);
+}
+
+
+/* Send (or queue for sending) a packet. */
+void
+dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+{
+ struct sk_buff *skb2;
+ int where = 0; /* used to say if the packet should go */
+ /* at the front or the back of the */
+ /* queue. */
+
+ DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n",
+ skb, dev, pri));
+
+ if (dev == NULL) {
+ printk("dev.c: dev_queue_xmit: dev = NULL\n");
+ return;
+ }
+
+ skb->dev = dev;
+ if (skb->next != NULL) {
+ /* Make sure we haven't missed an interrupt. */
+ dev->hard_start_xmit(NULL, dev);
+ return;
+ }
+
+ if (pri < 0) {
+ pri = -pri-1;
+ where = 1;
+ }
+
+ if (pri >= DEV_NUMBUFFS) {
+ printk("bad priority in dev_queue_xmit.\n");
+ pri = 1;
+ }
+
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ return;
+ }
+
+ /* Put skb into a bidirectional circular linked list. */
+ DPRINTF((DBG_DEV, "dev_queue_xmit 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 {
+ if (where) {
+ skb->next = (struct sk_buff *) dev->buffs[pri];
+ skb->prev = (struct sk_buff *) dev->buffs[pri]->prev;
+ skb->prev->next = skb;
+ skb->next->prev = skb;
+ dev->buffs[pri] = skb;
+ } else {
+ skb2 = (struct sk_buff *) dev->buffs[pri];
+ skb->next = skb2;
+ skb->prev = skb2->prev;
+ skb->next->prev = skb;
+ skb->prev->next = skb;
+ }
+ }
+ skb->magic = DEV_QUEUE_MAGIC;
+ sti();
+}
+
+
+/*
+ * Fetch a packet from a device driver.
+ * This function is the base level entry point for all drivers that
+ * want to send a packet to the upper (protocol) levels. It takes
+ * care of de-multiplexing the packet to the various modules based
+ * on their protocol ID.
+ *
+ * Return values: 1 <- exit I can't do any more
+ * 0 <- feed me more (i.e. "done", "OK").
+ */
+int
+dev_rint(unsigned char *buff, long len, int flags, struct device *dev)
+{
+ static int dropping = 0;
+ struct sk_buff *skb = NULL;
+ unsigned char *to;
+ int amount, left;
+ int len2;
+
+ if (dev == NULL || buff == NULL || len <= 0) return(1);
+ if (dropping && backlog != NULL) {
+ return(1);
+ }
+ if (dropping) printk("INET: dev_rint: no longer dropping packets.\n");
+ dropping = 0;
+
+ if (flags & IN_SKBUFF) {
+ skb = (struct sk_buff *) buff;
+ } else {
+ skb = kmalloc(sizeof(*skb) + len, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("dev_rint: packet dropped (no memory) !\n");
+ dropping = 1;
+ return(1);
+ }
+ skb->lock = 0;
+ skb->mem_len = sizeof(*skb) + len;
+ skb->mem_addr = (struct sk_buff *) skb;
+
+ /* First we copy the packet into a buffer, and save it for later. */
+ to = (unsigned char *) (skb + 1);
+ left = len;
+ len2 = len;
+ while (len2 > 0) {
+ amount = min(len2, (unsigned long) dev->rmem_end -
+ (unsigned long) buff);
+ memcpy(to, buff, amount);
+ len2 -= amount;
+ left -= amount;
+ buff += amount;
+ to += amount;
+ if ((unsigned long) buff == dev->rmem_end)
+ buff = (unsigned char *) dev->rmem_start;
+ }
+ }
+ skb->len = len;
+ skb->dev = dev;
+ skb->sk = NULL;
+
+ /* Now add it to the backlog. */
+ cli();
+ if (backlog == NULL) {
+ skb->prev = skb;
+ skb->next = skb;
+ backlog = skb;
+ } else {
+ skb->prev = (struct sk_buff *) backlog->prev;
+ skb->next = (struct sk_buff *) backlog;
+ skb->next->prev = skb;
+ skb->prev->next = skb;
+ }
+ sti();
+
+ /* If any packet arrived, mark it for processing. */
+ if (backlog != NULL) mark_bh(INET_BH);
+
+ /* OK, all done. */
+ return(0);
+}
+
+
+/* This routine causes all interfaces to try to send some data. */
+void
+dev_transmit(void)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (!dev->tbusy) {
+ dev_tint(dev);
+ }
+ }
+}
+
+
+/*
+ * This function gets called periodically, to see if we can
+ * process any data that came in from some interface.
+ */
+void
+inet_bh(void *tmp)
+{
+ struct sk_buff *skb;
+ struct packet_type *ptype;
+ unsigned short type;
+ unsigned char flag = 0;
+ static volatile int in_bh = 0;
+
+ /* Check && mark our BUSY state. */
+ cli();
+ if (in_bh != 0) {
+ sti();
+ return;
+ }
+ in_bh = 1;
+ sti();
+
+ /* Can we send anything now? */
+ dev_transmit();
+
+ /* Any data left to process? */
+ cli();
+ while (backlog != NULL) {
+ skb = (struct sk_buff *) backlog;
+ if (skb->next == skb) {
+ backlog = NULL;
+ } else {
+ backlog = skb->next;
+ skb->next->prev = skb->prev;
+ skb->prev->next = skb->next;
+ }
+ sti();
+
+ /*
+ * Bump the pointer to the next structure.
+ * This assumes that the basic 'skb' pointer points to
+ * the MAC header, if any (as indicated by its "length"
+ * field). Take care now!
+ */
+ skb->h.raw = (unsigned char *) (skb + 1) + skb->dev->hard_header_len;
+ skb->len -= skb->dev->hard_header_len;
+
+ /*
+ * Fetch the packet protocol ID. This is also quite ugly, as
+ * it depends on the protocol driver (the interface itself) to
+ * know what the type is, or where to get it from. The Ethernet
+ * interfaces fetch the ID from the two bytes in the Ethernet MAC
+ * header (the h_proto field in struct ethhdr), but drivers like
+ * SLIP and PLIP have no alternative but to force the type to be
+ * IP or something like that. Sigh- FvK
+ */
+ type = skb->dev->type_trans(skb, skb->dev);
+
+ /*
+ * We got a packet ID. Now loop over the "known protocols"
+ * table (which is actually a linked list, but this will
+ * change soon if I get my way- FvK), and forward the packet
+ * to anyone who wants it.
+ */
+ for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) {
+ if (ptype->type == type) {
+ struct sk_buff *skb2;
+
+ if (ptype->copy) { /* copy if we need to */
+ skb2 = kmalloc(skb->mem_len, GFP_ATOMIC);
+ if (skb2 == NULL) continue;
+ memcpy(skb2, (const void *) skb, skb->mem_len);
+ skb2->mem_addr = skb2;
+ skb2->lock = 0;
+ skb2->h.raw = (void *)(
+ (unsigned long) skb2 +
+ (unsigned long) skb->h.raw -
+ (unsigned long) skb
+ );
+ } else {
+ skb2 = skb;
+ }
+
+ /* This used to be in the 'else' part, but then
+ * we don't have this flag set when we get a
+ * protocol that *does* require copying... -FvK
+ */
+ flag = 1;
+
+ /* Kick the protocol handler. */
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ }
+
+ /*
+ * That's odd. We got an unknown packet. Who's using
+ * stuff like Novell or Amoeba on this network??
+ */
+ if (!flag) {
+ DPRINTF((DBG_DEV,
+ "INET: unknown packet type 0x%04X (ignored)\n", type));
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ }
+
+ /* Again, see if we can transmit anything now. */
+ dev_transmit();
+ cli();
+ }
+ in_bh = 0;
+ sti();
+}
+
+
+/*
+ * This routine is called when an device driver (i.e. an
+ * interface) is * ready to transmit a packet.
+ */
+void
+dev_tint(struct device *dev)
+{
+ int i;
+ struct sk_buff *skb;
+
+ for (i = 0; i < DEV_NUMBUFFS; i++) {
+ while (dev->buffs[i] != NULL) {
+ cli();
+ skb = (struct sk_buff *) dev->buffs[i];
+ if (skb->magic != DEV_QUEUE_MAGIC) {
+ printk("INET: dev: skb with bad magic-%X:", skb->magic);
+ printk("squashing queue\n");
+ cli();
+ dev->buffs[i] = NULL;
+ sti();
+ continue;
+ }
+
+ skb->magic = 0;
+
+ if (skb->next == skb) {
+ dev->buffs[i] = NULL;
+ } else {
+ /* Extra consistency check. */
+ if (skb->next == NULL
+#ifdef CONFIG_MAX_16M
+ || (unsigned long)(skb->next) > 16*1024*1024
+#endif
+ ) {
+ printk("INET: dev: *** bug bad skb->next,");
+ printk(", squashing queue\n");
+ cli();
+ 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();
+
+ /* This will send it through the process again. */
+ dev->queue_xmit(skb, dev, -i - 1);
+ if (dev->tbusy) return;
+ }
+ }
+}
+
+
+/* Perform a SIOCGIFCONF call. */
+static int
+dev_ifconf(char *arg)
+{
+ struct ifconf ifc;
+ struct ifreq ifr;
+ struct device *dev;
+ char *pos;
+ int len;
+
+ /* Fetch the caller's info block. */
+ verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
+ memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
+ len = ifc.ifc_len;
+ pos = ifc.ifc_buf;
+
+ /* Loop over the interfaces, and write an info block for each. */
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+
+ /* Write this block to the caller's space. */
+ memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+ pos += sizeof(struct ifreq);
+ len -= sizeof(struct ifreq);
+ if (len < sizeof(struct ifreq)) break;
+ }
+
+ /* All done. Write the updated control block back to the caller. */
+ ifc.ifc_len = (pos - ifc.ifc_buf);
+ ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+ memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
+ return(pos - arg);
+}
+
+
+/* Called from the PROCfs module. */
+int
+dev_get_info(char *buffer)
+{
+ return(dev_ifconf(buffer));
+}
+
+
+/* Perform the SIOCxIFxxx calls. */
+static int
+dev_ifsioc(void *arg, unsigned int getset)
+{
+ struct ifreq ifr;
+ struct device *dev;
+ int ret;
+
+ /* Fetch the caller's info block. */
+ verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+ memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+
+ /* See which interface the caller is talking about. */
+ if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-EINVAL);
+
+ switch(getset) {
+ case SIOCGIFFLAGS:
+ ifr.ifr_flags = dev->flags;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFFLAGS:
+ ret = dev->flags;
+ dev->flags = ifr.ifr_flags & (
+ IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
+ IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+ IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+ if ((ret & IFF_UP) && ((dev->flags & IFF_UP) == 0)) {
+ ret = dev_close(dev);
+ } else {
+ if (((ret & IFF_UP) == 0) && (dev->flags & IFF_UP)) {
+ ret = dev_open(dev);
+ } else ret = 0;
+ }
+ break;
+ case SIOCGIFADDR:
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFADDR:
+ dev->pa_addr = (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr;
+ dev->family = ifr.ifr_addr.sa_family;
+ dev->pa_mask = (dev->pa_addr & get_mask(dev->pa_addr));
+ dev->pa_brdaddr = dev->pa_mask | ~get_mask(dev->pa_addr);
+ ret = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFBRDADDR:
+ dev->pa_brdaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+ case SIOCGIFDSTADDR:
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFDSTADDR:
+ dev->pa_dstaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+ case SIOCGIFNETMASK:
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFNETMASK:
+ dev->pa_mask = (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr;
+ ret = 0;
+ break;
+ case SIOCGIFMETRIC:
+ ifr.ifr_metric = dev->metric;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFMETRIC:
+ dev->metric = ifr.ifr_metric;
+ ret = 0;
+ break;
+ case SIOCGIFMTU:
+ ifr.ifr_mtu = dev->mtu;
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ ret = 0;
+ break;
+ case SIOCSIFMTU:
+ dev->mtu = ifr.ifr_mtu;
+ ret = 0;
+ break;
+ case SIOCGIFMEM:
+ printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg);
+ ret = -EINVAL;
+ break;
+ case SIOCSIFMEM:
+ printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg);
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return(ret);
+}
+
+
+/* This function handles all "interface"-type I/O control requests. */
+int
+dev_ioctl(unsigned int cmd, void *arg)
+{
+ struct iflink iflink;
+ struct ddi_device *dev;
+ int ret;
+
+ switch(cmd) {
+ case IP_SET_DEV:
+ printk("INET: Warning: old-style ioctl(IP_SET_DEV) called!\n");
+ return(-EINVAL);
+ case SIOCGIFCONF:
+ (void) dev_ifconf((char *) arg);
+ ret = 0;
+ break;
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ if (!suser()) return(-EPERM);
+ ret = dev_ifsioc(arg, cmd);
+ break;
+ case SIOCSIFLINK:
+ if (!suser()) return(-EPERM);
+ memcpy_fromfs(&iflink, arg, sizeof(iflink));
+ dev = ddi_map(iflink.id);
+ if (dev == NULL) return(-EINVAL);
+
+ /* Now allocate an interface and connect it. */
+ printk("AF_INET: DDI \"%s\" linked to stream \"%s\"\n",
+ dev->name, iflink.stream);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return(ret);
+}
+
+
+/* Initialize the DEV module. */
+void
+dev_init(void)
+{
+ struct device *dev, *dev2;
+
+ /* Add the devices.
+ * If the call to dev->init fails, the dev is removed
+ * from the chain disconnecting the device until the
+ * next reboot.
+ */
+ dev2 = NULL;
+ for (dev = dev_base; dev != NULL; dev=dev->next) {
+ if (dev->init && dev->init(dev)) {
+ if (dev2 == NULL) dev_base = dev->next;
+ else dev2->next = dev->next;
+ } else {
+ dev2 = dev;
+ }
+ }
+
+ /* Set up some IP addresses. */
+ ip_bcast = in_aton("255.255.255.255");
+}
diff --git a/net/inet/dev.h b/net/inet/dev.h
new file mode 100644
index 0000000..454084d
--- /dev/null
+++ b/net/inet/dev.h
@@ -0,0 +1,170 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Interfaces handler.
+ *
+ * Version: @(#)dev.h 1.0.9 05/31/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _DEV_H
+#define _DEV_H
+
+#include <linux/if.h>
+
+
+/* for future expansion when we will have different priorities. */
+#define DEV_NUMBUFFS 3
+#define MAX_ADDR_LEN 6
+#define MAX_HEADER 14
+
+#define IS_MYADDR 1 /* address is (one of) our own */
+#define IS_LOOPBACK 2 /* address is for LOOPBACK */
+#define IS_BROADCAST 3 /* address is a valid broadcast */
+
+
+/*
+ * The DEVICE structure.
+ * Actually, this whole structure is a big mistake. It mixes I/O
+ * data with strictly "high-level" data, and it has to know about
+ * almost every data structure used in the INET module. We will
+ * gradually phase out this structure, and replace it with the
+ * more general (but stolen :-) BSD "ifnet" structure. -FvK
+ */
+struct device {
+
+ /*
+ * This is the first field of the "visible" part of this structure
+ * (i.e. as seen by users in the "Space.c" file). It is the name
+ * the interface.
+ */
+ char *name;
+
+ /* I/O specific fields. These will be moved to DDI soon. */
+ unsigned long rmem_end; /* shmem "recv" end */
+ unsigned long rmem_start; /* shmem "recv" start */
+ unsigned long mem_end; /* sahared mem end */
+ unsigned long mem_start; /* shared mem start */
+ unsigned short base_addr; /* device I/O address */
+ unsigned char irq; /* device IRQ number */
+
+ /* Low-level status flags. */
+ volatile unsigned char start, /* start an operation */
+ tbusy, /* transmitter busy */
+ interrupt; /* interrupt arrived */
+
+ /*
+ * Another mistake.
+ * This points to the next device in the "dev" chain. It will
+ * be moved to the "invisible" part of the structure as soon as
+ * it has been cleaned up. -FvK
+ */
+ struct device *next;
+
+ /* The device initialization function. Called only once. */
+ int (*init)(struct device *dev);
+
+ /*
+ * This marks the end of the "visible" part of the structure. All
+ * fields hereafter are internal to the system, and may change at
+ * will (read: may be cleaned up at will).
+ */
+
+ unsigned short flags; /* interface flags (a la BSD) */
+ unsigned short family; /* address family ID (AF_INET) */
+ unsigned short metric; /* routing metric (not used) */
+ unsigned short mtu; /* interface MTU value */
+ unsigned short type; /* interface hardware type */
+ unsigned long trans_start; /* ?? */
+ unsigned short hard_header_len; /* hardware hdr length */
+ void *private; /* pointer to private data */
+
+ /* Interface address info. */
+ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
+ unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */
+ unsigned char addr_len; /* harfware address length */
+ unsigned long pa_addr; /* protocol address */
+ unsigned long pa_brdaddr; /* protocol broadcast addr */
+ unsigned long pa_dstaddr; /* protocol P-P other side addr */
+ unsigned long pa_mask; /* protocol netmask */
+ unsigned short pa_alen; /* protocol address length */
+
+ /* Pointer to the interface buffers. */
+ struct sk_buff *volatile buffs[DEV_NUMBUFFS];
+
+ /* Pointers to interface service routines. */
+ 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);
+};
+
+
+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
+#define DEV_QUEUE_MAGIC 0x17432895
+
+
+extern struct device *dev_base;
+extern struct packet_type *ptype_base;
+
+
+extern int ip_addr_match(unsigned long addr1, unsigned long addr2);
+extern int chk_addr(unsigned long addr);
+extern struct device *dev_check(int which, unsigned long daddr);
+extern unsigned long my_addr(void);
+
+extern void dev_add_pack(struct packet_type *pt);
+extern void dev_remove_pack(struct packet_type *pt);
+extern struct device *dev_get(char *name);
+extern int dev_open(struct device *dev);
+extern int dev_close(struct device *dev);
+extern void dev_queue_xmit(struct sk_buff *skb, struct device *dev,
+ int pri);
+extern int dev_rint(unsigned char *buff, long len, int flags,
+ struct device * dev);
+extern void dev_transmit(void);
+extern void inet_bh(void *tmp);
+extern void dev_tint(struct device *dev);
+extern int dev_get_info(char *buffer);
+extern int dev_ioctl(unsigned int cmd, void *);
+
+extern void dev_init(void);
+
+#endif /* _DEV_H */
diff --git a/net/inet/el.c b/net/inet/el.c
new file mode 100644
index 0000000..2155f63
--- /dev/null
+++ b/net/inet/el.c
@@ -0,0 +1,382 @@
+/* el.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992,1993 by Donald Becker. This is alpha test code.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This driver should work with the 3c503 and 3c503/16. It must be used
+ in shared memory mode.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version = "el.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "dev.h"
+
+#include "8390.h"
+#include "elreg.h"
+
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+int el2autoprobe(int ioaddr, struct device *dev);
+int el2probe(int ioaddr, struct device *dev);
+
+static void el2_reset_8390(struct device *dev);
+static void el2_init_card(struct device *dev);
+static void el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int el2_block_input(struct device *dev, int count, char *buf,
+ int ring_offset);
+
+
+int
+el2autoprobe(int ioaddr, struct device *dev)
+{
+ int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+ int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+ /* Non-autoprobe case first: */
+ if (ioaddr > 0)
+ return el2probe(ioaddr, dev);
+
+ /* We check for a memory-mapped 3c503 board by looking at the
+ port location bitmap at the end of the jumpered boot PROM space.
+ This works even if a PROM isn't there. */
+ for (addr = addrs; *addr; addr++) {
+ int i;
+ unsigned int base_bits = *(unsigned char *)*addr;
+ /* Find first set bit. */
+ for(i = 7; i >= 0; i--, base_bits >>= 1)
+ if (base_bits & 0x1)
+ break;
+ if (base_bits == 1 && el2probe(ports[i], dev))
+ return dev->base_addr;
+ }
+#ifndef ignore_nonshared_memory
+ /* It's not memory mapped, bummer. Try all of the locations
+ that aren't obviously empty. */
+ { int i;
+ for (i = 0; i < 8; i++) {
+ inb_p(ports[i] + 0x40F); /* Reset any lurking NE2000 */
+ if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
+ && el2probe(ports[i], dev))
+ return dev->base_addr;
+ }
+ }
+#endif /* probe_nonshared_memory */
+ return 0;
+}
+
+/* Probe for the Etherlink II card at I/O port base IOADDR,
+ returning non-zero on sucess. If found, set the station
+ address and memory parameters in DEVICE. */
+int
+el2probe(int ioaddr, struct device *dev)
+{
+ int i, iobase_reg, membase_reg, saved_406;
+ unsigned char *station_addr = dev->dev_addr;
+
+ /* We verify that it's a 3C503 board by checking the first three octets
+ of its ethernet address. */
+ printk("3c503 probe at %#3x:", ioaddr);
+ iobase_reg = inb(ioaddr+0x403);
+ membase_reg = inb(ioaddr+0x404);
+ /* Verify ASIC register that should be 0 or have a single bit set. */
+ if ( (iobase_reg & (iobase_reg - 1))
+ || (membase_reg & (membase_reg - 1))) {
+ printk(" not found.\n");
+ return 0;
+ }
+ saved_406 = inb_p(ioaddr + 0x406);
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
+ outb_p(ECNTRL_THIN, ioaddr + 0x406);
+ /* Map the station addr PROM into the lower I/O ports. */
+ outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
+ }
+ if ( station_addr[0] != 0x02
+ || station_addr[1] != 0x60
+ || station_addr[2] != 0x8c) {
+ printk(" 3C503 not found.\n");
+ /* Restore the register we frobbed. */
+ outb_p(saved_406, ioaddr + 0x406);
+ return 0;
+ }
+ /* Map the 8390 back into the window. */
+ outb(ECNTRL_THIN, ioaddr + 0x406);
+ dev->base_addr = ioaddr;
+ /* Probe for, turn on and clear the board's shared memory. */
+ if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
+ outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
+ if ((membase_reg & 0xf0) == 0) {
+ dev->mem_start = 0;
+ } else {
+ dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+ ((membase_reg & 0xA0) ? 0x4000 : 0);
+
+#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
+#ifdef EL2MEMTEST
+ /* This has never found an error, but someone might care. */
+ { /* Check the card's memory. */
+ int *mem_base = (int *)dev->mem_start;
+ int memtest_value = 0xbbadf00d;
+ mem_base[0] = 0xba5eba5e;
+ for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
+ mem_base[i] = memtest_value;
+ if (mem_base[0] != 0xba5eba5e
+ || mem_base[i] != memtest_value) {
+ printk(" memory failure or memory address conflict.\n");
+ dev->mem_start = 0;
+ break;
+ }
+ memtest_value += 0x55555555;
+ mem_base[i] = 0;
+ }
+ }
+#endif /* EL2MEMTEST */
+ /* Divide the on-board memory into a single maximum-sized transmit
+ (double-sized for ping-pong transmit) buffer at the base, and
+ use the rest as a receive ring. */
+ dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
+ dev->rmem_start = TX_PAGES*256 + dev->mem_start;
+ }
+ if (ei_debug > 2)
+ printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
+ dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
+
+ /* Finish setting the board's parameters. */
+ ei_status.name = "3C503";
+ ei_status.tx_start_page = EL2SM_START_PG;
+ ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
+ ei_status.stop_page = EL2SM_STOP_PG;
+ ei_status.reset_8390 = &el2_reset_8390;
+ ei_status.block_input = &el2_block_input;
+ ei_status.block_output = &el2_block_output;
+/* This should be probed for (or set via an ioctl()) at run-time someday. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+ ei_status.interface_num = 1;
+#else
+ ei_status.interface_num = 0;
+#endif
+
+ if (dev->irq < 2) {
+ int irqlist[] = {5, 9, 3, 4, 0};
+ int *irqp = irqlist;
+ do {
+ if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen. */
+ autoirq_setup(0);
+ outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+ outb_p(0x00, E33G_IDCFR);
+ if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (dev->irq, &ei_interrupt) == 0) {
+ printk(" got IRQ %d", dev->irq);
+ break;
+ } else
+ printk(" IRQ%d busy..", dev->irq);
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ printk(" unable to find an free IRQ line.\n");
+ return 0;
+ }
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ else if (dev->irq > 5 && dev->irq != 9) {
+ printk("\n3c503: configured interrupt number %d out of range.\n",
+ dev->irq);
+ return 0;
+ }
+ if (request_irq(dev->irq, &ei_interrupt)) {
+ printk (" unable to get IRQ%d.\n", dev->irq);
+ return 0;
+ }
+ }
+
+ dev->start = 0;
+ el2_init_card(dev);
+
+ if (dev->mem_start)
+ printk("\n%s: %s using IRQ %d with shared memory at %#6x-%#6x,\n",
+ dev->name, ei_status.name, dev->irq,
+ dev->mem_start, dev->mem_end-1);
+ else
+ printk("\n%s: %s using IRQ %d with programmed I/O.\n",
+ dev->name, ei_status.name, dev->irq);
+ if (ei_debug > 1)
+ printk(version);
+
+ return ioaddr;
+}
+
+/* This is called whenever we have a unrecoverable failure:
+ transmit timeout
+ Bad ring buffer packet header
+ */
+static void
+el2_reset_8390(struct device *dev)
+{
+ if (ei_debug > 1) {
+ printk("%s: Resetting the 3c503 board...", dev->name);
+ printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+ E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
+ }
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+ ei_status.txing = 0;
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ el2_init_card(dev);
+ if (ei_debug > 1) printk("done\n");
+}
+
+/* Initialize the 3c503 GA registers after a reset. */
+static void
+el2_init_card(struct device *dev)
+{
+ /* Unmap the station PROM and select the DIX or BNC connector. */
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+
+ /* Set ASIC copy of rx's first and last+1 buffer pages */
+ /* These must be the same as in the 8390. */
+ outb(ei_status.rx_start_page, E33G_STARTPG);
+ outb(ei_status.stop_page, E33G_STOPPG);
+
+ /* Point the vector pointer registers somewhere ?harmless?. */
+ outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
+ outb(0xff, E33G_VP1);
+ outb(0x00, E33G_VP0);
+ /* Turn off all interrupts until we're opened. */
+ outb_p(0x00, dev->base_addr + EN0_IMR);
+ /* Enable IRQs iff started. */
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ /* Set the interrupt line. */
+ outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+ outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
+ outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
+ outb_p(0x00, E33G_DMAAL);
+ return; /* We always succeed */
+}
+
+/* Either use the shared memory (if enabled on the board) or put the packet
+ out through the ASIC FIFO. The latter is probably much slower. */
+static void
+el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int i; /* Buffer index */
+ int boguscount = 0; /* timeout counter */
+
+ /* This should really be set with during an open(). */
+ outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
+
+ if (dev->mem_start) { /* Shared memory transfer */
+ void *dest_addr = (void *)(dev->mem_start +
+ ((start_page - ei_status.tx_start_page) << 8));
+ memcpy(dest_addr, buf, count);
+ if (ei_debug > 2 && memcmp(dest_addr, buf, count))
+ printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
+ dev->name, dest_addr);
+ else if (ei_debug > 4)
+ printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
+ dev->name, dest_addr);
+ return;
+ }
+ /* No shared memory, put the packet out the slow way. */
+ /* Set up then start the internal memory transfer to Tx Start Page */
+ outb(0x00, E33G_DMAAL);
+ outb_p(start_page, E33G_DMAAH);
+ outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ for speed once everything is working. I think it is possible
+ to output 8 bytes between each check of the status bit. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ return;
+ }
+ outb(buf[i], E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ return;
+}
+
+/* Returns the new ring pointer. */
+static int
+el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ int boguscount = 0;
+ int end_of_ring = dev->rmem_end;
+ unsigned int i;
+
+ /* Maybe enable shared memory just be to be safe... nahh.*/
+ if (dev->mem_start) { /* Use the shared memory. */
+ ring_offset -= (EL2SM_START_PG<<8);
+ if (dev->mem_start + ring_offset + count > end_of_ring) {
+ /* We must wrap the input move. */
+ int semi_count = end_of_ring - (dev->mem_start + ring_offset);
+ if (ei_debug > 4)
+ printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
+ dev->name, dev->mem_start, ring_offset,
+ (char *)dev->mem_start + ring_offset);
+ memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
+ count -= semi_count;
+ memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+ return dev->rmem_start + count;
+ }
+ if (ei_debug > 4)
+ printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
+ dev->name, dev->mem_start, ring_offset,
+ (char *)dev->mem_start + ring_offset);
+ memcpy(buf, (char *)dev->mem_start + ring_offset, count);
+ return ring_offset + count;
+ }
+ /* No shared memory, use programmed I/O. */
+ outb(ring_offset & 0xff, E33G_DMAAL);
+ outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ for speed once everything is working. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ boguscount = 0;
+ break;
+ }
+ buf[i] = inb_p(E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/elreg.h b/net/inet/elreg.h
new file mode 100644
index 0000000..a1c8575
--- /dev/null
+++ b/net/inet/elreg.h
@@ -0,0 +1,59 @@
+/* Definitions for the 3Com 3c503 Etherlink 2. */
+/* This file is distributed under the GPL.
+ Some of these names and comments are from the Crynwr packet drivers. */
+
+#define EL2H (dev->base_addr + 0x400)
+#define EL2L (dev->base_addr)
+
+/* Shared memory management parameters */
+
+#define EL2SM_START_PG (0x20) /* First page of TX buffer */
+#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
+
+/* 3Com 3c503 ASIC registers */
+#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
+#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
+#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
+#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
+ /* (non-useful, but it also appears at the end of EPROM space) */
+#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
+#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
+#define E33G_CNTRL (EL2H+6) /* Board's main control register */
+#define E33G_STATUS (EL2H+7) /* Status on completions. */
+#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
+ /* (Which IRQ to assert, DMA chan to use) */
+#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
+#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
+/* "Vector pointer" - if this address matches a read, the EPROM (rather than
+ shared RAM) is mapped into memory space. */
+#define E33G_VP2 (EL2H+11)
+#define E33G_VP1 (EL2H+12)
+#define E33G_VP0 (EL2H+13)
+#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
+#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
+
+/* Bits in E33G_CNTRL register: */
+
+#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
+#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
+#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
+#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
+#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
+#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
+#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
+#define ECNTRL_START (0x80) /* Start the DMA logic */
+
+/* Bits in E33G_STATUS register: */
+
+#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
+#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
+#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
+#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
+#define ESTAT_DIP (0x08) /* DMA In Progress */
+
+/* Bits in E33G_GACFR register: */
+
+#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
+#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
+
+/* End of 3C503 parameter definitions */
diff --git a/net/inet/eth.c b/net/inet/eth.c
new file mode 100644
index 0000000..544c6ec
--- /dev/null
+++ b/net/inet/eth.c
@@ -0,0 +1,142 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Ethernet-type device handling.
+ *
+ * Version: @(#)eth.c 1.0.7 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#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 <linux/in.h>
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include "arp.h"
+
+
+/* Display an Ethernet address in readable format. */
+char *eth_print(unsigned char *ptr)
+{
+ static char buff[64];
+
+ if (ptr == NULL) return("[NONE]");
+ sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X",
+ (ptr[0] & 255), (ptr[1] & 255), (ptr[2] & 255),
+ (ptr[3] & 255), (ptr[4] & 255), (ptr[5] & 255)
+ );
+ return(buff);
+}
+
+
+/* Display the contents of the Ethernet MAC header. */
+void
+eth_dump(struct ethhdr *eth)
+{
+ if (inet_debug != DBG_ETH) return;
+
+ printk("eth: SRC = %s ", eth_print(eth->h_source));
+ printk("DST = %s ", eth_print(eth->h_dest));
+ printk("TYPE = %04X\n", ntohs(eth->h_proto));
+}
+
+
+/* Create the Ethernet MAC header. */
+int
+eth_header(unsigned char *buff, struct device *dev, unsigned short type,
+ unsigned long daddr, unsigned long saddr, unsigned len)
+{
+ struct ethhdr *eth;
+
+ DPRINTF((DBG_DEV, "ETH: header(%s, ", in_ntoa(saddr)));
+ DPRINTF((DBG_DEV, "%s, 0x%X)\n", in_ntoa(daddr), type));
+
+ /* Fill in the basic Ethernet MAC header. */
+ eth = (struct ethhdr *) buff;
+ eth->h_proto = ntohs(type);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+
+ /* We don't ARP for the LOOPBACK device... */
+ if (dev->flags & IFF_LOOPBACK) {
+ DPRINTF((DBG_DEV, "ETH: No header for loopback\n"));
+ memset(eth->h_dest, 0, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ /* Check if we can use the MAC BROADCAST address. */
+ if (chk_addr(daddr) == IS_BROADCAST) {
+ DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
+ memcpy(eth->h_dest, dev->broadcast, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ /* No. Ask ARP to resolve the Ethernet address. */
+ if (arp_find(eth->h_dest, daddr, dev, saddr)) {
+ return(-dev->hard_header_len);
+ } else return(dev->hard_header_len);
+}
+
+
+/* Rebuild the Ethernet MAC header. */
+int
+eth_rebuild_header(void *buff, struct device *dev)
+{
+ struct ethhdr *eth;
+ unsigned long src, dst;
+
+ DPRINTF((DBG_DEV, "ETH: Using MAC Broadcast\n"));
+ eth = buff;
+ src = *(unsigned long *) eth->h_source;
+ dst = *(unsigned long *) eth->h_dest;
+ DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src)));
+ DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst)));
+ if (arp_find(eth->h_dest, dst, dev, src)) return(1);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return(0);
+}
+
+
+/* Add an ARP entry for a host on this interface. */
+void
+eth_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+{
+ struct ethhdr *eth;
+
+ eth = (struct ethhdr *) (skb + 1);
+ arp_add(addr, eth->h_source, dev);
+}
+
+
+/* Determine the packet's protocol ID. */
+unsigned short
+eth_type_trans(struct sk_buff *skb, struct device *dev)
+{
+ struct ethhdr *eth;
+
+ eth = (struct ethhdr *) (skb + 1);
+ return(eth->h_proto);
+}
diff --git a/net/inet/eth.h b/net/inet/eth.h
new file mode 100644
index 0000000..f8fed44
--- /dev/null
+++ b/net/inet/eth.h
@@ -0,0 +1,35 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ETH_H
+#define _ETH_H
+
+
+#include <linux/if_ether.h>
+
+
+extern char *eth_print(unsigned char *ptr);
+extern void eth_dump(struct ethhdr *eth);
+extern int eth_header(unsigned char *buff, struct device *dev,
+ unsigned short type, unsigned long daddr,
+ unsigned long saddr, unsigned len);
+extern int eth_rebuild_header(void *buff, struct device *dev);
+extern void eth_add_arp(unsigned long addr, struct sk_buff *skb,
+ struct device *dev);
+extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev);
+
+#endif /* _ETH_H */
diff --git a/net/inet/hp.c b/net/inet/hp.c
new file mode 100644
index 0000000..d378dc7
--- /dev/null
+++ b/net/inet/hp.c
@@ -0,0 +1,321 @@
+/* hp.c: A HP LAN ethernet driver for linux. */
+/*
+ Written 1993 by Donald Becker. This is alpha test code.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a driver for the HP LAN adaptors.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version = "hp.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "dev.h"
+#include "8390.h"
+
+/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
+#define port_read(port,buf,nr) \
+__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
+#define port_write(port,buf,nr) \
+__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+#define port_read_b(port,buf,nr) \
+__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
+#define port_write_b(port,buf,nr) \
+__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
+#define HP_ID 0x07
+#define HP_CONFIGURE 0x08 /* Configuration register. */
+#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
+#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
+#define HP_DATAON 0x10 /* Turn on dataport */
+#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
+#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
+
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+int hpprobe(int ioaddr, struct device *dev);
+int hpprobe1(int ioaddr, struct device *dev);
+
+static void hp_reset_8390(struct device *dev);
+static int hp_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void hp_init_card(struct device *dev);
+
+/* The map from IRQ number to HP_CONFIGURE register setting. */
+/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
+static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+
+
+/* Probe for an HP LAN adaptor.
+ Also initialize the card and fill in STATION_ADDR with the station
+ address. */
+
+int hpprobe(int ioaddr, struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+ if (ioaddr > 0x100)
+ return hpprobe1(ioaddr, dev);
+
+ for (port = &ports[0]; *port; port++)
+ if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
+ return dev->base_addr;
+ return 0;
+}
+
+int hpprobe1(int ioaddr, struct device *dev)
+{
+ int i;
+ unsigned char *station_addr = dev->dev_addr;
+ unsigned char SA_prom[6];
+ int tmp;
+ int hplan;
+
+ printk("HP-LAN ethercard probe at %#3x:", ioaddr);
+ tmp = inb_p(ioaddr);
+ if (tmp == 0xFF) {
+ printk(" not found (nothing there).\n");
+ return 0;
+ }
+
+ for(i = 0; i < sizeof(SA_prom); i++) {
+ SA_prom[i] = inb(ioaddr + i);
+ if (i < ETHER_ADDR_LEN && station_addr) {
+ printk(" %2.2x", SA_prom[i]);
+ station_addr[i] = SA_prom[i];
+ }
+ }
+ hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
+ if (hplan == 0) {
+ printk(" not found (invalid station address prefix).\n");
+ return 0;
+ }
+
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_PAGES;
+ /* Set up the rest of the parameters. */
+ if ((tmp = inb_p(ioaddr + HP_ID)) & 0x80) {
+ ei_status.name = "HP27247";
+ ei_status.word16 = 1;
+ ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
+ } else {
+ ei_status.name = "HP27250";
+ ei_status.word16 = 0;
+ ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
+ }
+
+ /* Set the base address to point to the NIC! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ if (dev->irq < 2) {
+ int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+ int irq_8list[] = { 7, 5, 3, 4, 9, 0};
+ int *irqp = ei_status.word16 ? irq_16list : irq_8list;
+ do {
+ if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
+ autoirq_setup(0);
+ /* Twinkle the interrupt, and check if it's seen. */
+ outb_p(irqmap[dev->irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+ outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+ if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (dev->irq, &ei_interrupt) == 0) {
+ printk(" got IRQ %d", dev->irq);
+ break;
+ } else
+ printk(" IRQ%d busy..", dev->irq);
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ printk(" unable to find an free IRQ line.\n");
+ return 0;
+ }
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ if (irqaction(dev->irq, &ei_sigaction)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return 0;
+ }
+ }
+
+ printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq);
+ if (ei_debug > 1)
+ printk(version);
+
+ ei_status.reset_8390 = &hp_reset_8390;
+ ei_status.block_input = &hp_block_input;
+ ei_status.block_output = &hp_block_output;
+ hp_init_card(dev);
+ return dev->base_addr;
+}
+
+static void
+hp_reset_8390(struct device *dev)
+{
+ int hp_base = dev->base_addr - NIC_OFFSET;
+ int saved_config = inb_p(hp_base + HP_CONFIGURE);
+ int reset_start_time = jiffies;
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
+ outb_p(0x00, hp_base + HP_CONFIGURE);
+ ei_status.txing = 0;
+
+ sti();
+ /* We shouldn't use the boguscount for timing, but this hasn't been
+ checked yet, and you could hang your machine if jiffies break... */
+ {
+ int boguscount = 150000;
+ while(jiffies - reset_start_time < 2)
+ if (boguscount-- < 0) {
+ printk("jiffy failure (t=%d)...", jiffies);
+ break;
+ }
+ }
+
+ outb_p(saved_config, hp_base + HP_CONFIGURE);
+ while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2) {
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+ return;
+ }
+ if (ei_debug > 1) printk("8390 reset done.", jiffies);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ porting to a new ethercard look at the packet driver source for hints.
+ The HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static int
+hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+ int xfer_count = count;
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base);
+ if (ei_status.word16) {
+ port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+ } else {
+ port_read_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ /* Check only the lower 8 bits so we can ignore ring wrap. */
+ if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+ printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+ return ring_offset + count;
+}
+
+static void
+hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+
+#ifdef ei8390_bug
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work. */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0xff, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, EN_CMD);
+ /* Make certain that the dummy read has occured. */
+ inb_p(0x61);
+ inb_p(0x61);
+#endif
+
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base);
+ if (ei_status.word16) {
+ /* Use the 'rep' sequence for 16 bit boards. */
+ port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+ } else {
+ port_write_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
+
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ if ((start_page << 8) + count != addr)
+ printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
+ dev->name, (start_page << 8) + count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+/* This function resets the ethercard if something screws up. */
+static void
+hp_init_card(struct device *dev)
+{
+ int irq = dev->irq;
+ NS8390_init(dev, 0);
+ outb_p(irqmap[irq&0x0f] | HP_RUN,
+ dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/icmp.c b/net/inet/icmp.c
new file mode 100644
index 0000000..d60c822
--- /dev/null
+++ b/net/inet/icmp.c
@@ -0,0 +1,440 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Internet Control Message Protocol (ICMP)
+ *
+ * Version: @(#)icmp.c 1.0.11 06/02/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "icmp.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.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 }
+};
+
+
+/* Display the contents of an ICMP header. */
+static void
+print_icmp(struct icmphdr *icmph)
+{
+ if (inet_debug != DBG_ICMP) return;
+
+ printk("ICMP: type = %d, code = %d, checksum = %X\n",
+ icmph->type, icmph->code, icmph->checksum);
+ printk(" gateway = %s\n", in_ntoa(icmph->un.gateway));
+}
+
+
+/* Send an ICMP message. */
+void
+icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ int offset;
+ struct icmphdr *icmph;
+ int len;
+
+ DPRINTF((DBG_ICMP, "icmp_send(skb_in = %X, type = %d, code = %d, dev=%X)\n",
+ skb_in, type, code, dev));
+
+ /* Get some memory for the reply. */
+ len = sizeof(struct sk_buff) + dev->hard_header_len +
+ sizeof(struct iphdr) + sizeof(struct icmphdr) +
+ sizeof(struct iphdr) + 8; /* amount of header to return */
+
+ skb = kmalloc(len, GFP_ATOMIC);
+ if (skb == NULL) return;
+
+ skb->lock = 0;
+ skb->sk = NULL;
+ skb->mem_addr = skb;
+ skb->mem_len = len;
+ len -= sizeof(struct sk_buff);
+
+ /* Find the IP header. */
+ iph = (struct iphdr *) (skb_in + 1);
+ iph = (struct iphdr *) ((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, IPPROTO_ICMP, NULL, len);
+ if (offset < 0) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* Re-adjust length according to actual IP header size. */
+ skb->len = offset + sizeof(struct icmphdr) + 8;
+ icmph = (struct icmphdr *) ((unsigned char *) (skb + 1) + offset);
+ icmph->type = type;
+ icmph->code = code;
+ icmph->checksum = 0;
+ icmph->un.gateway = 0;
+ memcpy(icmph + 1, iph, sizeof(struct iphdr) + 8);
+
+ DPRINTF((DBG_ICMP, ">>\n"));
+ print_icmp(icmph);
+
+ /* Send it and free it. */
+ ip_queue_xmit(NULL, dev, skb, 1);
+}
+
+
+/* Handle ICMP_UNREACH and ICMP_QUENCH. */
+static void
+icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb)
+{
+ struct inet_protocol *ipprot;
+ struct iphdr *iph;
+ unsigned char hash;
+ int err;
+
+ err = (icmph->type << 8) | icmph->code;
+ iph = (struct iphdr *) (icmph + 1);
+ switch(icmph->code & 7) {
+ case ICMP_NET_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s: network unreachable.\n",
+ in_ntoa(iph->daddr)));
+ break;
+ case ICMP_HOST_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s: host unreachable.\n",
+ in_ntoa(iph->daddr)));
+ break;
+ case ICMP_PROT_UNREACH:
+ printk("ICMP: %s:%d: protocol unreachable.\n",
+ in_ntoa(iph->daddr), ntohs(iph->protocol));
+ break;
+ case ICMP_PORT_UNREACH:
+ DPRINTF((DBG_ICMP, "ICMP: %s:%d: port unreachable.\n",
+ in_ntoa(iph->daddr), -1 /* FIXME: ntohs(iph->port) */));
+ break;
+ case ICMP_FRAG_NEEDED:
+ printk("ICMP: %s: fragmentation needed and DF set.\n",
+ in_ntoa(iph->daddr));
+ break;
+ case ICMP_SR_FAILED:
+ printk("ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+ break;
+ default:
+ DPRINTF((DBG_ICMP, "ICMP: Unreachable: CODE=%d from %s\n",
+ (icmph->code & 7), in_ntoa(iph->daddr)));
+ break;
+ }
+
+ /* Get the protocol(s). */
+ hash = iph->protocol & (MAX_INET_PROTOS -1);
+
+ /* This can change while we are doing it. */
+ ipprot = (struct inet_protocol *) inet_protos[hash];
+ while(ipprot != NULL) {
+ struct inet_protocol *nextip;
+
+ nextip = (struct inet_protocol *) ipprot->next;
+
+ /* Pass it off to everyone who wants it. */
+ if (iph->protocol == ipprot->protocol && ipprot->err_handler) {
+ ipprot->err_handler(err, (unsigned char *)(icmph + 1),
+ iph->daddr, iph->saddr, ipprot);
+ }
+
+ ipprot = nextip;
+ }
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/* Handle ICMP_REDIRECT. */
+static void
+icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev)
+{
+ struct iphdr *iph;
+ unsigned long ip;
+
+ iph = (struct iphdr *) (icmph + 1);
+ ip = iph->daddr;
+ switch(icmph->code & 7) {
+ case ICMP_REDIR_NET:
+ rt_add((RTF_DYNAMIC | RTF_MODIFIED),
+ ip, icmph->un.gateway, dev);
+ break;
+ case ICMP_REDIR_HOST:
+ rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST),
+ ip, icmph->un.gateway, dev);
+ break;
+ case ICMP_REDIR_NETTOS:
+ case ICMP_REDIR_HOSTTOS:
+ printk("ICMP: cannot handle TOS redirects yet!\n");
+ break;
+ default:
+ DPRINTF((DBG_ICMP, "ICMP: Unreach: CODE=%d\n",
+ (icmph->code & 7)));
+ break;
+ }
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/* Handle ICMP_ECHO ("ping") requests. */
+static void
+icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+
+ size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
+ skb2 = kmalloc(size, GFP_ATOMIC);
+ if (skb2 == NULL) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->sk = NULL;
+ skb2->lock = 0;
+ skb2->mem_addr = skb2;
+ skb2->mem_len = size;
+
+ /* Build Layer 2-3 headers for message back to source */
+ offset = ip_build_header(skb2, daddr, saddr, &dev,
+ IPPROTO_ICMP, opt, len);
+ if (offset < 0) {
+ printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
+ kfree_s(skb2->mem_addr, skb2->mem_len);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* Re-adjust length according to actual IP header size. */
+ skb2->len = offset + len;
+
+ /* Build ICMP_ECHO Response message. */
+ icmphr = (struct icmphdr *) ((char *) (skb2 + 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((struct sock *)NULL, dev, skb2, 1);
+
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/* Handle the ICMP INFORMATION REQUEST. */
+static void
+icmp_info(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ /* NOT YET */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/* Handle ICMP_ADRESS_MASK requests. */
+static void
+icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
+ unsigned long saddr, unsigned long daddr, int len,
+ struct options *opt)
+{
+ struct icmphdr *icmphr;
+ struct sk_buff *skb2;
+ int size, offset;
+
+ size = sizeof(struct sk_buff) + dev->hard_header_len + 64 + len;
+ skb2 = kmalloc(size, GFP_ATOMIC);
+ if (skb2 == NULL) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ skb2->sk = NULL;
+ skb2->lock = 0;
+ skb2->mem_addr = skb2;
+ skb2->mem_len = size;
+
+ /* Build Layer 2-3 headers for message back to source */
+ offset = ip_build_header(skb2, daddr, saddr, &dev,
+ IPPROTO_ICMP, opt, len);
+ if (offset < 0) {
+ printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
+ kfree_s(skb2->mem_addr, skb2->mem_len);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /* Re-adjust length according to actual IP header size. */
+ skb2->len = offset + len;
+
+ /* Build ICMP ADDRESS MASK Response message. */
+ icmphr = (struct icmphdr *) ((char *) (skb2 + 1) + offset);
+ icmphr->type = ICMP_ADDRESSREPLY;
+ icmphr->code = 0;
+ icmphr->checksum = 0;
+ icmphr->un.echo.id = icmph->un.echo.id;
+ icmphr->un.echo.sequence = icmph->un.echo.sequence;
+ memcpy((char *) (icmphr + 1), (char *) dev->pa_mask, sizeof(dev->pa_mask));
+
+ if (icmph->checksum) { /* Calculate Checksum */
+ icmphr->checksum = ip_compute_csum((void *)icmphr, len);
+ }
+
+ /* Ship it out - free it when done */
+ ip_queue_xmit((struct sock *)NULL, dev, skb2, 1);
+
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+}
+
+
+/* Deal 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 inet_protocol *protocol)
+{
+ struct icmphdr *icmph;
+ unsigned char *buff;
+
+ /* Drop broadcast packets. */
+ if (chk_addr(daddr) == IS_BROADCAST) {
+ DPRINTF((DBG_ICMP, "ICMP: Discarded broadcast from %s\n",
+ in_ntoa(saddr)));
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+
+ buff = skb1->h.raw;
+ icmph = (struct icmphdr *) buff;
+
+ /* Validate the packet first */
+ if (icmph->checksum) { /* Checksums Enabled? */
+ if (ip_compute_csum((unsigned char *) icmph, len)) {
+ /* Failed checksum! */
+ printk("ICMP: failed checksum from %s!\n", in_ntoa(saddr));
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+ }
+ print_icmp(icmph);
+
+ /* Parse the ICMP message */
+ switch(icmph->type) {
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ icmp_unreach(icmph, skb1);
+ return(0);
+ case ICMP_REDIRECT:
+ icmp_redirect(icmph, skb1, dev);
+ return(0);
+ case ICMP_ECHO:
+ icmp_echo(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ECHOREPLY:
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_INFO_REQUEST:
+ icmp_info(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_INFO_REPLY:
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ case ICMP_ADDRESS:
+ icmp_address(icmph, skb1, dev, saddr, daddr, len, opt);
+ return 0;
+ case ICMP_ADDRESSREPLY:
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ default:
+ DPRINTF((DBG_ICMP,
+ "ICMP: Unsupported ICMP from %s, type = 0x%X\n",
+ in_ntoa(saddr), icmph->type));
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+ /*NOTREACHED*/
+ skb1->sk = NULL;
+ kfree_skb(skb1, FREE_READ);
+ return(-1);
+}
+
+
+/* Perform any ICMP-related I/O control requests. */
+int
+icmp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd) {
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_ICMP));
+ default:
+ return(-EINVAL);
+ }
+ return(0);
+}
diff --git a/net/inet/icmp.h b/net/inet/icmp.h
new file mode 100644
index 0000000..1e2ec52
--- /dev/null
+++ b/net/inet/icmp.h
@@ -0,0 +1,36 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP module.
+ *
+ * Version: @(#)icmp.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ICMP_H
+#define _ICMP_H
+
+#include <linux/icmp.h>
+
+
+extern struct icmp_err icmp_err_convert[];
+
+extern void icmp_send(struct sk_buff *skb_in, int type, int code,
+ struct device *dev);
+extern 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 inet_protocol *protocol);
+
+extern int icmp_ioctl(struct sock *sk, int cmd,
+ unsigned long arg);
+
+#endif /* _ICMP_H */
diff --git a/net/inet/inet.h b/net/inet/inet.h
new file mode 100644
index 0000000..1b41209
--- /dev/null
+++ b/net/inet/inet.h
@@ -0,0 +1,100 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * General Definitions for the TCP/IP (INET) module. This is
+ * mostly a bunch of "general" macros, plus the PROTOCOL link
+ * code and data.
+ *
+ * Version: @(#)inet.h 1.0.6 05/25/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This work was derived friom Ross Biro's inspirational work
+ * for the LINUX operating system. His version numbers were:
+ *
+ * $Id: Space.c,v 0.8.4.5 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: arp.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: arp.h,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: dev.c,v 0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: dev.h,v 0.8.4.7 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: eth.c,v 0.8.4.4 1993/01/22 23:21:38 bir7 Exp $
+ * $Id: eth.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: icmp.c,v 0.8.4.9 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: icmp.h,v 0.8.4.2 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: ip.c,v 0.8.4.8 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: ip.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: loopback.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: packet.c,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: protocols.c,v 0.8.4.3 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: raw.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: sock.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: sock.h,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.c,v 0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.h,v 0.8.4.7 1993/01/22 22:58:08 bir7 Exp $
+ * $Id: timer.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: timer.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: udp.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: udp.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: we.c,v 0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: wereg.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _INET_H
+#define _INET_H
+
+
+#include <linux/ddi.h>
+
+
+#define AF_INET_MAJOR 18 /* UNIX VFS major number */
+
+
+#define NET16(x) ((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00))
+
+
+#undef INET_DEBUG
+#ifdef INET_DEBUG
+# define DPRINTF(x) dprintf x
+#else
+# define DPRINTF(x) /*zilch*/
+#endif
+
+/* Debug levels. One per module. */
+#define DBG_OFF 0 /* no debugging */
+#define DBG_INET 1 /* sock.c */
+#define DBG_RT 2 /* route.c */
+#define DBG_DEV 3 /* dev.c */
+#define DBG_ETH 4 /* eth.c */
+#define DBG_PROTO 5 /* protocol.c */
+#define DBG_TMR 6 /* timer.c */
+#define DBG_PKT 7 /* packet.c */
+#define DBG_RAW 8 /* raw.c */
+
+#define DBG_LOOPB 10 /* loopback.c */
+#define DBG_SLIP 11 /* slip.c */
+
+#define DBG_ARP 20 /* arp.c */
+#define DBG_IP 21 /* ip.c */
+#define DBG_ICMP 22 /* icmp.c */
+#define DBG_TCP 23 /* tcp.c */
+#define DBG_UDP 24 /* udp.c */
+
+
+extern int inet_debug;
+
+
+extern void inet_proto_init(struct ddi_proto *pro);
+extern char *in_ntoa(unsigned long in);
+extern unsigned long in_aton(char *str);
+
+extern void dprintf(int level, char *fmt, ...);
+
+extern int dbg_ioctl(void *arg, int level);
+
+#endif /* _INET_H */
diff --git a/net/inet/ip.c b/net/inet/ip.c
new file mode 100644
index 0000000..19daff8
--- /dev/null
+++ b/net/inet/ip.c
@@ -0,0 +1,787 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The Internet Protocol (IP) module.
+ *
+ * Version: @(#)ip.c 1.0.16 06/02/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#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/errno.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "eth.h"
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+#include "icmp.h"
+
+
+void
+ip_print(struct iphdr *ip)
+{
+ unsigned char buff[32];
+ unsigned char *ptr;
+ int addr, len, i;
+
+ if (inet_debug != DBG_IP) return;
+
+ /* Dump the IP header. */
+ printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n",
+ ip->ihl, ip->version, ip->tos, ntohs(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(" soucre=%s ", in_ntoa(ip->saddr));
+ printk("dest=%s\n", in_ntoa(ip->daddr));
+ printk(" ----\n");
+
+ /* Dump the data. */
+ ptr = (unsigned char *)(ip + 1);
+ addr = 0;
+ len = ntohs(ip->tot_len) - (4 * ip->ihl);
+ while (len > 0) {
+ printk(" %04X: ", addr);
+ for(i = 0; i < 16; i++) {
+ if (len > 0) {
+ printk("%02X ", (*ptr & 0xFF));
+ buff[i] = *ptr++;
+ if (buff[i] < 32 || buff[i] > 126) buff[i] = '.';
+ } else {
+ printk(" ");
+ buff[i] = ' ';
+ }
+ addr++;
+ len--;
+ };
+ buff[i] = '\0';
+ printk(" \"%s\"\n", buff);
+ }
+ printk(" ----\n\n");
+}
+
+
+int
+ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd) {
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_IP));
+ default:
+ return(-EINVAL);
+ }
+}
+
+
+/* these two routines will do routining. */
+static void
+strict_route(struct iphdr *iph, struct options *opt)
+{
+}
+
+
+static void
+loose_route(struct iphdr *iph, struct options *opt)
+{
+}
+
+
+static void
+print_ipprot(struct inet_protocol *ipprot)
+{
+ DPRINTF((DBG_IP, "handler = %X, protocol = %d, copy=%d \n",
+ ipprot->handler, ipprot->protocol, ipprot->copy));
+}
+
+
+/* This routine will check to see if we have lost a gateway. */
+void
+ip_route_check(unsigned long daddr)
+{
+}
+
+
+#if 0
+/* this routine puts the options at the end of an ip header. */
+static int
+build_options(struct iphdr *iph, struct options *opt)
+{
+ unsigned char *ptr;
+ /* currently we don't support any options. */
+ ptr = (unsigned char *)(iph+1);
+ *ptr = 0;
+ return (4);
+}
+#endif
+
+
+/* Take an skb, and fill in the MAC header. */
+static int
+ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev,
+ unsigned long saddr)
+{
+ unsigned char *ptr;
+ int mac;
+
+ ptr = (unsigned char *)(skb + 1);
+ mac = 0;
+ skb->arp = 1;
+ if (dev->hard_header) {
+ mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len);
+ }
+ if (mac < 0) {
+ mac = -mac;
+ skb->arp = 0;
+ }
+ skb->dev = dev;
+ return(mac);
+}
+
+
+/*
+ * This routine builds the appropriate hardware/IP headers for
+ * the routine. It assumes that if *dev != NULL then the
+ * protocol knows what it's doing, otherwise it uses the
+ * routing/ARP tables to select a device 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 iphdr *iph;
+ struct rtable *rt;
+ unsigned char *buff;
+ unsigned long raddr;
+ static int count = 0;
+ int tmp;
+
+ if (saddr == 0) saddr = my_addr();
+ DPRINTF((DBG_IP, "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) {
+ rt = rt_route(daddr, &optmem);
+ if (rt == NULL) return(-ENETUNREACH);
+
+ *dev = rt->rt_dev;
+ if (daddr != 0x0100007F) saddr = rt->rt_dev->pa_addr;
+ raddr = rt->rt_gateway;
+
+ DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr)));
+ opt = &optmem;
+ } else {
+ /* We still need the address of the first hop. */
+ rt = rt_route(daddr, &optmem);
+ raddr = (rt == NULL) ? 0 : rt->rt_gateway;
+ }
+ if (raddr == 0) raddr = daddr;
+
+ /* Now build the MAC header. */
+ tmp = ip_send(skb, raddr, len, *dev, saddr);
+ buff += tmp;
+ len -= tmp;
+
+ skb->dev = *dev;
+ skb->saddr = saddr;
+ if (skb->sk) skb->sk->saddr = saddr;
+
+ /* Now build the IP header. */
+ iph = (struct iphdr *)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 = htons(count++);
+
+ /* Setup the IP options. */
+#ifdef Not_Yet_Avail
+ build_options(iph, opt);
+#endif
+
+ return(20 + tmp); /* IP header plus MAC header size */
+}
+
+
+static int
+do_options(struct iphdr *iph, struct options *opt)
+{
+ unsigned char *buff;
+ int done = 0;
+ int i, len = sizeof(struct iphdr);
+
+ /* 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 = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->compartment = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->handling = ntohs(*(unsigned short *)buff);
+ buff += 2;
+ opt->tcc = ((*buff) << 16) + ntohs(*(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;
+
+ /* Do the first multiple of 4 bytes and convert to 16 bits. */
+ if (len > 3) {
+ __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);
+}
+
+
+/* Check the header of an incoming IP datagram. */
+static int
+ip_csum(struct iphdr *iph)
+{
+ if (iph->check == 0) return(0);
+ if (ip_compute_csum((unsigned char *)iph, iph->ihl*4) == 0) return(0);
+ return(1);
+}
+
+
+/* Generate a checksym for an outgoing IP datagram. */
+static void
+ip_send_check(struct iphdr *iph)
+{
+ iph->check = 0;
+ iph->check = ip_compute_csum((unsigned char *)iph, iph->ihl*4);
+}
+
+
+/* Forward an IP datagram to its next destination. */
+static void
+ip_forward(struct sk_buff *skb, struct device *dev)
+{
+ struct device *dev2;
+ struct iphdr *iph;
+ struct sk_buff *skb2;
+ struct rtable *rt;
+ unsigned char *ptr;
+ unsigned long raddr;
+
+ /*
+ * According to the RFC, we must first decrease the TTL field. If
+ * that reaches zero, we must reply an ICMP control message telling
+ * that the packet's lifetime expired.
+ */
+ iph = skb->h.iph;
+ iph->ttl--;
+ if (iph->ttl <= 0) {
+ DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n"));
+ DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+
+ /* Tell the sender its packet died... */
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
+ return;
+ }
+
+ /* Re-compute the IP header checksum. */
+ ip_send_check(iph);
+
+ /*
+ * OK, the packet is still valid. Fetch its destination address,
+ * and give it to the IP sender for further processing.
+ */
+ rt = rt_route(iph->daddr, NULL);
+ if (rt == NULL) {
+ DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n"));
+
+ /* Tell the sender its packet cannot be delivered... */
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev);
+ return;
+ }
+
+ /*
+ * Gosh. Not only is the packet valid; we even know how to
+ * forward it onto its final destination. Can we say this
+ * is being plain lucky?
+ * If the router told us that there is no GW, use the dest.
+ * IP address itself- we seem to be connected directly...
+ */
+ raddr = rt->rt_gateway;
+ if (raddr != 0) {
+ rt = rt_route(raddr, NULL);
+ if (rt == NULL) {
+ DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n"));
+
+ /* Tell the sender its packet cannot be delivered... */
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
+ return;
+ }
+ if (rt->rt_gateway != 0) raddr = rt->rt_gateway;
+ } else raddr = iph->daddr;
+ dev2 = rt->rt_dev;
+
+ /*
+ * We now allocate a new buffer, and copy the datagram into it.
+ * If the indicated interface is up and running, kick it.
+ */
+ DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n",
+ in_ntoa(raddr), dev2->name, skb->len));
+
+ if (dev2->flags & IFF_UP) {
+ skb2 = kmalloc(sizeof(struct sk_buff) +
+ dev2->hard_header_len + skb->len, GFP_ATOMIC);
+ if (skb2 == NULL) {
+ printk("\nIP: No memory available for IP forward\n");
+ return;
+ }
+ ptr = (unsigned char *)(skb2 + 1);
+ skb2->lock = 0;
+ skb2->sk = NULL;
+ skb2->len = skb->len + dev2->hard_header_len;
+ skb2->mem_addr = skb2;
+ skb2->mem_len = sizeof(struct sk_buff) + skb2->len;
+ skb2->next = NULL;
+ skb2->h.raw = ptr;
+
+ /* Copy the packet data into the new buffer. */
+ skb2->h.raw = ptr;
+ memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
+
+ /* Now build the MAC header. */
+ (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
+
+ dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL);
+ }
+}
+
+
+/* This function receives all incoming IP datagrams. */
+int
+ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct iphdr *iph;
+ unsigned char hash;
+ unsigned char flag = 0;
+ struct inet_protocol *ipprot;
+ static struct options opt; /* since we don't use these yet, and they
+ take up stack space. */
+ int brd;
+
+ iph = skb->h.iph;
+ memset((char *) &opt, 0, sizeof(opt));
+ DPRINTF((DBG_IP, "<<\n"));
+ ip_print(iph);
+
+ /* Is the datagram acceptable? */
+ if (ip_csum(iph) || do_options(iph, &opt) || iph->version != 4) {
+ DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n"));
+ DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr)));
+ DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr)));
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /* Do any IP forwarding required. */
+ if ((brd = chk_addr(iph->daddr)) == 0) {
+ ip_forward(skb, dev);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /*
+ * Deal with fragments: not really...
+ * Fragmentation is definitely a required part of IP (yeah, guys,
+ * I read Linux-Activists.NET too :-), but the current "sk_buff"
+ * allocation stuff doesn't make things simpler. When we're all
+ * done cleaning up the mess, we'll add Ross Biro's "mbuf" stuff
+ * to the code, which will replace the sk_buff stuff completely.
+ * That will (a) make the code even cleaner, (b) allow me to do
+ * the DDI (Device Driver Interface) the way I want to, and (c),
+ * it will allow for easy addition of fragging. Any takers? -FvK
+ */
+ if ((iph->frag_off & 32) || (ntohs(iph->frag_off) & 0x1fff)) {
+ printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
+ printk(" SRC = %s ", in_ntoa(iph->saddr));
+ printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr));
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /* Point into the IP datagram, just past the header. */
+ skb->h.raw += iph->ihl*4;
+ hash = iph->protocol & (MAX_INET_PROTOS -1);
+ for (ipprot = (struct inet_protocol *)inet_protos[hash];
+ ipprot != NULL;
+ ipprot=(struct inet_protocol *)ipprot->next)
+ {
+ struct sk_buff *skb2;
+
+ if (ipprot->protocol != iph->protocol) continue;
+ DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot));
+ print_ipprot(ipprot);
+
+ /*
+ * 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 = kmalloc (skb->mem_len, GFP_ATOMIC);
+ if (skb2 == NULL) continue;
+ memcpy(skb2, skb, skb->mem_len);
+ skb2->mem_addr = skb2;
+ skb2->lock = 0;
+ skb2->h.raw = (void *)(
+ (unsigned long)skb2 +
+ (unsigned long) skb->h.raw -
+ (unsigned long)skb);
+ } else {
+ skb2 = skb;
+ }
+ flag = 1;
+
+ /*
+ * Pass on the datagram to each protocol that wants it,
+ * based on the datagram protocol. We should really
+ * check the protocol handler's return values here...
+ */
+ ipprot->handler(skb2, dev, &opt, iph->daddr,
+ (ntohs(iph->tot_len) - (iph->ihl * 4)),
+ iph->saddr, 0, ipprot);
+
+ }
+
+ /*
+ * All protocols checked.
+ * If this packet was a broadcast, we may *not* reply to it, since that
+ * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+ * ICMP reply messages get queued up for transmission...)
+ */
+ if (!flag) {
+ if (brd != IS_BROADCAST)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ }
+
+ 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(struct sock *sk, struct device *dev,
+ struct sk_buff *skb, int free)
+{
+ struct iphdr *iph;
+ unsigned char *ptr;
+
+ if (sk == NULL) free = 1;
+ if (dev == NULL) {
+ printk("IP: ip_queue_xmit dev = NULL\n");
+ return;
+ }
+ skb->free = free;
+ skb->dev = dev;
+ skb->when = jiffies;
+
+ DPRINTF((DBG_IP, ">>\n"));
+ ptr = (unsigned char *)(skb + 1);
+ ptr += dev->hard_header_len;
+ iph = (struct iphdr *)ptr;
+ iph->tot_len = ntohs(skb->len - dev->hard_header_len);
+ ip_send_check(iph);
+ ip_print(iph);
+ skb->next = NULL;
+
+ /* See if this is the one trashing our queue. Ross? */
+ skb->magic = 1;
+ if (!free) {
+ skb->link3 = NULL;
+ sk->packets_out++;
+ cli();
+ if (sk->send_head == NULL) {
+ sk->send_tail = skb;
+ sk->send_head = skb;
+ } else {
+ /* See if we've got a problem. */
+ if (sk->send_tail == NULL) {
+ extern void sort_send(volatile struct sock *sk);
+
+ printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n");
+ sort_send(sk);
+ } else {
+ sk->send_tail->link3 = skb;
+ sk->send_tail = skb;
+ }
+ }
+ sti();
+ sk->time_wait.len = sk->rtt<<1;
+ sk->timeout = TIME_WRITE;
+ reset_timer ((struct timer *)&sk->time_wait);
+ } else {
+ skb->sk = sk;
+ }
+
+ /* If the indicated interface is up and running, kick it. */
+ if (dev->flags & IFF_UP) {
+ if (sk != NULL) {
+ dev->queue_xmit(skb, dev, sk->priority);
+ } else {
+ dev->queue_xmit(skb, dev, SOPRI_NORMAL);
+ }
+ } else {
+ if (free) kfree_skb(skb, FREE_WRITE);
+ }
+}
+
+
+void
+ip_retransmit(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;
+
+ /*
+ * The rebuild_header function sees if the ARP is done.
+ * If not it sends a new ARP request, and if so it builds
+ * the header.
+ */
+ if (!skb->arp) {
+ if (dev->rebuild_header((struct enet_header *)(skb+1),dev)) {
+ if (!all) break;
+ skb = (struct sk_buff *)skb->link3;
+ continue;
+ }
+ }
+ skb->arp = 1;
+ skb->when = jiffies;
+
+ /* If the interface is (still) up and running, kick it. */
+ if (dev->flags & IFF_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 = (struct sk_buff *)skb->link3;
+ }
+
+ /*
+ * Double the RTT time every time we retransmit.
+ * This will cause exponential back off on how hard we try to
+ * get through again. Once we get through, the rtt will settle
+ * back down reasonably quickly.
+ */
+ sk->rtt *= 2;
+ sk->time_wait.len = sk->rtt;
+ sk->timeout = TIME_WRITE;
+ reset_timer((struct timer *)&sk->time_wait);
+}
diff --git a/net/inet/ip.h b/net/inet/ip.h
new file mode 100644
index 0000000..72c1251
--- /dev/null
+++ b/net/inet/ip.h
@@ -0,0 +1,42 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP module.
+ *
+ * Version: @(#)ip.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_H
+#define _IP_H
+
+
+#include <linux/ip.h>
+
+
+extern void ip_print(struct iphdr *ip);
+extern int ip_ioctl(struct sock *sk, int cmd,
+ unsigned long arg);
+extern void ip_route_check(unsigned long daddr);
+extern int ip_build_header(struct sk_buff *skb,
+ unsigned long saddr,
+ unsigned long daddr,
+ struct device **dev, int type,
+ struct options *opt, int len);
+extern unsigned short ip_compute_csum(unsigned char * buff, int len);
+extern int ip_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern void ip_queue_xmit(struct sock *sk,
+ struct device *dev, struct sk_buff *skb,
+ int free);
+extern void ip_retransmit(struct sock *sk, int all);
+
+#endif /* _IP_H */
diff --git a/net/inet/loopback.c b/net/inet/loopback.c
new file mode 100644
index 0000000..954a65e
--- /dev/null
+++ b/net/inet/loopback.c
@@ -0,0 +1,109 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Pseudo-driver for the loopback interface.
+ *
+ * Version: @(#)loopback.c 1.0.4 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#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 <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+static int
+loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int done;
+
+ DPRINTF((DBG_LOOPB, "loopback_xmit(dev=%X, skb=%X)\n", dev, skb));
+ if (skb == NULL || dev == NULL) return(0);
+
+ cli();
+ if (dev->tbusy != 0) {
+ sti();
+ return(1);
+ }
+ dev->tbusy = 1;
+ sti();
+
+ done = dev_rint((unsigned char *)(skb+1), skb->len, 0, dev);
+ if (skb->free) kfree_skb(skb, FREE_WRITE);
+
+ while (done != 1) {
+ done = dev_rint(NULL, 0, 0, dev);
+ }
+ dev->tbusy = 0;
+
+ return(0);
+}
+
+
+/* Initialize the rest of the LOOPBACK device. */
+int
+loopback_init(struct device *dev)
+{
+ dev->mtu = 2000; /* MTU */
+ dev->tbusy = 0;
+ dev->hard_start_xmit = loopback_xmit;
+ dev->open = NULL;
+#if 1
+ dev->hard_header = eth_header;
+ dev->add_arp = NULL;
+ dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->addr_len = ETH_ALEN; /* 6 */
+ dev->type = ARPHRD_ETHER; /* 0x0001 */
+ dev->type_trans = eth_type_trans;
+ dev->rebuild_header = eth_rebuild_header;
+#else
+ dev->hard_header_length = 0;
+ dev->add_arp = NULL;
+ dev->addr_len = 0;
+ dev->type = 0; /* loopback_type (0) */
+ dev->hard_header = NULL;
+ dev->type_trans = NULL;
+ dev->rebuild_header = NULL;
+#endif
+ dev->queue_xmit = dev_queue_xmit;
+
+ /* New-style flags. */
+ dev->flags = IFF_LOOPBACK;
+ dev->family = AF_INET;
+ dev->pa_addr = in_aton("127.0.0.1");
+ dev->pa_brdaddr = in_aton("127.255.255.255");
+ dev->pa_mask = in_aton("255.0.0.0");
+ dev->pa_alen = sizeof(unsigned long);
+
+ return(0);
+};
diff --git a/net/inet/ne.c b/net/inet/ne.c
new file mode 100644
index 0000000..6a63f77
--- /dev/null
+++ b/net/inet/ne.c
@@ -0,0 +1,393 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992,1993 by Donald Becker. This is alpha test code.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This driver should work with many 8390-based ethernet boards. Currently
+ it support the NE1000, NE2000 (and clones), and some Cabletron products.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static char *version =
+ "ne.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "dev.h"
+#include "8390.h"
+
+/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
+#define port_read(port,buf,nr) \
+__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
+#define port_write(port,buf,nr) \
+__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+#define port_read_b(port,buf,nr) \
+__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
+#define port_write_b(port,buf,nr) \
+__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+#define EN_CMD (dev->base_addr)
+#define NE_BASE (dev->base_addr)
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+int neprobe(int ioaddr, struct device *dev);
+static int neprobe1(int ioaddr, struct device *dev, int verbose);
+
+static void ne_reset_8390(struct device *dev);
+static int ne_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void ne_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compansated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+int neprobe(int ioaddr, struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x280, 0x320, 0x340, 0x360, 0};
+
+ if (ioaddr > 0x100)
+ return neprobe1(ioaddr, dev, 1);
+
+ for (port = &ports[0]; *port; port++)
+ if (inb_p(*port) != 0xff && neprobe1(*port, dev, 0))
+ return dev->base_addr = *port;
+ return 0;
+}
+
+static int neprobe1(int ioaddr, struct device *dev, int verbose)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ int neX000, ctron, dlink;
+
+
+ if ( inb_p(ioaddr) == 0xFF) {
+ if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr);
+ return 0;
+ }
+
+ printk("8390 ethercard probe at %#3x:", ioaddr);
+
+ /* Read the 16 bytes of station address prom, returning 1 for
+ an eight-bit interface and 2 for a 16-bit interface.
+ We must first initialize registers, similar to NS8390_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {char value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */
+ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, EN_CMD},
+ };
+ for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+ }
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+ SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb_p(ioaddr + NE_DATAPORT);
+ if (SA_prom[i] != SA_prom[i+1])
+ wordlength = 1;
+ }
+
+ if (wordlength == 2) {
+ /* We must set the 8390 for word mode, AND RESET IT. */
+ int tmp;
+ outb_p(0x49, ioaddr + EN0_DCFG);
+ tmp = inb_p(NE_BASE + NE_RESET);
+ ei_status.word16 = 1;
+ outb(tmp, NE_BASE + NE_RESET);
+ /* Un-double the SA_prom values. */
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+ } else
+ ei_status.word16 = 0;
+
+#if defined(show_all_SAPROM)
+ /* If your ethercard isn't detected define this to see the SA_PROM. */
+ for(i = 0; i < sizeof(SA_prom); i++)
+ printk(" %2.2x", SA_prom[i]);
+#else
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ dev->dev_addr[i] = SA_prom[i];
+ printk(" %2.2x", SA_prom[i]);
+ }
+#endif
+
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+ dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
+
+ /* Set up the rest of the parameters. */
+ if (neX000 && wordlength == 2) {
+ ei_status.name = "NE2000";
+ ei_status.tx_start_page = NESM_START_PG;
+ ei_status.stop_page = NESM_STOP_PG;
+ } else if (neX000 || dlink) {
+ ei_status.name = neX000 ? "NE1000" : "D-Link";
+ ei_status.tx_start_page = NE1SM_START_PG;
+ ei_status.stop_page = NE1SM_STOP_PG;
+ } else if (ctron) {
+ ei_status.name = "Cabletron";
+ ei_status.tx_start_page = 0x01;
+ ei_status.stop_page = (wordlength == 2) ? 0x40 : 0x20;
+ } else {
+ printk(" not found.\n");
+ return 0;
+ }
+ ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+ dev->base_addr = ioaddr;
+
+ if (dev->irq < 2) {
+ int nic_base = dev->base_addr;
+ autoirq_setup(0);
+ outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
+ outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
+ dev->irq = autoirq_report(0);
+ if (ei_debug > 2)
+ printk(" autoirq is %d", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ { int irqval = irqaction (dev->irq, &ei_sigaction);
+ if (irqval) {
+ printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
+ return 0;
+ }
+ }
+
+ printk("\n%s: %s found, using IRQ %d.\n",
+ dev->name, ei_status.name, dev->irq);
+ if (ei_debug > 1)
+ printk(version);
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ NS8390_init(dev, 0);
+ return dev->base_addr;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+static void
+ne_reset_8390(struct device *dev)
+{
+ int tmp = inb_p(NE_BASE + NE_RESET);
+ int reset_start_time = jiffies;
+
+ if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
+ ei_status.txing = 0;
+
+ outb_p(tmp, NE_BASE + NE_RESET);
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2) {
+ printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ porting to a new ethercard look at the packet driver source for hints.
+ The NEx000 doesn't share it on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static int
+ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ int xfer_count = count;
+ int nic_base = dev->base_addr;
+
+ if (ei_status.dmaing) {
+ if (ei_debug > 0)
+ printk("%s: DMAing conflict in ne_block_input."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return 0;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, EN_CMD);
+ if (ei_status.word16) {
+ port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
+ } else {
+ port_read_b(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ /* This was for the ALPHA version only, but enough people have
+ encountering problems that it is still here. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken! Check the "DMA" address instead. */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk("%s: RX transfer address mismatch,"
+ "%#4.4x (should be) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+ ei_status.dmaing &= ~0x01;
+ return ring_offset + count;
+}
+
+static void
+ne_block_output(struct device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int retries = 0;
+ int nic_base = NE_BASE;
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+ if (ei_status.dmaing) {
+ if (ei_debug > 0)
+ printk("%s: DMAing conflict in ne_block_output."
+ "[DMAstat:%1x][irqlock:%1x]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock);
+ return;
+ }
+ ei_status.dmaing |= 0x02;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
+
+ retry:
+#if defined(rw_bugfix)
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't aways work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, EN_CMD);
+ /* Make certain that the dummy read has occured. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+#endif /* rw_bugfix */
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, EN_CMD);
+ if (ei_status.word16) {
+ port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ port_write_b(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ /* This was for the ALPHA version only, but enough people have
+ encountering problems that it is still here. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken! Check the "DMA" address instead. */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ printk("%s: Packet buffer transfer address mismatch on TX,"
+ "%#4.4x vs. %#4.4x.\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+ ei_status.dmaing &= ~0x02;
+ return;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/packet.c b/net/inet/packet.c
new file mode 100644
index 0000000..822d314
--- /dev/null
+++ b/net/inet/packet.c
@@ -0,0 +1,298 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * PACKET - implements raw packet sockets.
+ *
+ * Version: @(#)packet.c 1.0.6 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include "udp.h"
+#include "raw.h"
+
+
+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)
+{
+ 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;
+ kfree_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;
+ kfree_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(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(VERIFY_WRITE, usin, sizeof(saddr));*/
+ memcpy_fromfs(&saddr, usin, sizeof(saddr));
+ } else
+ return(-EINVAL);
+
+ skb = sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL);
+
+ /* This shouldn't happen, but it could. */
+ if (skb == NULL) {
+ DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n"));
+ return(-EAGAIN);
+ }
+ skb->lock = 0;
+ skb->mem_addr = skb;
+ skb->mem_len = len + sizeof(*skb);
+ skb->sk = sk;
+ skb->free = 1;
+ saddr.sa_data[13] = 0;
+ dev = dev_get(saddr.sa_data);
+ if (dev == NULL) {
+ sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ return(-ENXIO);
+ }
+ /* verify_area(VERIFY_WRITE, from, len);*/
+ memcpy_fromfs (skb+1, from, len);
+ skb->len = len;
+ skb->next = NULL;
+ if (dev->flags & IFF_UP) dev->queue_xmit(skb, dev, sk->priority);
+ else kfree_skb(skb, FREE_WRITE);
+ return(len);
+}
+
+
+static int
+packet_write(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(struct sock *sk, int timeout)
+{
+ sk->inuse = 1;
+ sk->state = TCP_CLOSE;
+ dev_remove_pack((struct packet_type *)sk->pair);
+ kfree_s((void *)sk->pair, sizeof(struct packet_type));
+ sk->pair = NULL;
+ release_sock(sk);
+}
+
+
+static int
+packet_init(struct sock *sk)
+{
+ struct packet_type *p;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 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 = (struct sock *)p;
+
+ return(0);
+}
+
+
+/*
+ * This should be easy, if there is something there
+ * we return it, otherwise we block.
+ */
+int
+packet_recvfrom(struct sock *sk, unsigned char *to, int len,
+ int noblock, unsigned flags, struct sockaddr_in *sin,
+ int *addr_len)
+{
+ 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 (sk->shutdown & RCV_SHUTDOWN) return(0);
+ if (addr_len) {
+ verify_area(VERIFY_WRITE, 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);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+ skb = sk->rqueue;
+
+ if (!(flags & MSG_PEEK)) {
+ if (skb->next == skb) {
+ sk->rqueue = NULL;
+ } else {
+ sk->rqueue = (struct sk_buff *)sk->rqueue ->next;
+ skb->prev->next = skb->next;
+ skb->next->prev = skb->prev;
+ }
+ }
+ copied = min(len, skb->len);
+ verify_area(VERIFY_WRITE, 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(VERIFY_WRITE, saddr, sizeof(*saddr));
+ memcpy_tofs(saddr, &addr, sizeof(*saddr));
+ }
+
+ if (!(flags & MSG_PEEK)) {
+ kfree_skb(skb, FREE_READ);
+ }
+
+ release_sock(sk);
+ return(copied);
+}
+
+
+int
+packet_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags)
+{
+ return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+
+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,
+ NULL,
+ 128,
+ 0,
+ {NULL,},
+ "PACKET"
+};
diff --git a/net/inet/plip.c b/net/inet/plip.c
new file mode 100644
index 0000000..a564d67
--- /dev/null
+++ b/net/inet/plip.c
@@ -0,0 +1,392 @@
+/* plip.c: A parallel port "network" driver for linux. */
+/*
+ Written 1993 by Donald Becker. This is unreleased software.
+
+ This is parallel port packet pusher. It's actually more general
+ than the "IP" in its name suggests -- but 'plip' is just such a
+ great name!
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version =
+ "plip.c:v0.04 Mar 19 1993 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+
+/*
+ Sources:
+ Ideas and protocols came from Russ Nelson's (nelson@crynwr.com)
+ "parallel.asm" parallel port packet driver.
+ The "Crynwr" parallel port standard specifies the following protocol:
+ send header nibble '8'
+ count-low octet
+ count-high octet
+ ... data octets
+ checksum octet
+Each octet is sent as <wait for rx. '1'> <send 0x10+(octet&0x0F)>
+ <wait for rx. '0'> <send 0x00+((octet>>4)&0x0F)>
+The cable used is a de facto standard parallel null cable -- sold as
+a "LapLink" cable by various places. You'll need a 10-conductor cable to
+make one yourself. The wiring is:
+ INIT 16 - 16 SLCTIN 17 - 17
+ GROUND 25 - 25
+ D0->ERROR 2 - 15 15 - 2
+ D1->SLCT 3 - 13 13 - 3
+ D2->PAPOUT 4 - 12 12 - 4
+ D3->ACK 5 - 10 10 - 5
+ D4->BUSY 6 - 11 11 - 6
+ Do not connect the other pins. They are
+ D5,D6,D7 are 7,8,9
+ STROBE is 1, FEED is 14
+ extra grounds are 18,19,20,21,22,23,24
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <errno.h>
+
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef PLIP_DEBUG
+#define PLIP_DEBUG 9
+#endif
+static unsigned int plip_debug = PLIP_DEBUG;
+
+/* The map from IRQ number (as passed to the interrupt handler) to
+ 'struct device'. */
+extern struct device *irq2dev_map[16];
+
+#define PAR_DATA 0
+#define PAR_STATUS 1
+#define PAR_CONTROL 2
+/* Common network statistics -- these will be in *.h someday. */
+struct netstats {
+ int tx_packets;
+ int rx_packets;
+ int tx_errors;
+ int rx_errors;
+ int missed_packets;
+ int soft_tx_errors;
+ int soft_rx_errors;
+ int soft_trx_err_bits;
+};
+static struct netstats *localstats;
+
+/* Index to functions, as function prototypes. */
+extern int plip_probe(int ioaddr, struct device *dev);
+/* Put in the device structure. */
+static int plip_open(struct device *dev);
+static int plip_close(struct device *dev);
+static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
+
+/* Routines used internally. */
+/* Dispatch from interrupts. */
+static void plip_interrupt(int reg_ptr);
+static int plip_write(struct device *dev, unsigned char *buf, int length);
+
+int
+plip_init(struct device *dev)
+{
+ int i;
+
+ /* Alpha testers must have the version number to report bugs. */
+ if (plip_debug > 1) {
+ static int version_shown = 0;
+ if (! version_shown)
+ printk(version), version_shown++;
+ }
+
+ /* We don't actually probe for anything here, although we might
+ someday check to see if there's bi-directional port at
+ dev->base_addr. */
+
+ /* Initialize the device structure. */
+ dev->private = kmalloc(sizeof(struct netstats), GFP_KERNEL);
+ memset(dev->private, 0, sizeof(struct netstats));
+ localstats = (struct netstats*) dev->private;
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+ dev->hard_header = eth_header;
+ dev->add_arp = eth_add_arp;
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->type_trans = eth_type_trans;
+
+ dev->open = &plip_open;
+ dev->stop = &plip_close;
+ dev->hard_start_xmit = &plip_tx_packet;
+
+ /* These are ethernet specific. */
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ for (i = 0; i < dev->addr_len; i++) {
+ dev->broadcast[i]=0xff;
+ dev->dev_addr[i] = i; /* The physical address is 0:1:2:3:4:5! */
+ }
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ printk("%s: using parallel port at %#3x, IRQ %d.\n", dev->name,
+ dev->base_addr, dev->irq);
+
+ return 0;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'config <dev->name>' program is
+ run.
+
+ This routine gets exclusive access to the parallel port by allocating
+ its IRQ line.
+ */
+static int
+plip_open(struct device *dev)
+{
+ if (dev->irq == 0)
+ dev->irq = 7;
+ if (request_irq(dev->irq , &plip_interrupt) != 0) {
+ if (plip_debug > 2)
+ printk("%s: couldn't get the IRQ.\n", dev->name);
+ return EAGAIN;
+ }
+
+ irq2dev_map[dev->irq] = dev;
+ outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
+ dev->tbusy = 0; /* Transmit busy... */
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+/* The inverse routine to plip_open(). */
+static int
+plip_close(struct device *dev)
+{
+ dev->start = 0;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ outb(0x00, dev->base_addr); /* Release the interrupt. */
+ return 0;
+}
+
+static int
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ret_val;
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Pretend we are an ethernet and fill in the header. This could use
+ a simplified routine someday. */
+ if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
+ skb->dev = dev;
+ arp_queue (skb);
+ return 0;
+ }
+
+ dev->trans_start = jiffies;
+ ret_val = plip_write(dev, (void*)(skb+1), skb->len);
+ if (skb->free)
+ kfree_skb (skb, FREE_WRITE);
+ dev->tbusy = 0;
+ mark_bh (INET_BH);
+ return ret_val;
+}
+
+
+static inline int get_byte(struct device *dev)
+{
+ unsigned char val;
+ unsigned char low_nibble;
+ int boguscount = 1500;
+ do {
+ val = inb(dev->base_addr + PAR_STATUS);
+ } while ( ! (val & 0x80) && --boguscount > 0);
+ low_nibble = (val >> 3) & 0x0f;
+ if (plip_debug > 8)
+ printk("%1x", low_nibble);
+ outb(0x10, dev->base_addr + PAR_DATA);
+ do {
+ val = inb(dev->base_addr + PAR_STATUS);
+ } while ((val & 0x80) && --boguscount > 0);
+ if (plip_debug > 8)
+ printk("%1x %s", low_nibble,
+ boguscount <= 0 ? "timeout":"");
+ outb(0x00, dev->base_addr + PAR_DATA);
+ return low_nibble | ((val << 1) & 0xf0);
+}
+
+/* The typical workload of the driver:
+ Handle the parallel port interrupts. */
+static void
+plip_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int boguscount = 1500;
+ unsigned length;
+ int sksize;
+ struct sk_buff *skb;
+
+ if (dev == NULL) {
+ printk ("plip_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+ outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable the rx interrupt. */
+ sti(); /* Allow other interrupts. */
+
+ if (plip_debug >= 4)
+ printk("%s: interrupt.\n", dev->name);
+
+ localstats = (struct netstats*) dev->private;
+
+ /* Receive the packet here. */
+ if (inb(dev->base_addr + PAR_STATUS) != 0xc7) {
+ localstats->rx_errors++; /* No interrupt! */
+ if (plip_debug > 4)
+ printk("%s: No interrupt (status=%#02x)!\n",
+ dev->name, inb(dev->base_addr + PAR_STATUS));
+ return;
+ }
+ outb(1, dev->base_addr + PAR_DATA); /* Ack: 'Ready' */
+ length = get_byte(dev);
+ length |= (get_byte(dev) << 8);
+ if (length > dev->mtu) {
+ printk("%s: Bogus packet size %d, dropping it.\n", dev->name, length);
+ return;
+ }
+ boguscount = length << 5;
+ sksize = sizeof(struct sk_buff) + length;
+ skb = kmalloc(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ if (plip_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize);
+ localstats->rx_errors++;
+ return;
+ }
+ skb->lock = 0;
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ {
+ /* 'skb+1' points to the start of sk_buff data area. */
+ unsigned char *buf = (void*) (skb+1);
+ int checksum = 0;
+
+ while (length--) {
+ unsigned char new_byte = get_byte(dev);
+ checksum += new_byte, *buf++ = new_byte;
+ }
+ if (checksum != get_byte(dev))
+ localstats->soft_rx_errors++;
+ else if(dev_rint((void *)skb, length, IN_SKBUFF, dev)) {
+ printk("%s: receive buffers full.\n", dev->name);
+ localstats->rx_errors++;
+ return;
+ }
+ }
+ /* Wait for the remote end to reset. */
+ while (inb(dev->base_addr + PAR_STATUS) != 0x87)
+ if (boguscount-- <= 0 )
+ break;
+ outb(0x00, dev->base_addr + PAR_DATA);
+ outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
+ localstats->rx_packets++;
+ return;
+}
+
+
+static inline int send_byte(struct device *dev, unsigned char val)
+{
+ int boguscount = 1500;
+ if (plip_debug > 8)
+ printk("send(%02x) ", val);
+ outb(0x10 | val, dev->base_addr);
+ while(inb(dev->base_addr+PAR_STATUS) & 0x80)
+ if (--boguscount <= 0) break;
+ outb(val >> 4, dev->base_addr);
+ while((inb(dev->base_addr+PAR_STATUS) & 0x80) == 0)
+ if (--boguscount <= 0) break;
+ if (plip_debug > 4 && boguscount <= 0)
+ printk("timeout");
+}
+static int
+plip_write(struct device *dev, unsigned char *buf, int length)
+{
+ int timeout = 1000; /* Approx 1 ms. */
+ char checksum = 0;
+ int i;
+ if (plip_debug > 5)
+ printk("%s: plip_write(%d) %02x %02x %02x %02x %02x...",
+ dev->name, length, buf[0], buf[1], buf[2], buf[3], buf[4]);
+ if (length > dev->mtu) {
+ printk("%s: packet too big, %d.\n", dev->name, length);
+ return 1;
+ }
+ /* This starts the packet protocol by triggering a remote IRQ. */
+ outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable my rx interrupt. */
+ outb(0x08, dev->base_addr + PAR_DATA); /* Trigger remote rx interrupt. */
+ while((inb(dev->base_addr+PAR_STATUS) & 0x08) == 0 )
+ if (--timeout < 0) {
+ outb(0x00, dev->base_addr);
+ localstats->tx_errors++;
+ if (plip_debug > 3)
+ printk("%s: Connect failed during send_packet() (length=%d).\n",
+ dev->name, length);
+ /* We failed to send the packet. To emulate the ethernet we
+ should pretent the send worked fine, but we don't right now. */
+ return 1; /* Failed to send the packet! */
+ }
+ send_byte(dev, length); send_byte(dev, length >> 8);
+ for (i = 0; i < length; i++)
+ checksum += buf[i], send_byte(dev, buf[i]);
+ send_byte(dev, checksum);
+ outb(0x00, dev->base_addr);
+ outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
+ localstats->tx_packets++;
+ if (plip_debug > 5)
+ printk("plip_write(%d) done.\n", length);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c plip.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/inet/proc.c b/net/inet/proc.c
new file mode 100644
index 0000000..5d1dc3d
--- /dev/null
+++ b/net/inet/proc.c
@@ -0,0 +1,124 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * This file implements the various access functions for the
+ * PROC file system. It is mainly used for debugging and
+ * statistics.
+ *
+ * Version: @(#)proc.c 1.0.5 05/27/93
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
+ * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <linux/autoconf.h>
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/un.h>
+#include <linux/in.h>
+#include <sys/param.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "udp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "raw.h"
+
+extern struct timer *timer_base;
+
+/*
+ * Get__netinfo returns the length of that string.
+ *
+ * KNOWN BUGS
+ * As in get_unix_netinfo, the buffer might be too small. If this
+ * happens, get__netinfo returns only part of the available infos.
+ */
+static int
+get__netinfo(struct proto *pro, char *buffer)
+{
+ struct sock **s_array;
+ struct sock *sp;
+ char *pos=buffer;
+ int i;
+ unsigned long dest, src;
+ unsigned short destp, srcp;
+ struct timer *tp;
+
+ s_array = pro->sock_array;
+ pos+=sprintf(pos, "sl local_address rem_address st tx_queue rx_queue tr tm->when\n");
+ for(i = 0; i < SOCK_ARRAY_SIZE; i++) {
+ sp = s_array[i];
+ while(sp != NULL) {
+ dest = sp->daddr;
+ src = sp->saddr;
+ destp = sp->dummy_th.dest;
+ srcp = sp->dummy_th.source;
+
+ /* Since we are Little Endian we need to swap the bytes :-( */
+ destp = ntohs(destp);
+ srcp = ntohs(srcp);
+
+ pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X",
+ i, src, srcp, dest, destp, sp->state,
+ sp->send_seq-sp->rcv_ack_seq, sp->acked_seq-sp->copied_seq,
+ sp->time_wait.running, sp->time_wait.when-jiffies, sp->retransmits);
+
+ cli();
+ tp = timer_base;
+ while (tp) {
+ if (tp == &(sp->time_wait)) {
+ pos+=sprintf(pos, " *");
+ }
+ tp = tp->next;
+ }
+ sti();
+ pos+=sprintf(pos, "\n");
+
+ /* Is place in buffer too rare? then abort. */
+ if (pos > buffer+PAGE_SIZE-80) {
+ printk("oops, too many %s sockets for netinfo.\n",
+ pro->name);
+ return(strlen(buffer));
+ }
+
+ /*
+ * All sockets with (port mod SOCK_ARRAY_SIZE) = i
+ * are kept in sock_array[i], so we must follow the
+ * 'next' link to get them all.
+ */
+ sp = sp->next;
+ }
+ }
+ return(strlen(buffer));
+}
+
+
+int tcp_get_info(char *buffer)
+{
+ return get__netinfo(&tcp_prot, buffer);
+}
+
+
+int udp_get_info(char *buffer)
+{
+ return get__netinfo(&udp_prot, buffer);
+}
+
+
+int raw_get_info(char *buffer)
+{
+ return get__netinfo(&raw_prot, buffer);
+}
diff --git a/net/inet/protocol.c b/net/inet/protocol.c
new file mode 100644
index 0000000..fa03491
--- /dev/null
+++ b/net/inet/protocol.c
@@ -0,0 +1,150 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * INET protocol dispatch tables.
+ *
+ * Version: @(#)protocol.c 1.0.5 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#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 <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "icmp.h"
+#include "udp.h"
+
+
+static struct inet_protocol tcp_protocol = {
+ tcp_rcv, /* TCP handler */
+ tcp_err, /* TCP error control */
+ NULL, /* next */
+ IPPROTO_TCP, /* protocol ID */
+ 0, /* copy */
+ NULL /* data */
+};
+
+
+static struct inet_protocol udp_protocol = {
+ udp_rcv, /* UDP handler */
+ NULL, /* UDP error control */
+ &tcp_protocol, /* next */
+ IPPROTO_UDP, /* protocol ID */
+ 0, /* copy */
+ NULL /* data */
+};
+
+
+static struct inet_protocol icmp_protocol = {
+ icmp_rcv, /* ICMP handler */
+ NULL, /* ICMP error control */
+ &udp_protocol, /* next */
+ IPPROTO_ICMP, /* protocol ID */
+ 0, /* copy */
+ NULL /* data */
+};
+
+
+struct inet_protocol *inet_protocol_base = &icmp_protocol;
+struct inet_protocol *inet_protos[MAX_INET_PROTOS] = {
+ NULL
+};
+
+
+struct inet_protocol *
+inet_get_protocol(unsigned char prot)
+{
+ unsigned char hash;
+ struct inet_protocol *p;
+
+ DPRINTF((DBG_PROTO, "get_protocol (%d)\n ", prot));
+ hash = prot & (MAX_INET_PROTOS - 1);
+ for (p = inet_protos[hash] ; p != NULL; p=p->next) {
+ DPRINTF((DBG_PROTO, "trying protocol %d\n", p->protocol));
+ if (p->protocol == prot) return((struct inet_protocol *) p);
+ }
+ return(NULL);
+}
+
+
+void
+inet_add_protocol(struct inet_protocol *prot)
+{
+ unsigned char hash;
+ struct inet_protocol *p2;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ prot ->next = inet_protos[hash];
+ inet_protos[hash] = prot;
+ prot->copy = 0;
+
+ /* Set the copy bit if we need to. */
+ p2 = (struct inet_protocol *) prot->next;
+ while(p2 != NULL) {
+ if (p2->protocol == prot->protocol) {
+ prot->copy = 1;
+ break;
+ }
+ p2 = (struct inet_protocol *) prot->next;
+ }
+}
+
+
+int
+inet_del_protocol(struct inet_protocol *prot)
+{
+ struct inet_protocol *p;
+ struct inet_protocol *lp = NULL;
+ unsigned char hash;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ if (prot == inet_protos[hash]) {
+ inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next;
+ return(0);
+ }
+
+ p = (struct inet_protocol *) inet_protos[hash];
+ while(p != NULL) {
+ /*
+ * 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;
+ }
+
+ p = (struct inet_protocol *) p->next;
+ }
+ return(-1);
+}
diff --git a/net/inet/protocol.h b/net/inet/protocol.h
new file mode 100644
index 0000000..b5330b8
--- /dev/null
+++ b/net/inet/protocol.h
@@ -0,0 +1,49 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the protocol dispatcher.
+ *
+ * Version: @(#)protocol.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _PROTOCOL_H
+#define _PROTOCOL_H
+
+
+#define MAX_INET_PROTOS 32 /* Must be a power of 2 */
+
+
+/* This is used to register protocols. */
+struct inet_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 inet_protocol *protocol);
+ void (*err_handler)(int err, unsigned char *buff,
+ unsigned long daddr,
+ unsigned long saddr,
+ struct inet_protocol *protocol);
+ struct inet_protocol *next;
+ unsigned char protocol;
+ unsigned char copy:1;
+ void *data;
+};
+
+
+extern struct inet_protocol *inet_protocol_base;
+extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+
+
+extern void inet_add_protocol(struct inet_protocol *prot);
+extern int inet_del_protocol(struct inet_protocol *prot);
+
+
+#endif /* _PROTOCOL_H */
diff --git a/net/inet/raw.c b/net/inet/raw.c
new file mode 100644
index 0000000..c8d1324
--- /dev/null
+++ b/net/inet/raw.c
@@ -0,0 +1,407 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * RAW - implementation of IP "raw" sockets.
+ *
+ * Version: @(#)raw.c 1.0.4 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "icmp.h"
+#include "udp.h"
+
+
+static unsigned long
+min(unsigned long a, unsigned long b)
+{
+ if (a < b) return(a);
+ return(b);
+}
+
+
+/* raw_err gets called by the icmp module. */
+void
+raw_err (int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol)
+{
+ struct sock *sk;
+
+ DPRINTF((DBG_RAW, "raw_err(err=%d, hdr=%X, daddr=%X, saddr=%X, protocl=%X)\n",
+ err, header, daddr, saddr, protocol));
+
+ if (protocol == NULL) return;
+ sk = protocol->data;
+ if (sk == NULL) return;
+
+ /* This is meaningless in raw sockets. */
+ 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;
+
+ /* None of them are fatal for raw sockets. */
+#if 0
+ if (icmp_err_convert[err & 0xff].fatal) {
+ sk->prot->close(sk, 0);
+ }
+#endif
+ return;
+}
+
+
+/*
+ * 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 inet_protocol *protocol)
+{
+ struct sock *sk;
+
+ DPRINTF((DBG_RAW, "raw_rcv(skb=%X, dev=%X, opt=%X, daddr=%X,\n"
+ " len=%d, saddr=%X, redo=%d, protocol=%X)\n",
+ skb, dev, opt, daddr, len, saddr, redo, protocol));
+
+ if (skb == NULL) return(0);
+ if (protocol == NULL) {
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+ sk = protocol->data;
+ if (sk == NULL) {
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+
+ /* Now we need to copy this into memory. */
+ skb->sk = sk;
+ skb->len = len;
+ skb->dev = dev;
+ skb->saddr = daddr;
+ skb->daddr = saddr;
+
+ if (!redo) {
+ /* Now see if we are in use. */
+ cli();
+ if (sk->inuse) {
+ DPRINTF((DBG_RAW, "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();
+ }
+
+ /* Charge it too the socket. */
+ if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX) {
+ skb->sk = NULL;
+ kfree_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
+raw_sendto(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;
+
+ DPRINTF((DBG_RAW, "raw_sendto(sk=%X, from=%X, len=%d, noblock=%d, flags=%X,\n"
+ " usin=%X, addr_len = %d)\n", sk, from, len, noblock,
+ flags, usin, addr_len));
+
+ /* 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 (VERIFY_WRITE, 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;
+
+ sk->inuse = 1;
+ skb = NULL;
+ while (skb == NULL) {
+ skb = sk->prot->wmalloc(sk, len+sizeof(*skb) + sk->prot->max_header,
+ 0, GFP_KERNEL);
+ /* This shouldn't happen, but it could. */
+ /* FIXME: need to change this to sleep. */
+ if (skb == NULL) {
+ int tmp;
+
+ DPRINTF((DBG_RAW, "raw_sendto: write buffer full?\n"));
+ if (noblock) return(-EAGAIN);
+ tmp = sk->wmem_alloc;
+ release_sock(sk);
+ cli();
+ if (tmp <= sk->wmem_alloc) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+ }
+ skb->lock = 0;
+ 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) {
+ DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n"));
+ sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ release_sock(sk);
+ return(tmp);
+ }
+
+ /* verify_area(VERIFY_WRITE, from, len);*/
+ memcpy_fromfs ((unsigned char *)(skb+1)+tmp, from, len);
+ skb->len = tmp + len;
+ sk->prot->queue_xmit(sk, dev, skb, 1);
+ release_sock(sk);
+ return(len);
+}
+
+
+static int
+raw_write(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(struct sock *sk, int timeout)
+{
+ sk->inuse = 1;
+ sk->state = TCP_CLOSE;
+
+ DPRINTF((DBG_RAW, "raw_close: deleting protocol %d\n",
+ ((struct inet_protocol *)sk->pair)->protocol));
+
+ if (inet_del_protocol((struct inet_protocol *)sk->pair) < 0)
+ DPRINTF((DBG_RAW, "raw_close: del_protocol failed.\n"));
+ kfree_s((void *)sk->pair, sizeof (struct inet_protocol));
+ sk->pair = NULL;
+ release_sock(sk);
+}
+
+
+static int
+raw_init(struct sock *sk)
+{
+ struct inet_protocol *p;
+
+ p = kmalloc(sizeof (*p), GFP_KERNEL);
+ if (p == NULL) return(-ENOMEM);
+
+ p->handler = raw_rcv;
+ p->protocol = sk->protocol;
+ p->data = (void *)sk;
+ p->err_handler = raw_err;
+ inet_add_protocol(p);
+
+ /* We need to remember this somewhere. */
+ sk->pair = (struct sock *)p;
+
+ DPRINTF((DBG_RAW, "raw init added protocol %d\n", sk->protocol));
+
+ return(0);
+}
+
+
+/*
+ * This should be easy, if there is something there
+ * we return it, otherwise we block.
+ */
+int
+raw_recvfrom(struct sock *sk, unsigned char *to, int len,
+ int noblock, unsigned flags, struct sockaddr_in *sin,
+ int *addr_len)
+{
+ int copied=0;
+ struct sk_buff *skb;
+
+ DPRINTF((DBG_RAW, "raw_recvfrom (sk=%X, to=%X, len=%d, noblock=%d, flags=%X,\n"
+ " sin=%X, addr_len=%X)\n",
+ sk, to, len, noblock, flags, sin, addr_len));
+
+ if (len == 0) return(0);
+ if (len < 0) return(-EINVAL);
+
+ if (sk->shutdown & RCV_SHUTDOWN) return(0);
+ if (addr_len) {
+ verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+ put_fs_long(sizeof(*sin), addr_len);
+ }
+ sk->inuse = 1;
+ while (sk->rqueue == NULL) {
+ if (noblock) {
+ release_sock(sk);
+ if (copied) return(copied);
+ return(-EAGAIN);
+ }
+ release_sock(sk);
+ cli();
+ if (sk->rqueue == NULL) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+ skb = sk->rqueue;
+
+ if (!(flags & MSG_PEEK)) {
+ if (skb->next == skb) {
+ sk->rqueue = NULL;
+ } else {
+ sk->rqueue = (struct sk_buff *)sk->rqueue ->next;
+ skb->prev->next = skb->next;
+ skb->next->prev = skb->prev;
+ }
+ }
+ copied = min(len, skb->len);
+ verify_area(VERIFY_WRITE, 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(VERIFY_WRITE, sin, sizeof(*sin));
+ memcpy_tofs(sin, &addr, sizeof(*sin));
+ }
+
+ if (!(flags & MSG_PEEK)) {
+ kfree_skb(skb, FREE_READ);
+ }
+ release_sock(sk);
+ return (copied);
+}
+
+
+int
+raw_read (struct sock *sk, unsigned char *buff, int len, int noblock,
+ unsigned flags)
+{
+ return(raw_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
+}
+
+
+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,
+ NULL,
+ 128,
+ 0,
+ {NULL,},
+ "RAW"
+};
diff --git a/net/inet/raw.h b/net/inet/raw.h
new file mode 100644
index 0000000..80cb4b4
--- /dev/null
+++ b/net/inet/raw.h
@@ -0,0 +1,36 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the RAW-IP module.
+ *
+ * Version: @(#)raw.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _RAW_H
+#define _RAW_H
+
+
+extern struct proto raw_prot;
+
+
+extern void raw_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern 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 inet_protocol *protocol);
+extern int raw_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int raw_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+
+#endif /* _RAW_H */
diff --git a/net/inet/route.c b/net/inet/route.c
new file mode 100644
index 0000000..cfdca28
--- /dev/null
+++ b/net/inet/route.c
@@ -0,0 +1,390 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * ROUTE - implementation of the IP router.
+ *
+ * Version: @(#)route.c 1.0.14 05/31/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#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 <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+#include "icmp.h"
+
+
+static struct rtable *rt_base = NULL;
+
+
+/* Dump the contents of a routing table entry. */
+static void
+rt_print(struct rtable *rt)
+{
+ if (rt == NULL || inet_debug != DBG_RT) return;
+
+ printk("RT: %06lx NXT=%06lx FLAGS=0x%02lx\n",
+ (long) rt, (long) rt->rt_next, rt->rt_flags);
+ printk(" TARGET=%s ", in_ntoa(rt->rt_dst));
+ printk("GW=%s ", in_ntoa(rt->rt_gateway));
+ printk(" DEV=%s USE=%ld REF=%ld\n",
+ (rt->rt_dev == NULL) ? "NONE" : rt->rt_dev->name,
+ rt->rt_use, rt->rt_refcnt);
+}
+
+
+/* Remove a routing table entry. */
+static void
+rt_del(unsigned long dst)
+{
+ struct rtable *r, *x, *p;
+
+ DPRINTF((DBG_RT, "RT: flushing for dst %s\n", in_ntoa(dst)));
+ if ((r = rt_base) == NULL) return;
+ p = NULL;
+ while(r != NULL) {
+ if (r->rt_dst == dst) {
+ if (p == NULL) rt_base = r->rt_next;
+ else p->rt_next = r->rt_next;
+ x = r->rt_next;
+ kfree_s(r, sizeof(struct rtable));
+ r = x;
+ } else {
+ p = r;
+ r = r->rt_next;
+ }
+ }
+}
+
+
+/* Remove all routing table entries for a device. */
+void
+rt_flush(struct device *dev)
+{
+ struct rtable *r, *x, *p;
+
+ DPRINTF((DBG_RT, "RT: flushing for dev 0x%08lx (%s)\n", (long)dev, dev->name));
+ if ((r = rt_base) == NULL) return;
+ p = NULL;
+ while(r != NULL) {
+ if (r->rt_dev == dev) {
+ if (p == NULL) rt_base = r->rt_next;
+ else p->rt_next = r->rt_next;
+ x = r->rt_next;
+ kfree_s(r, sizeof(struct rtable));
+ r = x;
+ } else {
+ p = r;
+ r = r->rt_next;
+ }
+ }
+}
+
+
+void
+rt_add(short flags, unsigned long dst, unsigned long gw, struct device *dev)
+{
+ struct rtable *r, *r1;
+ struct rtable *rt;
+ int mask;
+
+ /* Allocate an entry. */
+ rt = kmalloc(sizeof(struct rtable), GFP_ATOMIC);
+ if (rt == NULL) {
+ DPRINTF((DBG_RT, "RT: no memory for new route!\n"));
+ return;
+ }
+
+ /* Fill in the fields. */
+ memset(rt, 0, sizeof(struct rtable));
+ rt->rt_flags = (flags | RTF_UP);
+ if (gw != 0) rt->rt_flags |= RTF_GATEWAY;
+ rt->rt_dev = dev;
+ rt->rt_gateway = gw;
+
+ /*
+ * If this is coming from an ICMP redirect message, truncate
+ * the TARGET if we are creating an entry for a NETWORK. Use
+ * an Internet class C network mask. Yuck :-(
+ */
+ if (flags & RTF_DYNAMIC) {
+ if (flags & RTF_HOST) rt->rt_dst = dst;
+ else rt->rt_dst = (dst & htonl(IN_CLASSC_NET));
+ } else rt->rt_dst = dst;
+
+ rt_print(rt);
+
+ if (rt_base == NULL) {
+ rt->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 & dst) {
+ mask = mask << 8;
+ break;
+ }
+ }
+ DPRINTF((DBG_RT, "RT: mask = %X\n", mask));
+ r1 = rt_base;
+
+ /* See if we are getting a duplicate. */
+ for (r = rt_base; r != NULL; r = r->rt_next) {
+ if (r->rt_dst == dst) {
+ if (r == rt_base) {
+ rt->rt_next = r->rt_next;
+ rt_base = rt;
+ } else {
+ rt->rt_next = r->rt_next;
+ r1->rt_next = rt;
+ }
+ kfree_s(r, sizeof(struct rtable));
+ return;
+ }
+
+ if (! (r->rt_dst & mask)) {
+ DPRINTF((DBG_RT, "RT: adding before r=%X\n", r));
+ rt_print(r);
+ if (r == rt_base) {
+ rt->rt_next = rt_base;
+ rt_base = rt;
+ return;
+ }
+ rt->rt_next = r;
+ r1->rt_next = rt;
+ return;
+ }
+ r1 = r;
+ }
+ DPRINTF((DBG_RT, "RT: adding after r1=%X\n", r1));
+ rt_print(r1);
+
+ /* Goes at the end. */
+ rt->rt_next = NULL;
+ r1->rt_next = rt;
+}
+
+
+static int
+rt_new(struct rtentry *r)
+{
+ struct device *dev;
+ struct rtable *rt;
+
+ if ((r->rt_dst.sa_family != AF_INET) ||
+ (r->rt_gateway.sa_family != AF_INET)) {
+ DPRINTF((DBG_RT, "RT: We only know about AF_INET !\n"));
+ return(-EAFNOSUPPORT);
+ }
+
+ /*
+ * I admit that the following bits of code were "inspired" by
+ * the Berkeley UNIX system source code. I could think of no
+ * other way to find out how to make it compatible with it (I
+ * want this to be compatible to get "routed" up and running).
+ * -FvK
+ */
+
+ /* If we have a 'gateway' route here, check the correct address. */
+ if (r->rt_flags & RTF_GATEWAY) {
+ dev = dev_check(0,
+ ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr);
+ if (dev == NULL) {
+ dev = dev_check(1,
+ ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr);
+ }
+
+ /*
+ * Aii. We are using an indirect route. Check the already
+ * existing routing table for an entry.
+ */
+ if (dev == NULL) {
+ for (rt = rt_base; rt != NULL; rt = rt->rt_next) {
+ if ((rt->rt_dev->flags & IFF_UP) == 0) continue;
+ if (ip_addr_match(rt->rt_dst,
+ ((struct sockaddr_in *)
+ &r->rt_gateway)->sin_addr.s_addr)) {
+ dev = rt->rt_dev;
+ break;
+ }
+ if (ip_addr_match(rt->rt_dev->pa_brdaddr,
+ ((struct sockaddr_in *)
+ &r->rt_gateway)->sin_addr.s_addr)) {
+ dev = rt->rt_dev;
+ break;
+ }
+ }
+ }
+ } else {
+ dev = dev_check(0,
+ ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr);
+ if (dev == NULL) {
+ dev = dev_check(1,
+ ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr);
+ }
+
+ /*
+ * Aii. We are using an indirect route. Check the already
+ * existing routing table for an entry.
+ */
+ if (dev == NULL) {
+ for (rt = rt_base; rt != NULL; rt = rt->rt_next) {
+ if ((rt->rt_dev->flags & IFF_UP) == 0) continue;
+ if (ip_addr_match(rt->rt_dst,
+ ((struct sockaddr_in *)
+ &r->rt_dst)->sin_addr.s_addr)) {
+ dev = rt->rt_dev;
+ break;
+ }
+ if (ip_addr_match(rt->rt_dev->pa_brdaddr,
+ ((struct sockaddr_in *)
+ &r->rt_dst)->sin_addr.s_addr)) {
+ dev = rt->rt_dev;
+ break;
+ }
+ }
+ }
+ }
+
+ DPRINTF((DBG_RT, "RT: dev for %s gw ",
+ in_ntoa((*(struct sockaddr_in *)&r->rt_dst).sin_addr.s_addr)));
+ DPRINTF((DBG_RT, "%s (0x%04X) is 0x%X (%s)\n",
+ in_ntoa((*(struct sockaddr_in *)&r->rt_gateway).sin_addr.s_addr),
+ r->rt_flags, dev, (dev == NULL) ? "NONE" : dev->name));
+
+ if (dev == NULL) return(-ENETUNREACH);
+
+ rt_add(r->rt_flags, (*(struct sockaddr_in *) &r->rt_dst).sin_addr.s_addr,
+ (*(struct sockaddr_in *) &r->rt_gateway).sin_addr.s_addr, dev);
+
+ return(0);
+}
+
+
+static int
+rt_kill(struct rtentry *r)
+{
+ struct sockaddr_in *trg;
+
+ trg = (struct sockaddr_in *) &r->rt_dst;
+ rt_del(trg->sin_addr.s_addr);
+
+ return(0);
+}
+
+
+/* Called from the PROCfs module. */
+int
+rt_get_info(char *buffer)
+{
+ struct rtable *r;
+ char *pos;
+
+ pos = buffer;
+
+ pos += sprintf(pos, "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\n");
+
+ for (r = rt_base; r != NULL; r = r->rt_next) {
+ pos += sprintf(pos, "%s\t%08X\t%08X\t%02X\t%d\t%d\n",
+ r->rt_dev->name, r->rt_dst, r->rt_gateway,
+ r->rt_flags, r->rt_refcnt, r->rt_use);
+ }
+ return(pos - buffer);
+}
+
+
+struct rtable *
+rt_route(unsigned long daddr, struct options *opt)
+{
+ struct rtable *rt;
+
+ /*
+ * This is a hack, I think. -FvK
+ */
+ if (chk_addr(daddr) == IS_MYADDR) daddr = my_addr();
+
+ /*
+ * Loop over the IP routing table to find a route suitable
+ * for this packet. Note that we really should have a look
+ * at the IP options to see if we have been given a hint as
+ * to what kind of path we should use... -FvK
+ */
+ for (rt = rt_base; rt != NULL; rt = rt->rt_next) {
+ DPRINTF((DBG_RT, "RT: %s via ", in_ntoa(daddr)));
+ if (ip_addr_match(rt->rt_dst, daddr)) {
+ DPRINTF((DBG_RT, "%s (%s)\n",
+ rt->rt_dev->name, in_ntoa(rt->rt_gateway)));
+ rt->rt_use++;
+ return(rt);
+ }
+ if (ip_addr_match(rt->rt_dev->pa_brdaddr, daddr)) {
+ DPRINTF((DBG_RT, "%s (BCAST %s)\n",
+ rt->rt_dev->name, in_ntoa(rt->rt_dev->pa_brdaddr)));
+ rt->rt_use++;
+ return(rt);
+ }
+ }
+
+ DPRINTF((DBG_RT, "NONE\n"));
+ return(NULL);
+};
+
+
+int
+rt_ioctl(unsigned int cmd, void *arg)
+{
+ struct rtentry rt;
+ int ret;
+
+ switch(cmd) {
+ case DDIOCSDBG:
+ ret = dbg_ioctl(arg, DBG_RT);
+ break;
+ case SIOCADDRT:
+ if (!suser()) return(-EPERM);
+ verify_area(VERIFY_WRITE, arg, sizeof(struct rtentry));
+ memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
+ ret = rt_new(&rt);
+ break;
+ case SIOCDELRT:
+ if (!suser()) return(-EPERM);
+ verify_area(VERIFY_WRITE, arg, sizeof(struct rtentry));
+ memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
+ ret = rt_kill(&rt);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return(ret);
+}
diff --git a/net/inet/route.h b/net/inet/route.h
new file mode 100644
index 0000000..38536d9
--- /dev/null
+++ b/net/inet/route.h
@@ -0,0 +1,45 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP router.
+ *
+ * Version: @(#)route.h 1.0.4 05/27/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ROUTE_H
+#define _ROUTE_H
+
+
+#include <linux/route.h>
+
+
+/* This is an entry in the IP routing table. */
+struct rtable {
+ struct rtable *rt_next;
+ unsigned long rt_dst;
+ unsigned long rt_gateway;
+ u_char rt_flags;
+ u_char rt_metric;
+ short rt_refcnt;
+ u_long rt_use;
+ struct device *rt_dev;
+};
+
+
+extern void rt_flush(struct device *dev);
+extern void rt_add(short flags, unsigned long addr,
+ unsigned long gw, struct device *dev);
+extern struct rtable *rt_route(unsigned long daddr, struct options *opt);
+extern int rt_get_info(char * buffer);
+extern int rt_ioctl(unsigned int cmd, void *arg);
+
+#endif /* _ROUTE_H */
diff --git a/net/inet/skbuff.h b/net/inet/skbuff.h
new file mode 100644
index 0000000..13847d4
--- /dev/null
+++ b/net/inet/skbuff.h
@@ -0,0 +1,67 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the 'struct sk_buff' memory handlers.
+ *
+ * Version: @(#)skbuff.h 1.0.4 05/20/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _SKBUFF_H
+#define _SKBUFF_H
+
+
+#define FREE_READ 1
+#define FREE_WRITE 0
+
+
+struct sk_buff {
+ struct sk_buff *next;
+ struct sk_buff *prev;
+ struct sk_buff *link3;
+ struct sock *sk;
+ volatile unsigned long when; /* used to compute rtt's */
+ struct device *dev;
+ void *mem_addr;
+ union {
+ struct tcphdr *th;
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ struct arphdr *arp;
+ unsigned char *raw;
+ unsigned long seq;
+ } h;
+ unsigned long mem_len;
+ unsigned long len;
+ unsigned long saddr;
+ unsigned long daddr;
+ int magic;
+ volatile char acked,
+ used,
+ free,
+ arp,
+ urg_used,
+ lock;
+ unsigned char tries;
+};
+
+#define SK_WMEM_MAX 8192
+#define SK_RMEM_MAX 32767
+
+
+extern void print_skb(struct sk_buff *);
+extern void kfree_skb(struct sk_buff *skb, int rw);
+extern void lock_skb(struct sk_buff *skb);
+extern void unlock_skb(struct sk_buff *skb, int rw);
+
+#endif /* _SKBUFF_H */
diff --git a/net/inet/slip.c b/net/inet/slip.c
new file mode 100644
index 0000000..e86adbc
--- /dev/null
+++ b/net/inet/slip.c
@@ -0,0 +1,661 @@
+/*
+ * slip.c This module implements the SLIP protocol for kernel-based
+ * devices like TTY. It interfaces between a raw TTY, and the
+ * kernel's INET protocol layers (via DDI).
+ *
+ * Version: @(#)slip.c 0.7.6 05/25/93
+ *
+ * Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#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 <linux/termios.h>
+#include <linux/sockios.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/in.h>
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+#include "slip.h"
+
+
+#define SLIP_VERSION "0.7.5"
+
+
+/* Define some IP layer stuff. Not all systems have it. */
+#ifdef SL_DUMP
+# define IP_VERSION 4 /* version# of our IP software */
+# define IPF_F_OFFSET 0x1fff /* Offset field */
+# define IPF_DF 0x4000 /* Don't fragment flag */
+# define IPF_MF 0x2000 /* More Fragments flag */
+# define IP_OF_COPIED 0x80 /* Copied-on-fragmentation flag */
+# define IP_OF_CLASS 0x60 /* Option class */
+# define IP_OF_NUMBER 0x1f /* Option number */
+#endif
+
+
+static struct slip sl_ctrl[SL_NRUNIT];
+static struct tty_ldisc sl_ldisc;
+static int already = 0;
+
+
+/* Dump the contents of an IP datagram. */
+static void
+ip_dump(unsigned char *ptr, int len)
+{
+#ifdef SL_DUMP
+ struct iphdr *ip;
+ int dlen, doff;
+
+ if (inet_debug != DBG_SLIP) return;
+
+ ip = (struct iphdr *) ptr;
+ dlen = ntohs(ip->tot_len);
+ doff = ((ntohs(ip->frag_off) & IPF_F_OFFSET) << 3);
+
+ printk("\r*****\n");
+ printk("SLIP: %s->", in_ntoa(ip->saddr));
+ printk("%s\n", in_ntoa(ip->daddr));
+ printk(" len %u ihl %u ver %u ttl %u prot %u",
+ dlen, ip->ihl, ip->version, ip->ttl, ip->protocol);
+
+ if (ip->tos != 0) printk(" tos %u", ip->tos);
+ if (doff != 0 || (ntohs(ip->frag_off) & IPF_MF))
+ printk(" id %u offs %u", ntohs(ip->id), doff);
+
+ if (ntohs(ip->frag_off) & IPF_DF) printk(" DF");
+ if (ntohs(ip->frag_off) & IPF_MF) printk(" MF");
+ printk("\n*****\n");
+#endif
+}
+
+
+/* Initialize a SLIP control block for use. */
+static void
+sl_initialize(struct slip *sl, struct device *dev)
+{
+ sl->inuse = 0;
+ sl->sending = 0;
+ sl->escape = 0;
+
+ sl->line = dev->base_addr;
+ sl->tty = NULL;
+ sl->dev = dev;
+
+ /* Clear all pointers. */
+ sl->rbuff = NULL;
+ sl->xbuff = NULL;
+
+ sl->rhead = NULL;
+ sl->rend = NULL;
+ dev->rmem_end = (unsigned long) NULL;
+ dev->rmem_start = (unsigned long) NULL;
+ dev->mem_end = (unsigned long) NULL;
+ dev->mem_start = (unsigned long) NULL;
+}
+
+
+/* Find a SLIP channel from its `tty' link. */
+static struct slip *
+sl_find(struct tty_struct *tty)
+{
+ struct slip *sl;
+ int i;
+
+ if (tty == NULL) return(NULL);
+ for (i = 0; i < SL_NRUNIT; i++) {
+ sl = &sl_ctrl[i];
+ if (sl->tty == tty) return(sl);
+ }
+ return(NULL);
+}
+
+
+/* Find a free SLIP channel, and link in this `tty' line. */
+static inline struct slip *
+sl_alloc(void)
+{
+ unsigned long flags;
+ struct slip *sl;
+ int i;
+
+ for (i = 0; i < SL_NRUNIT; i++) {
+ sl = &sl_ctrl[i];
+ if (sl->inuse == 0) {
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->inuse = 1;
+ sl->tty = NULL;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ return(sl);
+ }
+ }
+ return(NULL);
+}
+
+
+/* Free a SLIP channel. */
+static inline void
+sl_free(struct slip *sl)
+{
+ unsigned long flags;
+
+ if (sl->inuse) {
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->inuse = 0;
+ sl->tty = NULL;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ }
+}
+
+
+/* Stuff one byte into a SLIP receiver buffer. */
+static inline void
+sl_enqueue(struct slip *sl, unsigned char c)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ if (sl->rhead < sl->rend) {
+ *sl->rhead = c;
+ sl->rhead++;
+ sl->rcount++;
+ } else sl->roverrun++;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Release 'i' bytes from a SLIP receiver buffer. */
+static inline void
+sl_dequeue(struct slip *sl, int i)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ if (sl->rhead > sl->rbuff) {
+ sl->rhead -= i;
+ sl->rcount -= i;
+ }
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Set the "sending" flag. This must be atomic, hence the ASM. */
+static inline void
+sl_lock(struct slip *sl)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->sending = 1;
+ sl->dev->tbusy = 1;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Clear the "sending" flag. This must be atomic, hence the ASM. */
+static inline void
+sl_unlock(struct slip *sl)
+{
+ unsigned long flags;
+
+ __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ sl->sending = 0;
+ sl->dev->tbusy = 0;
+ __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+}
+
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+static void
+sl_bump(struct slip *sl)
+{
+ int done;
+
+ DPRINTF((DBG_SLIP, "<< \"%s\" recv:\r\n", sl->dev->name));
+ ip_dump(sl->rbuff, sl->rcount);
+
+ /* Bump the datagram to the upper layers... */
+ do {
+ DPRINTF((DBG_SLIP, "SLIP: packet is %d at 0x%X\n",
+ sl->rcount, sl->rbuff));
+ done = dev_rint(sl->rbuff, sl->rcount, 0, sl->dev);
+ if (done == 0 || done == 1) break;
+ } while(1);
+ sl->rpacket++;
+}
+
+
+/* TTY finished sending a datagram, so clean up. */
+static void
+sl_next(struct slip *sl)
+{
+ DPRINTF((DBG_SLIP, "SLIP: sl_next(0x%X) called!\n", sl));
+ sl_unlock(sl);
+}
+
+
+/* Encapsulate one IP datagram and stuff into a TTY queue. */
+static void
+sl_encaps(struct slip *sl, unsigned char *p, int len)
+{
+ unsigned char *bp;
+ unsigned char c;
+ int count;
+
+ DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", p, len));
+ DPRINTF((DBG_SLIP, ">> \"%s\" sent:\r\n", sl->dev->name));
+ ip_dump(p, len);
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+ bp = (unsigned char *) sl->xbuff;
+ *bp++ = END;
+ count = 1;
+
+ /*
+ * For each byte in the packet, send the appropriate
+ * character sequence, according to the SLIP protocol.
+ */
+ while(len-- > 0) {
+ c = *p++;
+ switch((c & 0377)) {
+ case END:
+ *bp++ = ESC;
+ *bp++ = ESC_END;
+ count += 2;
+ break;
+ case ESC:
+ *bp++ = ESC;
+ *bp++ = ESC_ESC;
+ count += 2;
+ break;
+ default:
+ *bp++ = c;
+ count++;
+ }
+ }
+ *bp++ = END;
+ count++;
+ sl->spacket++;
+ bp = (unsigned char *) sl->xbuff;
+
+ /* Tell TTY to send it on its way. */
+ DPRINTF((DBG_SLIP, "SLIP: kicking TTY for %d bytes at 0x%X\n", count, bp));
+ if (tty_write_data(sl->tty, (char *) bp, count,
+ (void (*)(void *))sl_next, (void *) sl) == 0) {
+ DPRINTF((DBG_SLIP, "SLIP: TTY already done with %d bytes!\n", count));
+ sl_next(sl);
+ }
+}
+
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+static int
+sl_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct tty_struct *tty;
+ struct slip *sl;
+
+ /* Find the correct SLIP channel to use. */
+ sl = &sl_ctrl[dev->base_addr];
+ tty = sl->tty;
+ DPRINTF((DBG_SLIP, "SLIP: sl_xmit(\"%s\") skb=0x%X busy=%d\n",
+ dev->name, skb, sl->sending));
+
+ /*
+ * If we are busy already- too bad. We ought to be able
+ * to queue things at this point, to allow for a little
+ * frame buffer. Oh well...
+ */
+ if (sl->sending) {
+ DPRINTF((DBG_SLIP, "SLIP: sl_xmit: BUSY\r\n"));
+ sl->sbusy++;
+ return(1);
+ }
+
+ /* We were not, so we are now... :-) */
+ if (skb != NULL) {
+ sl_lock(sl);
+ sl_encaps(sl, (unsigned char *) (skb + 1), skb->len);
+ if (skb->free) kfree_skb(skb, FREE_WRITE);
+ }
+ return(0);
+}
+
+
+/* Return the frame type ID. This is always IP. */
+static unsigned short
+sl_type_trans (struct sk_buff *skb, struct device *dev)
+{
+ return(NET16(ETH_P_IP));
+}
+
+
+/* Fill in the MAC-level header. Not used by SLIP. */
+static int
+sl_header(unsigned char *buff, struct device *dev, unsigned short type,
+ unsigned long daddr, unsigned long saddr, unsigned len)
+{
+ return(0);
+}
+
+
+/* Add an ARP-entry for this device's broadcast address. Not used. */
+static void
+sl_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+{
+}
+
+
+/* Rebuild the MAC-level header. Not used by SLIP. */
+static int
+sl_rebuild_header(void *buff, struct device *dev)
+{
+ return(0);
+}
+
+
+/* Open the low-level part of the SLIP channel. Easy! */
+static int
+sl_open(struct device *dev)
+{
+ struct slip *sl;
+ unsigned char *p;
+ unsigned long l;
+
+ sl = &sl_ctrl[dev->base_addr];
+ if (sl->tty == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
+ return(-ENXIO);
+ }
+ sl->dev = dev;
+
+ /*
+ * Allocate the SLIP frame buffers:
+ *
+ * mem_end Top of frame buffers
+ * mem_start Start of frame buffers
+ * rmem_end Top of RECV frame buffer
+ * rmem_start Start of RECV frame buffer
+ */
+ l = (dev->mtu * 2);
+ p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
+ if (p == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP XMIT buffer!\n"));
+ return(-ENOMEM);
+ }
+ sl->dev->mem_start = (unsigned long) p;
+ sl->dev->mem_end = (unsigned long) (sl->dev->mem_start + l);
+
+ p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
+ if (p == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP RECV buffer!\n"));
+ return(-ENOMEM);
+ }
+ sl->dev->rmem_start = (unsigned long) p;
+ sl->dev->rmem_end = (unsigned long) (sl->dev->rmem_start + l);
+
+ sl->xbuff = (unsigned char *) sl->dev->mem_start;
+ sl->rbuff = (unsigned char *) sl->dev->rmem_start;
+ sl->rend = (unsigned char *) sl->dev->rmem_end;
+ sl->rhead = sl->rbuff;
+
+ sl->escape = 0;
+ sl->sending = 0;
+ sl->rcount = 0;
+
+ DPRINTF((DBG_SLIP, "SLIP: channel %d opened.\n", sl->line));
+ return(0);
+}
+
+
+/* Close the low-level part of the SLIP channel. Easy! */
+static int
+sl_close(struct device *dev)
+{
+ struct slip *sl;
+
+ sl = &sl_ctrl[dev->base_addr];
+ if (sl->tty == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
+ return(-EBUSY);
+ }
+ sl_free(sl);
+
+ /* Free all SLIP frame buffers. */
+ kfree(sl->rbuff);
+ kfree(sl->xbuff);
+ sl_initialize(sl, dev);
+
+ DPRINTF((DBG_SLIP, "SLIP: channel %d closed.\n", sl->line));
+ return(0);
+}
+
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLIP data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void
+slip_recv(struct tty_struct *tty)
+{
+ unsigned char buff[128];
+ unsigned char *p, c;
+ struct slip *sl;
+ int count;
+
+ DPRINTF((DBG_SLIP, "SLIP: slip_recv(%d) called\n", tty->line));
+ if ((sl = sl_find(tty)) == NULL) return; /* not connected */
+
+ /* Suck the bytes out of the TTY queues. */
+ do {
+ memset(buff, 0, 128);
+ count = tty_read_raw_data(tty, buff, 128);
+ if (count <= 0) break;
+
+ p = buff;
+ while(count-- > 0) {
+ c = *p++;
+ switch((c & 0377)) {
+ case ESC:
+ sl->escape = 1;
+ break;
+ case ESC_ESC:
+ if (sl->escape) sl_enqueue(sl, ESC);
+ else sl_enqueue(sl, c);
+ sl->escape = 0;
+ break;
+ case ESC_END:
+ if (sl->escape) sl_enqueue(sl, END);
+ else sl_enqueue(sl, c);
+ sl->escape = 0;
+ break;
+ case END:
+ if (sl->rcount > 3) sl_bump(sl);
+ sl_dequeue(sl, sl->rcount);
+ sl->rcount = 0;
+ sl->escape = 0;
+ break;
+ default:
+ sl_enqueue(sl, c);
+ sl->escape = 0;
+ }
+ }
+ } while(1);
+}
+
+
+/*
+ * Open the high-level part of the SLIP channel.
+ * This function is called by the TTY module when the
+ * SLIP line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLIP channel...
+ */
+static int
+slip_open(struct tty_struct *tty)
+{
+ struct slip *sl;
+
+ /* First make sure we're not already connected. */
+ if ((sl = sl_find(tty)) != NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: TTY %d already connected to %s !\n",
+ tty->line, sl->dev->name));
+ return(-EEXIST);
+ }
+
+ /* OK. Find a free SLIP channel to use. */
+ if ((sl = sl_alloc()) == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected: all channels in use!\n",
+ tty->line));
+ return(-ENFILE);
+ }
+ sl->tty = tty;
+ tty_read_flush(tty);
+ tty_write_flush(tty);
+
+ /* Perform the low-level SLIP initialization. */
+ (void) sl_open(sl->dev);
+ DPRINTF((DBG_SLIP, "SLIP: TTY %d connected to %s.\n",
+ tty->line, sl->dev->name));
+
+ /* Done. We have linked the TTY line to a channel. */
+ return(sl->line);
+}
+
+
+/*
+ * Close down a SLIP channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to SLIP
+ * (which usually is TTY again).
+ */
+static void
+slip_close(struct tty_struct *tty)
+{
+ struct slip *sl;
+
+ /* First make sure we're connected. */
+ if ((sl = sl_find(tty)) == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected !\n", tty->line));
+ return;
+ }
+
+ (void) dev_close(sl->dev);
+ DPRINTF((DBG_SLIP, "SLIP: TTY %d disconnected from %s.\n",
+ tty->line, sl->dev->name));
+}
+
+
+/* Perform I/O control on an active SLIP channel. */
+static int
+slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+ struct slip *sl;
+
+ /* First make sure we're connected. */
+ if ((sl = sl_find(tty)) == NULL) {
+ DPRINTF((DBG_SLIP, "SLIP: ioctl: TTY %d not connected !\n", tty->line));
+ return(-EINVAL);
+ }
+
+ DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
+ switch(cmd) {
+ case SIOCGIFNAME:
+ verify_area(VERIFY_WRITE, arg, 16);
+ memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
+ return(0);
+ default:
+ return(-EINVAL);
+ }
+ return(-EINVAL);
+}
+
+
+/* Initialize the SLIP driver. Called by DDI. */
+int
+slip_init(struct device *dev)
+{
+ struct slip *sl;
+ int i;
+
+ sl = &sl_ctrl[dev->base_addr];
+
+ if (already++ == 0) {
+ printk("SLIP: version %s (%d channels): ",
+ SLIP_VERSION, SL_NRUNIT);
+
+ /* Fill in our LDISC request block. */
+ sl_ldisc.flags = 0;
+ sl_ldisc.open = slip_open;
+ sl_ldisc.close = slip_close;
+ sl_ldisc.read = NULL;
+ sl_ldisc.write = NULL;
+ sl_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) slip_ioctl;
+ sl_ldisc.handler = slip_recv;
+ if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) == 0) printk("OK\n");
+ else printk("ERROR: %d\n", i);
+ }
+
+ /* Set up the "SLIP Control Block". */
+ sl_initialize(sl, dev);
+
+ /* Clear all statistics. */
+ sl->rcount = 0; /* SLIP receiver count */
+ sl->rpacket = 0; /* #frames received */
+ sl->roverrun = 0; /* "overrun" counter */
+ sl->spacket = 0; /* #frames sent out */
+ sl->sbusy = 0; /* "xmit busy" counter */
+ sl->errors = 0; /* not used at present */
+
+ /* Finish setting up the DEVICE info. */
+ dev->mtu = SL_MTU;
+ dev->hard_start_xmit = sl_xmit;
+ dev->open = sl_open;
+ dev->stop = sl_close;
+ dev->hard_header = sl_header;
+ dev->add_arp = sl_add_arp;
+ dev->type_trans = sl_type_trans;
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->type = 0;
+ dev->queue_xmit = dev_queue_xmit;
+ dev->rebuild_header = sl_rebuild_header;
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+
+ /* New-style flags. */
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return(0);
+}
diff --git a/net/inet/slip.h b/net/inet/slip.h
new file mode 100644
index 0000000..3bd021f
--- /dev/null
+++ b/net/inet/slip.h
@@ -0,0 +1,58 @@
+/*
+ * slip.h Define the SLIP device driver interface and constants.
+ *
+ * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY
+ * AS SOON AS POSSIBLE!
+ *
+ * Version: @(#)slip.h 1.2.0 03/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_SLIP_H
+#define _LINUX_SLIP_H
+
+/* SLIP configuration. */
+#define SL_NRUNIT 4 /* number of SLIP channels */
+#define SL_MTU 296 /* 296; I am used to 600- FvK */
+
+/* SLIP protocol characters. */
+#define END 0300 /* indicates end of frame */
+#define ESC 0333 /* indicates byte stuffing */
+#define ESC_END 0334 /* ESC ESC_END means END 'data' */
+#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
+
+
+struct slip {
+ /* Bitmapped flag fields. */
+ char inuse; /* are we allocated? */
+ char sending; /* "channel busy" indicator */
+ char escape; /* SLIP state machine */
+ char unused; /* fillers */
+
+ /* Various fields. */
+ int line; /* SLIP channel number */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct device *dev; /* easy for intr handling */
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char *rbuff; /* receiver buffer */
+ unsigned char *xbuff; /* transmitter buffer */
+
+ /* These are the various pointers into the buffers. */
+ unsigned char *rhead; /* RECV buffer pointer (head) */
+ unsigned char *rend; /* RECV buffer pointer (end) */
+ int rcount; /* SLIP receive counter */
+
+ /* SLIP interface statistics. */
+ unsigned long rpacket; /* inbound frame counter */
+ unsigned long roverrun; /* "buffer overrun" counter */
+ unsigned long spacket; /* outbound frames counter */
+ unsigned long sbusy; /* "transmitter busy" counter */
+ unsigned long errors; /* error count */
+};
+
+
+extern int slip_init(struct device *dev);
+
+
+#endif /* _LINUX_SLIP.H */
diff --git a/net/inet/sock.c b/net/inet/sock.c
new file mode 100644
index 0000000..544c4d7
--- /dev/null
+++ b/net/inet/sock.c
@@ -0,0 +1,1734 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * SOCK - AF_INET protocol family socket handler.
+ *
+ * Version: @(#)sock.c 1.0.17 06/02/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "arp.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include "raw.h"
+#include "icmp.h"
+
+
+int inet_debug = DBG_OFF; /* INET module debug flag */
+
+
+#define min(a,b) ((a)<(b)?(a):(b))
+#define swap(a,b) {unsigned long c; c=a; a=b; b=c;}
+
+
+extern struct proto packet_prot;
+
+
+void
+print_sk(struct sock *sk)
+{
+ if (!sk) {
+ printk(" print_sk(NULL)\n");
+ return;
+ }
+ 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);
+ printk(" urg = %d shutdown=%d\n", sk->urg, sk->shutdown);
+}
+
+
+void
+print_skb(struct sk_buff *skb)
+{
+ if (!skb) {
+ printk(" print_skb(NULL)\n");
+ return;
+ }
+ 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);
+}
+
+
+void
+lock_skb(struct sk_buff *skb)
+{
+ if (skb->lock) {
+ printk("*** bug more than one lock on sk_buff. \n");
+ }
+ skb->lock = 1;
+}
+
+
+void
+kfree_skb(struct sk_buff *skb, int rw)
+{
+ if (skb == NULL) {
+ printk("kfree_skb: skb = NULL\n");
+ return;
+ }
+
+ if (skb->lock) {
+ skb->free = 1;
+ return;
+ }
+ skb->magic = 0;
+ 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 {
+ kfree_s(skb->mem_addr, skb->mem_len);
+ }
+}
+
+
+void
+unlock_skb(struct sk_buff *skb, int rw)
+{
+ if (skb->lock != 1) {
+ printk("INET: *** bug unlocking non-locked sk_buff. \n");
+ }
+ skb->lock = 0;
+ if (skb->free) kfree_skb(skb, rw);
+}
+
+
+static int
+sk_inuse(struct proto *prot, int num)
+{
+ struct sock *sk;
+
+ for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
+ sk != NULL;
+ sk=sk->next) {
+ if (sk->num == 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. */
+ struct sock *sk;
+
+ 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) {
+ start =(i+1+start )%1024;
+ DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
+ i + base + 1, start));
+ 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;
+ }
+ DPRINTF((DBG_INET, "get_new_socknum returning %d, start = %d\n",
+ best + base + 1, start));
+ return(best+base+1);
+}
+
+
+void
+put_sock(unsigned short num, struct sock *sk)
+{
+ struct sock *sk1;
+ struct sock *sk2;
+ int mask;
+
+ DPRINTF((DBG_INET, "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 re-enter 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 & sk->saddr) != (mask & 0xffffffff)) {
+ mask = mask << 8;
+ break;
+ }
+ }
+ DPRINTF((DBG_INET, "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(struct sock *sk1)
+{
+ struct sock *sk2;
+
+ DPRINTF((DBG_INET, "remove_sock(sk1=%X)\n", sk1));
+ if (!sk1) {
+ printk("sock.c: remove_sock: sk1 == NULL\n");
+ return;
+ }
+
+ if (!sk1->prot) {
+ printk("sock.c: remove_sock: sk1->prot == NULL\n");
+ return;
+ }
+
+ /* 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 && sk2->next != sk1) {
+ sk2 = sk2->next;
+ }
+
+ if (sk2) {
+ sk2->next = sk1->next;
+ sti();
+ return;
+ }
+ sti();
+
+ if (sk1->num != 0) DPRINTF((DBG_INET, "remove_sock: sock not found.\n"));
+}
+
+
+void
+destroy_sock(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ DPRINTF((DBG_INET, "destroying socket %X\n", sk));
+ sk->inuse = 1; /* just to be safe. */
+
+ /* Incase it's sleeping somewhere. */
+ if (!sk->dead) wake_up(sk->sleep);
+
+ remove_sock(sk);
+
+ /* Now we can no longer get new packets. */
+ delete_timer((struct timer *)&sk->time_wait);
+
+ if (sk->send_tmp != NULL) kfree_skb(sk->send_tmp, FREE_WRITE);
+
+ /* Cleanup up the write buffer. */
+ for(skb = sk->wfront; skb != NULL; ) {
+ struct sk_buff *skb2;
+
+ skb2=(struct sk_buff *)skb->next;
+ if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
+ printk("sock.c:destroy_sock write queue with bad magic(%X)\n",
+ skb->magic);
+ break;
+ }
+ kfree_skb(skb, FREE_WRITE);
+ skb = skb2;
+ }
+
+ sk->wfront = NULL;
+ sk->wback = NULL;
+
+ if (sk->rqueue != NULL) {
+ skb = sk->rqueue;
+ do {
+ struct sk_buff *skb2;
+
+ skb2 = (struct sk_buff *)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);
+ }
+ kfree_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,
+ * or maybe the arp queue.
+ */
+ cli();
+ /* see if it's in a transmit queue. */
+ /* this can be simplified quite a bit. Look */
+ /* at tcp.c:tcp_ack to see how. */
+ if (skb->next != NULL) {
+ int i;
+
+ if (skb->next != skb) {
+ skb->next->prev = skb->prev;
+ skb->prev->next = skb->next;
+
+ if (skb == arp_q) {
+ if (skb->magic != ARP_QUEUE_MAGIC) {
+ sti();
+ printk("sock.c: destroy_sock skb on arp queue with"
+ "bas magic(%X)\n", skb->magic);
+ cli();
+ arp_q = NULL;
+ continue;
+ }
+ arp_q = skb->next;
+ } else {
+ for(i = 0; i < DEV_NUMBUFFS; i++) {
+ if (skb->dev &&
+ skb->dev->buffs[i] == skb) {
+ if (skb->magic != DEV_QUEUE_MAGIC) {
+ sti();
+ printk("sock.c: destroy sock skb on dev queue"
+ "with bad magic(%X)\n", skb->magic);
+ cli();
+ break;
+ }
+ skb->dev->buffs[i]= skb->next;
+ break;
+ }
+ }
+ }
+ } else {
+ if (skb == arp_q) {
+ if (skb->magic != ARP_QUEUE_MAGIC) {
+ sti();
+ printk("sock.c: destroy_sock skb on arp queue with"
+ "bas magic(%X)\n", skb->magic);
+ cli();
+ }
+ arp_q = NULL;
+ } else {
+ for(i = 0; i < DEV_NUMBUFFS; i++) {
+ if (skb->dev &&
+ skb->dev->buffs[i] == skb) {
+ if (skb->magic != DEV_QUEUE_MAGIC) {
+ sti();
+ printk("sock.c: destroy sock skb on dev queue"
+ "with bad magic(%X)\n", skb->magic);
+ cli();
+ break;
+ }
+ skb->dev->buffs[i]= NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ skb->dev = NULL;
+ sti();
+ skb2 = (struct sk_buff *)skb->link3;
+ kfree_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 = (struct sk_buff *)sk->back_log;
+ do {
+ struct sk_buff *skb2;
+
+ skb2 = (struct sk_buff *)skb->next;
+ kfree_skb(skb, FREE_READ);
+ skb = skb2;
+ } while(skb != sk->back_log);
+ sti();
+ }
+ sk->back_log = NULL;
+
+ /* Now if it has a half accepted/ closed socket. */
+ if (sk->pair) {
+ sk->pair->dead = 1;
+ sk->pair->prot->close(sk->pair, 0);
+ sk->pair = 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) {
+ kfree_s((void *)sk,sizeof(*sk));
+ } else {
+ /* this should never happen. */
+ /* actually it can if an ack has just been sent. */
+ DPRINTF((DBG_INET, "possible memory leak in socket = %X\n", 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);
+ }
+ DPRINTF((DBG_INET, "leaving destroy_sock\n"));
+}
+
+
+static int
+inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ switch(cmd) {
+ case F_SETOWN:
+ /*
+ * This is a little restrictive, but it's the only
+ * way to make sure that you can't send a sigurg to
+ * another process.
+ */
+ if (!suser() && current->pgrp != -arg &&
+ current->pid != arg) return(-EPERM);
+ sk->proc = arg;
+ return(0);
+ case F_GETOWN:
+ return(sk->proc);
+ default:
+ return(-EINVAL);
+ }
+}
+
+
+static int
+inet_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ 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);
+ }
+ if (optval == NULL) return(-EINVAL);
+
+ /* verify_area(VERIFY_WRITE, optval, sizeof(int));*/
+ val = get_fs_long((unsigned long *)optval);
+ switch(optname) {
+ case SO_TYPE:
+ case SO_ERROR:
+ 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 = 0;
+ 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);
+
+ default:
+ return(-ENOPROTOOPT);
+ }
+}
+
+
+static int
+inet_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ 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) {
+ 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;
+
+ default:
+ return(-ENOPROTOOPT);
+ }
+ verify_area(VERIFY_WRITE, optlen, sizeof(int));
+ put_fs_long(sizeof(int),(unsigned long *) optlen);
+
+ verify_area(VERIFY_WRITE, optval, sizeof(int));
+ put_fs_long(val,(unsigned long *)optval);
+
+ return(0);
+}
+
+
+static int
+inet_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ /* We might as well re use these. */
+ sk->max_ack_backlog = backlog;
+ if (sk->state != TCP_LISTEN) {
+ sk->ack_backlog = 0;
+ sk->state = TCP_LISTEN;
+ }
+ return(0);
+}
+
+
+static int
+inet_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct proto *prot;
+ int err;
+
+ sk = kmalloc(sizeof(*sk), GFP_KERNEL);
+ if (sk == NULL) return(-ENOMEM);
+ sk->num = 0;
+
+ switch(sock->type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ if (protocol && protocol != IPPROTO_TCP) {
+ kfree_s((void *)sk, sizeof(*sk));
+ return(-EPROTONOSUPPORT);
+ }
+ sk->no_check = TCP_NO_CHECK;
+ prot = &tcp_prot;
+ break;
+
+ case SOCK_DGRAM:
+ if (protocol && protocol != IPPROTO_UDP) {
+ kfree_s((void *)sk, sizeof(*sk));
+ return(-EPROTONOSUPPORT);
+ }
+ sk->no_check = UDP_NO_CHECK;
+ prot=&udp_prot;
+ break;
+
+ case SOCK_RAW:
+ if (!suser()) {
+ kfree_s((void *)sk, sizeof(*sk));
+ return(-EPERM);
+ }
+ if (!protocol) {
+ kfree_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()) {
+ kfree_s((void *)sk, sizeof(*sk));
+ return(-EPERM);
+ }
+ if (!protocol) {
+ kfree_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:
+ kfree_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;
+ sk->send_tmp = NULL;
+ sk->mss = 0; /* we will try not to send any packets smaller than this. */
+
+ /* 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 = 0;
+ sk->inuse = 0;
+ sk->delay_acks = 0;
+ 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_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) {
+ /*
+ * It assumes that any protocol which allows
+ * the user to assign a number at socket
+ * creation time automatically
+ * shares.
+ */
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ if (sk->prot->init) {
+ err = sk->prot->init(sk);
+ if (err != 0) {
+ destroy_sock(sk);
+ return(err);
+ }
+ }
+ return(0);
+}
+
+
+static int
+inet_dup(struct socket *newsock, struct socket *oldsock)
+{
+ return(inet_create(newsock,
+ ((struct sock *)(oldsock->data))->protocol));
+}
+
+
+/* The peer socket should always be NULL. */
+static int
+inet_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) return(0);
+
+ DPRINTF((DBG_INET, "inet_release(sock = %X, peer = %X)\n", sock, peer));
+ 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 {
+ DPRINTF((DBG_INET, "sk->linger set.\n"));
+ 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;
+ DPRINTF((DBG_INET, "inet_release returning\n"));
+ return(0);
+}
+
+
+/* this needs to be changed to dissallow
+ the rebinding of sockets. What error
+ should it return? */
+
+static int
+inet_bind(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len)
+{
+ struct sockaddr_in addr;
+ 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);
+ if (sk->num != 0) return(-EINVAL);
+
+ /* verify_area(VERIFY_WRITE, uaddr, addr_len);*/
+ memcpy_fromfs(&addr, uaddr, min(sizeof(addr), addr_len));
+
+#if 0 /* FIXME: */
+ if (addr.sin_family && addr.sin_family != AF_INET) {
+ /*
+ * This is really a bug in BSD which we need
+ * to emulate because ftp expects it.
+ */
+ return(-EINVAL);
+ }
+#endif
+
+ snum = ntohs(addr.sin_port);
+ DPRINTF((DBG_INET, "bind sk =%X to port = %d\n", sk, snum));
+ sk = sock->data;
+
+ /*
+ * We can't just leave the socket bound wherever it is, it might
+ * be bound to a privileged port. However, since there seems to
+ * be a bug here, we will leave it if the port is not privileged.
+ */
+ if (snum == 0) {
+ if (sk->num > PROT_SOCK) return(0);
+ snum = get_new_socknum(sk->prot, 0);
+ }
+ if (snum <= PROT_SOCK && !suser()) return(-EACCES);
+
+ if (chk_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0)
+ sk->saddr = addr.sin_addr.s_addr;
+
+ DPRINTF((DBG_INET, "sock_array[%d] = %X:\n", snum &(SOCK_ARRAY_SIZE -1),
+ sk->prot->sock_array[snum &(SOCK_ARRAY_SIZE -1)]));
+
+ /* Make sure we are allowed to bind here. */
+outside_loop:
+ cli();
+ for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
+ sk2 != NULL; sk2 = sk2->next) {
+#if 1 /* should be below! */
+ if (sk2->num != snum) continue;
+ if (sk2->saddr != sk->saddr) continue;
+#endif
+ if (sk2->dead) {
+ destroy_sock(sk2);
+ goto outside_loop;
+ }
+ if (!sk->reuse) {
+ sti();
+ return(-EADDRINUSE);
+ }
+ if (!sk2->reuse) {
+ sti();
+ return(-EADDRINUSE);
+ }
+ }
+ sti();
+
+ remove_sock(sk);
+ put_sock(snum, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ sk->daddr = 0;
+ sk->dummy_th.dest = 0;
+ return(0);
+}
+
+
+static int
+inet_connect(struct socket *sock, struct sockaddr * uaddr,
+ int addr_len, int flags)
+{
+ 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 (sock->state != SS_CONNECTING) {
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = htons(sk->num);
+ }
+
+ if (sk->prot->connect == NULL) return(-EOPNOTSUPP);
+
+ err = sk->prot->connect(sk, (struct sockaddr_in *)uaddr, addr_len);
+ if (err < 0) return(err);
+
+ sock->state = SS_CONNECTING;
+ }
+
+ if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) return(-EINPROGRESS);
+
+ cli(); /* avoid the race condition */
+ while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ return(-ERESTARTSYS);
+ }
+ }
+ sti();
+ sock->state = SS_CONNECTED;
+
+ if (sk->state != TCP_ESTABLISHED && sk->err) {
+ sock->state = SS_UNCONNECTED;
+ return(-sk->err);
+ }
+ return(0);
+}
+
+
+static int
+inet_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return(-EOPNOTSUPP);
+}
+
+
+static int
+inet_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk1, *sk2;
+
+ sk1 = sock->data;
+ if (sk1 == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ /*
+ * We've been passed an extra socket.
+ * We need to free it up because the tcp module creates
+ * it's own when it accepts one.
+ */
+ if (newsock->data) kfree_s(newsock->data, sizeof(struct sock));
+ 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) {
+ if (sk1->err <= 0)
+ printk("Warning sock.c:sk1->err <= 0. Returning non-error.\n");
+ 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 > 0) {
+ int err;
+
+ err = -sk2->err;
+ destroy_sock(sk2);
+ newsock->data = NULL;
+ return(err);
+ }
+ newsock->state = SS_CONNECTED;
+ return(0);
+}
+
+
+static int
+inet_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ struct sockaddr_in sin;
+ struct sock *sk;
+ int len;
+
+ len = get_fs_long(uaddr_len);
+
+ /* Check this error. */
+ if (len < sizeof(sin)) return(-EINVAL);
+
+ sin.sin_family = AF_INET;
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+ if (peer) {
+ if (!tcp_connected(sk->state)) return(-ENOTCONN);
+ sin.sin_port = sk->dummy_th.dest;
+ sin.sin_addr.s_addr = sk->daddr;
+ } else {
+ sin.sin_port = sk->dummy_th.source;
+ if (sk->saddr == 0) sin.sin_addr.s_addr = my_addr();
+ else sin.sin_addr.s_addr = sk->saddr;
+ }
+ len = sizeof(sin);
+ verify_area(VERIFY_WRITE, uaddr, len);
+ memcpy_tofs(uaddr, &sin, sizeof(sin));
+ verify_area(VERIFY_WRITE, uaddr_len, sizeof(len));
+ put_fs_long(len, uaddr_len);
+ return(0);
+}
+
+
+static int
+inet_read(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+ return(sk->prot->read(sk, ubuf, size, noblock,0));
+}
+
+
+static int
+inet_recv(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+ return(sk->prot->read(sk, ubuf, size, noblock, flags));
+}
+
+
+static int
+inet_write(struct socket *sock, char *ubuf, int size, int noblock)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 1);
+ return(-EPIPE);
+ }
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ return(sk->prot->write(sk, ubuf, size, noblock, 0));
+}
+
+
+static int
+inet_send(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 1);
+ return(-EPIPE);
+ }
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ return(sk->prot->write(sk, ubuf, size, noblock, flags));
+}
+
+
+static int
+inet_sendto(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags, struct sockaddr *sin, int addr_len)
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 1);
+ return(-EPIPE);
+ }
+
+ if (sk->prot->sendto == NULL) return(-EOPNOTSUPP);
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ return(sk->prot->sendto(sk, ubuf, size, noblock, flags,
+ (struct sockaddr_in *)sin, addr_len));
+}
+
+
+static int
+inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
+ unsigned flags, struct sockaddr *sin, int *addr_len )
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ if (sk->prot->recvfrom == NULL) return(-EOPNOTSUPP);
+
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) return(-EAGAIN);
+ put_sock(sk->num, sk);
+ sk->dummy_th.source = ntohs(sk->num);
+ }
+
+ return(sk->prot->recvfrom(sk, ubuf, size, noblock, flags,
+ (struct sockaddr_in*)sin, addr_len));
+}
+
+
+static int
+inet_shutdown(struct socket *sock, int how)
+{
+ 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 (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+ sock->state = SS_CONNECTED;
+
+ if (!tcp_connected(sk->state)) return(-ENOTCONN);
+ sk->shutdown |= how;
+ if (sk->prot->shutdown) sk->prot->shutdown(sk, how);
+ return(0);
+}
+
+
+static int
+inet_select(struct socket *sock, int sel_type, select_table *wait )
+{
+ struct sock *sk;
+
+ sk = sock->data;
+ if (sk == NULL) {
+ printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
+ return(0);
+ }
+
+ if (sk->prot->select == NULL) {
+ DPRINTF((DBG_INET, "select on non-selectable socket.\n"));
+ return(0);
+ }
+ return(sk->prot->select(sk, sel_type, wait));
+}
+
+
+static int
+inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk;
+
+ DPRINTF((DBG_INET, "INET: in inet_ioctl\n"));
+ sk = NULL;
+ if (sock && (sk = sock->data) == NULL) {
+ printk("AF_INET: Warning: sock->data = NULL: %d\n" , __LINE__);
+ return(0);
+ }
+
+ switch(cmd) {
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ if (sk)
+ sk->proc = get_fs_long((void *) arg);
+ return(0);
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ if (sk) {
+ verify_area(VERIFY_WRITE,(void *) arg, sizeof(long));
+ put_fs_long(sk->proc,(void *)arg);
+ }
+ return(0);
+#if 0 /* FIXME: */
+ case SIOCATMARK:
+ printk("AF_INET: ioctl(SIOCATMARK, 0x%08X)\n",(void *) arg);
+ return(-EINVAL);
+#endif
+
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_INET));
+
+ case SIOCADDRT:
+ case SIOCDELRT:
+ return(rt_ioctl(cmd,(void *) arg));
+
+ case SIOCDARP:
+ case SIOCGARP:
+ case SIOCSARP:
+ return(arp_ioctl(cmd,(void *) arg));
+
+ case IP_SET_DEV:
+ case SIOCGIFCONF:
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ case SIOCSIFLINK:
+ return(dev_ioctl(cmd,(void *) arg));
+
+ default:
+ if (!sk || !sk->prot->ioctl) return(-EINVAL);
+ return(sk->prot->ioctl(sk, cmd, arg));
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+
+void *
+sock_wmalloc(struct sock *sk, unsigned long size, int force,
+ int priority)
+{
+ if (sk) {
+ if (sk->wmem_alloc + size < SK_WMEM_MAX || force) {
+ cli();
+ sk->wmem_alloc+= size;
+ sti();
+ return(kmalloc(size, priority));
+ }
+ DPRINTF((DBG_INET, "sock_wmalloc(%X,%d,%d,%d) returning NULL\n",
+ sk, size, force, priority));
+ return(NULL);
+ }
+ return(kmalloc(size, priority));
+}
+
+
+void *
+sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
+{
+ if (sk) {
+ if (sk->rmem_alloc + size < SK_RMEM_MAX || force) {
+ void *c = kmalloc(size, priority);
+ cli();
+ if (c) sk->rmem_alloc += size;
+ sti();
+ return(c);
+ }
+ DPRINTF((DBG_INET, "sock_rmalloc(%X,%d,%d,%d) returning NULL\n",
+ sk,size,force, priority));
+ return(NULL);
+ }
+ return(kmalloc(size, priority));
+}
+
+
+unsigned long
+sock_rspace(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(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(struct sock *sk, void *mem, unsigned long size)
+{
+ DPRINTF((DBG_INET, "sock_wfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
+
+ kfree_s(mem, size);
+ if (sk) {
+ sk->wmem_alloc -= size;
+
+ /* In case it might be waiting for more memory. */
+ if (!sk->dead) wake_up(sk->sleep);
+ if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
+ DPRINTF((DBG_INET,
+ "recovered lost memory, destroying sock = %X\n", sk));
+ delete_timer((struct timer *)&sk->time_wait);
+ kfree_s((void *)sk, sizeof(*sk));
+ }
+ return;
+ }
+}
+
+
+void
+sock_rfree(struct sock *sk, void *mem, unsigned long size)
+{
+ DPRINTF((DBG_INET, "sock_rfree(sk=%X, mem=%X, size=%d)\n", sk, mem, size));
+
+ kfree_s(mem, size);
+ if (sk) {
+ sk->rmem_alloc -= size;
+ if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
+ delete_timer((struct timer *)&sk->time_wait);
+ kfree_s((void *)sk, sizeof(*sk));
+ }
+ }
+}
+
+
+/*
+ * This routine must find a socket given a TCP or UDP header.
+ * Everyhting is assumed to be in net order.
+ */
+struct sock *get_sock(struct proto *prot, unsigned short num,
+ unsigned long raddr,
+ unsigned short rnum, unsigned long laddr)
+{
+ struct sock *s;
+ unsigned short hnum;
+
+ hnum = ntohs(num);
+ DPRINTF((DBG_INET, "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[hnum & (SOCK_ARRAY_SIZE - 1)];
+ s != NULL; s = s->next) {
+ if (s->num == hnum) {
+ /* We need to see if this is the socket that we want. */
+ if (ip_addr_match(s->daddr, raddr) == 0) continue;
+ if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) continue;
+#if 1 /* C.E. Hawkins ceh@eng.cam.ac.uk */
+ if (s->prot != &udp_prot || prot != &udp_prot)
+#endif
+ if (ip_addr_match(s->saddr, laddr) == 0) continue;
+ if (s->dead && (s->state == TCP_CLOSE)) continue;
+ return(s);
+ }
+ }
+ return(NULL);
+}
+
+
+void release_sock(struct sock *sk)
+{
+ if (!sk) {
+ printk("sock.c: release_sock sk == NULL\n");
+ return;
+ }
+ if (!sk->prot) {
+ printk("sock.c: release_sock sk->prot == NULL\n");
+ return;
+ }
+
+ 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 =(struct sk_buff *)sk->back_log;
+ DPRINTF((DBG_INET, "release_sock: skb = %X:\n", 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();
+ DPRINTF((DBG_INET, "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 inet_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);
+ }
+}
+
+
+static int
+inet_fioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int minor, ret;
+
+ /* Extract the minor number on which we work. */
+ minor = MINOR(inode->i_rdev);
+ if (minor != 0) return(-ENODEV);
+
+ /* Now dispatch on the minor device. */
+ switch(minor) {
+ case 0: /* INET */
+ ret = inet_ioctl(NULL, cmd, arg);
+ break;
+ case 1: /* IP */
+ ret = ip_ioctl(NULL, cmd, arg);
+ break;
+ case 2: /* ICMP */
+ ret = icmp_ioctl(NULL, cmd, arg);
+ break;
+ case 3: /* TCP */
+ ret = tcp_ioctl(NULL, cmd, arg);
+ break;
+ case 4: /* UDP */
+ ret = udp_ioctl(NULL, cmd, arg);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+
+ return(ret);
+}
+
+
+static struct file_operations inet_fops = {
+ NULL, /* LSEEK */
+ NULL, /* READ */
+ NULL, /* WRITE */
+ NULL, /* READDIR */
+ NULL, /* SELECT */
+ inet_fioctl, /* IOCTL */
+ NULL, /* MMAP */
+ NULL, /* OPEN */
+ NULL /* CLOSE */
+};
+
+
+static struct proto_ops inet_proto_ops = {
+ AF_INET,
+
+ inet_create,
+ inet_dup,
+ inet_release,
+ inet_bind,
+ inet_connect,
+ inet_socketpair,
+ inet_accept,
+ inet_getname,
+ inet_read,
+ inet_write,
+ inet_select,
+ inet_ioctl,
+ inet_listen,
+ inet_send,
+ inet_recv,
+ inet_sendto,
+ inet_recvfrom,
+ inet_shutdown,
+ inet_setsockopt,
+ inet_getsockopt,
+ inet_fcntl,
+};
+
+
+void inet_proto_init(struct ddi_proto *pro)
+{
+ struct inet_protocol *p;
+ int i;
+
+ /* Set up our UNIX VFS major device. */
+ if (register_chrdev(AF_INET_MAJOR, "af_inet", &inet_fops) < 0) {
+ printk("5s: cannot register major device %d!\n",
+ pro->name, AF_INET_MAJOR);
+ return;
+ }
+
+ /* Tell SOCKET that we are alive... */
+ (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+
+ 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 = inet_protocol_base; p != NULL;) {
+ struct inet_protocol *tmp;
+
+ tmp = (struct inet_protocol *) p->next;
+ inet_add_protocol(p);
+ p = tmp;
+ }
+
+ /* Initialize the DEV module. */
+ dev_init();
+
+ /* Initialize the "Buffer Head" pointers. */
+ bh_base[INET_BH].routine = inet_bh;
+ timer_table[NET_TIMER].fn = net_timer;
+}
diff --git a/net/inet/sock.h b/net/inet/sock.h
new file mode 100644
index 0000000..783c0cf
--- /dev/null
+++ b/net/inet/sock.h
@@ -0,0 +1,188 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the AF_INET socket handler.
+ *
+ * Version: @(#)sock.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _SOCK_H
+#define _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;
+ volatile unsigned long wmem_alloc;
+ volatile 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;
+
+ /*
+ * Not all are volatile, but some are, so we
+ * might as well say they all are.
+ */
+ volatile char inuse,
+ dead,
+ urginline,
+ intr,
+ blog,
+ done,
+ reuse,
+ keepopen,
+ linger,
+ delay_acks,
+ timeout,
+ destroy,
+ ack_timed,
+ no_check,
+ exp_growth;
+ int proc;
+ struct sock *next;
+ struct sock *pair;
+ struct sk_buff *send_tail;
+ struct sk_buff *send_head;
+ struct sk_buff *volatile back_log;
+ struct sk_buff *send_tmp;
+ 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;
+ volatile unsigned short cong_window;
+ volatile unsigned short packets_out;
+ volatile unsigned short urg;
+ volatile unsigned short shutdown;
+ unsigned short mss;
+ volatile short rtt;
+ volatile short err;
+ unsigned char protocol;
+ volatile unsigned char state;
+ volatile unsigned char ack_backlog;
+ unsigned char max_ack_backlog;
+ unsigned char priority;
+ struct tcphdr dummy_th;
+ struct timer time_wait;
+};
+
+struct proto {
+ void *(*wmalloc)(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+ void *(*rmalloc)(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+ void (*wfree)(struct sock *sk, void *mem,
+ unsigned long size);
+ void (*rfree)(struct sock *sk, void *mem,
+ unsigned long size);
+ unsigned long (*rspace)(struct sock *sk);
+ unsigned long (*wspace)(struct sock *sk);
+ void (*close)(struct sock *sk, int timeout);
+ int (*read)(struct sock *sk, unsigned char *to,
+ int len, int nonblock, unsigned flags);
+ int (*write)(struct sock *sk, unsigned char *to,
+ int len, int nonblock, unsigned flags);
+ int (*sendto)(struct sock *sk,
+ unsigned char *from, int len, int noblock,
+ unsigned flags, struct sockaddr_in *usin,
+ int addr_len);
+ int (*recvfrom)(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)(struct sock *sk,
+ struct sockaddr_in *usin, int addr_len);
+ struct sock *(*accept) (struct sock *sk, int flags);
+ void (*queue_xmit)(struct sock *sk,
+ struct device *dev, struct sk_buff *skb,
+ int free);
+ void (*retransmit)(struct sock *sk, int all);
+ void (*write_wakeup)(struct sock *sk);
+ void (*read_wakeup)(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 inet_protocol *protocol);
+ int (*select)(struct sock *sk, int which,
+ select_table *wait);
+ int (*ioctl)(struct sock *sk, int cmd,
+ unsigned long arg);
+ int (*init)(struct sock *sk);
+ void (*shutdown)(struct sock *sk, int how);
+ unsigned short max_header;
+ unsigned long retransmits;
+ struct sock *sock_array[SOCK_ARRAY_SIZE];
+ char name[80];
+};
+
+#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 */
+
+
+#define PROT_SOCK 1024
+#define SHUTDOWN_MASK 3
+#define RCV_SHUTDOWN 1
+#define SEND_SHUTDOWN 2
+
+
+extern void destroy_sock(struct sock *sk);
+extern unsigned short get_new_socknum(struct proto *, unsigned short);
+extern void put_sock(unsigned short, struct sock *);
+extern void release_sock(struct sock *sk);
+extern struct sock *get_sock(struct proto *, unsigned short,
+ unsigned long, unsigned short,
+ unsigned long);
+extern void print_sk(struct sock *);
+extern void *sock_wmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern void *sock_rmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern void sock_wfree(struct sock *sk, void *mem,
+ unsigned long size);
+extern void sock_rfree(struct sock *sk, void *mem,
+ unsigned long size);
+extern unsigned long sock_rspace(struct sock *sk);
+extern unsigned long sock_wspace(struct sock *sk);
+
+#endif /* _SOCK_H */
diff --git a/net/inet/tcp.c b/net/inet/tcp.c
new file mode 100644
index 0000000..8c0f971
--- /dev/null
+++ b/net/inet/tcp.c
@@ -0,0 +1,3124 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: @(#)tcp.c 1.0.16 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or(at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/termios.h>
+#include <linux/in.h>
+#include <linux/fcntl.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "icmp.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+
+#define tmax(a,b)(before((a),(b)) ?(b) :(a))
+#define swap(a,b) {unsigned long c; c=a; a=b; b=c;}
+
+
+static int
+min(unsigned int a, unsigned int b)
+{
+ if (a < b) return(a);
+ return(b);
+}
+
+
+void
+print_th(struct tcphdr *th)
+{
+ unsigned char *ptr;
+
+ if (inet_debug != DBG_TCP) return;
+
+ printk("TCP header:\n");
+ ptr =(unsigned char *)(th + 1);
+ printk(" source=%d, dest=%d, seq =%d, ack_seq = %d\n",
+ ntohs(th->source), ntohs(th->dest),
+ ntohl(th->seq), ntohl(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",
+ ntohs(th->window), ntohs(th->check), ntohs(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(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ skb = sk->rqueue;
+ if (skb == NULL) return(NULL);
+ sk->rqueue =(struct sk_buff *)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(struct sock *sk)
+{
+ sk->state = TCP_TIME_WAIT;
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead) wake_up(sk->sleep);
+ sk->time_wait.len = TCP_TIMEWAIT_LEN;
+ sk->timeout = TIME_CLOSE;
+ reset_timer((struct timer *)&sk->time_wait);
+}
+
+
+static void
+tcp_retransmit(struct sock *sk, int all)
+{
+ if (all) {
+ ip_retransmit(sk, all);
+ return;
+ }
+
+ if (sk->cong_window > 4)
+ 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 inet_protocol *protocol)
+{
+ struct tcphdr *th;
+ struct sock *sk;
+
+ DPRINTF((DBG_TCP, "TCP: tcp_err(%d, hdr=%X, daddr=%X saddr=%X, protocol=%X)\n",
+ err, header, daddr, saddr, protocol));
+
+ th =(struct tcphdr *)header;
+ sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
+ print_th(th);
+
+ if (sk == NULL) return;
+
+ if ((err & 0xff00) == (ICMP_SOURCE_QUENCH << 8)) {
+ /*
+ * FIXME:
+ * For now we will just trigger a linear backoff.
+ * The slow start code should cause a real backoff here.
+ */
+ if (sk->cong_window > 4) sk->cong_window--;
+ return;
+ }
+
+ DPRINTF((DBG_TCP, "TCP: icmp_err got error\n"));
+ sk->err = icmp_err_convert[err & 0xff].errno;
+
+ /*
+ * If we've already connected we will keep trying
+ * until we time out, or the user gives up.
+ */
+ if (icmp_err_convert[err & 0xff].fatal) {
+ if (sk->state == TCP_SYN_SENT) {
+ sk->state = TCP_CLOSE;
+ sk->prot->close(sk, 0);
+ }
+ }
+ return;
+}
+
+
+static int
+tcp_readable(struct sock *sk)
+{
+ unsigned long counted;
+ unsigned long amount;
+ struct sk_buff *skb;
+ int count=0;
+ int sum;
+
+ DPRINTF((DBG_TCP, "tcp_readable(sk=%X)\n", sk));
+
+ if (sk == NULL || sk->rqueue == NULL) return(0);
+
+ counted = sk->copied_seq+1;
+ amount = 0;
+ skb =(struct sk_buff *)sk->rqueue->next;
+
+ /* Do until a push or until we are out of data. */
+ do {
+ count++;
+ if (count > 20) {
+ DPRINTF((DBG_TCP, "tcp_readable, more than 20 packets without a psh\n"));
+ DPRINTF((DBG_TCP, "possible read_queue corruption.\n"));
+ return(amount);
+ }
+ if (before(counted, skb->h.th->seq)) break;
+ sum = skb->len -(counted - skb->h.th->seq);
+ if (skb->h.th->syn) sum++;
+ if (skb->h.th->urg) {
+ sum -= ntohs(skb->h.th->urg_ptr);
+ }
+ if (sum >= 0) {
+ amount += sum;
+ if (skb->h.th->syn) amount--;
+ counted += sum;
+ }
+ if (amount && skb->h.th->psh) break;
+ skb =(struct sk_buff *)skb->next;
+ } while(skb != sk->rqueue->next);
+ DPRINTF((DBG_TCP, "tcp readable returning %d bytes\n", amount));
+ return(amount);
+}
+
+
+static int
+tcp_select(struct sock *sk, int sel_type, select_table *wait)
+{
+ DPRINTF((DBG_TCP, "tcp_select(sk=%X, sel_type = %d, wait = %X)\n",
+ sk, sel_type, wait));
+
+ sk->inuse = 1;
+ switch(sel_type) {
+ case SEL_IN:
+ select_wait(sk->sleep, wait);
+ if (sk->rqueue != NULL) {
+ if (sk->state == TCP_LISTEN || tcp_readable(sk)) {
+ release_sock(sk);
+ return(1);
+ }
+ }
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ release_sock(sk);
+ return(1);
+ } else {
+ release_sock(sk);
+ return(0);
+ }
+ case SEL_OUT:
+ select_wait(sk->sleep, wait);
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ DPRINTF((DBG_TCP,
+ "write select on shutdown socket.\n"));
+
+ /* FIXME: should this return an error? */
+ release_sock(sk);
+ return(0);
+ }
+
+ /*
+ * FIXME:
+ * Hack so it will probably be able to write
+ * something if it says it's ok to write.
+ */
+ if (sk->prot->wspace(sk) >= sk->mtu) {
+ release_sock(sk);
+ /* This should cause connect to work ok. */
+ if (sk->state == TCP_SYN_RECV ||
+ sk->state == TCP_SYN_SENT) return(0);
+ return(1);
+ }
+ DPRINTF((DBG_TCP,
+ "tcp_select: sleeping on write sk->wmem_alloc = %d, "
+ "sk->packets_out = %d\n"
+ "sk->wback = %X, sk->wfront = %X\n"
+ "sk->send_seq = %u, sk->window_seq=%u\n",
+ sk->wmem_alloc, sk->packets_out,
+ sk->wback, sk->wfront,
+ sk->send_seq, sk->window_seq));
+
+ release_sock(sk);
+ return(0);
+ case SEL_EX:
+ select_wait(sk->sleep,wait);
+ if (sk->err) {
+ release_sock(sk);
+ return(1);
+ }
+ release_sock(sk);
+ return(0);
+ }
+
+ release_sock(sk);
+ return(0);
+}
+
+
+int
+tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ DPRINTF((DBG_TCP, "tcp_ioctl(sk=%X, cmd = %d, arg=%X)\n", sk, cmd, arg));
+ switch(cmd) {
+ case DDIOCSDBG:
+ return(dbg_ioctl((void *) arg, DBG_TCP));
+
+ case TIOCINQ:
+#ifdef FIXME /* FIXME: */
+ case FIONREAD:
+#endif
+ {
+ unsigned long amount;
+
+ if (sk->state == TCP_LISTEN) return(-EINVAL);
+
+ amount = 0;
+ sk->inuse = 1;
+ if (sk->rqueue != NULL) {
+ amount = tcp_readable(sk);
+ }
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "returning %d\n", amount));
+ verify_area(VERIFY_WRITE,(void *)arg,
+ sizeof(unsigned long));
+ put_fs_long(amount,(unsigned long *)arg);
+ return(0);
+ }
+ case SIOCATMARK:
+ {
+ struct sk_buff *skb;
+ int answ = 0;
+
+ /*
+ * Try to figure out if we need to read
+ * some urgent data.
+ */
+ sk->inuse = 1;
+ if (sk->rqueue != NULL) {
+ skb =(struct sk_buff *)sk->rqueue->next;
+ if (sk->copied_seq+1 == skb->h.th->seq &&
+ skb->h.th->urg) answ = 1;
+ }
+ release_sock(sk);
+ verify_area(VERIFY_WRITE,(void *) arg,
+ sizeof(unsigned long));
+ put_fs_long(answ,(void *) arg);
+ return(0);
+ }
+ case TIOCOUTQ:
+ {
+ unsigned long amount;
+
+ if (sk->state == TCP_LISTEN) return(-EINVAL);
+ amount = sk->prot->wspace(sk)/2;
+ verify_area(VERIFY_WRITE,(void *)arg,
+ sizeof(unsigned long));
+ put_fs_long(amount,(unsigned long *)arg);
+ return(0);
+ }
+ default:
+ return(-EINVAL);
+ }
+}
+
+
+/* This routine computes a TCP checksum. */
+static unsigned short
+tcp_check(struct tcphdr *th, int len,
+ unsigned long saddr, unsigned long daddr)
+{
+ unsigned long sum;
+
+ if (saddr == 0) saddr = my_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"((ntohs(len) << 16) + IPPROTO_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 tcphdr *th, unsigned long saddr,
+ unsigned long daddr, int len, struct sock *sk)
+{
+ th->check = 0;
+ if (sk && sk->no_check) return;
+ th->check = tcp_check(th, len, saddr, daddr);
+ return;
+}
+
+
+static void
+tcp_send_partial(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ if (sk == NULL || sk->send_tmp == NULL) return;
+
+ skb = sk->send_tmp;
+
+ /* We need to complete and send the packet. */
+ tcp_send_check(skb->h.th, sk->saddr, sk->daddr,
+ skb->len-(unsigned long)skb->h.th +
+ (unsigned long)(skb+1), sk);
+
+ skb->h.seq = sk->send_seq;
+ if (after(sk->send_seq , sk->window_seq) ||
+ sk->packets_out >= sk->cong_window) {
+ DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n",
+ sk->cong_window, sk->packets_out));
+ DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n",
+ sk->send_seq, sk->window_seq));
+ skb->next = NULL;
+ skb->magic = TCP_WRITE_QUEUE_MAGIC;
+ if (sk->wback == NULL) {
+ sk->wfront=skb;
+ } else {
+ sk->wback->next = skb;
+ }
+ sk->wback = skb;
+ } else {
+ sk->prot->queue_xmit(sk, skb->dev, skb,0);
+ }
+ sk->send_tmp = NULL;
+}
+
+
+/* This routine sends an ack and also updates the window. */
+static void
+tcp_send_ack(unsigned long sequence, unsigned long ack,
+ struct sock *sk,
+ struct tcphdr *th, unsigned long daddr)
+{
+ struct sk_buff *buff;
+ struct tcphdr *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, GFP_ATOMIC);
+ if (buff == NULL) {
+ /* Force it to send an ack. */
+ sk->ack_backlog++;
+ if (sk->timeout != TIME_WRITE && tcp_connected(sk->state)) {
+ 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->lock = 0;
+ buff->len = sizeof(struct tcphdr);
+ buff->sk = sk;
+ t1 =(struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
+ IPPROTO_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 tcphdr *)((char *)t1 +tmp);
+
+ /* FIXME: */
+ 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 = ntohl(sequence);
+ t1->ack = 1;
+ sk->window = sk->prot->rspace(sk);
+ t1->window = ntohs(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 = ntohl(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 tcphdr *th, struct sock *sk, int push)
+{
+
+ /* FIXME: want to get rid of this. */
+ memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
+ th->seq = ntohl(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 = ntohl(sk->acked_seq);
+ sk->window = sk->prot->rspace(sk);
+ th->window = ntohs(sk->window);
+
+ return(sizeof(*th));
+}
+
+
+/*
+ * This routine copies from a user buffer into a socket,
+ * and starts the transmit system.
+ */
+static int
+tcp_write(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;
+
+ DPRINTF((DBG_TCP, "tcp_write(sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n",
+ sk, from, len, nonblock, flags));
+
+ prot = sk->prot;
+ while(len > 0) {
+ if (sk->err) {
+ if (copied) return(copied);
+ tmp = -sk->err;
+ sk->err = 0;
+ return(tmp);
+ }
+
+ /* First thing we do is make sure that we are established. */
+ sk->inuse = 1; /* no one else will use this socket.*/
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ release_sock(sk);
+ sk->err = EPIPE;
+ if (copied) return(copied);
+ sk->err = 0;
+ return(-EPIPE);
+ }
+
+ while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) {
+ if (sk->err) {
+ if (copied) return(copied);
+ tmp = -sk->err;
+ sk->err = 0;
+ return(tmp);
+ }
+
+ if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) {
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_write: return 1\n"));
+ if (copied) return(copied);
+
+ if (sk->err) {
+ tmp = -sk->err;
+ sk->err = 0;
+ return(tmp);
+ }
+
+ if (sk->keepopen) {
+ send_sig(SIGPIPE, current, 0);
+ }
+ return(-EPIPE);
+ }
+
+ if (nonblock || copied) {
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_write: return 2\n"));
+ if (copied) return(copied);
+ return(-EAGAIN);
+ }
+
+ /*
+ * FIXME:
+ * 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 &&
+ sk->state != TCP_CLOSE_WAIT && sk->err == 0) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ DPRINTF((DBG_TCP, "tcp_write: return 3\n"));
+ if (copied) return(copied);
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+
+ /* Now we need to check if we have a half built packet. */
+ if (sk->send_tmp != NULL) {
+ /* If sk->mss has been changed this could cause problems. */
+
+ /* Add more stuff to the end of skb->len */
+ skb = sk->send_tmp;
+ if (!(flags & MSG_OOB)) {
+ copy = min(sk->mss - skb->len + 128 +
+ prot->max_header, len);
+
+ /* FIXME: this is really a bug. */
+ if (copy <= 0) {
+ printk("TCP: **bug**: \"copy\" <= 0!!\n");
+ copy = 0;
+ }
+
+ memcpy_fromfs((unsigned char *)(skb+1) + skb->len, from, copy);
+ skb->len += copy;
+ from += copy;
+ copied += copy;
+ len -= copy;
+ sk->send_seq += copy;
+ }
+
+ if (skb->len -(unsigned long)skb->h.th +
+ (unsigned long)(skb+1) >= sk->mss ||(flags & MSG_OOB)) {
+ tcp_send_partial(sk);
+ }
+ continue;
+ }
+
+ /*
+ * 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));
+
+ /* FIXME: redundent check here. */
+ if (copy < 200 || copy > sk->mtu) copy = sk->mtu;
+ copy = min(copy, len);
+
+ /* We should really check the window here also. */
+ if (sk->packets_out && copy < sk->mss && !(flags & MSG_OOB)) {
+ /* We will release the socket incase we sleep here. */
+ release_sock(sk);
+ skb = prot->wmalloc(sk, sk->mss + 128 + prot->max_header +
+ sizeof(*skb), 0, GFP_KERNEL);
+ sk->inuse = 1;
+ sk->send_tmp = skb;
+ if (skb != NULL)
+ skb->mem_len = sk->mss + 128 + prot->max_header + sizeof(*skb);
+ } else {
+ /* We will release the socket incase we sleep here. */
+ release_sock(sk);
+ skb = prot->wmalloc(sk, copy + prot->max_header +
+ sizeof(*skb), 0, GFP_KERNEL);
+ sk->inuse = 1;
+ if (skb != NULL)
+ skb->mem_len = copy+prot->max_header + sizeof(*skb);
+ }
+
+ /* If we didn't get any memory, we need to sleep. */
+ if (skb == NULL) {
+ if (nonblock || copied) {
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_write: return 4\n"));
+ if (copied) return(copied);
+ return(-EAGAIN);
+ }
+
+ /* FIXME: 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 &&
+ (sk->state == TCP_ESTABLISHED||sk->state == TCP_CLOSE_WAIT)
+ && sk->err == 0) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ DPRINTF((DBG_TCP, "tcp_write: return 5\n"));
+ if (copied) return(copied);
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ continue;
+ }
+
+ skb->mem_addr = skb;
+ skb->len = 0;
+ skb->sk = sk;
+ skb->lock = 0;
+ skb->free = 0;
+
+ buff =(unsigned char *)(skb+1);
+
+ /*
+ * FIXME: we need to optimize this.
+ * Perhaps some hints here would be good.
+ */
+ tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
+ IPPROTO_TCP, sk->opt, skb->mem_len);
+ if (tmp < 0 ) {
+ prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_write: return 6\n"));
+ if (copied) return(copied);
+ return(tmp);
+ }
+ skb->len += tmp;
+ skb->dev = dev;
+ buff += tmp;
+ skb->h.th =(struct tcphdr *) buff;
+ tmp = tcp_build_header((struct tcphdr *)buff, sk, len-copy);
+ if (tmp < 0) {
+ prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_write: return 7\n"));
+ if (copied) return(copied);
+ return(tmp);
+ }
+
+ if (flags & MSG_OOB) {
+ ((struct tcphdr *)buff)->urg = 1;
+ ((struct tcphdr *)buff)->urg_ptr = ntohs(copy);
+ }
+ skb->len += tmp;
+ memcpy_fromfs(buff+tmp, from, copy);
+
+ from += copy;
+ copied += copy;
+ len -= copy;
+ skb->len += copy;
+ skb->free = 0;
+ sk->send_seq += copy;
+
+ if (sk->send_tmp != NULL) continue;
+
+ tcp_send_check((struct tcphdr *)buff, sk->saddr, sk->daddr,
+ copy + sizeof(struct tcphdr), sk);
+
+ skb->h.seq = sk->send_seq;
+ if (after(sk->send_seq , sk->window_seq) ||
+ sk->packets_out >= sk->cong_window) {
+ DPRINTF((DBG_TCP, "sk->cong_window = %d, sk->packets_out = %d\n",
+ sk->cong_window, sk->packets_out));
+ DPRINTF((DBG_TCP, "sk->send_seq = %d, sk->window_seq = %d\n",
+ sk->send_seq, sk->window_seq));
+ skb->next = NULL;
+ skb->magic = TCP_WRITE_QUEUE_MAGIC;
+ 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);
+ DPRINTF((DBG_TCP, "tcp_write: return 8\n"));
+ return(copied);
+}
+
+
+static int
+tcp_sendto(struct sock *sk, unsigned char *from,
+ int len, int nonblock, unsigned flags,
+ struct sockaddr_in *addr, int addr_len)
+{
+ struct sockaddr_in sin;
+
+ if (addr_len < sizeof(sin)) return(-EINVAL);
+ memcpy_fromfs(&sin, addr, sizeof(sin));
+ if (sin.sin_family && sin.sin_family != AF_INET) return(-EINVAL);
+ if (sin.sin_port != sk->dummy_th.dest) return(-EINVAL);
+ if (sin.sin_addr.s_addr != sk->daddr) return(-EINVAL);
+ return(tcp_write(sk, from, len, nonblock, flags));
+}
+
+
+static void
+tcp_read_wakeup(struct sock *sk)
+{
+ int tmp;
+ struct device *dev = NULL;
+ struct tcphdr *t1;
+ struct sk_buff *buff;
+
+ DPRINTF((DBG_TCP, "in tcp read wakeup\n"));
+ if (!sk->ack_backlog) return;
+
+ /*
+ * FIXME: we need to put code here to prevent this routine from
+ * being called. Being called once in a while is ok, so only check
+ * if this is the second time in a row.
+ */
+
+ /*
+ * We need to grab some memory, and put together an ack,
+ * and then put it into the queue to be sent.
+ */
+ buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
+ 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->lock = 0;
+ buff->len = sizeof(struct tcphdr);
+ buff->sk = sk;
+
+ /* Put in the IP header and routing stuff. */
+ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
+ if (tmp < 0) {
+ sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+ return;
+ }
+
+ buff->len += tmp;
+ t1 =(struct tcphdr *)((char *)(buff+1) +tmp);
+
+ memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
+ t1->seq = ntohl(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 = ntohs(sk->window);
+ t1->ack_seq = ntohl(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);
+}
+
+
+/*
+ * FIXME:
+ * 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(struct sock *sk)
+{
+ int left;
+
+ DPRINTF((DBG_TCP, "cleaning rbuf for sk=%X\n", sk));
+ left = sk->prot->rspace(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 =(struct sk_buff *)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;
+ kfree_skb(skb, FREE_READ);
+ }
+
+ /*
+ * FIXME:
+ * 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.
+ */
+ DPRINTF((DBG_TCP, "sk->window left = %d, sk->prot->rspace(sk)=%d\n",
+ sk->window - sk->bytes_rcv, sk->prot->rspace(sk)));
+
+ if (sk->prot->rspace(sk) != left) {
+ /*
+ * This area has caused the most trouble. The current strategy
+ * is to simply do nothing if the other end has room to send at
+ * least 3 full packets, because the ack from those will auto-
+ * matically update the window. If the other end doesn't think
+ * we have much space left, but we have room for atleast 1 more
+ * complete packet than it thinks we do, we will send an ack
+ * immediatedly. Otherwise we will wait up to .5 seconds in case
+ * the user reads some more.
+ */
+ sk->ack_backlog++;
+ if ((sk->prot->rspace(sk) > (sk->window - sk->bytes_rcv + sk->mtu))) {
+ /* Send an ack right now. */
+ tcp_read_wakeup(sk);
+ } else {
+ /* Force it to send an ack soon. */
+ if (before(jiffies + TCP_ACK_TIME, sk->time_wait.when)) {
+ 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(struct sock * sk, int nonblock,
+ unsigned char *to, int len, unsigned flags)
+{
+ int copied = 0;
+ struct sk_buff *skb;
+
+ DPRINTF((DBG_TCP, "tcp_read_urg(sk=%X, to=%X, len=%d, flags=%X)\n",
+ sk, to, len, flags));
+
+ while(len > 0) {
+ sk->inuse = 1;
+ while(sk->urg==0 || sk->rqueue == NULL) {
+ if (sk->err) {
+ int tmp;
+
+ release_sock(sk);
+ if (copied) return(copied);
+ tmp = -sk->err;
+ sk->err = 0;
+ return(tmp);
+ }
+
+ if (sk->state == TCP_CLOSE || sk->done) {
+ release_sock(sk);
+ if (copied) return(copied);
+ if (!sk->done) {
+ sk->done = 1;
+ return(0);
+ }
+ return(-ENOTCONN);
+ }
+
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ release_sock(sk);
+ if (copied == 0) sk->done = 1;
+ return(copied);
+ }
+
+ if (nonblock || copied) {
+ release_sock(sk);
+ if (copied) return(copied);
+ return(-EAGAIN);
+ }
+
+ /* Now at this point, we may have gotten some data. */
+ release_sock(sk);
+ cli();
+ if ((sk->urg == 0 || sk->rqueue == NULL) &&
+ sk->err == 0 && !(sk->shutdown & RCV_SHUTDOWN)) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ if (copied) return(copied);
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+
+ skb =(struct sk_buff *)sk->rqueue->next;
+ do {
+ int amt;
+
+ if (skb->h.th->urg && !skb->urg_used) {
+ if (skb->h.th->urg_ptr == 0) {
+ skb->h.th->urg_ptr = ntohs(skb->len);
+ }
+ amt = min(ntohs(skb->h.th->urg_ptr),len);
+ verify_area(VERIFY_WRITE, to, amt);
+ memcpy_tofs(to,(unsigned char *)(skb->h.th) +
+ skb->h.th->doff*4, amt);
+
+ if (!(flags & MSG_PEEK)) {
+ skb->urg_used = 1;
+ sk->urg--;
+ }
+ release_sock(sk);
+ copied += amt;
+ return(copied);
+ }
+ skb =(struct sk_buff *)skb->next;
+ } while(skb != sk->rqueue->next);
+ }
+ sk->urg = 0;
+ release_sock(sk);
+ return(0);
+}
+
+
+/* This routine copies from a sock struct into the user buffer. */
+static int
+tcp_read(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, nonblock, to, len, flags));
+
+ /* So no-one else will use this socket. */
+ sk->inuse = 1;
+ if (sk->rqueue != NULL) skb =(struct sk_buff *)sk->rqueue->next;
+ else skb = NULL;
+
+ DPRINTF((DBG_TCP, "tcp_read(sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n",
+ sk, to, len, nonblock, flags));
+
+ while(len > 0) {
+ /* skb->used just checks to see if we've gone all the way around. */
+ while(skb == NULL ||
+ before(sk->copied_seq+1, skb->h.th->seq) || skb->used) {
+ DPRINTF((DBG_TCP, "skb = %X:\n", skb));
+ cleanup_rbuf(sk);
+ if (sk->err) {
+ int tmp;
+
+ release_sock(sk);
+ if (copied) {
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+ copied));
+ return(copied);
+ }
+ tmp = -sk->err;
+ sk->err = 0;
+ return(tmp);
+ }
+
+ if (sk->state == TCP_CLOSE) {
+ release_sock(sk);
+ if (copied) {
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+ copied));
+ return(copied);
+ }
+ if (!sk->done) {
+ sk->done = 1;
+ return(0);
+ }
+ return(-ENOTCONN);
+ }
+
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ release_sock(sk);
+ if (copied == 0) sk->done = 1;
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
+ return(copied);
+ }
+
+ if (nonblock || copied) {
+ release_sock(sk);
+ if (copied) {
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+ copied));
+ return(copied);
+ }
+ return(-EAGAIN);
+ }
+
+ if ((flags & MSG_PEEK) && copied != 0) {
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
+ return(copied);
+ }
+
+ DPRINTF((DBG_TCP, "tcp_read about to sleep. state = %d\n",
+ sk->state));
+ release_sock(sk);
+
+ /*
+ * Now we may have some data waiting or we could
+ * have changed state.
+ */
+ cli();
+ if (sk->shutdown & RCV_SHUTDOWN || sk->err != 0) {
+ sk->inuse = 1;
+ sti();
+ continue;
+ }
+
+ 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) {
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n",
+ copied));
+ return(copied);
+ }
+ return(-ERESTARTSYS);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ DPRINTF((DBG_TCP, "tcp_read woke up. \n"));
+
+
+ if (sk->rqueue == NULL) skb = NULL;
+ else skb =(struct sk_buff *)sk->rqueue->next;
+
+ }
+
+ /*
+ * 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) {
+ sk->copied_seq += ntohs(skb->h.th->urg_ptr);
+ offset += ntohs(skb->h.th->urg_ptr);
+ if (offset >= skb->len) {
+ skb->used = 1;
+ skb =(struct sk_buff *)skb->next;
+ continue;
+ }
+ } else {
+ release_sock(sk);
+ if (copied) return(copied);
+ send_sig(SIGURG, current, 0);
+ return(-EINTR);
+ }
+ }
+ used = min(skb->len - offset, len);
+ verify_area(VERIFY_WRITE, 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 =(struct sk_buff *)skb->next;
+ }
+ cleanup_rbuf(sk);
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "tcp_read: returing %d\n", copied));
+ if (copied == 0 && nonblock) return(-EAGAIN);
+ return(copied);
+}
+
+
+/*
+ * Send a FIN without closing the connection.
+ * Not called at interrupt time.
+ */
+void
+tcp_shutdown(struct sock *sk, int how)
+{
+ struct sk_buff *buff;
+ struct tcphdr *t1, *th;
+ struct proto *prot;
+ int tmp;
+ struct device *dev = NULL;
+
+ /*
+ * We need to grab some memory, and put together a FIN,
+ * and then put it into the queue to be sent.
+ * FIXME:
+ * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
+ * Most of this is guesswork, so maybe it will work...
+ */
+ /* If we've already sent a FIN, return. */
+ if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) return;
+ if (!(how & SEND_SHUTDOWN)) return;
+ sk->inuse = 1;
+
+ /* Clear out any half completed packets. */
+ if (sk->send_tmp) tcp_send_partial(sk);
+
+ prot =(struct proto *)sk->prot;
+ th =(struct tcphdr *)&sk->dummy_th;
+ release_sock(sk); /* incase the malloc sleeps. */
+ buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
+ if (buff == NULL) return;
+ sk->inuse = 1;
+
+ DPRINTF((DBG_TCP, "tcp_shutdown_send buff = %X\n", buff));
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_RESET_SIZE;
+ buff->lock = 0;
+ buff->sk = sk;
+ buff->len = sizeof(*t1);
+ t1 =(struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
+ IPPROTO_TCP, sk->opt,
+ sizeof(struct tcphdr));
+ if (tmp < 0) {
+ prot->wfree(sk,buff->mem_addr, buff->mem_len);
+ release_sock(sk);
+ DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
+ return;
+ }
+
+ t1 =(struct tcphdr *)((char *)t1 +tmp);
+ buff ->len += tmp;
+ buff->dev = dev;
+ memcpy(t1, th, sizeof(*t1));
+ t1->seq = ntohl(sk->send_seq);
+ sk->send_seq++;
+ buff->h.seq = sk->send_seq;
+ t1->ack = 1;
+ t1->ack_seq = ntohl(sk->acked_seq);
+ t1->window = ntohs(sk->prot->rspace(sk));
+ t1->fin = 1;
+ t1->rst = 0;
+ 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;
+ buff->magic = TCP_WRITE_QUEUE_MAGIC;
+ } else {
+ sk->prot->queue_xmit(sk, dev, buff, 0);
+ }
+
+ if (sk->state == TCP_ESTABLISHED) sk->state = TCP_FIN_WAIT1;
+ else sk->state = TCP_FIN_WAIT2;
+
+ release_sock(sk);
+}
+
+
+static int
+tcp_recvfrom(struct sock *sk, unsigned char *to,
+ int to_len, int nonblock, unsigned flags,
+ struct sockaddr_in *addr, int *addr_len)
+{
+ struct sockaddr_in sin;
+ int len;
+ int result = tcp_read(sk, to, to_len, nonblock, flags);
+
+ if (result < 0) return(result);
+ len = get_fs_long(addr_len);
+ if (len > sizeof(sin)) len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = sk->dummy_th.dest;
+ sin.sin_addr.s_addr = sk->daddr;
+ verify_area(VERIFY_WRITE, addr, len);
+ memcpy_tofs(addr, &sin, len);
+ verify_area(VERIFY_WRITE, addr_len, sizeof(len));
+ put_fs_long(len, addr_len);
+ return(result);
+}
+
+
+/* This routine will send an RST to the other tcp. */
+static void
+tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
+ struct proto *prot, struct options *opt, struct device *dev)
+{
+ struct sk_buff *buff;
+ struct tcphdr *t1;
+ int tmp;
+
+ /*
+ * We need to grab some memory, and put together an RST,
+ * and then put it into the queue to be sent.
+ */
+ buff = prot->wmalloc(NULL, MAX_RESET_SIZE, 1, GFP_ATOMIC);
+ if (buff == NULL) return;
+
+ DPRINTF((DBG_TCP, "tcp_reset buff = %X\n", buff));
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_RESET_SIZE;
+ buff->lock = 0;
+ buff->len = sizeof(*t1);
+ buff->sk = NULL;
+ buff->dev = dev;
+
+ t1 =(struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
+ sizeof(struct tcphdr));
+ if (tmp < 0) {
+ prot->wfree(NULL, buff->mem_addr, buff->mem_len);
+ return;
+ }
+ t1 =(struct tcphdr *)((char *)t1 +tmp);
+ buff->len += tmp;
+ memcpy(t1, th, sizeof(*t1));
+
+ /* Wwap 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->window = 0; /* should be set to 0 -FB */
+ 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.
+ * It 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(struct sock *sk, struct sk_buff *skb,
+ unsigned long daddr, unsigned long saddr,
+ struct options *opt, struct device *dev)
+{
+ struct sk_buff *buff;
+ struct tcphdr *t1;
+ unsigned char *ptr;
+ struct sock *newsk;
+ struct tcphdr *th;
+ int tmp;
+
+ DPRINTF((DBG_TCP, "tcp_conn_request(sk = %X, skb = %X, daddr = %X, sadd4= %X, \n"
+ " opt = %X, dev = %X)\n",
+ sk, skb, daddr, saddr, opt, dev));
+
+ th = skb->h.th;
+
+ /* If the socket is dead, don't accept the connection. */
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ } else {
+ DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
+ tcp_reset(daddr, saddr, th, sk->prot, opt, dev);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Make sure we can accept more. This will prevent a
+ * flurry of syns from eating up all our memory.
+ */
+ if (sk->ack_backlog >= sk->max_ack_backlog) {
+ kfree_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 = kmalloc(sizeof(struct sock), GFP_ATOMIC);
+ if (newsk == NULL) {
+ /* just ignore the syn. It will get retransmitted. */
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ DPRINTF((DBG_TCP, "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->rtt = TCP_CONNECT_TIME;
+ newsk->blog = 0;
+ newsk->intr = 0;
+ newsk->proc = 0;
+ newsk->done = 0;
+ newsk->send_tmp = NULL;
+ 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->h.th + 1);
+ if (ptr[0] != 2 || ptr[1] != 4) {
+ newsk->mtu = dev->mtu - HEADER_SIZE;
+ } else {
+ newsk->mtu = min(ptr[2] * 256 + ptr[3] - HEADER_SIZE,
+ dev->mtu - HEADER_SIZE);
+ }
+ }
+
+ buff = newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
+ if (buff == NULL) {
+ sk->err = -ENOMEM;
+ newsk->dead = 1;
+ release_sock(newsk);
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ buff->lock = 0;
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_SYN_SIZE;
+ buff->len = sizeof(struct tcphdr)+4;
+ buff->sk = newsk;
+
+ t1 =(struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev,
+ IPPROTO_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);
+ skb->sk = sk;
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ buff->len += tmp;
+ t1 =(struct tcphdr *)((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 = ntohl(newsk->send_seq++);
+ t1->ack = 1;
+ newsk->window = newsk->prot->rspace(newsk);
+ t1->window = ntohs(newsk->window);
+ t1->res1 = 0;
+ t1->res2 = 0;
+ t1->rst = 0;
+ t1->urg = 0;
+ t1->psh = 0;
+ t1->syn = 1;
+ t1->ack_seq = ntohl(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;
+ DPRINTF((DBG_TCP, "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;
+ }
+ sk->ack_backlog++;
+ release_sock(newsk);
+}
+
+
+static void
+tcp_close(struct sock *sk, int timeout)
+{
+ struct sk_buff *buff;
+ int need_reset = 0;
+ struct tcphdr *t1, *th;
+ struct proto *prot;
+ struct device *dev=NULL;
+ int tmp;
+
+ /*
+ * We need to grab some memory, and put together a FIN,
+ * and then put it into the queue to be sent.
+ */
+ DPRINTF((DBG_TCP, "tcp_close((struct sock *)%X, %d)\n",sk, timeout));
+ sk->inuse = 1;
+ sk->keepopen = 1;
+ 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 =(struct sk_buff *)skb->next;
+ /* if there is some real unread data, send a reset. */
+ if (skb->len > 0 &&
+ after(skb->h.th->seq + skb->len + 1, sk->copied_seq))
+ need_reset = 1;
+ kfree_skb(skb, FREE_READ);
+ skb = skb2;
+ } while(skb != sk->rqueue);
+ }
+ sk->rqueue = NULL;
+
+ /* Get rid off any half-completed packets. */
+ if (sk->send_tmp) {
+ tcp_send_partial(sk);
+ }
+
+ switch(sk->state) {
+ case TCP_FIN_WAIT1:
+ case TCP_FIN_WAIT2:
+ case TCP_LAST_ACK:
+ /* start a timer. */
+ sk->time_wait.len = 4*sk->rtt;;
+ sk->timeout = TIME_CLOSE;
+ reset_timer((struct timer *)&sk->time_wait);
+ if (timeout) tcp_time_wait(sk);
+ release_sock(sk);
+ 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_CLOSE_WAIT:
+ case TCP_ESTABLISHED:
+ case TCP_SYN_SENT:
+ case TCP_SYN_RECV:
+ prot =(struct proto *)sk->prot;
+ th =(struct tcphdr *)&sk->dummy_th;
+ buff = prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC);
+ if (buff == NULL) {
+ /* This will force it to try again later. */
+ if (sk->state != TCP_CLOSE_WAIT)
+ 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->lock = 0;
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_FIN_SIZE;
+ buff->sk = sk;
+ buff->len = sizeof(*t1);
+ t1 =(struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
+ IPPROTO_TCP, sk->opt,
+ sizeof(struct tcphdr));
+ if (tmp < 0) {
+ prot->wfree(sk,buff->mem_addr, buff->mem_len);
+ DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
+ release_sock(sk);
+ return;
+ }
+
+ t1 =(struct tcphdr *)((char *)t1 +tmp);
+ buff ->len += tmp;
+ buff->dev = dev;
+ memcpy(t1, th, sizeof(*t1));
+ t1->seq = ntohl(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 = ntohl(sk->acked_seq);
+ t1->window = ntohs(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;
+ buff->magic = TCP_WRITE_QUEUE_MAGIC;
+ }
+
+ if (sk->state == TCP_CLOSE_WAIT) {
+ sk->state = TCP_FIN_WAIT2;
+ } else {
+ 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(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ DPRINTF((DBG_TCP, "tcp_write_xmit(sk=%X)\n", sk));
+ while(sk->wfront != NULL &&
+ before(sk->wfront->h.seq, sk->window_seq) &&
+ sk->packets_out < sk->cong_window) {
+ skb = sk->wfront;
+ sk->wfront =(struct sk_buff *)skb->next;
+ if (sk->wfront == NULL) sk->wback = NULL;
+ skb->next = NULL;
+ if (skb->magic != TCP_WRITE_QUEUE_MAGIC) {
+ DPRINTF((DBG_TCP, "tcp.c skb with bad magic(%X) on write queue. Squashing "
+ "queue\n", skb->magic));
+ sk->wfront = NULL;
+ sk->wback = NULL;
+ return;
+ }
+ skb->magic = 0;
+ DPRINTF((DBG_TCP, "Sending a packet.\n"));
+
+ /* See if we really need to send the packet. */
+ if (before(skb->h.seq, sk->rcv_ack_seq +1)) {
+ sk->retransmits = 0;
+ kfree_skb(skb, FREE_WRITE);
+ if (!sk->dead) wake_up(sk->sleep);
+ } else {
+ sk->prot->queue_xmit(sk, skb->dev, skb, skb->free);
+ }
+ }
+}
+
+
+/*
+ * This routine sorts the send list, and resets the
+ * sk->send_head and sk->send_tail pointers.
+ */
+void
+sort_send(struct sock *sk)
+{
+ struct sk_buff *list = NULL;
+ struct sk_buff *skb,*skb2,*skb3;
+
+ for (skb = sk->send_head; skb != NULL; skb = skb2) {
+ skb2 = (struct sk_buff *)skb->link3;
+ if (list == NULL || before (skb2->h.seq, list->h.seq)) {
+ skb->link3 = list;
+ sk->send_tail = skb;
+ list = skb;
+ } else {
+ for (skb3 = list; ; skb3 = (struct sk_buff *)skb3->link3) {
+ if (skb3->link3 == NULL ||
+ before(skb->h.seq, skb3->link3->h.seq)) {
+ skb->link3 = skb3->link3;
+ skb3->link3 = skb;
+ if (skb->link3 == NULL) sk->send_tail = skb;
+ break;
+ }
+ }
+ }
+ }
+ sk->send_head = list;
+}
+
+
+/* This routine deals with incoming acks, but not outgoing ones. */
+static int
+tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
+{
+ unsigned long ack;
+ int flag = 0;
+
+ ack = ntohl(th->ack_seq);
+ DPRINTF((DBG_TCP, "tcp_ack ack=%d, window=%d, "
+ "sk->rcv_ack_seq=%d, sk->window_seq = %d\n",
+ ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq));
+
+ if (after(ack, sk->send_seq+1) || before(ack, sk->rcv_ack_seq-1)) {
+ if (after(ack, sk->send_seq) ||
+ (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)) {
+ return(0);
+ }
+ if (sk->keepopen) {
+ sk->time_wait.len = TCP_TIMEOUT_LEN;
+ sk->timeout = TIME_KEEPOPEN;
+ reset_timer((struct timer *)&sk->time_wait);
+ }
+ return(1);
+ }
+
+ if (len != th->doff*4) flag |= 1;
+
+ /* See if our window has been shrunk. */
+ if (after(sk->window_seq, ack+ntohs(th->window))) {
+ /*
+ * We may need to move packets from the send queue
+ * to the write queue, if the window has been shrunk on us.
+ * The RFC says you are not allowed to shrink your window
+ * like this, but if the other end does, you must be able
+ * to deal with it.
+ */
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ struct sk_buff *wskb = NULL;
+
+ skb2 = sk->send_head;
+ sk->send_head = NULL;
+ sk->send_tail = NULL;
+
+ flag |= 4;
+
+ sk->window_seq = ack + ntohs(th->window);
+ cli();
+ while (skb2 != NULL) {
+ skb = skb2;
+ skb2 = (struct sk_buff *)skb->link3;
+ skb->link3 = NULL;
+ if (after(skb->h.seq, sk->window_seq)) {
+ if (sk->packets_out > 0) sk->packets_out--;
+
+ /* We may need to remove this from the dev send list. */
+ if (skb->next != NULL) {
+ int i;
+
+ if (skb->next != skb) {
+ skb->next->prev = skb->prev;
+ skb->prev->next = skb->next;
+ }
+
+ for(i = 0; i < DEV_NUMBUFFS; i++) {
+ if (skb->dev->buffs[i] == skb) {
+ if (skb->next == skb)
+ skb->dev->buffs[i] = NULL;
+ else
+ skb->dev->buffs[i] = skb->next;
+ break;
+ }
+ }
+ if (arp_q == skb) {
+ if (skb->next == skb) arp_q = NULL;
+ else arp_q = skb->next;
+ }
+ }
+
+ /* Now add it to the write_queue. */
+ skb->magic = TCP_WRITE_QUEUE_MAGIC;
+ if (wskb == NULL) {
+ skb->next = sk->wfront;
+ sk->wfront = skb;
+ } else {
+ skb->next = wskb->next;
+ wskb->next = skb;
+ }
+ if (sk->wback == wskb) sk->wback = skb;
+ wskb = skb;
+ } else {
+ if (sk->send_head == NULL) {
+ sk->send_head = skb;
+ sk->send_tail = skb;
+ } else {
+ sk->send_tail->link3 = skb;
+ sk->send_tail = skb;
+ }
+ skb->link3 = NULL;
+ }
+ }
+ sti();
+ }
+
+ if (sk->send_tail == NULL || sk->send_head == NULL) {
+ sk->send_head = NULL;
+ sk->send_tail = NULL;
+ sk->packets_out= 0;
+ }
+
+ sk->window_seq = ack + ntohs(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++;
+ }
+
+ DPRINTF((DBG_TCP, "tcp_ack: Updating rcv ack sequence.\n"));
+ sk->rcv_ack_seq = ack;
+
+ /* See if we can take anything off of the retransmit queue. */
+ while(sk->send_head != NULL) {
+ /* Check for a bug. */
+ if (sk->send_head->link3 &&
+ after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) {
+ printk("INET: tcp.c: *** bug send_list out of order.\n");
+ sort_send(sk);
+ }
+
+ if (before(sk->send_head->h.seq, ack+1)) {
+ struct sk_buff *oskb;
+
+ sk->retransmits = 0;
+
+ /* We have one less packet out there. */
+ if (sk->packets_out > 0) sk->packets_out --;
+ DPRINTF((DBG_TCP, "skb=%X skb->h.seq = %d acked ack=%d\n",
+ sk->send_head, sk->send_head->h.seq, ack));
+
+ /* Wake up the process, it can probably write more. */
+ if (!sk->dead) wake_up(sk->sleep);
+
+ cli();
+
+ oskb = sk->send_head;
+
+ /* Estimate the RTT. Ignore the ones right after a retransmit. */
+ if (sk->retransmits == 0 && !(flag&2))
+ sk->rtt += ((jiffies - oskb->when) - sk->rtt)>>2;
+
+ flag |= 2;
+ if (sk->rtt < 1) sk->rtt = 1;
+
+ sk->send_head =(struct sk_buff *)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) {
+ int i;
+
+ if (oskb->next != oskb) {
+ oskb->next->prev = oskb->prev;
+ oskb->prev->next = oskb->next;
+ }
+ for(i = 0; i < DEV_NUMBUFFS; i++) {
+ if (oskb->dev->buffs[i] == oskb) {
+ if (oskb== oskb->next)
+ oskb->dev->buffs[i]= NULL;
+ else
+ oskb->dev->buffs[i] = oskb->next;
+ break;
+ }
+ }
+ if (arp_q == oskb) {
+ if (oskb == oskb->next) arp_q = NULL;
+ else arp_q =(struct sk_buff *)oskb->next;
+ }
+ }
+ oskb->magic = 0;
+ kfree_skb(oskb, FREE_WRITE); /* write. */
+ sti();
+ if (!sk->dead) wake_up(sk->sleep);
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * Maybe we can take some stuff off of the write queue,
+ * and put it onto the xmit queue.
+ */
+ if (sk->wfront != NULL) {
+ if (after (sk->window_seq, sk->wfront->h.seq) &&
+ sk->packets_out < sk->cong_window) {
+ flag |= 1;
+ tcp_write_xmit(sk);
+ }
+ } else {
+ if (sk->send_head == NULL && sk->ack_backlog == 0 &&
+ sk->state != TCP_TIME_WAIT && !sk->keepopen) {
+ DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n"));
+ if (!sk->dead) wake_up(sk->sleep);
+
+ delete_timer((struct timer *)&sk->time_wait);
+ sk->timeout = 0;
+ } else {
+ if (sk->state != (unsigned char) sk->keepopen) {
+ sk->timeout = TIME_WRITE;
+ sk->time_wait.len = sk->rtt*2;
+ reset_timer((struct timer *)&sk->time_wait);
+ }
+ if (sk->state == TCP_TIME_WAIT) {
+ sk->time_wait.len = TCP_TIMEWAIT_LEN;
+ reset_timer((struct timer *)&sk->time_wait);
+ sk->timeout = TIME_CLOSE;
+ }
+ }
+ }
+
+ if (sk->packets_out == 0 && sk->send_tmp != NULL &&
+ sk->wfront == NULL && sk->send_head == NULL) {
+ flag |= 1;
+ tcp_send_partial(sk);
+ }
+
+ /* See if we are done. */
+ if (sk->state == TCP_TIME_WAIT) {
+ if (!sk->dead) wake_up(sk->sleep);
+ if (sk->rcv_ack_seq == sk->send_seq && sk->acked_seq == sk->fin_seq) {
+ flag |= 1;
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ }
+ }
+
+ if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) {
+ if (!sk->dead) wake_up(sk->sleep);
+ if (sk->rcv_ack_seq == sk->send_seq) {
+ flag |= 1;
+ if (sk->acked_seq != sk->fin_seq) {
+ tcp_time_wait(sk);
+ } else {
+ DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk));
+ tcp_send_ack(sk->send_seq, sk->acked_seq, sk,
+ th, sk->daddr);
+ sk->shutdown = SHUTDOWN_MASK;
+ sk->state = TCP_CLOSE;
+ }
+ }
+ }
+
+ if (((!flag) || (flag&4)) && sk->send_head != NULL &&
+ (sk->send_head->when + sk->rtt < jiffies)) {
+ sk->exp_growth = 0;
+ ip_retransmit(sk, 0);
+ }
+
+ DPRINTF((DBG_TCP, "leaving tcp_ack\n"));
+ 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, struct sock *sk,
+ unsigned long saddr, unsigned short len)
+{
+ struct sk_buff *skb1, *skb2;
+ struct tcphdr *th;
+
+ th = skb->h.th;
+ print_th(th);
+ skb->len = len -(th->doff*4);
+
+ DPRINTF((DBG_TCP, "tcp_data len = %d sk = %X:\n", skb->len, 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);
+ kfree_skb(skb, FREE_READ);
+ return(0);
+ }
+
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
+ tcp_reset(sk->saddr, sk->daddr, skb->h.th,
+ sk->prot, NULL, skb->dev);
+ sk->state = TCP_CLOSE;
+ sk->err = EPIPE;
+ sk->shutdown = SHUTDOWN_MASK;
+ DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk));
+ kfree_skb(skb, FREE_READ);
+ if (!sk->dead) wake_up(sk->sleep);
+ 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.
+ */
+
+ /* This should start at the last one, and then go around forwards. */
+ if (sk->rqueue == NULL) {
+ DPRINTF((DBG_TCP, "tcp_data: skb = %X:\n", skb));
+
+ sk->rqueue = skb;
+ skb->next = skb;
+ skb->prev = skb;
+ skb1= NULL;
+ } else {
+ DPRINTF((DBG_TCP, "tcp_data adding to chain sk = %X:\n", sk));
+
+ for(skb1=sk->rqueue; ; skb1 =(struct sk_buff *)skb1->prev) {
+ DPRINTF((DBG_TCP, "skb1=%X\n", skb1));
+ DPRINTF((DBG_TCP, "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;
+ }
+ }
+ DPRINTF((DBG_TCP, "skb = %X:\n", skb));
+ }
+
+ 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)) {
+ if (after(th->ack_seq, sk->acked_seq))
+ sk->acked_seq = th->ack_seq;
+ skb->acked = 1;
+
+ /* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */
+ if (skb->h.th->fin) {
+ if (!sk->dead) wake_up(sk->sleep);
+ sk->shutdown |= RCV_SHUTDOWN;
+ }
+
+ for(skb2 = (struct sk_buff *)skb->next;
+ skb2 !=(struct sk_buff *) sk->rqueue->next;
+ skb2 = (struct sk_buff *)skb2->next) {
+ if (before(skb2->h.th->seq, sk->acked_seq+1)) {
+ if (after(skb2->h.th->ack_seq, sk->acked_seq))
+ sk->acked_seq = skb2->h.th->ack_seq;
+ skb2->acked = 1;
+
+ /*
+ * When we ack the fin, we turn on
+ * the RCV_SHUTDOWN flag.
+ */
+ if (skb2->h.th->fin) {
+ sk->shutdown |= RCV_SHUTDOWN;
+ if (!sk->dead) wake_up(sk->sleep);
+ }
+
+ /* 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->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);
+ }
+ }
+ }
+
+ /*
+ * If we've missed a packet, send an ack.
+ * Also start a timer to send another.
+ */
+ if (!skb->acked) {
+ /*
+ * This is important. If we don't have much room left,
+ * we need to throw out a few packets so we have a good
+ * window.
+ */
+ while (sk->prot->rspace(sk) < sk->mtu) {
+ skb1 = (struct sk_buff *)sk->rqueue;
+ if (skb1 == NULL) {
+ printk("INET: tcp.c:tcp_data memory leak detected.\n");
+ break;
+ }
+
+ /* Don't throw out something that has been acked. */
+ if (skb1->acked) {
+ break;
+ }
+ if (skb1->prev == skb1) {
+ sk->rqueue = NULL;
+ } else {
+ sk->rqueue = (struct sk_buff *)skb1->prev;
+ skb1->next->prev = skb1->prev;
+ skb1->prev->next = skb1->next;
+ }
+ kfree_skb(skb1, FREE_READ);
+ }
+ tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ 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 {
+ DPRINTF((DBG_TCP, "data received on dead socket.\n"));
+ }
+
+ if (sk->state == TCP_FIN_WAIT2 &&
+ sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->send_seq) {
+ DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk));
+
+ tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ sk->shutdown = SHUTDOWN_MASK;
+ sk->state = TCP_LAST_ACK;
+ if (!sk->dead) wake_up(sk->sleep);
+ }
+
+ return(0);
+}
+
+
+static int
+tcp_urg(struct sock *sk, struct tcphdr *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);
+ }
+
+ if (!sk->urg) {
+ /* So if we get more urgent data, we don't signal the user again. */
+ if (sk->proc != 0) {
+ if (sk->proc > 0) {
+ kill_proc(sk->proc, SIGURG, 1);
+ } else {
+ kill_pg(-sk->proc, SIGURG, 1);
+ }
+ }
+ }
+ sk->urg++;
+ return(0);
+}
+
+
+/* This deals with incoming fins. */
+static int
+tcp_fin(struct sock *sk, struct tcphdr *th,
+ unsigned long saddr, struct device *dev)
+{
+ DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n",
+ sk, th, saddr, dev));
+
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+
+ switch(sk->state) {
+ case TCP_SYN_RECV:
+ case TCP_SYN_SENT:
+ case TCP_ESTABLISHED:
+ /* Contains the one that needs to be acked */
+ sk->fin_seq = th->seq+1;
+ sk->state = TCP_CLOSE_WAIT;
+ if (th->rst) sk->shutdown = SHUTDOWN_MASK;
+ break;
+
+ case TCP_CLOSE_WAIT:
+ case TCP_FIN_WAIT2:
+ break; /* we got a retransmit of the fin. */
+
+ case TCP_FIN_WAIT1:
+ /* Contains the one that needs to be acked */
+ sk->fin_seq = th->seq+1;
+ sk->state = TCP_FIN_WAIT2;
+ break;
+
+ default:
+ 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);
+ }
+ sk->ack_backlog++;
+
+ return(0);
+}
+
+
+/* This will accept the next outstanding connection. */
+static struct sock *
+tcp_accept(struct sock *sk, int flags)
+{
+ struct sock *newsk;
+ struct sk_buff *skb;
+
+ DPRINTF((DBG_TCP, "tcp_accept(sk=%X, flags=%X, addr=%s)\n",
+ sk, flags, in_ntoa(sk->saddr)));
+
+ /*
+ * 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. */
+ cli();
+ sk->inuse = 1;
+ 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;
+
+ kfree_skb(skb, FREE_READ);
+ sk->ack_backlog--;
+ release_sock(sk);
+ return(newsk);
+}
+
+
+/* This will initiate an outgoing connection. */
+static int
+tcp_connect(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 tcphdr *t1;
+
+ if (sk->state != TCP_CLOSE) return(-EISCONN);
+ if (addr_len < 8) return(-EINVAL);
+
+ /* verify_area(VERIFY_WRITE, usin, addr_len);*/
+ memcpy_fromfs(&sin,usin, min(sizeof(sin), addr_len));
+
+ if (sin.sin_family && sin.sin_family != AF_INET) return(-EAFNOSUPPORT);
+
+ DPRINTF((DBG_TCP, "TCP connect daddr=%s\n", in_ntoa(sin.sin_addr.s_addr)));
+
+ /* Don't want a TCP connection going to a broadcast address */
+ if (chk_addr(sin.sin_addr.s_addr) == IS_BROADCAST) {
+ DPRINTF((DBG_TCP, "TCP connection to broadcast address not allowed\n"));
+ return(-ENETUNREACH);
+ }
+ sk->inuse = 1;
+ 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;
+ release_sock(sk);
+
+ buff=sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
+ if (buff == NULL) {
+ return(-ENOMEM);
+ }
+ sk->inuse = 1;
+ buff->lock = 0;
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_SYN_SIZE;
+ buff->len = 24;
+ buff->sk = sk;
+ t1 = (struct tcphdr *)(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,
+ IPPROTO_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 tcphdr *)((char *)t1 +tmp);
+
+ memcpy(t1,(void *)&(sk->dummy_th), sizeof(*t1));
+ t1->seq = ntohl(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 tcphdr) + 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;
+ sk->rtt = TCP_CONNECT_TIME;
+ reset_timer((struct timer *)&sk->time_wait);
+ sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
+ release_sock(sk);
+ return(0);
+}
+
+
+/* This functions checks to see if the tcp header is actually acceptible. */
+static int
+tcp_sequence(struct sock *sk, struct tcphdr *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.
+ */
+ DPRINTF((DBG_TCP, "tcp_sequence(sk=%X, th=%X, len = %d, opt=%d, saddr=%X)\n",
+ sk, th, len, opt, saddr));
+
+ if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)||
+ between(th->seq + len-(th->doff*4), sk->acked_seq + 1,
+ sk->acked_seq + sk->window) ||
+ (before(th->seq, sk->acked_seq) &&
+ after(th->seq + len -(th->doff*4), sk->acked_seq + sk->window))) {
+ return(1);
+ }
+ DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n"));
+
+ /*
+ * 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);
+ }
+
+ /* In case it's just a late ack, let it through. */
+ if (th->ack && len == (th->doff * 4) &&
+ after(th->seq, sk->acked_seq - 32767) &&
+ !th->fin && !th->syn) return(1);
+
+ if (!th->rst) {
+ /* Try to resync things. */
+ tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr);
+ }
+ return(0);
+}
+
+
+/* This deals with the tcp option. It isn't very general yet. */
+static void
+tcp_options(struct sock *sk, struct tcphdr *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 inet_protocol * protocol)
+{
+ struct tcphdr *th;
+ struct sock *sk;
+
+ if (!skb) {
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n"));
+ return(0);
+ }
+#if 0 /* FIXME: it's ok for protocol to be NULL */
+ if (!protocol) {
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv protocol = NULL\n"));
+ return(0);
+ }
+
+ if (!opt) { /* FIXME: it's ok for opt to be NULL */
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv opt = NULL\n"));
+ }
+#endif
+ if (!dev) {
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n"));
+ return(0);
+ }
+ th = skb->h.th;
+
+ /* Find the socket. */
+ sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
+ DPRINTF((DBG_TCP, "<<\n"));
+ DPRINTF((DBG_TCP, "len = %d, redo = %d, skb=%X\n", len, redo, skb));
+
+ if (sk) {
+ DPRINTF((DBG_TCP, "sk = %X:\n", sk));
+ }
+
+ if (!redo) {
+ if (th->check && tcp_check(th, len, saddr, daddr )) {
+ skb->sk = NULL;
+ DPRINTF((DBG_TCP, "packet dropped with bad checksum.\n"));
+ kfree_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;
+ kfree_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 = ntohl(th->seq);
+
+ /* We may need to add it to the backlog here. */
+ 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();
+ } else {
+ if (!sk) {
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv bug sk=NULL redo = 1\n"));
+ return(0);
+ }
+ }
+
+ if (!sk->prot) {
+ DPRINTF((DBG_TCP, "tcp.c: tcp_rcv sk->prot = NULL \n"));
+ return(0);
+ }
+
+ /* Charge the memory to the socket. */
+ if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX) {
+ skb->sk = NULL;
+ DPRINTF((DBG_TCP, "dropping packet due to lack of buffer space.\n"));
+ kfree_skb(skb, 0);
+ release_sock(sk);
+ return(0);
+ }
+ sk->rmem_alloc += skb->mem_len;
+
+ DPRINTF((DBG_TCP, "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;
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ case TCP_ESTABLISHED:
+ case TCP_CLOSE_WAIT:
+ case TCP_FIN_WAIT1:
+ case TCP_FIN_WAIT2:
+ case TCP_TIME_WAIT:
+ if (!tcp_sequence(sk, th, len, opt, saddr)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ if (th->rst) {
+ /* This means the thing should really be closed. */
+ sk->err = ECONNRESET;
+
+ if (sk->state == TCP_CLOSE_WAIT) {
+ sk->err = EPIPE;
+ }
+
+ /*
+ * A reset with a fin just means that
+ * the data was not all read.
+ */
+ if (!th->fin) {
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ }
+#if 0
+ if (opt && (opt->security != 0 ||
+ opt->compartment != 0 || th->syn)) {
+ sk->err = ECONNRESET;
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+#endif
+ if (th->ack) {
+ if (!tcp_ack(sk, th, saddr, len)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ }
+ if (th->urg) {
+ if (tcp_urg(sk, th, saddr)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ }
+
+ if (th->fin && tcp_fin(sk, th, saddr, dev)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ if (tcp_data(skb, sk, saddr, len)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ release_sock(sk);
+ return(0);
+
+ case TCP_CLOSE:
+ if (sk->dead || sk->daddr) {
+ DPRINTF((DBG_TCP, "packet received for closed,dead socket\n"));
+ kfree_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);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+
+ case TCP_LISTEN:
+ if (th->rst) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ if (th->ack) {
+ tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ if (th->syn) {
+#if 0
+ if (opt->security != 0 || opt->compartment != 0) {
+ tcp_reset(daddr, saddr, th, prot, opt,dev);
+ release_sock(sk);
+ return(0);
+ }
+#endif
+
+ /*
+ * 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);
+ }
+
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+
+ default:
+ if (!tcp_sequence(sk, th, len, opt, saddr)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ case TCP_SYN_SENT:
+ if (th->rst) {
+ sk->err = ECONNREFUSED;
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+#if 0
+ if (opt->security != 0 || opt->compartment != 0) {
+ sk->err = ECONNRESET;
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ tcp_reset(daddr, saddr, th, sk->prot, opt, dev);
+ if (!sk->dead) {
+ wake_up(sk->sleep);
+ }
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+#endif
+ if (!th->ack) {
+ if (th->syn) {
+ sk->state = TCP_SYN_RECV;
+ }
+
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+
+ switch(sk->state) {
+ case TCP_SYN_SENT:
+ if (!tcp_ack(sk, th, saddr, len)) {
+ tcp_reset(daddr, saddr, th,
+ sk->prot, opt,dev);
+ kfree_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) {
+ kfree_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, len)) {
+ tcp_reset(daddr, saddr, th,
+ sk->prot, opt, dev);
+ kfree_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)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ }
+ if (tcp_data(skb, sk, saddr, len))
+ kfree_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)) {
+ kfree_skb(skb, FREE_READ);
+ release_sock(sk);
+ return(0);
+ }
+ }
+
+ if (tcp_data(skb, sk, saddr, len)) {
+ kfree_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(struct sock *sk)
+{
+ struct sk_buff *buff;
+ struct tcphdr *t1;
+ struct device *dev=NULL;
+ int tmp;
+
+ if (sk -> state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) return;
+
+ buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
+ if (buff == NULL) return;
+
+ buff->lock = 0;
+ buff->mem_addr = buff;
+ buff->mem_len = MAX_ACK_SIZE;
+ buff->len = sizeof(struct tcphdr);
+ buff->free = 1;
+ buff->sk = sk;
+ DPRINTF((DBG_TCP, "in tcp_write_wakeup\n"));
+ t1 = (struct tcphdr *)(buff + 1);
+
+ /* Put in the IP header and routing stuff. */
+ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
+ if (tmp < 0) {
+ sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
+ return;
+ }
+
+ buff->len += tmp;
+ t1 = (struct tcphdr *)((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 = ntohl(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 = ntohl(sk->acked_seq);
+ t1->window = ntohs(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,
+ tcp_sendto,
+ tcp_recvfrom,
+ 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,
+ tcp_shutdown,
+ 128,
+ 0,
+ {NULL,},
+ "TCP"
+};
diff --git a/net/inet/tcp.h b/net/inet/tcp.h
new file mode 100644
index 0000000..637247e
--- /dev/null
+++ b/net/inet/tcp.h
@@ -0,0 +1,140 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP module.
+ *
+ * Version: @(#)tcp.h 1.0.5 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TCP_H
+#define _TCP_H
+
+#include <linux/tcp.h>
+
+#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 4096
+#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 15 /*
+ * This should take at least
+ * 90 minutes to time out.
+ */
+
+#define TCP_TIMEOUT_LEN 720000 /* should be about 2 hrs */
+#define TCP_TIMEWAIT_LEN 1000 /* how long to wait to sucessfully
+ * close the socket, about 60 seconds */
+#define TCP_ACK_TIME 30000 /* time to delay before sending an ACK */
+#define TCP_DONE_TIME 250 /* maximum time to wait before actually
+ * destroying a socket */
+#define TCP_WRITE_TIME 30000 /* initial time to wait for an ACK,
+ * after last transmit */
+#define TCP_CONNECT_TIME 2000 /* time to retransmit first SYN */
+#define TCP_SYN_RETRIES 5 /* number of times to retry openning a
+ * connection */
+#define TCP_PROBEWAIT_LEN 100 /* time to wait between probes when
+ * I've got something to write and
+ * there is no window */
+
+#define TCP_NO_CHECK 0 /* turn to one if you want the default
+ * to be no checksum */
+
+#define TCP_WRITE_QUEUE_MAGIC 0xa5f23477
+
+/*
+ * The next routines deal with comparing 32 bit unsigned ints
+ * and worry about wraparound. 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 < 65536UL) {
+ 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 < 8192UL && seq1 > (0xffffffffUL - 8192UL)) {
+ 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));
+}
+
+
+/*
+ * List all states of a TCP socket that can be viewed as a "connected"
+ * state. This now includes TCP_SYN_RECV, although I am not yet fully
+ * convinced that this is the solution for the 'getpeername(2)'
+ * problem. Thanks to Stephen A. Wood <saw@cebaf.gov> -FvK
+ */
+static inline const int
+tcp_connected(const int state)
+{
+ return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
+ state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2 ||
+ state == TCP_SYN_RECV);
+}
+
+
+extern struct proto tcp_prot;
+
+
+extern void print_th(struct tcphdr *);
+extern void tcp_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern void tcp_shutdown (struct sock *sk, int how);
+extern 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 inet_protocol *protocol);
+
+extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+
+#endif /* _TCP_H */
diff --git a/net/inet/timer.c b/net/inet/timer.c
new file mode 100644
index 0000000..1b337f7
--- /dev/null
+++ b/net/inet/timer.c
@@ -0,0 +1,323 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * TIMER - implementation of software timers.
+ *
+ * Version: @(#)timer.c 1.0.7 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <linux/interrupt.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+struct timer *timer_base = NULL;
+unsigned long seq_offset;
+
+
+void
+delete_timer(struct timer *t)
+{
+ struct timer *tm;
+
+ DPRINTF((DBG_TMR, "delete_timer(t=%X)\n", t));
+ cli();
+ if (timer_base == NULL || t == NULL) {
+ t->running = 9;
+ sti();
+ return;
+ }
+ 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);
+ }
+ t->running = 0;
+ sti();
+ return;
+ }
+ tm = timer_base;
+ while (tm != NULL) {
+ if (tm->next == t) {
+ tm->next = t->next;
+ t->running = 0;
+ sti();
+ return;
+ }
+ tm = tm->next;
+ }
+ sti();
+ t->running = 9;
+}
+
+
+void
+reset_timer(struct timer *t)
+{
+ struct timer *tm;
+
+ DPRINTF((DBG_TMR, "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"::);
+ }
+ if (t->running) {
+ DPRINTF((DBG_TMR, "t->running has value of %d, len %d\n",
+ t->running, t->len));
+ }
+ t->running = 1;
+ delete_timer(t); /* here is another race condition ! */
+ cli(); /* what about a new reset_timer while being\ */
+ if (!((t->running == 0) || (t->running == 9))) { /* about to install on old one ? -FB */
+ printk("reset_timer(): t->running after delete_timer: %d !\n",
+ t->running);
+ sti();
+ return;
+ }
+ t->running = 2;
+ if ((int) t->len < 0) /* prevent close to infinite timers. THEY _DO_ */
+ t->len = 3; /* happen (negative values ?) - don't ask me why ! -FB */
+
+ delete_timer(t);
+ t->when = timer_seq + t->len;
+
+ /* First see if it goes at the beginning. */
+
+ if (timer_base == NULL) {
+ t->next = NULL;
+ timer_base = t;
+ timer_table[NET_TIMER].expires = timer_base->when;
+ timer_active |= (1 << NET_TIMER);
+ t->running = 3;
+ sti();
+ return;
+ }
+ if (before(t->when, timer_base->when)) {
+ t->next = timer_base;
+ timer_base = t;
+ timer_table[NET_TIMER].expires = timer_base->when;
+ timer_active |= (1 << NET_TIMER);
+ t->running = 4;
+
+ sti();
+ return;
+ }
+ tm = timer_base;
+ while (tm != NULL) {
+ if (tm->next == NULL || t->when < tm->next->when) {
+ t->next = tm->next;
+ tm->next = t;
+ timer_table[NET_TIMER].expires = timer_base->when;
+ timer_active |= (1 << NET_TIMER);
+ t->running = 5;
+ sti();
+ return;
+ }
+ tm = tm->next;
+ }
+ sti();
+}
+
+
+/*
+ * 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.
+ */
+void
+net_timer(void)
+{
+ struct sock *sk;
+ struct timer *tm;
+
+ cli(); /* a timer might expire and a new one with */
+ /* earlier expiration could be inserted before -FB */
+ tm = timer_base;
+ while ((tm != NULL) && (jiffies >= tm->when)) {
+ int why;
+
+ sk = tm->sk;
+ if (sk->inuse) {
+ break;
+ }
+ sk->inuse = 1;
+ sti();
+ why = sk->timeout;
+
+ DPRINTF((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why));
+ if (sk->keepopen) {
+ sk->time_wait.len = TCP_TIMEOUT_LEN;
+ sk->timeout = TIME_KEEPOPEN;
+ reset_timer(tm);
+ } else {
+ sk->timeout = 0;
+ delete_timer(tm);
+ }
+
+ /* 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.
+ */
+ DPRINTF((DBG_TMR, "possible memory leak. sk = %X\n", sk));
+ destroy_sock(sk);
+ sk->inuse = 0;
+ break;
+ case TIME_CLOSE:
+ /* We've waited long enough, close the socket. */
+ sk->state = TCP_CLOSE;
+ delete_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);
+ sk->shutdown = SHUTDOWN_MASK;
+ sk->time_wait.len = TCP_DONE_TIME;
+ sk->timeout = TIME_DESTROY;
+ reset_timer (&sk->time_wait);
+ release_sock(sk);
+ break;
+ case TIME_WRITE: /* try to retransmit. */
+ /*
+ * It could be we got here because we
+ * needed to send an ack. So we need
+ * to check for that.
+ */
+ if (sk->send_head != NULL) {
+ if (before(jiffies, sk->send_head->when +
+ sk->rtt)) {
+ sk->time_wait.len = sk->rtt;
+ sk->timeout = TIME_WRITE;
+ reset_timer(&sk->time_wait);
+ release_sock(sk);
+ break;
+ }
+ DPRINTF((DBG_TMR, "retransmitting.\n"));
+ sk->prot->retransmit(sk, 0);
+
+ if (sk->retransmits > TCP_RETR1) {
+ DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 1\n"));
+ arp_destroy(sk->daddr);
+ ip_route_check(sk->daddr);
+ }
+
+ if (sk->retransmits > TCP_RETR2) {
+ DPRINTF((DBG_TMR, "timer.c TIME_WRITE time-out 2\n"));
+ sk->err = ETIMEDOUT;
+ 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(&sk->time_wait);
+ } else {
+ sk->prot->close(sk,1);
+ break;
+ }
+ }
+ }
+ release_sock(sk);
+ break;
+ case TIME_KEEPOPEN:
+ /* Send something to keep the connection open. */
+ if (sk->prot->write_wakeup != NULL)
+ sk->prot->write_wakeup(sk);
+ sk->retransmits ++;
+ if (sk->shutdown == SHUTDOWN_MASK) {
+ sk->prot->close(sk,1);
+ sk->state = TCP_CLOSE;
+ }
+
+ if (sk->retransmits > TCP_RETR1) {
+ DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n"));
+ arp_destroy(sk->daddr);
+ ip_route_check(sk->daddr);
+ release_sock(sk);
+ break;
+ }
+ if (sk->retransmits > TCP_RETR2) {
+ DPRINTF((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n"));
+ 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->prot->close (sk, 1);
+ }
+ break;
+ }
+ release_sock(sk);
+ break;
+ default:
+ printk("net timer expired - reason unknown, sk=%08X\n",
+ (int) sk);
+ release_sock(sk);
+ break;
+ }
+ cli();
+ tm = 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;
+ }
+ sti();
+}
diff --git a/net/inet/timer.h b/net/inet/timer.h
new file mode 100644
index 0000000..39f6bd7
--- /dev/null
+++ b/net/inet/timer.h
@@ -0,0 +1,43 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TIMER module.
+ *
+ * Version: @(#)timer.h 1.0.2 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TIMER_H
+#define _TIMER_H
+
+
+#define SEQ_TICK 3
+#define timer_seq jiffies
+
+
+struct timer {
+ unsigned long len;
+ struct sock *sk;
+ unsigned long when;
+ int running;
+ struct timer *next;
+};
+
+
+extern unsigned long seq_offset;
+
+
+extern void delete_timer(struct timer *);
+extern void reset_timer(struct timer *);
+extern void net_timer(void);
+
+
+#endif /* _TIMER_H */
diff --git a/net/inet/udp.c b/net/inet/udp.c
new file mode 100644
index 0000000..2344634
--- /dev/null
+++ b/net/inet/udp.c
@@ -0,0 +1,630 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The User Datagram Protocol (UDP).
+ *
+ * Version: @(#)udp.c 1.0.13 06/02/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/termios.h>
+#include <linux/mm.h>
+#include "inet.h"
+#include "timer.h"
+#include "dev.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "udp.h"
+#include "icmp.h"
+
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+
+static void
+print_udp(struct udphdr *uh)
+{
+ if (inet_debug != DBG_UDP) return;
+
+ if (uh == NULL) {
+ printk("(NULL)\n");
+ return;
+ }
+ printk("UDP: source = %d, dest = %d\n", ntohs(uh->source), ntohs(uh->dest));
+ printk(" len = %d, check = %d\n", ntohs(uh->len), ntohs(uh->check));
+}
+
+
+int
+udp_select(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 inet_protocol *protocol)
+{
+ struct udphdr *th;
+ struct sock *sk;
+
+ DPRINTF((DBG_UDP,
+ "UDP: err(err=%d, header=%X, daddr=%X, saddr=%X, protocl=%X)\n",
+ err, header, daddr, saddr, protocol));
+
+ th = (struct udphdr *)header;
+ sk = get_sock(&udp_prot, 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;
+
+ /* It's only fatal if we have connected to them. */
+ if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) {
+ sk->prot->close(sk, 0);
+ }
+}
+
+
+static unsigned short
+udp_check(struct udphdr *uh, int len,
+ unsigned long saddr, unsigned long daddr)
+{
+ unsigned long sum;
+
+ DPRINTF((DBG_UDP, "UDP: check(uh=%X, len = %d, saddr = %X, daddr = %X)\n",
+ uh, len, saddr, daddr));
+
+ print_udp(uh);
+
+ __asm__("\t addl %%ecx,%%ebx\n"
+ "\t adcl %%edx,%%ebx\n"
+ "\t adcl $0, %%ebx\n"
+ : "=b"(sum)
+ : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_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 udphdr *uh, unsigned long saddr,
+ unsigned long daddr, int len, struct sock *sk)
+{
+ uh->check = 0;
+ if (sk && sk->no_check) return;
+ uh->check = udp_check(uh, len, saddr, daddr);
+}
+
+
+static int
+udp_send(struct sock *sk, struct sockaddr_in *sin,
+ unsigned char *from, int len)
+{
+ struct sk_buff *skb;
+ struct device *dev;
+ struct udphdr *uh;
+ unsigned char *buff;
+ unsigned long saddr;
+ int size, tmp;
+
+ DPRINTF((DBG_UDP, "UDP: send(dst=%s:%d buff=%X len=%d)\n",
+ in_ntoa(sin->sin_addr.s_addr), ntohs(sin->sin_port),
+ from, len));
+
+ /* Allocate a copy of the packet. */
+ size = sizeof(struct sk_buff) + sk->prot->max_header + len;
+ skb = sk->prot->wmalloc(sk, size, 0, GFP_KERNEL);
+ if (skb == NULL) return(-ENOMEM);
+
+ skb->lock = 0;
+ skb->mem_addr = skb;
+ skb->mem_len = size;
+ skb->sk = NULL;
+ skb->free = 1;
+ skb->arp = 0;
+
+ /* Now build the IP and MAC header. */
+ buff = (unsigned char *) (skb+1);
+ saddr = 0;
+ dev = NULL;
+ DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
+ saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
+ tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
+ &dev, IPPROTO_UDP, sk->opt, skb->mem_len);
+ if (tmp < 0 ) {
+ sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ return(tmp);
+ }
+ buff += tmp;
+ saddr = dev->pa_addr;
+ DPRINTF((DBG_UDP, "UDP: >> MAC+IP len=%d\n", tmp));
+
+ skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */
+ skb->dev = dev;
+
+ /*
+ * This code used to hack in some form of fragmentation.
+ * I removed that, since it didn't work anyway, and it made the
+ * code a bad thing to read and understand. -FvK
+ */
+ if (len > dev->mtu) {
+ printk("UDP: send: length %d > mtu %d (ignored)\n", len, dev->mtu);
+ sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);
+ return(-EINVAL);
+ }
+
+ /* Fill in the UDP header. */
+ uh = (struct udphdr *) buff;
+ uh->len = htons(len + sizeof(struct udphdr));
+ uh->source = sk->dummy_th.source;
+ uh->dest = sin->sin_port;
+ buff = (unsigned char *) (uh + 1);
+
+ /* Copy the user data. */
+ verify_area(VERIFY_WRITE, from, len);
+ memcpy_fromfs(buff, from, len);
+
+ /* Set up the UDP checksum. */
+ udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);
+
+ /* Send the datagram to the interface. */
+ sk->prot->queue_xmit(sk, dev, skb, 1);
+
+ return(len);
+}
+
+
+static int
+udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
+ unsigned flags, struct sockaddr_in *usin, int addr_len)
+{
+ struct sockaddr_in sin;
+ int tmp;
+
+ DPRINTF((DBG_UDP, "UDP: sendto(len=%d, flags=%X)\n", len, flags));
+
+ /* Check the flags. */
+ if (flags) return(-EINVAL);
+ if (len < 0) return(-EINVAL);
+ if (len == 0) return(0);
+
+ /* Get and verify the address. */
+ if (usin) {
+ if (addr_len < sizeof(sin)) return(-EINVAL);
+ /* verify_area(VERIFY_WRITE, 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;
+ }
+ sk->inuse = 1;
+
+ /* Send the packet. */
+ tmp = udp_send(sk, &sin, from, len);
+
+ /* The datagram has been sent off. Release the socket. */
+ release_sock(sk);
+ return(tmp);
+}
+
+
+static int
+udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,
+ unsigned flags)
+{
+ return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));
+}
+
+
+int
+udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd) {
+ case DDIOCSDBG:
+ {
+ int val;
+
+ if (!suser()) return(-EPERM);
+ verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
+ val = get_fs_long((void *)arg);
+ switch(val) {
+ case 0:
+ inet_debug = 0;
+ break;
+ case 1:
+ inet_debug = DBG_UDP;
+ break;
+ default:
+ return(-EINVAL);
+ }
+ }
+ break;
+ case TIOCOUTQ:
+ {
+ unsigned long amount;
+
+ if (sk->state == TCP_LISTEN) return(-EINVAL);
+ amount = sk->prot->wspace(sk)/2;
+ verify_area(VERIFY_WRITE,(void *)arg,
+ sizeof(unsigned long));
+ put_fs_long(amount,(unsigned long *)arg);
+ return(0);
+ }
+
+ case TIOCINQ:
+#if 0 /* FIXME: */
+ case FIONREAD:
+#endif
+ {
+ struct sk_buff *skb;
+ unsigned long amount;
+
+ if (sk->state == TCP_LISTEN) return(-EINVAL);
+ amount = 0;
+ skb = sk->rqueue;
+ if (skb != NULL) {
+ /*
+ * We will only return the amount
+ * of this packet since that is all
+ * that will be read.
+ */
+ amount = skb->len;
+ }
+ verify_area(VERIFY_WRITE,(void *)arg,
+ sizeof(unsigned long));
+ put_fs_long(amount,(unsigned long *)arg);
+ return(0);
+ }
+
+ default:
+ return(-EINVAL);
+ }
+ return(0);
+}
+
+
+/*
+ * This should be easy, if there is something there we\
+ * return it, otherwise we block.
+ */
+int
+udp_recvfrom(struct sock *sk, unsigned char *to, int len,
+ int noblock, unsigned flags, struct sockaddr_in *sin,
+ int *addr_len)
+{
+ int copied = 0;
+ struct sk_buff *skb;
+
+ if (len == 0) return(0);
+ if (len < 0) return(-EINVAL);
+
+ /*
+ * This will pick up errors that occured while the program
+ * was doing something else.
+ */
+ if (sk->err) {
+ int err;
+
+ err = -sk->err;
+ sk->err = 0;
+ return(err);
+ }
+ if (addr_len) {
+ verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len));
+ put_fs_long(sizeof(*sin), addr_len);
+ }
+ sk->inuse = 1;
+ while(sk->rqueue == NULL) {
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ return(0);
+ }
+
+ 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);
+ }
+ }
+ sk->inuse = 1;
+ sti();
+ }
+ skb = sk->rqueue;
+
+ if (!(flags & MSG_PEEK)) {
+ if (skb->next == skb) {
+ sk->rqueue = NULL;
+ } else {
+ sk->rqueue =(struct sk_buff *)sk->rqueue ->next;
+ skb->prev->next = skb->next;
+ skb->next->prev = skb->prev;
+ }
+ }
+ copied = min(len, skb->len);
+ verify_area(VERIFY_WRITE, to, copied);
+ memcpy_tofs(to, skb->h.raw + sizeof(struct udphdr), 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(VERIFY_WRITE, sin, sizeof(*sin));
+ memcpy_tofs(sin, &addr, sizeof(*sin));
+ }
+
+ if (!(flags & MSG_PEEK)) {
+ kfree_skb(skb, FREE_READ);
+ }
+ release_sock(sk);
+ return(copied);
+}
+
+
+int
+udp_read(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(struct sock *sk, struct sockaddr_in *usin, int addr_len)
+{
+ struct sockaddr_in sin;
+
+ if (addr_len < sizeof(sin)) return(-EINVAL);
+
+ /* verify_area(VERIFY_WRITE, 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(struct sock *sk, int timeout)
+{
+ sk->inuse = 1;
+ sk->state = TCP_CLOSE;
+ if (sk->dead) destroy_sock(sk);
+ else release_sock(sk);
+}
+
+
+/* All we need to do is get the socket, and then do a checksum. */
+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 inet_protocol *protocol)
+{
+ struct sock *sk;
+ struct udphdr *uh;
+
+ uh = (struct udphdr *) skb->h.uh;
+ sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
+ if (sk == NULL) {
+ if (chk_addr(daddr) == IS_MYADDR)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
+
+ /*
+ * Hmm. We got an UDP broadcast to a port to which we
+ * don't wanna listen. The only thing we can do now is
+ * to ignore the packet... -FvK
+ */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ if (!redo) {
+ if (uh->check && udp_check(uh, len, saddr, daddr)) {
+ DPRINTF((DBG_UDP, "UDP: bad checksum\n"));
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ 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 to the socket. */
+ if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_WRITE);
+ release_sock(sk);
+ return(0);
+ }
+ sk->rmem_alloc += skb->mem_len;
+
+ /* At this point we should print the thing out. */
+ DPRINTF((DBG_UDP, "<< \n"));
+ print_udp(uh);
+
+ /* 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,
+ udp_ioctl,
+ NULL,
+ NULL,
+ 128,
+ 0,
+ {NULL,},
+ "UDP"
+};
diff --git a/net/inet/udp.h b/net/inet/udp.h
new file mode 100644
index 0000000..b6c72d6
--- /dev/null
+++ b/net/inet/udp.h
@@ -0,0 +1,47 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP module.
+ *
+ * Version: @(#)udp.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _UDP_H
+#define _UDP_H
+
+#include <linux/udp.h>
+
+
+#define UDP_NO_CHECK 1
+
+
+extern struct proto udp_prot;
+
+
+extern int udp_select(struct sock *sk, int sel_type, select_table *wait);
+extern void udp_err(int err, unsigned char *header, unsigned long daddr,
+ unsigned long saddr, struct inet_protocol *protocol);
+extern int udp_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int udp_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+extern int udp_connect(struct sock *sk,
+ struct sockaddr_in *usin, int addr_len);
+extern 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 inet_protocol *protocol);
+extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+
+#endif /* _UDP_H */
diff --git a/net/inet/utils.c b/net/inet/utils.c
new file mode 100644
index 0000000..96948dd
--- /dev/null
+++ b/net/inet/utils.c
@@ -0,0 +1,142 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Various kernel-resident INET utility functions; mainly
+ * for format conversion and debugging output.
+ *
+ * Version: @(#)utils.c 1.0.7 05/18/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#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 <linux/in.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <stdarg.h>
+#include "inet.h"
+#include "dev.h"
+#include "eth.h"
+#include "timer.h"
+#include "ip.h"
+#include "protocol.h"
+#include "tcp.h"
+#include "skbuff.h"
+#include "sock.h"
+#include "arp.h"
+
+
+/* Display an IP address in readable format. */
+char *in_ntoa(unsigned long in)
+{
+ static char buff[18];
+ register char *p;
+
+ p = (char *) &in;
+ sprintf(buff, "%d.%d.%d.%d",
+ (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
+ return(buff);
+}
+
+
+/* Convert an ASCII string to binary IP. */
+unsigned long
+in_aton(char *str)
+{
+ unsigned long l;
+ unsigned int val;
+ int i;
+
+ l = 0;
+ for (i = 0; i < 4; i++) {
+ l <<= 8;
+ if (*str != '\0') {
+ val = 0;
+ while (*str != '\0' && *str != '.') {
+ val *= 10;
+ val += *str - '0';
+ str++;
+ }
+ l |= val;
+ if (*str != '\0') str++;
+ }
+ }
+ return(htonl(l));
+}
+
+
+void
+dprintf(int level, char *fmt, ...)
+{
+ va_list args;
+ char *buff;
+ extern int vsprintf(char * buf, const char * fmt, va_list args);
+
+ if (level != inet_debug) return;
+
+ buff = kmalloc(256, GFP_ATOMIC);
+ if (buff != NULL) {
+ va_start(args, fmt);
+ vsprintf(buff, fmt, args);
+ va_end(args);
+ printk(buff);
+ kfree(buff);
+ }
+}
+
+
+int
+dbg_ioctl(void *arg, int level)
+{
+ int val;
+
+ if (!suser()) return(-EPERM);
+ verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
+ val = get_fs_long((void *)arg);
+ switch(val) {
+ case 0: /* OFF */
+ inet_debug = DBG_OFF;
+ break;
+ case 1: /* ON, INET */
+ inet_debug = level;
+ break;
+
+ case DBG_RT: /* modules */
+ case DBG_DEV:
+ case DBG_ETH:
+ case DBG_PROTO:
+ case DBG_TMR:
+ case DBG_PKT:
+ case DBG_RAW:
+
+ case DBG_LOOPB: /* drivers */
+ case DBG_SLIP:
+
+ case DBG_ARP: /* protocols */
+ case DBG_IP:
+ case DBG_ICMP:
+ case DBG_TCP:
+ case DBG_UDP:
+
+ inet_debug = val;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+
+ return(0);
+}
diff --git a/net/inet/wd.c b/net/inet/wd.c
new file mode 100644
index 0000000..5e1322b
--- /dev/null
+++ b/net/inet/wd.c
@@ -0,0 +1,416 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a driver for the WD8003 and WD8013 ethercards.
+
+ The Author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+*/
+
+static char *version =
+ "wd.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <memory.h>
+
+#include "dev.h"
+#include "8390.h"
+
+
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+int wdprobe(int ioaddr, struct device *dev);
+int wdprobe1(int ioaddr, struct device *dev);
+
+static void wd_reset_8390(struct device *dev);
+static int wd_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void wd_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int wd_close_card(struct device *dev);
+
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* Offset to ASIC command register. */
+#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
+#define WD_MEMENB 0x40 /* Enable the shared memory. */
+#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
+#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
+#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
+
+/* Probe for the WD8003 and WD8013. These cards have the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following. A Soundblaster can have the same checksum as an WDethercard,
+ so we have an extra exclusionary check for it.
+
+ The wdprobe1() routine initializes the card and fills the
+ station address field. */
+
+int wdprobe(int ioaddr, struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x280, 0x380, 0x240, 0};
+
+ if (ioaddr > 0x100)
+ return wdprobe1(ioaddr, dev);
+
+ for (port = &ports[0]; *port; port++)
+ if (inb(*port + 8) != 0xff
+ && inb(*port + 9) != 0xff /* Extra check to avoid soundcard. */
+ && wdprobe1(*port, dev))
+ return *port;
+ return 0;
+}
+
+int wdprobe1(int ioaddr, struct device *dev)
+{
+ int i;
+ unsigned char *station_addr = dev->dev_addr;
+ int checksum = 0;
+ int ancient = 0; /* An old card without config registers. */
+
+#if defined(EI_DEBUG) && EI_DEBUG > 2
+ printk("WD80x3 ethercard at %#3x:", ioaddr);
+ for (i = 0; i < 16; i++) {
+ printk(" %2.2X", inb(ioaddr+i));
+ }
+ printk("\n");
+ printk("WD80x3 ethercard at %#3x:", ioaddr+i);
+ for (;i < 33; i++) {
+ printk(" %2.2X", inb(ioaddr+i));
+ }
+ printk("\n");
+#endif
+ printk("WD80x3 ethercard probe at %#3x:", ioaddr);
+ for (i = 0; i < 8; i++) {
+ int inval = inb(ioaddr + 8 + i);
+ checksum += inval;
+ if (i < 6)
+ printk(" %2.2X", (station_addr[i] = inval));
+ }
+
+ if ((checksum & 0xff) != 0xFF) {
+ printk(" not found (%#2.2x).\n", checksum);
+ return 0;
+ }
+
+ ei_status.name = "WD8003";
+ ei_status.word16 = 0;
+
+ /* The following PureData probe code was contributed by
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. */
+ /* Puredata seem to do software configuration differently from
+ * others so we have to check for them.
+ */
+ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+ ei_status.name = "PDI8023";
+
+ /* We can also check whether it is an 8 bit (-8), 16 bit (-16)
+ * or dumb (Toshiba) card. The Toshiba version doesn't support
+ * anything except jumper configuration.
+ */
+ switch (inb(ioaddr+2)) {
+ case 0x03:
+ ei_status.word16 = 0;
+ ei_status.name = "PDI8023-8";
+ break;
+
+ case 0x05:
+ /* Not sure really... */
+ ei_status.word16 = 0;
+ ei_status.name = "PDUC8023";
+ break;
+
+ case 0x0a:
+ ei_status.word16 = 1;
+ ei_status.name = "PDI8023-16";
+ break;
+
+ default:
+ /* Either 0x01 (dumb) or they've released a new version. */
+ ei_status.word16 = 0;
+ ei_status.name = "PDI8023";
+ break;
+ }
+ dev->mem_start = ((inb(ioaddr+5) & 0x1c) + 0xc0) << 12;
+ dev->irq = (inb(ioaddr+5) >> 5) & 0x07;
+ if (dev->irq == 7)
+ dev->irq = 10;
+ else
+ dev->irq++;
+ } /* End of PureData probe */
+
+ /* This method of checking for a 16-bit board is borrowed from the
+ we.c driver. A simpler method is just to look in ASIC reg. 0x03.
+ I'm comparing the two method in alpha test to make certain they
+ return the same result. */
+#ifndef FORCE_8BIT /* Same define as we.c. */
+ /* Check for 16 bit board - it doesn't have register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs some old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ ei_status.name = "WD8003-old";
+ } else {
+ int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
+ int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ /* Magic to set ASIC to word-wide mode. */
+ outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ ei_status.name = "WD8013";
+ ei_status.word16 = 1; /* We have a 16bit board here! */
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
+ }
+#endif /* not FORCE_8BIT */
+
+#ifndef final_version
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
+ printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+ /* Allow an override. */
+ dev->mem_start = WD_SHMEM;
+#else
+ if (dev->mem_start == 0) {
+ /* Sanity and old 8003 check */
+ int reg0 = inb(ioaddr);
+ if (reg0 == 0xff) {
+ /* Future plan: this could check a few likely locations first. */
+ dev->mem_start = 0xd0000;
+ printk(" assigning address %#x", dev->mem_start);
+ } else {
+ int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ /* Some boards don't have the register 5 -- it returns 0xff. */
+ if (high_addr_bits == 0x1f || ei_status.word16 == 0)
+ high_addr_bits = 0x01;
+ dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ }
+ }
+#endif
+
+ /* The 8390 isn't at the base address -- the ASIC regs are there! */
+ dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+ ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG;
+
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+
+ if (dev->irq < 2) {
+ int irqmap[] = {9,3,5,7,10,11,15,4};
+ int reg1 = inb(ioaddr+1);
+ int reg4 = inb(ioaddr+4);
+ if (reg1 == 0xff){ /* Ack!! No way to read the IRQ! */
+ dev->irq = ei_status.word16 ? 10 : 5;
+ } else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03)
+ + (reg1 & 0x04)];
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ { int irqval = irqaction (dev->irq, &ei_sigaction);
+ if (irqval) {
+ printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
+ return 0;
+ }
+ }
+
+ printk("\n%s: %s using IRQ %d with shared memory at %#x-%#x.\n",
+ dev->name, ei_status.name, dev->irq, dev->mem_start, dev->mem_end-1);
+ if (ei_debug > 1)
+ printk(version);
+
+#if defined(EI_DEBUG) && EI_DEBUG > 2
+ printk("%s: Address read from register is %#x, setting address %#x\n",
+ ei_status.name,
+ ((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
+ dev->mem_start);
+#endif
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ if (ei_status.word16)
+ outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
+
+ ei_status.reset_8390 = &wd_reset_8390;
+ ei_status.block_input = &wd_block_input;
+ ei_status.block_output = &wd_block_output;
+ dev->stop = &wd_close_card;
+ NS8390_init(dev, 0);
+
+ return dev->base_addr;
+}
+
+static void
+wd_reset_8390(struct device *dev)
+{
+ int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ int reset_start_time = jiffies;
+
+ outb(WD_RESET, wd_cmd_port);
+ if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
+ ei_status.txing = 0;
+
+ sti();
+ /* We shouldn't use the boguscount for timing, but this hasn't been
+ checked yet, and you could hang your machine if jiffies break... */
+ {
+ int boguscount = 150000;
+ while(jiffies - reset_start_time < 2)
+ if (boguscount-- < 0) {
+ printk("jiffy failure (t=%d)...", jiffies);
+ break;
+ }
+ }
+
+ /* Set up the ASIC registers, just in case something changed them. */
+ if (ei_status.word16)
+ outb(NIC16 | ((dev->mem_start>>19) & 0x1f),
+ wd_cmd_port+WD_CMDREG5);
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+
+ while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2) {
+ printk("%s: wd_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+#if defined(EI_DEBUG) && EI_DEBUG > 2
+ {
+ int i;
+ printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
+ for (i = 0; i < 16; i++) {
+ printk(" %2.2X", inb(wd_cmd_port+i));
+ }
+ printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port+i);
+ for (;i < 33; i++) {
+ printk(" %2.2X", inb(wd_cmd_port+i));
+ }
+ printk("\n");
+ }
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+ on the Western digital card where there is no choice of how to do it. */
+
+static int
+wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
+#if !defined(WD_no_mapout)
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ int reg5_val = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ /* Map in the shared memory. */
+ if (ei_status.word16)
+ outb(ISA16 | reg5_val, wd_cmdreg + WD_CMDREG5 );
+ outb((((dev->mem_start>>13) & 0x3f) | WD_MEMENB), wd_cmdreg);
+#endif
+ if (xfer_start + count > (void*) dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = (void*)dev->rmem_end - xfer_start;
+ memcpy(buf, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+ return dev->rmem_start + count;
+ }
+ memcpy(buf, xfer_start, count);
+ if (ei_debug > 4) {
+ unsigned short *board = xfer_start;
+ printk("%s: wd8013 block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
+ dev->name, count, ring_offset, xfer_start,
+ board[-1], board[0], board[1]);
+ }
+#if !defined(WD_no_mapout)
+ /* Turn off 16 bit access so that reboot works. */
+ if (ei_status.word16)
+ outb(reg5_val, wd_cmdreg + WD_CMDREG5 );
+#endif
+ return ring_offset + count;
+}
+
+/* This could only be outputting to the transmit buffer. The
+ ping-pong transmit setup doesn't work with this yet. */
+static void
+wd_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ unsigned char *shmem
+ = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
+#if !defined(WD_no_mapout)
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ int reg5_val = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ /* Map in the shared memory. */
+ if (ei_status.word16)
+ outb(ISA16 | reg5_val, wd_cmdreg + WD_CMDREG5 );
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmdreg);
+#endif
+ memcpy(shmem, buf, count);
+ if (ei_debug > 4)
+ printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
+ shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
+#if !defined(WD_no_mapout)
+ /* Turn off 16 bit access so that reboot works. */
+ if (ei_status.word16)
+ outb(reg5_val, wd_cmdreg + WD_CMDREG5 );
+#endif
+}
+
+/* This function resets the ethercard if something screws up. */
+static int
+wd_close_card(struct device *dev)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ NS8390_init(dev, 0);
+
+ /* Turn off 16-bit shared memory so reboot works. */
+ outb(((dev->mem_start>>19) & 0x1f) | NIC16, wd_cmdreg + WD_CMDREG5 );
+ outb((((dev->mem_start>>13) & 0x3f)), wd_cmdreg);
+
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/net/kern_sock.h b/net/kern_sock.h
deleted file mode 100644
index c9c853c..0000000
--- a/net/kern_sock.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef _KERN_SOCK_H
-#define _KERN_SOCK_H
-#undef SOCK_DEBUG
-#define NSOCKETS 128 /* should be dynamic, later... */
-
-typedef enum {
- SS_FREE = 0, /* not allocated */
- SS_UNCONNECTED, /* unconnected to any socket */
- SS_CONNECTING, /* in process of connecting */
- SS_CONNECTED, /* connected to socket */
- SS_DISCONNECTING, /* in process of disconnecting */
-} socket_state;
-
-#define SO_ACCEPTCON (1<<16) /* performed a listen */
-
-/*
- * internel representation of a socket. not all the fields are used by
- * all configurations:
- *
- * server client
- * conn client connected to server connected to
- * iconn list of clients -unused-
- * awaiting connections
- * wait sleep for clients, sleep for connection,
- * sleep for i/o sleep for i/o
- */
-struct socket {
- short type; /* SOCK_STREAM, ... */
- socket_state state;
- long flags;
- struct proto_ops *ops; /* protocols do most everything */
- void *data; /* protocol data */
- struct socket *conn; /* server socket connected to */
- struct socket *iconn; /* incomplete client connections */
- struct socket *next;
- struct wait_queue **wait; /* ptr to place to wait on */
- void *dummy;
-};
-
-#define SOCK_INODE(S) ((struct inode *)(S)->dummy)
-extern struct socket sockets[NSOCKETS];
-#define last_socket (sockets + NSOCKETS - 1)
-
-struct proto_ops {
- int (*init)(void);
- int (*create)(struct socket *sock, int protocol);
- int (*dup)(struct socket *newsock, struct socket *oldsock);
- int (*release)(struct socket *sock, struct socket *peer);
- int (*bind)(struct socket *sock, struct sockaddr *umyaddr,
- int sockaddr_len);
- int (*connect)(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags);
- int (*socketpair)(struct socket *sock1, struct socket *sock2);
- int (*accept)(struct socket *sock, struct socket *newsock, int flags);
- int (*getname)(struct socket *sock, struct sockaddr *uaddr,
- int *usockaddr_len, int peer);
- int (*read)(struct socket *sock, char *ubuf, int size, int nonblock);
- int (*write)(struct socket *sock, char *ubuf, int size, int nonblock);
- int (*select)(struct socket *sock, int sel_type, select_table * wait);
- int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
- int (*listen)(struct socket *sock, int len);
- int (*send)(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags);
- int (*recv)(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags);
- int (*sendto)(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *, int addr_len);
- int (*recvfrom)(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *, int *addr_len);
- int (*shutdown)(struct socket *sock, int flags);
- int (*setsockopt)(struct socket *sock, int level, int optname,
- char *optval, int optlen);
- int (*getsockopt)(struct socket *sock, int level, int optname,
- char *optval, int *optlen);
- int (*fcntl) (struct socket *sock, unsigned int cmd,
- unsigned long arg);
-};
-
-extern int sock_awaitconn(struct socket *mysock, struct socket *servsock);
-
-#ifdef SOCK_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-#endif /* _KERN_SOCK_H */
diff --git a/net/socket.c b/net/socket.c
index 2adfcd7..2f3788d 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1,4 +1,20 @@
-/* modified by Ross Biro to help support inet sockets. */
+/*
+ * NET An implementation of the SOCKET network access protocol.
+ *
+ * Version: @(#)socket.c 1.0.5 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/config.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -7,43 +23,16 @@
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/termios.h>
-#include <linux/config.h>
-
-#include <asm/system.h>
-#include <asm/segment.h>
-
-#include "kern_sock.h"
-#include "socketcall.h"
-
-extern int sys_close(int fd);
-
-extern struct proto_ops unix_proto_ops;
-#ifdef CONFIG_TCPIP
-extern struct proto_ops inet_proto_ops;
-#endif
-
-static struct {
- short family;
- char *name;
- struct proto_ops *ops;
-} proto_table[] = {
- {AF_UNIX, "AF_UNIX", &unix_proto_ops},
-#ifdef CONFIG_TCPIP
- {AF_INET, "AF_INET", &inet_proto_ops},
+#include <linux/net.h>
+#include <linux/ddi.h>
+#include <stdarg.h>
+
+#undef SOCK_DEBUG
+#ifdef SOCK_DEBUG
+#define DPRINTF(x) dprintf x
+#else
+#define DPRINTF(x) /**/
#endif
-};
-#define NPROTO (sizeof(proto_table) / sizeof(proto_table[0]))
-
-static char *
-family_name(int family)
-{
- int i;
-
- for (i = 0; i < NPROTO; ++i)
- if (proto_table[i].family == family)
- return proto_table[i].name;
- return "UNKNOWN";
-}
static int sock_lseek(struct inode *inode, struct file *file, off_t offset,
int whence);
@@ -58,921 +47,974 @@ static int sock_select(struct inode *inode, struct file *file, int which, select
static int sock_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
+
static struct file_operations socket_file_ops = {
- sock_lseek,
- sock_read,
- sock_write,
- sock_readdir,
- sock_select,
- sock_ioctl,
- NULL, /* mmap */
- NULL, /* no special open code... */
- sock_close
+ sock_lseek,
+ sock_read,
+ sock_write,
+ sock_readdir,
+ sock_select,
+ sock_ioctl,
+ NULL, /* mmap */
+ NULL, /* no special open code... */
+ sock_close
};
-
struct socket sockets[NSOCKETS];
static struct wait_queue *socket_wait_free = NULL;
+static struct proto_ops *pops[NPROTO];
+static int net_debug = 0;
-/*
- * obtains the first available file descriptor and sets it up for use
- */
+
+extern int sys_close(int fd);
+
+#ifdef SOCK_DEBUG
+/* Module debugging. */
+static void
+dprintf(int level, char *fmt, ...)
+{
+ char buff[1024];
+ va_list args;
+ extern int vsprintf(char * buf, const char * fmt, va_list args);
+
+ if (level == 0) return;
+ va_start(args, fmt);
+ vsprintf(buff, fmt, args);
+ va_end(args);
+ printk(buff);
+}
+#endif
+
+/* Obtains the first available file descriptor and sets it up for use. */
static int
get_fd(struct inode *inode)
{
- int fd;
- struct file *file;
-
- /*
- * find a file descriptor suitable for return to the user.
- */
- file = get_empty_filp();
- if (!file)
- return -1;
- for (fd = 0; fd < NR_OPEN; ++fd)
- if (!current->filp[fd])
- break;
- if (fd == NR_OPEN) {
- file->f_count = 0;
- return -1;
- }
- FD_CLR(fd, &current->close_on_exec);
- current->filp[fd] = file;
- file->f_op = &socket_file_ops;
- file->f_mode = 3;
- file->f_flags = 0;
- file->f_count = 1;
- file->f_inode = inode;
- if (inode)
- inode->i_count++;
- file->f_pos = 0;
- return fd;
+ int fd;
+ struct file *file;
+
+ /* Find a file descriptor suitable for return to the user. */
+ file = get_empty_filp();
+ if (!file) return(-1);
+ for (fd = 0; fd < NR_OPEN; ++fd)
+ if (!current->filp[fd]) break;
+ if (fd == NR_OPEN) {
+ file->f_count = 0;
+ return(-1);
+ }
+ FD_CLR(fd, &current->close_on_exec);
+ current->filp[fd] = file;
+ file->f_op = &socket_file_ops;
+ file->f_mode = 3;
+ file->f_flags = 0;
+ file->f_count = 1;
+ file->f_inode = inode;
+ if (inode) inode->i_count++;
+ file->f_pos = 0;
+ return(fd);
}
+
/*
- * reverses the action of get_fd() by releasing the file. it closes the
- * descriptor, but makes sure it does nothing more. called when an incomplete
- * socket must be closed, along with sock_release().
+ * Reverses the action of get_fd() by releasing the file. it closes
+ * the descriptor, but makes sure it does nothing more. Called when
+ * an incomplete socket must be closed, along with sock_release().
*/
static inline void
toss_fd(int fd)
{
- /* the count protects us from iput. */
- sys_close(fd);
+ sys_close(fd); /* the count protects us from iput */
}
+
struct socket *
socki_lookup(struct inode *inode)
{
- struct socket *sock;
+ struct socket *sock;
- for (sock = sockets; sock <= last_socket; ++sock)
- if (sock->state != SS_FREE && SOCK_INODE(sock) == inode)
- return sock;
- return NULL;
+ for (sock = sockets; sock <= last_socket; ++sock)
+ if (sock->state != SS_FREE && SOCK_INODE(sock) == inode) return(sock);
+ return(NULL);
}
+
static inline struct socket *
sockfd_lookup(int fd, struct file **pfile)
{
- struct file *file;
+ struct file *file;
- if (fd < 0 || fd >= NR_OPEN || !(file = current->filp[fd]))
- return NULL;
- if (pfile)
- *pfile = file;
- return socki_lookup(file->f_inode);
+ if (fd < 0 || fd >= NR_OPEN || !(file = current->filp[fd])) return(NULL);
+ if (pfile) *pfile = file;
+ return(socki_lookup(file->f_inode));
}
+
static struct socket *
sock_alloc(int wait)
{
- struct socket *sock;
-
- while (1) {
- cli();
- for (sock = sockets; sock <= last_socket; ++sock)
- if (sock->state == SS_FREE) {
- sock->state = SS_UNCONNECTED;
- sti();
- sock->flags = 0;
- sock->ops = NULL;
- sock->data = NULL;
- sock->conn = NULL;
- sock->iconn = NULL;
- /*
- * this really shouldn't be necessary, but
- * everything else depends on inodes, so we
- * grab it.
- * sleeps are also done on the i_wait member
- * of this inode.
- * the close system call will iput this inode
- * for us.
- */
- if (!(SOCK_INODE(sock) = get_empty_inode())) {
- printk("sock_alloc: no more inodes\n");
- sock->state = SS_FREE;
- return NULL;
- }
- SOCK_INODE(sock)->i_mode = S_IFSOCK;
- SOCK_INODE(sock)->i_uid = current->euid;
- SOCK_INODE(sock)->i_gid = current->egid;
-
- sock->wait = &SOCK_INODE(sock)->i_wait;
- PRINTK(("sock_alloc: socket 0x%x,inode 0x%x\n",
- sock, SOCK_INODE(sock)));
- return sock;
+ struct socket *sock;
+
+ while (1) {
+ cli();
+ for (sock = sockets; sock <= last_socket; ++sock) {
+ if (sock->state == SS_FREE) {
+ sock->state = SS_UNCONNECTED;
+ sti();
+ sock->flags = 0;
+ sock->ops = NULL;
+ sock->data = NULL;
+ sock->conn = NULL;
+ sock->iconn = NULL;
+
+ /*
+ * This really shouldn't be necessary, but everything
+ * else depends on inodes, so we grab it.
+ * Sleeps are also done on the i_wait member of this
+ * inode. The close system call will iput this inode
+ * for us.
+ */
+ if (!(SOCK_INODE(sock) = get_empty_inode())) {
+ printk("NET: sock_alloc: no more inodes\n");
+ sock->state = SS_FREE;
+ return(NULL);
}
- sti();
- if (!wait)
- return NULL;
- PRINTK(("sock_alloc: no free sockets, sleeping...\n"));
- interruptible_sleep_on(&socket_wait_free);
- if (current->signal & ~current->blocked) {
- PRINTK(("sock_alloc: sleep was interrupted\n"));
- return NULL;
+ SOCK_INODE(sock)->i_mode = S_IFSOCK;
+ SOCK_INODE(sock)->i_uid = current->euid;
+ SOCK_INODE(sock)->i_gid = current->egid;
+
+ sock->wait = &SOCK_INODE(sock)->i_wait;
+ DPRINTF((net_debug,
+ "NET: sock_alloc: sk 0x%x, ino 0x%x\n",
+ sock, SOCK_INODE(sock)));
+ return(sock);
}
- PRINTK(("sock_alloc: wakeup... trying again...\n"));
}
+ sti();
+ if (!wait) return(NULL);
+ DPRINTF((net_debug, "NET: sock_alloc: no free sockets, sleeping...\n"));
+ interruptible_sleep_on(&socket_wait_free);
+ if (current->signal & ~current->blocked) {
+ DPRINTF((net_debug, "NET: sock_alloc: sleep was interrupted\n"));
+ return(NULL);
+ }
+ DPRINTF((net_debug, "NET: sock_alloc: wakeup... trying again...\n"));
+ }
}
+
static inline void
sock_release_peer(struct socket *peer)
{
- peer->state = SS_DISCONNECTING;
- wake_up(peer->wait);
+ peer->state = SS_DISCONNECTING;
+ wake_up(peer->wait);
}
+
static void
sock_release(struct socket *sock)
{
- int oldstate;
- struct socket *peersock, *nextsock;
-
- PRINTK(("sock_release: socket 0x%x, inode 0x%x\n", sock,
- SOCK_INODE(sock)));
- if ((oldstate = sock->state) != SS_UNCONNECTED)
- sock->state = SS_DISCONNECTING;
- /*
- * wake up anyone waiting for connections
- */
- for (peersock = sock->iconn; peersock; peersock = nextsock) {
- nextsock = peersock->next;
- sock_release_peer(peersock);
- }
- /*
- * wake up anyone we're connected to. first, we release the
- * protocol, to give it a chance to flush data, etc.
- */
- peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
- if (sock->ops)
- sock->ops->release(sock, peersock);
- if (peersock)
- sock_release_peer(peersock);
- sock->state = SS_FREE; /* this really releases us */
- wake_up(&socket_wait_free);
- iput(SOCK_INODE(sock)); /* we need to do this. If sock alloc was
- called we already have an inode. */
+ int oldstate;
+ struct socket *peersock, *nextsock;
+
+ DPRINTF((net_debug, "NET: sock_release: socket 0x%x, inode 0x%x\n",
+ sock, SOCK_INODE(sock)));
+ if ((oldstate = sock->state) != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+
+ /* Wake up anyone waiting for connections. */
+ for (peersock = sock->iconn; peersock; peersock = nextsock) {
+ nextsock = peersock->next;
+ sock_release_peer(peersock);
+ }
+
+ /*
+ * Wake up anyone we're connected to. First, we release the
+ * protocol, to give it a chance to flush data, etc.
+ */
+ peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
+ if (sock->ops) sock->ops->release(sock, peersock);
+ if (peersock) sock_release_peer(peersock);
+ sock->state = SS_FREE; /* this really releases us */
+ wake_up(&socket_wait_free);
+
+ /* We need to do this. If sock alloc was called we already have an inode. */
+ iput(SOCK_INODE(sock));
}
+
static int
sock_lseek(struct inode *inode, struct file *file, off_t offset, int whence)
{
- PRINTK(("sock_lseek: huh?\n"));
- return -ESPIPE;
+ DPRINTF((net_debug, "NET: sock_lseek: huh?\n"));
+ return(-ESPIPE);
}
+
static int
sock_read(struct inode *inode, struct file *file, char *ubuf, int size)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sock_read: buf=0x%x, size=%d\n", ubuf, size));
- if (!(sock = socki_lookup(inode))) {
- printk("sock_read: can't find socket for inode!\n");
- return -EBADF;
- }
- if (sock->flags & SO_ACCEPTCON)
- return -EINVAL;
- return sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK));
+ DPRINTF((net_debug, "NET: sock_read: buf=0x%x, size=%d\n", ubuf, size));
+ if (!(sock = socki_lookup(inode))) {
+ printk("NET: sock_read: can't find socket for inode!\n");
+ return(-EBADF);
+ }
+ if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ return(sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)));
}
+
static int
sock_write(struct inode *inode, struct file *file, char *ubuf, int size)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sock_write: buf=0x%x, size=%d\n", ubuf, size));
- if (!(sock = socki_lookup(inode))) {
- printk("sock_write: can't find socket for inode!\n");
- return -EBADF;
- }
- if (sock->flags & SO_ACCEPTCON)
- return -EINVAL;
- return sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK));
+ DPRINTF((net_debug, "NET: sock_write: buf=0x%x, size=%d\n", ubuf, size));
+ if (!(sock = socki_lookup(inode))) {
+ printk("NET: sock_write: can't find socket for inode!\n");
+ return(-EBADF);
+ }
+ if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ return(sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)));
}
+
static int
sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent,
int count)
{
- PRINTK(("sock_readdir: huh?\n"));
- return -EBADF;
+ DPRINTF((net_debug, "NET: sock_readdir: huh?\n"));
+ return(-EBADF);
}
+
int
sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sock_ioctl: inode=0x%x cmd=0x%x arg=%d\n", inode, cmd, arg));
- if (!(sock = socki_lookup(inode))) {
- printk("sock_ioctl: can't find socket for inode!\n");
- return -EBADF;
- }
- return sock->ops->ioctl(sock, cmd, arg);
+ DPRINTF((net_debug, "NET: sock_ioctl: inode=0x%x cmd=0x%x arg=%d\n",
+ inode, cmd, arg));
+ if (!(sock = socki_lookup(inode))) {
+ printk("NET: sock_ioctl: can't find socket for inode!\n");
+ return(-EBADF);
+ }
+ return(sock->ops->ioctl(sock, cmd, arg));
}
+
static int
sock_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sock_select: inode = 0x%x, kind = %s\n", inode,
- (sel_type == SEL_IN) ? "in" :
- (sel_type == SEL_OUT) ? "out" : "ex"));
- if (!(sock = socki_lookup(inode))) {
- printk("sock_select: can't find socket for inode!\n");
- return 0;
- }
- /*
- * we can't return errors to select, so its either yes or no.
- */
- if (sock->ops && sock->ops->select)
- return sock->ops->select(sock, sel_type, wait);
- return 0;
+ DPRINTF((net_debug, "NET: sock_select: inode = 0x%x, kind = %s\n", inode,
+ (sel_type == SEL_IN) ? "in" :
+ (sel_type == SEL_OUT) ? "out" : "ex"));
+ if (!(sock = socki_lookup(inode))) {
+ printk("NET: sock_select: can't find socket for inode!\n");
+ return(0);
+ }
+
+ /* We can't return errors to select, so its either yes or no. */
+ if (sock->ops && sock->ops->select)
+ return(sock->ops->select(sock, sel_type, wait));
+ return(0);
}
+
void
sock_close(struct inode *inode, struct file *file)
{
- struct socket *sock;
-
- PRINTK(("sock_close: inode=0x%x (cnt=%d)\n", inode, inode->i_count));
- /*
- * it's possible the inode is NULL if we're closing an unfinished
- * socket.
- */
- if (!inode)
- return;
- if (!(sock = socki_lookup(inode))) {
- printk("sock_close: can't find socket for inode!\n");
- return;
- }
- sock_release(sock);
+ struct socket *sock;
+
+ DPRINTF((net_debug, "NET: sock_close: inode=0x%x (cnt=%d)\n",
+ inode, inode->i_count));
+
+ /* It's possible the inode is NULL if we're closing an unfinished socket. */
+ if (!inode) return;
+ if (!(sock = socki_lookup(inode))) {
+ printk("NET: sock_close: can't find socket for inode!\n");
+ return;
+ }
+ sock_release(sock);
}
+
int
sock_awaitconn(struct socket *mysock, struct socket *servsock)
{
- struct socket **last;
-
- PRINTK(("sock_awaitconn: trying to connect socket 0x%x to 0x%x\n",
- mysock, servsock));
- if (!(servsock->flags & SO_ACCEPTCON)) {
- PRINTK(("sock_awaitconn: server not accepting connections\n"));
- return -EINVAL;
- }
-
- /*
- * put ourselves on the server's incomplete connection queue.
- */
- mysock->next = NULL;
- cli();
- last = &servsock->iconn;
- while (*last)
- last = &(*last)->next;
- *last = mysock;
- mysock->state = SS_CONNECTING;
- mysock->conn = servsock;
- sti();
-
- /*
- * wake up server, then await connection. server will set state to
- * SS_CONNECTED if we're connected.
- */
- wake_up(servsock->wait);
+ struct socket *last;
+
+ DPRINTF((net_debug,
+ "NET: sock_awaitconn: trying to connect socket 0x%x to 0x%x\n",
+ mysock, servsock));
+ if (!(servsock->flags & SO_ACCEPTCON)) {
+ DPRINTF((net_debug,
+ "NET: sock_awaitconn: server not accepting connections\n"));
+ return(-EINVAL);
+ }
+
+ /* Put ourselves on the server's incomplete connection queue. */
+ mysock->next = NULL;
+ cli();
+ if (!(last = servsock->iconn)) servsock->iconn = mysock;
+ else {
+ while (last->next) last = last->next;
+ last->next = mysock;
+ }
+ mysock->state = SS_CONNECTING;
+ mysock->conn = servsock;
+ sti();
+
+ /*
+ * Wake up server, then await connection. server will set state to
+ * SS_CONNECTED if we're connected.
+ */
+ wake_up(servsock->wait);
+ if (mysock->state != SS_CONNECTED) {
+ interruptible_sleep_on(mysock->wait);
if (mysock->state != SS_CONNECTED) {
- interruptible_sleep_on(mysock->wait);
- if (mysock->state != SS_CONNECTED &&
- mysock->state != SS_DISCONNECTING)
- /* SS_DISCONNECTING means the server accepted
- * the connection, maybe wrote some data, and then
- * closed it again, all before we got to run.
- * The only way a socket can get to SS_DISCONNECTING
- * is through sock_release or sock_release_peer.
- * The former is only called in case of various setup
- * failures or sock_close (which obviously isn't the
- * case as we still have it open); the latter is
- * called when the server (to which we obviously
- * must have been connected) closes its end.
- */
- {
- /*
- * if we're not connected we could have been
- * 1) interrupted, so we need to remove ourselves
- * from the server list
- * 2) rejected (mysock->conn == NULL), and have
- * already been removed from the list
- */
- if (mysock->conn == servsock) {
- cli();
- last = &servsock->iconn;
- while (*last) {
- if (*last == mysock) {
- *last = mysock->next;
- break;
- }
- last = &(*last)->next;
- }
- sti();
+ /*
+ * if we're not connected we could have been
+ * 1) interrupted, so we need to remove ourselves
+ * from the server list
+ * 2) rejected (mysock->conn == NULL), and have
+ * already been removed from the list
+ */
+ if (mysock->conn == servsock) {
+ cli();
+ if ((last = servsock->iconn) == mysock)
+ servsock->iconn = mysock->next;
+ else {
+ while (last->next != mysock) last = last->next;
+ last->next = mysock->next;
}
- return mysock->conn ? -EINTR : -EACCES;
+ sti();
}
+ return(mysock->conn ? -EINTR : -EACCES);
}
- return 0;
+ }
+ return(0);
}
+
/*
- * perform the socket system call. we locate the appropriate family, then
- * create a fresh socket.
+ * Perform the socket system call. we locate the appropriate
+ * family, then create a fresh socket.
*/
static int
sock_socket(int family, int type, int protocol)
{
- int i, fd;
- struct socket *sock;
- struct proto_ops *ops;
-
- PRINTK(("sys_socket: family = %d (%s), type = %d, protocol = %d\n",
- family, family_name(family), type, protocol));
-
- /*
- * locate the correct protocol family
- */
- for (i = 0; i < NPROTO; ++i)
- if (proto_table[i].family == family)
- break;
- if (i == NPROTO) {
- PRINTK(("sys_socket: family not found\n"));
- return -EINVAL;
- }
- ops = proto_table[i].ops;
-
- /*
- * check that this is a type that we know how to manipulate and
- * the protocol makes sense here. the family can still reject the
- * protocol later.
- */
- if ((type != SOCK_STREAM &&
- type != SOCK_DGRAM &&
- type != SOCK_SEQPACKET &&
- type != SOCK_RAW) ||
- protocol < 0)
- return -EINVAL;
-
- /*
- * allocate the socket and allow the family to set things up. if
- * the protocol is 0, the family is instructed to select an appropriate
- * default.
- */
- if (!(sock = sock_alloc(1))) {
- printk("sys_socket: no more sockets\n");
- return -EAGAIN;
- }
- sock->type = type;
- sock->ops = ops;
- if ((i = sock->ops->create(sock, protocol)) < 0) {
- sock_release(sock);
- return i;
- }
+ int i, fd;
+ struct socket *sock;
+ struct proto_ops *ops;
+
+ DPRINTF((net_debug,
+ "NET: sock_socket: family = %d, type = %d, protocol = %d\n",
+ family, type, protocol));
+
+ /* Locate the correct protocol family. */
+ for (i = 0; i < NPROTO; ++i) {
+ if (pops[i] == NULL) continue;
+ if (pops[i]->family == family) break;
+ }
+ if (i == NPROTO) {
+ DPRINTF((net_debug, "NET: sock_socket: family not found\n"));
+ return(-EINVAL);
+ }
+ ops = pops[i];
+
+ /*
+ * Check that this is a type that we know how to manipulate and
+ * the protocol makes sense here. The family can still reject the
+ * protocol later.
+ */
+ if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
+ type != SOCK_SEQPACKET && type != SOCK_RAW) || protocol < 0)
+ return(-EINVAL);
+
+ /*
+ * allocate the socket and allow the family to set things up. if
+ * the protocol is 0, the family is instructed to select an appropriate
+ * default.
+ */
+ if (!(sock = sock_alloc(1))) {
+ printk("sock_socket: no more sockets\n");
+ return(-EAGAIN);
+ }
+ sock->type = type;
+ sock->ops = ops;
+ if ((i = sock->ops->create(sock, protocol)) < 0) {
+ sock_release(sock);
+ return(i);
+ }
- if ((fd = get_fd(SOCK_INODE(sock))) < 0) {
- sock_release(sock);
- return -EINVAL;
- }
+ if ((fd = get_fd(SOCK_INODE(sock))) < 0) {
+ sock_release(sock);
+ return(-EINVAL);
+ }
- return fd;
+ return(fd);
}
-static int
-sock_socketpair(int family, int type, int protocol, int usockvec[2])
-{
- int fd1, fd2, i;
- struct socket *sock1, *sock2;
-
- PRINTK(("sys_socketpair: family = %d, type = %d, protocol = %d\n",
- family, type, protocol));
-
- /*
- * obtain the first socket and check if the underlying protocol
- * supports the socketpair call
- */
- if ((fd1 = sock_socket(family, type, protocol)) < 0)
- return fd1;
- sock1 = sockfd_lookup(fd1, NULL);
- if (!sock1->ops->socketpair) {
- sys_close(fd1);
- return -EINVAL;
- }
- /*
- * now grab another socket and try to connect the two together
- */
- if ((fd2 = sock_socket(family, type, protocol)) < 0) {
- sys_close(fd1);
- return -EINVAL;
- }
- sock2 = sockfd_lookup(fd2, NULL);
- if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) {
- sys_close(fd1);
- sys_close(fd2);
- return i;
- }
- sock1->conn = sock2;
- sock2->conn = sock1;
- sock1->state = SS_CONNECTED;
- sock2->state = SS_CONNECTED;
-
- verify_area(VERIFY_WRITE,usockvec, 2 * sizeof(int));
- put_fs_long(fd1, &usockvec[0]);
- put_fs_long(fd2, &usockvec[1]);
-
- return 0;
+static int
+sock_socketpair(int family, int type, int protocol, unsigned long usockvec[2])
+{
+ int fd1, fd2, i;
+ struct socket *sock1, *sock2;
+
+ DPRINTF((net_debug,
+ "NET: sock_socketpair: family = %d, type = %d, protocol = %d\n",
+ family, type, protocol));
+
+ /*
+ * Obtain the first socket and check if the underlying protocol
+ * supports the socketpair call.
+ */
+ if ((fd1 = sock_socket(family, type, protocol)) < 0) return(fd1);
+ sock1 = sockfd_lookup(fd1, NULL);
+ if (!sock1->ops->socketpair) {
+ sys_close(fd1);
+ return(-EINVAL);
+ }
+
+ /* Now grab another socket and try to connect the two together. */
+ if ((fd2 = sock_socket(family, type, protocol)) < 0) {
+ sys_close(fd1);
+ return(-EINVAL);
+ }
+ sock2 = sockfd_lookup(fd2, NULL);
+ if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) {
+ sys_close(fd1);
+ sys_close(fd2);
+ return(i);
+ }
+ sock1->conn = sock2;
+ sock2->conn = sock1;
+ sock1->state = SS_CONNECTED;
+ sock2->state = SS_CONNECTED;
+
+ verify_area(VERIFY_WRITE, usockvec, 2 * sizeof(int));
+ put_fs_long(fd1, &usockvec[0]);
+ put_fs_long(fd2, &usockvec[1]);
+
+ return(0);
}
+
/*
- * binds a name to a socket. nothing much to do here since its the
- * protocol's responsibility to handle the local address
+ * Bind a name to a socket. Nothing much to do here since its
+ * the protocol's responsibility to handle the local address.
*/
static int
sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)
{
- struct socket *sock;
- int i;
+ struct socket *sock;
+ int i;
- PRINTK(("sys_bind: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return -EBADF;
- if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
- PRINTK(("sys_bind: bind failed\n"));
- return i;
- }
- return 0;
+ DPRINTF((net_debug, "NET: sock_bind: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+ if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
+ DPRINTF((net_debug, "NET: sock_bind: bind failed\n"));
+ return(i);
+ }
+ return(0);
}
+
/*
- * perform a listen. basically, we allow the protocol to do anything
+ * Perform a listen. Basically, we allow the protocol to do anything
* necessary for a listen, and if that works, we mark the socket as
* ready for listening.
*/
static int
sock_listen(int fd, int backlog)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sys_listen: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return -EBADF;
- if (sock->state != SS_UNCONNECTED) {
- PRINTK(("sys_listen: socket isn't unconnected\n"));
- return -EINVAL;
- }
- if (sock->flags & SO_ACCEPTCON) {
- PRINTK(("sys_listen: socket already accepting connections!\n"));
- return -EINVAL;
- }
- if (sock->ops && sock->ops->listen)
- sock->ops->listen (sock, backlog);
- sock->flags |= SO_ACCEPTCON;
- return 0;
+ DPRINTF((net_debug, "NET: sock_listen: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+ if (sock->state != SS_UNCONNECTED) {
+ DPRINTF((net_debug, "NET: sock_listen: socket isn't unconnected\n"));
+ return(-EINVAL);
+ }
+ if (sock->ops && sock->ops->listen) sock->ops->listen(sock, backlog);
+ sock->flags |= SO_ACCEPTCON;
+ return(0);
}
+
/*
- * for accept, we attempt to create a new socket, set up the link with the
- * client, wake up the client, then return the new connected fd.
+ * For accept, we attempt to create a new socket, set up the link
+ * with the client, wake up the client, then return the new
+ * connected fd.
*/
static int
sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
- struct file *file;
- struct socket *sock, *newsock;
- int i;
-
- PRINTK(("sys_accept: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, &file)))
- return -EBADF;
- if (sock->state != SS_UNCONNECTED) {
- PRINTK(("sys_accept: socket isn't unconnected\n"));
- return -EINVAL;
- }
- if (!(sock->flags & SO_ACCEPTCON)) {
- PRINTK(("sys_accept: socket not accepting connections!\n"));
- return -EINVAL;
- }
-
- if (!(newsock = sock_alloc(0))) {
- printk("sys_accept: no more sockets\n");
- return -EAGAIN;
- }
- newsock->type = sock->type;
- newsock->ops = sock->ops;
- if ((i = sock->ops->dup(newsock, sock)) < 0) {
- sock_release(newsock);
- return i;
- }
-
- i = newsock->ops->accept(sock, newsock, file->f_flags);
-
- if ( i < 0)
- {
- sock_release(newsock);
- return (i);
- }
-
- if ((fd = get_fd(SOCK_INODE(newsock))) < 0) {
- sock_release(newsock);
- return -EINVAL;
- }
-
- PRINTK(("sys_accept: connected socket 0x%x via 0x%x\n",
- sock, newsock));
-
- if (upeer_sockaddr)
- newsock->ops->getname(newsock, upeer_sockaddr,
- upeer_addrlen, 1);
-
- return fd;
-}
-
-/*
- * attempt to connect to a socket with the server address.
- */
+ struct file *file;
+ struct socket *sock, *newsock;
+ int i;
+
+ DPRINTF((net_debug, "NET: sock_accept: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, &file))) return(-EBADF);
+ if (sock->state != SS_UNCONNECTED) {
+ DPRINTF((net_debug, "NET: sock_accept: socket isn't unconnected\n"));
+ return(-EINVAL);
+ }
+ if (!(sock->flags & SO_ACCEPTCON)) {
+ DPRINTF((net_debug,
+ "NET: sock_accept: socket not accepting connections!\n"));
+ return(-EINVAL);
+ }
+
+ if (!(newsock = sock_alloc(0))) {
+ printk("NET: sock_accept: no more sockets\n");
+ return(-EAGAIN);
+ }
+ newsock->type = sock->type;
+ newsock->ops = sock->ops;
+ if ((i = sock->ops->dup(newsock, sock)) < 0) {
+ sock_release(newsock);
+ return(i);
+ }
+
+ i = newsock->ops->accept(sock, newsock, file->f_flags);
+ if ( i < 0) {
+ sock_release(newsock);
+ return(i);
+ }
+
+ if ((fd = get_fd(SOCK_INODE(newsock))) < 0) {
+ sock_release(newsock);
+ return(-EINVAL);
+ }
+
+ DPRINTF((net_debug, "NET: sock_accept: connected socket 0x%x via 0x%x\n",
+ sock, newsock));
+
+ if (upeer_sockaddr)
+ newsock->ops->getname(newsock, upeer_sockaddr, upeer_addrlen, 1);
+
+ return(fd);
+}
+
+
+/* Attempt to connect to a socket with the server address. */
static int
sock_connect(int fd, struct sockaddr *uservaddr, int addrlen)
{
- struct socket *sock;
- struct file *file;
- int i;
-
- PRINTK(("sys_connect: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, &file)))
- return -EBADF;
- switch (sock->state) {
- case SS_UNCONNECTED:
- /* This is ok... continue with connect */
- break;
- case SS_CONNECTED:
- /* Socket is already connected */
- return -EISCONN;
- case SS_CONNECTING:
- /* Not yet connected... */
- /* we will check this. */
- return (sock->ops->connect(sock, uservaddr, addrlen, file->f_flags));
- default:
- PRINTK(("sys_connect: socket not unconnected\n"));
- return -EINVAL;
- }
- i = sock->ops->connect(sock, uservaddr, addrlen, file->f_flags);
- if (i < 0) {
- PRINTK(("sys_connect: connect failed\n"));
- return i;
- }
- return 0;
+ struct socket *sock;
+ struct file *file;
+ int i;
+
+ DPRINTF((net_debug, "NET: sock_connect: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, &file))) return(-EBADF);
+ switch(sock->state) {
+ case SS_UNCONNECTED:
+ /* This is ok... continue with connect */
+ break;
+ case SS_CONNECTED:
+ /* Socket is already connected */
+ return -EISCONN;
+ case SS_CONNECTING:
+ /* Not yet connected... we will check this. */
+ return(sock->ops->connect(sock, uservaddr,
+ addrlen, file->f_flags));
+ default:
+ DPRINTF((net_debug,
+ "NET: sock_connect: socket not unconnected\n"));
+ return(-EINVAL);
+ }
+ i = sock->ops->connect(sock, uservaddr, addrlen, file->f_flags);
+ if (i < 0) {
+ DPRINTF((net_debug, "NET: sock_connect: connect failed\n"));
+ return(i);
+ }
+ return(0);
}
+
static int
sock_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sys_getsockname: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return -EBADF;
- return sock->ops->getname(sock, usockaddr, usockaddr_len, 0);
+ DPRINTF((net_debug, "NET: sock_getsockname: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+ return(sock->ops->getname(sock, usockaddr, usockaddr_len, 0));
}
+
static int
sock_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
{
- struct socket *sock;
+ struct socket *sock;
- PRINTK(("sys_getpeername: fd = %d\n", fd));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return -EBADF;
- return sock->ops->getname(sock, usockaddr, usockaddr_len, 1);
+ DPRINTF((net_debug, "NET: sock_getpeername: fd = %d\n", fd));
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-EBADF);
+ return(sock->ops->getname(sock, usockaddr, usockaddr_len, 1));
}
-/* send - shutdown added by bir7@leland.stanford.edu */
-
static int
-sys_send( int fd, void * buff, int len, unsigned flags)
+sock_send(int fd, void * buff, int len, unsigned flags)
{
- struct socket *sock;
- struct file *file;
+ struct socket *sock;
+ struct file *file;
- PRINTK(("sys_send (fd = %d, buff = %X, len = %d, flags = %X)\n",
- fd, buff, len, flags));
+ DPRINTF((net_debug,
+ "NET: sock_send(fd = %d, buff = %X, len = %d, flags = %X)\n",
+ fd, buff, len, flags));
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
-
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
-
- return (sock->ops->send (sock, buff, len, (file->f_flags & O_NONBLOCK),
- flags));
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->send(sock, buff, len, (file->f_flags & O_NONBLOCK), flags));
}
+
static int
-sys_sendto( int fd, void * buff, int len, unsigned flags,
+sock_sendto(int fd, void * buff, int len, unsigned flags,
struct sockaddr *addr, int addr_len)
{
- struct socket *sock;
- struct file *file;
+ struct socket *sock;
+ struct file *file;
- PRINTK(("sys_sendto (fd = %d, buff = %X, len = %d, flags = %X,"
- " addr=%X, alen = %d\n", fd, buff, len, flags, addr, addr_len));
+ DPRINTF((net_debug,
+ "NET: sock_sendto(fd = %d, buff = %X, len = %d, flags = %X,"
+ " addr=%X, alen = %d\n", fd, buff, len, flags, addr, addr_len));
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
-
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
-
- return (sock->ops->sendto (sock, buff, len,
- (file->f_flags & O_NONBLOCK),
- flags, addr, addr_len));
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->sendto(sock, buff, len, (file->f_flags & O_NONBLOCK),
+ flags, addr, addr_len));
}
static int
-sys_recv( int fd, void * buff, int len, unsigned flags)
+sock_recv(int fd, void * buff, int len, unsigned flags)
{
- struct socket *sock;
- struct file *file;
-
- PRINTK(("sys_recv (fd = %d, buff = %X, len = %d, flags = %X)\n",
- fd, buff, len, flags));
+ struct socket *sock;
+ struct file *file;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
+ DPRINTF((net_debug,
+ "NET: sock_recv(fd = %d, buff = %X, len = %d, flags = %X)\n",
+ fd, buff, len, flags));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
-
- return (sock->ops->recv (sock, buff, len,(file->f_flags & O_NONBLOCK),
- flags));
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->recv(sock, buff, len,(file->f_flags & O_NONBLOCK), flags));
}
+
static int
-sys_recvfrom( int fd, void * buff, int len, unsigned flags,
+sock_recvfrom(int fd, void * buff, int len, unsigned flags,
struct sockaddr *addr, int *addr_len)
{
- struct socket *sock;
- struct file *file;
-
- PRINTK(("sys_recvfrom (fd = %d, buff = %X, len = %d, flags = %X,"
- " addr=%X, alen=%X\n", fd, buff, len, flags, addr, addr_len));
+ struct socket *sock;
+ struct file *file;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
+ DPRINTF((net_debug,
+ "NET: sock_recvfrom(fd = %d, buff = %X, len = %d, flags = %X,"
+ " addr=%X, alen=%X\n", fd, buff, len, flags, addr, addr_len));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
-
- return (sock->ops->recvfrom (sock, buff, len,
- (file->f_flags & O_NONBLOCK),
- flags, addr, addr_len));
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->recvfrom(sock, buff, len, (file->f_flags & O_NONBLOCK),
+ flags, addr, addr_len));
}
static int
-sys_setsockopt (int fd, int level, int optname, char *optval, int optlen)
+sock_setsockopt(int fd, int level, int optname, char *optval, int optlen)
{
- struct socket *sock;
- struct file *file;
+ struct socket *sock;
+ struct file *file;
- PRINTK (("sys_setsockopt(fd=%d, level=%d, optname=%d,\n",fd, level,
- optname));
- PRINTK ((" optval = %X, optlen = %d)\n", optval, optlen));
-
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
-
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
+ DPRINTF((net_debug, "NET: sock_setsockopt(fd=%d, level=%d, optname=%d,\n",
+ fd, level, optname));
+ DPRINTF((net_debug, " optval = %X, optlen = %d)\n",
+ optval, optlen));
- return (sock->ops->setsockopt (sock, level, optname, optval, optlen));
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
}
+
static int
-sys_getsockopt (int fd, int level, int optname, char *optval, int *optlen)
+sock_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
{
- struct socket *sock;
- struct file *file;
- PRINTK (("sys_getsockopt(fd=%d, level=%d, optname=%d,\n",fd, level,
- optname));
- PRINTK ((" optval = %X, optlen = %X)\n", optval, optlen));
+ struct socket *sock;
+ struct file *file;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
- return (-EBADF);
+ DPRINTF((net_debug, "NET: sock_getsockopt(fd=%d, level=%d, optname=%d,\n",
+ fd, level, optname));
+ DPRINTF((net_debug, " optval = %X, optlen = %X)\n",
+ optval, optlen));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
+ if (fd < 0 || fd >= NR_OPEN || ((file = current->filp[fd]) == NULL))
+ return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
- if (!sock->ops || !sock->ops->getsockopt)
- return 0;
- return sock->ops->getsockopt(sock, level, optname, optval, optlen);
-
+ if (!sock->ops || !sock->ops->getsockopt) return(0);
+ return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
}
static int
-sys_shutdown( int fd, int how)
+sock_shutdown(int fd, int how)
{
- struct socket *sock;
- struct file *file;
-
- PRINTK(("sys_shutdown (fd = %d, how = %d)\n",fd, how));
+ struct socket *sock;
+ struct file *file;
- file = current->filp[fd];
- if (fd < 0 || fd >= NR_OPEN || file == NULL)
- return (-EBADF);
+ DPRINTF((net_debug, "NET: sock_shutdown(fd = %d, how = %d)\n", fd, how));
- if (!(sock = sockfd_lookup(fd, NULL)))
- return (-ENOTSOCK);
-
- return (sock->ops->shutdown (sock, how));
+ file = current->filp[fd];
+ if (fd < 0 || fd >= NR_OPEN || file == NULL) return(-EBADF);
+ if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK);
+ return(sock->ops->shutdown(sock, how));
}
+
int
sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
{
- struct socket *sock;
- sock = socki_lookup (filp->f_inode);
-
- if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
- return (sock->ops->fcntl (sock, cmd, arg));
+ struct socket *sock;
- return (-EINVAL);
+ sock = socki_lookup (filp->f_inode);
+ if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
+ return(sock->ops->fcntl(sock, cmd, arg));
+ return(-EINVAL);
}
/*
- * system call vectors. since i want to rewrite sockets as streams, we have
- * this level of indirection. not a lot of overhead, since more of the work is
- * done via read/write/select directly
+ * System call vectors. Since I (RIB) want to rewrite sockets as streams,
+ * we have this level of indirection. Not a lot of overhead, since more of
+ * the work is done via read/write/select directly.
*/
int
sys_socketcall(int call, unsigned long *args)
{
- switch (call) {
+ switch(call) {
case SYS_SOCKET:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_socket(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_socket(get_fs_long(args+0),
get_fs_long(args+1),
- get_fs_long(args+2));
-
+ get_fs_long(args+2)));
case SYS_BIND:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_bind(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_bind(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
- get_fs_long(args+2));
-
+ get_fs_long(args+2)));
case SYS_CONNECT:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_connect(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_connect(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
- get_fs_long(args+2));
-
+ get_fs_long(args+2)));
case SYS_LISTEN:
- verify_area(VERIFY_WRITE,args, 2 * sizeof(long));
- return sock_listen(get_fs_long(args+0),
- get_fs_long(args+1));
-
+ verify_area(VERIFY_WRITE, args, 2 * sizeof(long));
+ return(sock_listen(get_fs_long(args+0),
+ get_fs_long(args+1)));
case SYS_ACCEPT:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_accept(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_accept(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
- (int *)get_fs_long(args+2));
-
+ (int *)get_fs_long(args+2)));
case SYS_GETSOCKNAME:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_getsockname(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_getsockname(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
- (int *)get_fs_long(args+2));
-
+ (int *)get_fs_long(args+2)));
case SYS_GETPEERNAME:
- verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
- return sock_getpeername(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 3 * sizeof(long));
+ return(sock_getpeername(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
- (int *)get_fs_long(args+2));
-
+ (int *)get_fs_long(args+2)));
case SYS_SOCKETPAIR:
- verify_area(VERIFY_WRITE,args, 4 * sizeof(long));
- return sock_socketpair(get_fs_long(args+0),
+ verify_area(VERIFY_WRITE, args, 4 * sizeof(long));
+ return(sock_socketpair(get_fs_long(args+0),
get_fs_long(args+1),
get_fs_long(args+2),
- (int *)get_fs_long(args+3));
-
- case SYS_SEND:
- verify_area(VERIFY_WRITE,args, 4 * sizeof (unsigned long));
- return ( sys_send (get_fs_long(args+0),
- (void *)get_fs_long(args+1),
- get_fs_long(args+2),
- get_fs_long(args+3)));
-
- case SYS_SENDTO:
- verify_area(VERIFY_WRITE,args, 6 * sizeof (unsigned long));
- return ( sys_sendto (get_fs_long(args+0),
- (void *)get_fs_long(args+1),
- get_fs_long(args+2),
- get_fs_long(args+3),
- (struct sockaddr *)get_fs_long(args+4),
- get_fs_long(args+5)));
-
-
- case SYS_RECV:
- verify_area(VERIFY_WRITE,args, 4 * sizeof (unsigned long));
- return ( sys_recv (get_fs_long(args+0),
- (void *)get_fs_long(args+1),
- get_fs_long(args+2),
- get_fs_long(args+3)));
-
- case SYS_RECVFROM:
- verify_area(VERIFY_WRITE,args, 6 * sizeof (unsigned long));
- return ( sys_recvfrom (get_fs_long(args+0),
+ (unsigned long *)get_fs_long(args+3)));
+ case SYS_SEND:
+ verify_area(VERIFY_WRITE, args, 4 * sizeof(unsigned long));
+ return(sock_send(get_fs_long(args+0),
+ (void *)get_fs_long(args+1),
+ get_fs_long(args+2),
+ get_fs_long(args+3)));
+ case SYS_SENDTO:
+ verify_area(VERIFY_WRITE, args, 6 * sizeof(unsigned long));
+ return(sock_sendto(get_fs_long(args+0),
+ (void *)get_fs_long(args+1),
+ get_fs_long(args+2),
+ get_fs_long(args+3),
+ (struct sockaddr *)get_fs_long(args+4),
+ get_fs_long(args+5)));
+ case SYS_RECV:
+ verify_area(VERIFY_WRITE, args, 4 * sizeof(unsigned long));
+ return(sock_recv(get_fs_long(args+0),
(void *)get_fs_long(args+1),
get_fs_long(args+2),
- get_fs_long(args+3),
- (struct sockaddr *)get_fs_long(args+4),
- (int *)get_fs_long(args+5)));
-
- case SYS_SHUTDOWN:
- verify_area(VERIFY_WRITE, args, 2* sizeof (unsigned long));
- return ( sys_shutdown (get_fs_long (args+0),
- get_fs_long (args+1)));
-
- case SYS_SETSOCKOPT:
- verify_area(VERIFY_WRITE, args, 5*sizeof (unsigned long));
- return (sys_setsockopt (get_fs_long (args+0),
- get_fs_long (args+1),
- get_fs_long (args+2),
- (char *)get_fs_long (args+3),
- get_fs_long (args+4)));
-
-
- case SYS_GETSOCKOPT:
- verify_area(VERIFY_WRITE, args, 5*sizeof (unsigned long));
- return (sys_getsockopt (get_fs_long (args+0),
- get_fs_long (args+1),
- get_fs_long (args+2),
- (char *)get_fs_long (args+3),
- (int *)get_fs_long (args+4)));
+ get_fs_long(args+3)));
+ case SYS_RECVFROM:
+ verify_area(VERIFY_WRITE, args, 6 * sizeof(unsigned long));
+ return(sock_recvfrom(get_fs_long(args+0),
+ (void *)get_fs_long(args+1),
+ get_fs_long(args+2),
+ get_fs_long(args+3),
+ (struct sockaddr *)get_fs_long(args+4),
+ (int *)get_fs_long(args+5)));
+ case SYS_SHUTDOWN:
+ verify_area(VERIFY_WRITE, args, 2* sizeof(unsigned long));
+ return(sock_shutdown(get_fs_long(args+0),
+ get_fs_long(args+1)));
+ case SYS_SETSOCKOPT:
+ verify_area(VERIFY_WRITE, args, 5*sizeof(unsigned long));
+ return(sock_setsockopt(get_fs_long(args+0),
+ get_fs_long(args+1),
+ get_fs_long(args+2),
+ (char *)get_fs_long(args+3),
+ get_fs_long(args+4)));
+ case SYS_GETSOCKOPT:
+ verify_area(VERIFY_WRITE, args, 5*sizeof(unsigned long));
+ return(sock_getsockopt(get_fs_long(args+0),
+ get_fs_long(args+1),
+ get_fs_long(args+2),
+ (char *)get_fs_long(args+3),
+ (int *)get_fs_long(args+4)));
+ default:
+ return(-EINVAL);
+ }
+}
+
+static int
+net_ioctl(unsigned int cmd, unsigned long arg)
+{
+ switch(cmd) {
+ case DDIOCSDBG:
+ verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
+ net_debug = get_fs_long((void *)arg);
+ if (net_debug != 0 && net_debug != 1) {
+ net_debug = 0;
+ return(-EINVAL);
+ }
+ return(0);
default:
- return -EINVAL;
- }
+ return(-EINVAL);
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+
+/*
+ * Handle the IOCTL system call for the NET devices. This basically
+ * means I/O control for the SOCKET layer (future expansions could be
+ * a variable number of socket table entries, et al), and for the more
+ * general protocols like ARP. The latter currently lives in the INET
+ * module, so we have to get ugly a tiny little bit. Later... -FvK
+ */
+static int
+net_fioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ extern int arp_ioctl(unsigned int, void *);
+
+ /* Dispatch on the minor device. */
+ switch(MINOR(inode->i_rdev)) {
+ case 0: /* NET (SOCKET) */
+ DPRINTF((net_debug, "NET: SOCKET level I/O control request.\n"));
+ return(net_ioctl(cmd, arg));
+#ifdef CONFIG_INET
+ case 1: /* ARP */
+ DPRINTF((net_debug, "NET: ARP level I/O control request.\n"));
+ return(arp_ioctl(cmd, (void *) arg));
+#endif
+ default:
+ return(-ENODEV);
+ }
+ /*NOTREACHED*/
+ return(-EINVAL);
+}
+
+
+static struct file_operations net_fops = {
+ NULL, /* LSEEK */
+ NULL, /* READ */
+ NULL, /* WRITE */
+ NULL, /* READDIR */
+ NULL, /* SELECT */
+ net_fioctl, /* IOCTL */
+ NULL, /* MMAP */
+ NULL, /* OPEN */
+ NULL /* CLOSE */
+};
+
+
+/*
+ * This function is called by a protocol handler that wants to
+ * advertise its address family, and have it linked into the
+ * SOCKET module.
+ */
+int
+sock_register(int family, struct proto_ops *ops)
+{
+ int i;
+
+ cli();
+ for(i = 0; i < NPROTO; i++) {
+ if (pops[i] != NULL) continue;
+ pops[i] = ops;
+ pops[i]->family = family;
+ sti();
+ DPRINTF((net_debug, "NET: Installed protocol %d in slot %d (0x%X)\n",
+ family, i, (long)ops));
+ return(i);
+ }
+ sti();
+ return(-ENOMEM);
}
+
void
sock_init(void)
{
- struct socket *sock;
- int i, ok;
-
- for (sock = sockets; sock <= last_socket; ++sock)
- sock->state = SS_FREE;
- for (i = ok = 0; i < NPROTO; ++i) {
- if ((*proto_table[i].ops->init)() < 0) {
- printk("sock_init: init failed family %d (%s)\n",
- proto_table[i].family,
- proto_table[i].name);
- proto_table[i].family = -1;
- }
- else
- ++ok;
- }
- if (!ok)
- printk("sock_init: warning: no protocols initialized\n");
+ struct socket *sock;
+ int i;
+
+ /* Set up our SOCKET VFS major device. */
+ if (register_chrdev(SOCKET_MAJOR, "socket", &net_fops) < 0) {
+ printk("NET: cannot register major device %d!\n", SOCKET_MAJOR);
return;
-}
+ }
+ /* Release all sockets. */
+ for (sock = sockets; sock <= last_socket; ++sock) sock->state = SS_FREE;
+
+ /* Initialize all address (protocol) families. */
+ for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
+
+ /* Initialize the DDI module. */
+ ddi_init();
+
+ /* Initialize the ARP module. */
+#if 0
+ arp_init();
+#endif
+}
diff --git a/net/socketcall.h b/net/socketcall.h
deleted file mode 100644
index 29fd1ae..0000000
--- a/net/socketcall.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _SOCKETCALL_
-#define _SOCKETCALL_
-
-#define SYS_SOCKET 1
-#define SYS_BIND 2
-#define SYS_CONNECT 3
-#define SYS_LISTEN 4
-#define SYS_ACCEPT 5
-#define SYS_GETSOCKNAME 6
-#define SYS_GETPEERNAME 7
-#define SYS_SOCKETPAIR 8
-#define SYS_SEND 9
-#define SYS_RECV 10
-#define SYS_SENDTO 11
-#define SYS_RECVFROM 12
-#define SYS_SHUTDOWN 13
-#define SYS_SETSOCKOPT 14
-#define SYS_GETSOCKOPT 15
-
-#endif _SOCKETCALL_
diff --git a/net/tcp/Space.c b/net/tcp/Space.c
deleted file mode 100644
index 8bcfce9..0000000
--- a/net/tcp/Space.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Space.c */
-
-/* Holds initial configuration information for devices. */
-/* $Id: Space.c,v 0.8.4.7 1993/01/22 23:21:38 bir7 Exp $ */
-/* $Log: Space.c,v $
- * Revision 0.8.4.7 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.6 1993/01/22 22:58:08 bir7
- * *** empty log message ***
- *
- * Revision 0.8.4.5 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.4 1992/12/05 21:35:53 bir7
- * Updated dev->init type.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Removed ctrl-h so diff no longer thinks it's a binary file.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#include "dev.h"
-#include <linux/stddef.h>
-
-extern int wd8003_init(struct device *);
-extern int loopback_init(struct device *dev);
-
-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 */
-};
-
-static struct device loopback_dev =
-{
- "loopback",
- -1, /* recv memory end. */
- 0x0, /* recv memory start. */
- -1, /* memory end. */
- 0, /* memory start. */
- 0, /* base i/o address. */
- 0, /* irq */
- 0,0,1,0,0, /* flags */
- &wd8003_dev, /* next device */
- loopback_init,
- /* loopback_init should set up the rest. */
- 0, /* trans start. */
- {NULL}, /* buffs */
- NULL, /* backlog */
- NULL, /* open */
- NULL, /* stop */
- NULL, /* hard_start_xmit */
- NULL, /* hard_header */
- NULL, /* add arp */
- NULL, /* queue xmit */
- NULL, /* rebuild header */
- NULL, /* type_trans */
- NULL, /* send_packet */
- NULL, /* private */
- 0, /* type. */
- 0, /* hard_header_len */
- 0, /* mtu */
- {0,}, /* broadcast address */
- {0,}, /* device address */
- 0 /* addr len */
-};
-
-struct device *dev_base = &loopback_dev;
diff --git a/net/tcp/arp.c b/net/tcp/arp.c
deleted file mode 100644
index f1243b7..0000000
--- a/net/tcp/arp.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/* 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
-*/
-/* $Id: arp.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: arp.c,v $
- * Revision 0.8.4.8 1993/01/23 18:00:11 bir7
- * Added ioctls as supplied by R.P. Bellis <rpb@psy.ox.ac.uk>
- *
- * Revision 0.8.4.7 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.5 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.4 1992/12/03 19:52:20 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Put more cli/sti pairs in send_q and another sanity check
- * in arp_queue.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/config.h>
-
-#include <linux/socket.h>
-#include <netinet/in.h>
-#include <linux/sock_ioctl.h>
-#include <linux/errno.h>
-#include <asm/segment.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(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-static volatile struct arp_table *arp_table[ARP_TABLE_SIZE] ={NULL, };
-volatile 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;
- struct sk_buff *next;
-
- cli();
- next = (struct sk_buff *)arp_q;
- arp_q = NULL;
- sti();
- while ((skb = next) != NULL) {
- if (skb->magic != ARP_QUEUE_MAGIC)
- {
- printk ("arp.c skb with bad magic - %X: squashing queue\n", skb->magic);
- return;
- }
- /* extra consistancy check. */
- if (skb->next == NULL
-#ifdef CONFIG_MAX_16M
- || (unsigned long)(skb->next) > 16*1024*1024
-#endif
- )
- {
- printk ("dev.c: *** bug bad skb->next, squashing queue \n");
- return;
- }
-
- /* first remove skb from the queue. */
- next = (struct sk_buff *)skb->next;
- if (next == skb)
- {
- next = NULL;
- }
- else
- {
- skb->prev->next = next;
- next->prev = skb->prev;
- }
-
- skb->magic = 0;
- skb->next = NULL;
- skb->prev = NULL;
-
- if (!skb->dev->rebuild_header (skb+1, skb->dev))
- {
- skb->next = NULL;
- skb->prev = NULL;
- skb->arp = 1;
- skb->dev->queue_xmit (skb, skb->dev, 0);
- }
- else
- {
- cli();
- skb->magic = ARP_QUEUE_MAGIC;
- if (arp_q == NULL)
- {
- skb->next = skb;
- skb->prev = skb;
- arp_q = skb;
- }
- else
- {
- skb->next = arp_q;
- skb->prev = arp_q->prev;
- arp_q->prev->next = skb;
- arp_q->prev = skb;
- }
- sti();
- }
- }
-}
-
-static void
-print_arp(struct arp *arp)
-{
- int i;
- unsigned long *lptr;
- unsigned char *ptr;
- PRINTK (("arp: \n"));
- if (arp == NULL)
- {
- PRINTK (("(null)\n"));
- return;
- }
- 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)
-{
- kfree_s(ptr, len);
-}
-
-static void *
-arp_malloc (unsigned long amount, int priority)
-{
- return (kmalloc (amount, priority));
-}
-
-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,
- GFP_ATOMIC);
- if (skb == NULL) return (1);
-
- skb->lock = 0;
- 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 = (struct arp_table *)arp_table[hash];
- apt != NULL;
- apt = (struct arp_table *)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. */
- lapt = (struct arp_table **) &arp_table[hash];
- while ((apt = *lapt) != NULL) {
- if (apt->ip == paddr)
- {
- *lapt = (struct arp_table *) apt->next;
- arp_free(apt, sizeof(*apt));
- sti();
- return;
- }
- lapt = (struct arp_table **) &apt->next;
- }
- 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), GFP_ATOMIC);
- 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;
- cli();
- apt->next = arp_table[hash];
- arp_table[hash] = apt;
- sti();
- 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))
- {
- kfree_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)
- {
- kfree_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)) != IS_MYADDR)
- {
- kfree_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))
- {
- kfree_skb (skb, FREE_READ);
- return (0);
- }
-
- /* now we need to create a new packet. */
- ret = arp_response(arp, dev);
- kfree_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, GFP_ATOMIC);
- if (skb == NULL) return;
-
- skb->lock = 0;
- 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);
- }
- else
- {
- arp_destroy(paddr);
- }
- }
-
- /* this assume haddr are at least 4 bytes.
- If this isn't true we can use a lookup
- table, one for every dev. */
- *(unsigned long *)haddr = paddr;
-
- /* if we didn't find an entry, we will try to
- send an arp packet. */
- arp_snd(paddr,dev,saddr);
-
- 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 (skb->next != NULL)
- {
- sti();
- printk ("arp.c: arp_queue skb already on queue magic=%X. \n",
- skb->magic);
- return;
- }
- 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;
- }
- skb->magic = ARP_QUEUE_MAGIC;
- sti();
-}
-
-static int arpreq_check(struct arpreq *req)
-{
- /* Check protocol familys */
- if (req->arp_pa.sa_family != AF_INET) return -1;
-
- if (req->arp_ha.sa_family != AF_UNSPEC) return -1;
-
- return 0;
-}
-
-int arp_ioctl_set(struct arpreq *req)
-{
- struct arpreq r;
- struct arp_table *apt;
-
- memcpy_fromfs(&r, req, sizeof(r));
- if (arpreq_check(&r) != 0) return -EPFNOSUPPORT;
-
- apt = arp_lookup(*(unsigned long *)r.arp_pa.sa_data);
- if (apt) {
- apt->last_used = timer_seq;
- memcpy(apt->hard, r.arp_ha.sa_data , 6);
- } else {
- if (!create_arp(*(unsigned long *)r.arp_pa.sa_data,
- r.arp_ha.sa_data, 6)) {
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
-int arp_ioctl_get(struct arpreq *req)
-{
- struct arpreq r;
- struct arp_table *apt;
-
- memcpy_fromfs(&r, req, sizeof(r));
- if (arpreq_check(&r) != 0) return -EPFNOSUPPORT;
- apt = arp_lookup(*(unsigned long *)r.arp_pa.sa_data);
- if (apt) {
- memcpy(r.arp_ha.sa_data, apt->hard, apt->hlen);
- } else {
- return -ENXIO;
- }
-
- /* Copy the information back */
- memcpy_tofs(req, &r, sizeof(r));
- return 0;
-}
-
-int arp_ioctl_del(struct arpreq *req)
-{
- struct arpreq r;
-
- memcpy_fromfs(&r, req, sizeof(r));
- if (arpreq_check(&r) != 0) return -EPFNOSUPPORT;
-
- arp_destroy(*(unsigned long *)r.arp_pa.sa_data);
-
- return 0;
-}
diff --git a/net/tcp/arp.h b/net/tcp/arp.h
deleted file mode 100644
index 7d36f5a..0000000
--- a/net/tcp/arp.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* 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
-*/
-/* $Id: arp.h,v 0.8.4.4 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: arp.h,v $
- * Revision 0.8.4.4 1993/01/23 18:00:11 bir7
- * Added ioctls as supplied by R.P. Bellis <rpb@psy.ox.ac.uk>
- *
- * Revision 0.8.4.3 1992/12/03 19:54:12 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.2 1992/11/15 14:55:30 bir7
- * make arp_q global so sock.c can mess with it.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: arp.h,v $
- * Revision 0.8.4.4 1993/01/23 18:00:11 bir7
- * Added ioctls as supplied by R.P. Bellis <rpb@psy.ox.ac.uk>
- *
- * Revision 0.8.4.3 1992/12/03 19:54:12 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.2 1992/11/15 14:55:30 bir7
- * make arp_q global so sock.c can mess with it.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-#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
-{
- volatile struct arp_table *next;
- volatile unsigned long last_used;
- unsigned long ip;
- unsigned char hlen;
- unsigned char hard[MAX_ADDR_LEN];
-};
-
-volatile struct sk_buff *arp_q;
-
-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);
-
-int arp_ioctl_set(struct arpreq *req);
-int arp_ioctl_get(struct arpreq *req);
-int arp_ioctl_del(struct arpreq *req);
-
-#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. */
-
-#define ARP_QUEUE_MAGIC 0x0432447A
-
-#endif
diff --git a/net/tcp/dev.c b/net/tcp/dev.c
deleted file mode 100644
index 90ce81f..0000000
--- a/net/tcp/dev.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/* 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
-*/
-/* $Id: dev.c,v 0.8.4.13 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: dev.c,v $
- * Revision 0.8.4.13 1993/01/23 18:00:11 bir7
- * Fixed problems from merging.
- *
- * Revision 0.8.4.12 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.11 1993/01/22 22:58:08 bir7
- * Changed so transmitting takes place in bottom half of interrupt routine.
- *
- * Revision 0.8.4.10 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.9 1992/12/12 01:50:49 bir7
- * *** empty log message ***
- *
- * Revision 0.8.4.8 1992/12/08 20:49:15 bir7
- * Edited ctrl-h's out of log messages.
- *
- * Revision 0.8.4.7 1992/12/06 23:29:59 bir7
- * Converted to using lower half interrupt routine.
- *
- * Revision 0.8.4.6 1992/12/05 21:35:53 bir7
- * Updated dev->init type.
- *
- * Revision 0.8.4.5 1992/12/03 19:52:20 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.4 1992/11/18 15:38:03 bir7
- * Fixed bug in copying packets and changed some printk's
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * More sanity checks.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.5 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <linux/config.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 "dev.h"
-#include "eth.h"
-#include "timer.h"
-#include "ip.h"
-#include "tcp.h"
-#include "sock.h"
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include "arp.h"
-
-#undef DEV_DEBUG
-#ifdef DEV_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#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;
- int where=0; /* used to say if the packet should go at the
- front or the back of the queue. */
-
- PRINTK (("dev_queue_xmit (skb=%X, dev=%X, pri = %d)\n", skb, dev, pri));
-
- if (dev == NULL)
- {
- printk ("dev.c: dev_queue_xmit: dev = NULL\n");
- return;
- }
-
- skb->dev = dev;
- if (skb->next != NULL)
- {
- /* make sure we haven't missed an interrupt. */
- dev->hard_start_xmit (NULL, dev);
- return;
- }
-
- if (pri < 0)
- {
- pri = -pri-1;
- where = 1;
- }
-
- if ( pri >= DEV_NUMBUFFS)
- {
- printk ("bad priority in dev_queue_xmit.\n");
- pri = 1;
- }
-
- if (dev->hard_start_xmit(skb, dev) == 0)
- {
- return;
- }
-
- /* put skb into a bidirectional circular linked list. */
- PRINTK (("dev_queue_xmit 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
- {
- if (where)
- {
- skb->next = (struct sk_buff *)dev->buffs[pri];
- skb->prev = (struct sk_buff *)dev->buffs[pri]->prev;
- skb->prev->next = skb;
- skb->next->prev = skb;
- dev->buffs[pri] = skb;
- }
- else
- {
- skb2= (struct sk_buff *)dev->buffs[pri];
- skb->next = skb2;
- skb->prev = skb2->prev;
- skb->next->prev = skb;
- skb->prev->next = skb;
- }
- }
- skb->magic = DEV_QUEUE_MAGIC;
- 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.
-
- It's changed now
- 1 <- exit I can't do any more
- 0 <- feed me more.
-
- */
-
-static volatile struct sk_buff * volatile backlog = NULL;
-
-int
-dev_rint(unsigned char *buff, long len, int flags,
- struct device * dev)
-{
- struct sk_buff *skb=NULL;
- unsigned char *to;
- int amount;
-
- if (dev == NULL || buff == NULL || len <= 0) return (1);
-
- if (flags & IN_SKBUFF)
- {
- skb = (struct sk_buff *)buff;
- }
- else
- {
- skb = kmalloc (sizeof (*skb) + len, GFP_ATOMIC);
- if (skb == NULL)
- {
- printk ("dev_rint:dropping packet due to lack of memory.\n");
- return (1);
- }
- skb->lock = 0;
- skb->mem_len = sizeof (*skb) + len;
- skb->mem_addr = (struct sk_buff *)skb;
- /* first we copy the packet into a buffer, and save it for later. */
-
- 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;
- }
- }
-
- skb->len = len;
- skb->dev = dev;
- skb->sk = NULL;
-
- /* now add it to the backlog. */
- cli();
- if (backlog == NULL)
- {
- skb->prev = skb;
- skb->next = skb;
- backlog = skb;
- }
- else
- {
- skb->prev = (struct sk_buff *)backlog->prev;
- skb->next = (struct sk_buff *)backlog;
- skb->next->prev = skb;
- skb->prev->next = skb;
- }
- sti();
-
- if (backlog != NULL)
- mark_bh(INET_BH);
-
- return (0);
-}
-
-void
-dev_transmit(void)
-{
- struct device *dev;
-
- for (dev = dev_base; dev != NULL; dev=dev->next)
- {
- if (!dev->tbusy)
- {
- dev_tint (dev);
- }
- }
-}
-
-void
-inet_bh(void *tmp)
-{
- struct sk_buff *skb;
- struct packet_type *ptype;
- unsigned short type;
- unsigned char flag =0;
- static volatile int in_bh=0;
-
- cli();
- if (in_bh != 0)
- {
- sti();
- return;
- }
- in_bh=1;
- sti();
-
- dev_transmit();
- /* anything left to process? */
-
- cli();
- while (backlog != NULL)
- {
- skb= (struct sk_buff *)backlog;
- if (skb->next == skb)
- {
- backlog = NULL;
- }
- else
- {
- 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) + skb->dev->hard_header_len;
- skb->len -= skb->dev->hard_header_len;
-
- /* convert the type to an ethernet type. */
- type = skb->dev->type_trans ((struct sk_buff *)skb, 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 = kmalloc (skb->mem_len, GFP_ATOMIC);
- if (skb2 == NULL) continue;
- memcpy (skb2, (const void *) skb, skb->mem_len);
- skb2->mem_addr = skb2;
- skb2->lock = 0;
- skb2->h.raw = (void *)((unsigned long)skb2
- + (unsigned long)skb->h.raw
- - (unsigned long)skb);
-
- }
- else
- {
- skb2 = (struct sk_buff *)skb;
- flag = 1;
- }
-
- ptype->func (skb2, skb->dev, ptype);
- }
- }
-
- if (!flag)
- {
- PRINTK (("discarding packet type = %X\n", type));
- kfree_skb ((struct sk_buff *)skb, FREE_READ);
- }
- dev_transmit();
- cli();
- }
- in_bh = 0;
- sti();
-}
-
-/* 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. */
-
-/* now the packet is passed on via the other call. */
-
-void
-dev_tint( struct device *dev)
-{
- int i;
- struct sk_buff *skb;
- for (i=0; i < DEV_NUMBUFFS; i++)
- {
- while (dev->buffs[i]!=NULL)
- {
- cli();
- skb=(struct sk_buff *)dev->buffs[i];
- if (skb->magic != DEV_QUEUE_MAGIC)
- {
- printk ("dev.c skb with bad magic-%X: squashing queue\n",
- skb->magic);
- cli();
- dev->buffs[i] = NULL;
- sti();
- continue;
- }
-
- skb->magic = 0;
-
- if (skb->next == skb)
- {
- dev->buffs[i] = NULL;
- }
- else
- {
- /* extra consistancy check. */
- if (skb->next == NULL
-#ifdef CONFIG_MAX_16M
- || (unsigned long)(skb->next) > 16*1024*1024
-#endif
- )
-
- {
- printk ("dev.c: *** bug bad skb->next, squashing queue \n");
- cli();
- 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();
- /* this will send it through the process again. */
- dev->queue_xmit (skb, dev, -i-1);
- if (dev->tbusy)
- return;
- }
- }
-}
diff --git a/net/tcp/dev.h b/net/tcp/dev.h
deleted file mode 100644
index f590fc9..0000000
--- a/net/tcp/dev.h
+++ /dev/null
@@ -1,127 +0,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
-*/
-/* $Id: dev.h,v 0.8.4.7 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: dev.h,v $
- * Revision 0.8.4.7 1993/01/23 18:00:11 bir7
- * Fixed problems from merging.
- *
- * Revision 0.8.4.6 1993/01/22 22:58:08 bir7
- * Changed so transmitting takes place in bottom half of interrupt routine.
- *
- * Revision 0.8.4.5 1992/12/08 20:49:15 bir7
- * Edited ctrl-h's out of log messages.
- *
- * Revision 0.8.4.4 1992/12/06 23:29:59 bir7
- * Converted to using lower half interrupt routine.
- *
- * Revision 0.8.4.3 1992/12/05 21:35:53 bir7
- * Updated dev->init type.
- *
- * Revision 0.8.4.2 1992/12/03 19:54:12 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#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;
- volatile unsigned char start:1,
- tbusy:1,
- loopback:1,
- interrupt:1,
- up:1;
- struct device *next;
- int (*init)(struct device *dev);
- unsigned long trans_start;
- volatile struct sk_buff * volatile buffs[DEV_NUMBUFFS];
- struct sk_buff *backlog; /* no longer used. */
- 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); /* no longer
- used */
- 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
-#define DEV_QUEUE_MAGIC 0x17432895
-
-
-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, long len, int flags, struct device *dev);
-void dev_tint ( 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);
-void inet_bh (void *tmp);
-
-
-#endif
diff --git a/net/tcp/eth.c b/net/tcp/eth.c
deleted file mode 100644
index a277d5d..0000000
--- a/net/tcp/eth.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* 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
-*/
-/* $Id: eth.c,v 0.8.4.4 1993/01/22 23:21:38 bir7 Exp $ */
-/* $Log: eth.c,v $
- * Revision 0.8.4.4 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.3 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- *
- */
-
-#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 "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(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-void
-print_eth (struct enet_header *eth)
-{
- int i;
- PRINTK (("ether source addr: "));
- for (i =0 ; i < ETHER_ADDR_LEN; i++)
- {
- PRINTK (("0x%2X ",eth->saddr[i]));
- }
- PRINTK (("\n"));
-
- PRINTK (("ether dest addr: "));
- for (i =0 ; i < ETHER_ADDR_LEN; i++)
- {
- PRINTK (("0x%2X ",eth->daddr[i]));
- }
- PRINTK (("\n"));
- PRINTK (("ethertype = %X\n",net16(eth->type)));
-}
-
-int
-eth_hard_header (unsigned char *buff, struct device *dev,
- unsigned short type, unsigned long daddr,
- unsigned long saddr, unsigned len)
-{
- struct enet_header *eth;
- eth = (struct enet_header *)buff;
- eth->type = net16(type);
- memcpy (eth->saddr, dev->dev_addr, dev->addr_len);
- if (daddr == 0)
- {
- memset (eth->daddr, 0xff, dev->addr_len);
- return (14);
- }
- if (!arp_find (eth->daddr, daddr, dev, saddr))
- {
- return (14);
- }
- else
- {
- *(unsigned long *)eth->saddr = saddr;
- return (-14);
- }
-}
-
-int
-eth_rebuild_header (void *buff, struct device *dev)
-{
- struct enet_header *eth;
- eth = buff;
- if (arp_find(eth->daddr, *(unsigned long*)eth->daddr, dev,
- *(unsigned long *)eth->saddr))
- return (1);
- memcpy (eth->saddr, dev->dev_addr, dev->addr_len);
- return (0);
-}
-
-void
-eth_add_arp (unsigned long addr, struct sk_buff *skb, struct device *dev)
-{
- struct enet_header *eh;
- eh = (struct enet_header *)(skb + 1);
- arp_add (addr, eh->saddr, dev);
-}
-
-unsigned short
-eth_type_trans (struct sk_buff *skb, struct device *dev)
-{
- struct enet_header *eh;
- eh = (struct enet_header *)(skb + 1);
- return (eh->type);
-}
diff --git a/net/tcp/eth.h b/net/tcp/eth.h
deleted file mode 100644
index 493760a..0000000
--- a/net/tcp/eth.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* 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
-*/
-/* $Id: eth.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $ */
-/* $Log: eth.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: eth.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-#ifndef _TCP_ETH_H
-#define _TCP_ETH_H
-
-#define ETHER_MIN_LEN 64
-#define ETHER_ADDR_LEN 6
-
-#define ETHERTYPE_ARP 0x806
-#define ETHERTYPE_IP 0x800
-#define ETHER_TYPE 1
-
-/* Reciever modes */
-#define ETH_MODE_MONITOR 1 /* Monitor mode - no receive */
-#define ETH_MODE_PHYSICAL 2 /* Physical address receive only */
-#define ETH_MODE_BROADCAST 3 /* Broadcast receive + mode 2 */
-#define ETH_MODE_MULTICAST 4 /* Multicast receive + mode 3 */
-#define ETH_MODE_PROMISCUOUS 5 /* Promiscuous mode - receive all */
-
-#define WD_RX_SAVE_ERRORS 1 /* save error packets */
-#define WD_RX_RUNT 2 /* accept runt packets */
-#define WD_RX_BROAD 4 /* accept broadcast packets */
-#define WD_RX_MULTI 8 /* accept multicast packets */
-#define WD_RX_PROM 0x10 /* accept all packets */
-#define WD_RX_MON 0x20 /* monitor mode (just count packets) */
-
-#define NET16(x) (((x&0xff)<<8)|((x>>8)&0xff))
-
-struct enet_header
-{
- unsigned char daddr[ETHER_ADDR_LEN];
- unsigned char saddr[ETHER_ADDR_LEN];
- unsigned short type;
-};
-
-#define ETHER_HEADER sizeof(struct enet_header)
-
-struct enet_statistics{
- int rx_packets; /* total packets received */
- int tx_packets; /* total packets transmitted */
- int rx_errors; /* bad packets received */
- int tx_errors; /* packet transmit problems */
- int rx_dropped; /* no space in linux buffers */
- int tx_dropped; /* no space available in linux */
- int collisions; /* total number of collisions */
- int multicast; /* multicast packets received */
- /* detailed rx_errors: */
- int rx_length_errors;
- int rx_over_errors; /* receiver overwrote ring buffer in card */
- int rx_crc_errors; /* received packet with crc error */
- int rx_frame_errors; /* received frame alignment error */
- int rx_fifo_errors; /* receiver fifo overrun */
- int rx_missed_errors; /* receiver missed packet */
- /* detailed tx_errors */
- int tx_aborted_errors;
- int tx_carrier_errors;
- int tx_fifo_errors;
- int tx_heartbeat_errors;
- int tx_window_errors;
-};
-
-void print_eth(struct enet_header *eth);
-int eth_hard_header (unsigned char *buff, struct device *dev,
- unsigned short type, unsigned long daddr,
- unsigned long saddr, unsigned len);
-
-int eth_rebuild_header(void *eth, struct device *dev);
-void eth_add_arp (unsigned long addr, struct sk_buff *skb,
- struct device *dev);
-unsigned short eth_type_trans (struct sk_buff *skb, struct device *dev);
-
-#endif
diff --git a/net/tcp/icmp.c b/net/tcp/icmp.c
deleted file mode 100644
index 0b3c515..0000000
--- a/net/tcp/icmp.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/* 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.
-*/
-/* $Id: icmp.c,v 0.8.4.9 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: icmp.c,v $
- * Revision 0.8.4.9 1993/01/23 18:00:11 bir7
- * added volatile keyword to many variables.
- *
- * Revision 0.8.4.8 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.7 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.6 1992/12/12 01:50:49 bir7
- * Fixed bug in call to err routine.
- *
- * Revision 0.8.4.5 1992/12/05 21:35:53 bir7
- * fixed type mismatch.
- *
- * Revision 0.8.4.4 1992/12/03 19:52:20 bir7
- * Fixed minor pugs in icmp_reply.
- *
- * Revision 0.8.4.3 1992/11/18 15:38:03 bir7
- * Fixed some printk's.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-/* 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> /* kfree_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 "icmp.h"
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#undef ICMP_DEBUG
-
-#ifdef ICMP_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-#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;
-
- PRINTK (("icmp_reply (skb_in = %X, type = %d, code = %d, dev=%X)\n",
- skb_in, type, code, dev));
-
- /* get some memory for the reply. */
- len = sizeof (*skb) + 8 /* amount of header to return. */ +
- sizeof (struct icmp_header) +
- 64 /* enough for an ip header. */ +
- dev->hard_header_len;
-
- skb = kmalloc (len, GFP_ATOMIC);
- if (skb == NULL) return;
-
- skb->lock = 0;
- 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, IPPROTO_ICMP, NULL, len );
-
- if (offset < 0)
- {
- skb->sk = NULL;
- kfree_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;
- kfree_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(("ICMP ECHO failed checksum!\n"));
- skb1->sk = NULL;
- kfree_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 );
-
- /* this can change while we are doing it. */
- for (ipprot = (struct ip_protocol *)ip_protos[hash];
- ipprot != NULL; )
- {
- struct ip_protocol *nextip;
- nextip = (struct ip_protocol *)ipprot->next;
- /* pass it off to everyone who wants it. */
- if (iph->protocol == ipprot->protocol && ipprot->err_handler)
- ipprot->err_handler (err, (unsigned char *)(icmph+1),
- iph->daddr, iph->saddr, ipprot);
- ipprot = nextip;
- }
-
- skb1->sk = NULL;
- kfree_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 = kmalloc (sizeof (*rt), GFP_ATOMIC);
- 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;
- kfree_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 = kmalloc( size, GFP_ATOMIC );
- if (skb == NULL)
- {
- skb1->sk = NULL;
- kfree_skb (skb1, FREE_READ);
- return (0);
- }
- skb->sk = NULL;
- skb->lock = 0;
- 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, IPPROTO_ICMP, opt, len );
- if (offset < 0)
- {
- /* Problems building header */
- PRINTK(("Could not build IP Header for ICMP ECHO Response\n"));
- kfree_s (skb->mem_addr, skb->mem_len);
- skb1->sk = NULL;
- kfree_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;
- kfree_skb (skb1, FREE_READ);
- return( 0 );
-
- default:
- PRINTK(("Unsupported ICMP type = x%x\n", icmph->type ));
- skb1->sk = NULL;
- kfree_skb (skb1, FREE_READ);
- return( 0 ); /* just toss the packet */
- }
-
- /* should be unecessary, but just in case. */
- skb1->sk = NULL;
- kfree_skb (skb1, FREE_READ);
- return( 0 ); /* just toss the packet */
-}
-
diff --git a/net/tcp/icmp.h b/net/tcp/icmp.h
deleted file mode 100644
index 3188955..0000000
--- a/net/tcp/icmp.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Internet Control Message Protocol (ICMP) header file */
-
-/* $Id: icmp.h,v 0.8.4.2 1992/11/15 14:55:30 bir7 Exp $ */
-/* $Log: icmp.h,v $
- * Revision 0.8.4.2 1992/11/15 14:55:30 bir7
- * Remove ctrl-h so diff no longer thinks it's a binary file.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#define ICMP_ECHOREPLY 0
-#define ICMP_DEST_UNREACH 3
-#define ICMP_SOURCE_QUENCH 4
-#define ICMP_REDIRECT 5
-#define ICMP_ECHO 8
-#define ICMP_TIME_EXCEEDED 11
-#define ICMP_PARAMETERPROB 12
-#define ICMP_TIMESTAMP 13
-#define ICMP_TIMESTAMPREPLY 14
-#define ICMP_INFO_REQUEST 15
-#define ICMP_INFO_REPLY 16
-
-
-/* used by unreachable. */
-
-#define ICMP_NET_UNREACH 0
-#define ICMP_HOST_UNREACH 1
-#define ICMP_PROT_UNREACH 2
-#define ICMP_PORT_UNREACH 3 /* lots of room for confusion. */
-#define ICMP_FRAG_NNEDED 4
-#define ICMP_SR_FAILED 5
-#define ICMP_NET_UNKNOWN 6
-#define ICMP_HOST_UNKNOWN 7
-#define ICMP_HOST_ISOLATED 8
-#define ICMP_NET_ANO 9
-#define ICMP_HOST_ANO 10
-#define ICMP_NET_UNR_TOS 11
-#define ICMP_HOST_UNR_TOS 12
-
-
-struct icmp_header
-{
- unsigned char type;
- unsigned char code;
- unsigned short checksum;
- union
- {
- struct
- {
- unsigned short id;
- unsigned short sequence;
- } echo;
- unsigned long gateway;
- } un;
-};
-
-struct icmp_err
-{
- int errno;
- unsigned fatal:1;
-};
-
-extern struct icmp_err icmp_err_convert[];
-
-int
-icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- unsigned long daddr, unsigned short len,
- unsigned long saddr, int redo, struct ip_protocol *protocol);
-
-void
-icmp_reply (struct sk_buff *skb_in, int type, int code, struct device *dev);
diff --git a/net/tcp/ip.c b/net/tcp/ip.c
deleted file mode 100644
index e517a6b..0000000
--- a/net/tcp/ip.c
+++ /dev/null
@@ -1,1048 +0,0 @@
-/* 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
-*/
-/* $Id: ip.c,v 0.8.4.10 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: ip.c,v $
- * Revision 0.8.4.10 1993/01/23 18:00:11 bir7
- * added volatile keyword to many variables.
- *
- * Revision 0.8.4.9 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.8 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.7 1992/12/06 23:29:59 bir7
- * Changed retransmit to double rtt.
- *
- * Revision 0.8.4.6 1992/12/05 21:35:53 bir7
- * fixed checking of wrong fragmentation bit.
- *
- * Revision 0.8.4.5 1992/12/03 19:52:20 bir7
- * added paranoid queue checking
- *
- * Revision 0.8.4.4 1992/11/18 15:38:03 bir7
- * Fixed bug in copying packet and checking packet type.
- *
- * Revision 0.8.4.3 1992/11/17 14:19:47 bir7
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#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};
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#undef IP_DEBUG
-
-#ifdef IP_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-static struct rtable *rt_base=NULL; /* used to base all the routing data. */
-
-volatile struct ip_protocol *ip_protos[MAX_IP_PROTOS] = { NULL, };
-int ip_ads = 0;
-
-static char *in_ntoa(unsigned long addr)
-{
- static char buf[100];
-
- sprintf(buf,"%d.%d.%d.%d",
- (addr & 0xff),
- ((addr >> 8) & 0xff),
- ((addr >> 16) & 0xff),
- ((addr >> 24) & 0xff));
- return buf;
-}
-
-#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 = (struct ip_protocol *)prot->next;
- p2 != NULL;
- p2= (struct ip_protocol *)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]=(struct ip_protocol *)ip_protos[hash]->next;
- return (0);
- }
-
- for (p = (struct ip_protocol *)ip_protos[hash];
- p != NULL;
- p = (struct ip_protocol *) 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 (IS_MYADDR);
- 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 (IS_BROADCAST);
- }
- }
- return (IS_MYADDR);
-}
-
-int
-my_ip_addr(unsigned long addr)
-{
- int i;
- int result;
- for (i = 0; i < MAX_IP_ADDRES; i++)
- {
- if (ip_addr[i] == 0) return (0);
- result = ip_addr_match (addr, ip_addr[i]);
- if (result) return result;
- }
- 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)
-{
-#ifdef IP_DEBUG
- printk("RT: %06lx NXT=%06lx DEV=%06lx(%s) NET=%s ",
- (long) rt, (long) rt->next, (long) rt->dev,
- rt->dev->name, in_ntoa(rt->net));
- printk("ROUTER=%s\n", in_ntoa(rt->router));
-#endif
-}
-
-void
-print_ipprot (struct ip_protocol *ipprot)
-{
- PRINTK (("handler = %X, protocol = %d, copy=%d \n",
- ipprot->handler, ipprot->protocol, ipprot->copy));
-}
-
-/* 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))
- {
- PRINTK (("IP: %X via %s (%X)\n", daddr, rt->dev->name, rt->router));
- *raddr = rt->router;
- return (rt->dev);
- }
- }
- return (NULL);
-};
-
-/* Remove all routing table entries for a device. */
-void
-del_devroute (struct device *dev)
-{
- struct rtable *r, *x, *p;
-
- if ((r = rt_base) == NULL) return; /* nothing to remove! */
- PRINTK (("IFACE DOWN: clearing routing table for dev 0x%08lx (%s)\n",
- (long) dev, dev->name));
- p = NULL;
- while(r != NULL)
- {
- PRINTK ((">> R=%06lx N=%06lx P=%06lx DEV=%06lx(%s) A=%s\n",
- (long) r, (long) r->next, (long) p, (long) r->dev,
- r->dev->name, in_ntoa(r->net)));
- if (r->dev == dev)
- {
- PRINTK ((">>> MATCH: removing rt=%08lx\n", (long) r));
- if (p == NULL) rt_base = r->next;
- else p->next = r->next;
- x = r->next;
- kfree_s(r, sizeof(*r));
- r = x;
- }
- else
- {
- p = r;
- r = r->next;
- }
- }
-}
-
-void
-add_route (struct rtable *rt)
-{
- int mask;
- struct rtable *r;
- struct rtable *r1;
-
- 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;
- }
- kfree_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;
-
-
-
-/* verify_area (VERIFY_WRITE, u_ipc, sizeof (ipc));*/
- memcpy_fromfs(&ipc, u_ipc, sizeof (ipc));
- ipc.name[MAX_IP_NAME-1] = 0;
- dev = get_dev (ipc.name);
-
-#if 1 /* making this a 0 will let you remove an ip address from
- the list, which is useful under SLIP. But it may not
- be compatible with older configs. */
- ipc.destroy = 0;
-#endif
-
- if (dev == NULL) return (-EINVAL);
- if (ip_ads >= MAX_IP_ADDRES && !ipc.destroy && ipc.paddr != -1)
- return (-EINVAL);
-
- /* see if we need to add a broadcast address. */
- if (ipc.net != -1)
- {
- PRINTK (("new broadcast for %s: %08X\n", dev->name, ipc.net));
- arp_add_broad (ipc.net, dev);
- rt = kmalloc (sizeof (*rt), GFP_KERNEL);
- 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)
- {
- PRINTK (("new router for %s: %08X\n", dev->name, ipc.router));
- rt = kmalloc (sizeof (*rt),GFP_KERNEL);
- if (rt == NULL) return (-ENOMEM);
- rt->net = 0;
- rt->dev = dev;
- rt->router = ipc.router;
- add_route (rt);
- }
-
- if (dev->loopback)
- {
- PRINTK (("new loopback addr: %08X\n", ipc.paddr));
- rt = kmalloc (sizeof (*rt), GFP_KERNEL);
- if (rt == NULL) return (-ENOMEM);
- rt->net = ipc.paddr;
- rt->dev = dev;
- rt->router = 0;
- add_route (rt);
- }
-
- if (ipc.destroy)
- {
- int i;
- for (i = 0; i <MAX_IP_ADDRES; i++)
- {
- if (ip_addr[i] == ipc.paddr)
- {
- break;
- }
- }
- if (i != MAX_IP_ADDRES)
- {
- PRINTK (("ip.c: Destroying Identity %8X, entry %d\n", ipc.paddr, i));
- i++;
- ip_ads--;
- while (i < MAX_IP_ADDRES)
- {
- ip_addr[i-1] = ip_addr[i];
- i++;
- }
- ip_addr[MAX_IP_ADDRES-1] = 0;
- }
- }
-
- /* FIX per FvK 92/11/15 */
- /* When "downing" an interface, this must be done with paddr = -1L. */
- if (ipc.paddr != -1L && !ipc.destroy)
- {
- if (!my_ip_addr (ipc.paddr))
- {
- PRINTK (("new identity: %08X\n", 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);
- del_devroute(dev); /* clear routing table for dev */
- }
-
- return (0);
-}
-
-/* this routine will check to see if we have lost a gateway. */
-void
-ip_route_check (unsigned long daddr)
-{
-}
-
-#if 0
-/* 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);
-}
-#endif
-
-/* 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;
- kfree_skb(skb, 0);
- return (0);
- }
-
- /* for now we will only deal with packets meant for us. */
- if (!my_ip_addr(iph->daddr))
- {
- PRINTK(("\nIP: *** datagram routing not yet implemented ***\n"));
- PRINTK((" SRC = %s ", in_ntoa(iph->saddr)));
- PRINTK((" DST = %s (ignored)\n", in_ntoa(iph->daddr)));
-/* icmp_reply (skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); */
-
- skb->sk = NULL;
- kfree_skb(skb, 0);
- return (0);
- }
-
- /* deal with fragments. or don't for now.*/
- if ((iph->frag_off & 32) || (net16(iph->frag_off)&0x1fff))
- { /* FIXME: this ^^^ used to be 64, as per bugfix */
- printk("\nIP: *** datagram fragmentation not yet implemented ***\n");
- printk(" SRC = %s ", in_ntoa(iph->saddr));
- printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr));
- icmp_reply (skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
- skb->sk = NULL;
- kfree_skb(skb, 0);
- return(0);
- }
-
- skb->h.raw += iph->ihl*4;
-
- hash = iph->protocol & (MAX_IP_PROTOS -1);
- for (ipprot = (struct ip_protocol *)ip_protos[hash];
- ipprot != NULL;
- ipprot=(struct ip_protocol *)ipprot->next)
- {
- struct sk_buff *skb2;
- if (ipprot->protocol != iph->protocol) continue;
- 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 = kmalloc (skb->mem_len, GFP_ATOMIC);
- if (skb2 == NULL) continue;
- memcpy (skb2, skb, skb->mem_len);
- skb2->mem_addr = skb2;
- skb2->lock = 0;
- skb2->h.raw = (void *)((unsigned long)skb2
- + (unsigned long)skb->h.raw
- - (unsigned long)skb);
- }
- 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;
- kfree_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;
-
- if (dev == NULL)
- {
- printk ("ip.c: ip_queue_xmit dev = NULL\n");
- return;
- }
-
- 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;
-
- /* see if this is the one
- trashing our queue. */
- skb->magic = 1;
-
- 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 != NULL)
- {
- dev->queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- dev->queue_xmit (skb, dev, SOPRI_NORMAL);
- }
- }
- else
- {
- if (free)
- kfree_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=(struct sk_buff *)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=(struct sk_buff *)skb->link3;
- }
- /* double the rtt time every time we retransmit.
- This will cause exponential back off on how
- hard we try to get through again. Once we
- get through, the rtt will settle back down
- reasonably quickly. */
-
- sk->rtt *= 2;
- sk->time_wait.len = sk->rtt;
- 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));
-}
diff --git a/net/tcp/ip.h b/net/tcp/ip.h
deleted file mode 100644
index 744efe5..0000000
--- a/net/tcp/ip.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/* 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
-*/
-/* $Id: ip.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: ip.h,v $
- * Revision 0.8.4.2 1993/01/23 18:00:11 bir7
- * added volatile keyword to many variables.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: ip.h,v $
- * Revision 0.8.4.2 1993/01/23 18:00:11 bir7
- * added volatile keyword to many variables.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-#ifndef _TCP_IP_H
-#define _TCP_IP_H
-
-#include "dev.h"
-#include <linux/sock_ioctl.h>
-/* #include <netinet/protocols.h>*/
-#include <netinet/in.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);
- volatile struct ip_protocol *next;
- unsigned char protocol;
- unsigned char copy:1;
- void *data;
-};
-
-extern struct ip_protocol *ip_protocol_base;
-extern volatile struct ip_protocol *ip_protos[MAX_IP_PROTOS];
-
-#define MAX_IP_ADDRES 5
-extern unsigned long ip_addr[MAX_IP_ADDRES];
-extern int ip_ads;
-#define MY_IP_ADDR ip_addr[0]
-int my_ip_addr(unsigned long);
-
-/*
- * returned by my_ip_addr..
- */
-#define IS_MYADDR 1
-#define IS_BROADCAST 2
-
-#include "eth.h"
-
-void
-print_iph (struct ip_header *);
-
-void
-print_eth (struct enet_header *);
-
-int ip_set_dev (struct ip_config *);
-
-int ip_build_header(struct sk_buff *skb, unsigned long saddr,
- unsigned long daddr, struct device **dev, int type,
- struct options *opt, int len);
-void ip_queue_xmit (volatile struct sock *sk, struct device *dev,
- struct sk_buff *skb, int free);
-void ip_retransmit(volatile struct sock *sk, int all);
-int ip_rcv(struct sk_buff *buff, struct device *dev, struct packet_type *);
-void add_ip_protocol (struct ip_protocol *);
-int delete_ip_protocol (struct ip_protocol *);
-int ip_handoff (volatile struct sock *sk);
-unsigned short ip_compute_csum (unsigned char *buff, int len);
-int ip_addr_match (unsigned long, unsigned long);
-void add_route (struct rtable *rt);
-void ip_route_check (unsigned long daddr);
-
-#endif
-
-
-
-
-
-
diff --git a/net/tcp/loopback.c b/net/tcp/loopback.c
deleted file mode 100644
index 21849c4..0000000
--- a/net/tcp/loopback.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* 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
-*/
-/* $Id: loopback.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: loopback.c,v $
- * Revision 0.8.4.8 1993/01/23 18:00:11 bir7
- * Fixed problems introduced by merge.
- *
- * Revision 0.8.4.7 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.6 1993/01/22 22:58:08 bir7
- * Changed so transmitting takes place in bottom half of interrupt routine.
- *
- * Revision 0.8.4.5 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.4 1992/12/05 21:35:53 bir7
- * changed dev->init to return an int.
- *
- * Revision 0.8.4.3 1992/11/18 15:38:03 bir7
- * Fixed bug in start_xmit.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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 <linux/string.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/io.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"
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#ifdef LOOPBACK_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-static int
-loopback_xmit(struct sk_buff *skb, struct device *dev)
-{
- int done;
- if (!skb || !dev) return 0;
- PRINTK (("loopback_xmit (dev = %X)\n", dev));
- cli();
- if (dev->tbusy != 0)
- {
- sti();
- return (1);
- }
- dev->tbusy = 1;
- sti();
-
- done = dev_rint ((unsigned char *)(skb+1), skb->len, 0, dev);
-
- if (skb->free)
- kfree_skb (skb, FREE_WRITE);
-
- while (done != 1)
- {
- done = dev_rint (NULL, 0, 0, dev);
- }
-
- dev->tbusy = 0;
-
- return (0);
-}
-
-int
-loopback_init(struct device *dev)
-{
- printk ("Loopback device init\n");
- /* initialize the rest of the device structure. */
- dev->mtu = 2000; /* mtu */
- dev->tbusy = 0;
- 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;
- return (0);
-}
diff --git a/net/tcp/pack_type.c b/net/tcp/pack_type.c
deleted file mode 100644
index f5b3d5d..0000000
--- a/net/tcp/pack_type.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* 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
-*/
-/* $Id: pack_type.c,v 0.8.4.3 1992/12/12 19:25:04 bir7 Exp $ */
-/* $Log: pack_type.c,v $
- * Revision 0.8.4.3 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#include <linux/stddef.h>
-#include "dev.h"
-#include "eth.h"
-
-extern int arp_rcv (struct sk_buff *skb, struct device *dev,
- struct packet_type *pt);
-
-static struct packet_type arp_packet_type=
-{
- NET16(ETHERTYPE_ARP),
- 0, /* copy */
- arp_rcv,
- NULL,
- NULL /* next */
-};
-
-extern int ip_rcv (struct sk_buff *skb, struct device *dev,
- struct packet_type *pt);
-
-static struct packet_type ip_packet_type=
-{
- NET16(ETHERTYPE_IP),
- 0, /* copy */
- ip_rcv,
- NULL,
- &arp_packet_type
-};
-
-struct packet_type *ptype_base = &ip_packet_type;
-
-
-
-
-
diff --git a/net/tcp/packet.c b/net/tcp/packet.c
deleted file mode 100644
index b749aab..0000000
--- a/net/tcp/packet.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/* 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
-*/
-/* $Id: packet.c,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: packet.c,v $
- * Revision 0.8.4.7 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.6 1993/01/23 18:00:11 bir7
- * Added volatile keyword
- *
- * Revision 0.8.4.5 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.4 1992/12/12 01:50:49 bir7
- * Fixed bug in call to err routine.
- *
- * Revision 0.8.4.3 1992/11/17 14:19:47 bir7
- * *** empty log message ***
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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;
- kfree_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;
- kfree_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 (VERIFY_WRITE, usin, sizeof (saddr));*/
- memcpy_fromfs (&saddr, usin, sizeof(saddr));
- }
- else
- return (-EINVAL);
-
- skb = sk->prot->wmalloc (sk, len+sizeof (*skb), 0, GFP_KERNEL);
-
- /* this shouldn't happen, but it could. */
- if (skb == NULL)
- {
- PRINTK (("packet_sendto: write buffer full?\n"));
- return (-EAGAIN);
- }
- skb->lock = 0;
- skb->mem_addr = skb;
- skb->mem_len = len + sizeof (*skb);
- 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 (VERIFY_WRITE, 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
- kfree_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);
- kfree_s ((void *)sk->pair, sizeof (struct packet_type));
- sk->pair = NULL;
- release_sock (sk);
-}
-
-static int
-packet_init (volatile struct sock *sk)
-{
- struct packet_type *p;
- p = kmalloc (sizeof (*p), GFP_KERNEL);
- 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 (sk->shutdown & RCV_SHUTDOWN) return (0);
-
- if (addr_len)
- {
- verify_area (VERIFY_WRITE, 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)
- {
- sti();
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- }
- skb = sk->rqueue;
-
- if (!(flags & MSG_PEEK))
- {
- if (skb->next == skb )
- {
- sk->rqueue = NULL;
- }
- else
- {
- sk->rqueue = (struct sk_buff *)sk->rqueue ->next;
- skb->prev->next = skb->next;
- skb->next->prev = skb->prev;
- }
- }
- copied = min (len, skb->len);
- verify_area (VERIFY_WRITE, 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 (VERIFY_WRITE, saddr, sizeof (*saddr));
- memcpy_tofs(saddr, &addr, sizeof (*saddr));
- }
-
- if (!(flags & MSG_PEEK))
- {
- kfree_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,
- NULL,
- 128,
- 0,
- {NULL,},
- "PACKET"
-};
diff --git a/net/tcp/protocols.c b/net/tcp/protocols.c
deleted file mode 100644
index fa61e7b..0000000
--- a/net/tcp/protocols.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/* protocols.c */
-
-/* these headers are overkill, but until I clean up the socket header
- files, this is the best way. */
-
-/* $Id: protocols.c,v 0.8.4.3 1992/11/15 14:55:30 bir7 Exp $ */
-/* $Log: protocols.c,v $
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Remove ctrl-h so diff no longer thinks it's a binary file.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#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,
- IPPROTO_TCP,
- 0, /* copy */
- NULL
-};
-
-static struct ip_protocol udp_protocol =
-{
- udp_rcv,
- udp_err,
- &tcp_protocol,
- IPPROTO_UDP,
- 0, /* copy */
- NULL
-};
-
-static struct ip_protocol icmp_protocol =
-{
- icmp_rcv,
- NULL,
- &udp_protocol,
- IPPROTO_ICMP,
- 0, /* copy */
- NULL
-};
-
-struct ip_protocol *ip_protocol_base = &icmp_protocol;
diff --git a/net/tcp/raw.c b/net/tcp/raw.c
deleted file mode 100644
index a7c40d9..0000000
--- a/net/tcp/raw.c
+++ /dev/null
@@ -1,489 +0,0 @@
-/* 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
-*/
-/* $Id: raw.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: raw.c,v $
- * Revision 0.8.4.12 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.11 1993/01/23 18:00:11 bir7
- * Added volatile keyword
- *
- * Revision 0.8.4.10 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.9 1992/12/12 19:25:04 bir7
- * Cleaned up Log messages.
- *
- * Revision 0.8.4.8 1992/12/12 01:50:49 bir7
- * Fixed bug in call to err routine.
- *
- * Revision 0.8.4.7 1992/12/06 11:31:47 bir7
- * added raw_err.
- *
- * Revision 0.8.4.6 1992/11/18 15:38:03 bir7
- * Works now.
- *
- *
- * Revision 0.8.4.4 1992/11/17 09:27:07 bir7
- * Fixed error in header building.
- *
- * Revision 0.8.4.3 1992/11/16 16:13:40 bir7
- * Added debuggin information.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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 <linux/mm.h>
-#include <linux/kernel.h>
-#include "icmp.h"
-
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#undef RAW_DEBUG
-#ifdef RAW_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-extern struct proto raw_prot;
-
-static unsigned long
-min(unsigned long a, unsigned long b)
-{
- if (a < b) return (a);
- return (b);
-}
-
-/* raw_err gets called by the icmp module. */
-void
-raw_err (int err, unsigned char *header, unsigned long daddr,
- unsigned long saddr, struct ip_protocol *protocol)
-{
- volatile struct sock *sk;
-
- PRINTK (("raw_err (err=%d, header=%X, daddr=%X, saddr=%X, ip_protocl=%X)\n"));
-
- if (protocol == NULL) return;
-
- sk = protocol->data;
-
- if (sk == NULL) return;
-
- /* This is meaningless in raw sockets. */
- 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;
- /* none of them are fatal for raw sockets. */
-/* if (icmp_err_convert[err & 0xff].fatal)
- {
- sk->prot->close(sk, 0);
- } */
-
- return;
-
-}
-
-/* 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;
-
- PRINTK (("raw_rcv (skb=%X, dev=%X, opt=%X, daddr=%X,\n"
- " len=%d, saddr=%X, redo=%d, protocol=%X)\n",
- skb, dev, opt, daddr, len, saddr, redo, protocol));
-
- if (skb == NULL) return (0);
- if (protocol == NULL)
- {
- kfree_skb (skb, FREE_READ);
- return (0);
- }
- sk = protocol->data;
- if (sk == NULL)
- {
- kfree_skb (skb, FREE_READ);
- return (0);
- }
-
- /* now we need to copy this into memory. */
- skb->sk = sk;
- skb->len = len;
- skb->dev = dev;
- skb->saddr = daddr;
- skb->daddr = saddr;
-
- if (!redo )
- {
- /* 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();
- }
-
- /* charge it too the socket. */
- if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
- {
- skb->sk = NULL;
- kfree_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
-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;
-
- PRINTK (("raw_sendto (sk=%X, from=%X, len=%d, noblock=%d, flags=%X,\n"
- " usin=%X, addr_len = %d)\n", sk, from, len, noblock,
- flags, usin, addr_len));
-
- /* 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 (VERIFY_WRITE, 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;
-
- sk->inuse = 1;
- skb = NULL;
- while (skb == NULL)
- {
- skb = sk->prot->wmalloc (sk, len+sizeof (*skb) + sk->prot->max_header,
- 0, GFP_KERNEL);
- /* this shouldn't happen, but it could. */
- /* need to change this to sleep. */
- if (skb == NULL)
- {
- int tmp;
- PRINTK (("raw_sendto: write buffer full?\n"));
- if (noblock) return (-EAGAIN);
- tmp = sk->wmem_alloc;
- release_sock (sk);
- cli();
- if (tmp <= sk->wmem_alloc)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- }
- }
- skb->lock = 0;
- 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)
- {
- PRINTK (("raw_sendto: error building ip header.\n"));
- sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
- release_sock (sk);
- return (tmp);
- }
-
-/* verify_area (VERIFY_WRITE, from, len);*/
- memcpy_fromfs ((unsigned char *)(skb+1)+tmp, from, len);
- skb->len = tmp + len;
- sk->prot->queue_xmit (sk, dev, skb, 1);
- release_sock (sk);
- 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;
- PRINTK (("raw_close: deleting ip_protocol %d\n",
- ((struct ip_protocol *)sk->pair)->protocol));
- if (delete_ip_protocol ((struct ip_protocol *)sk->pair) < 0)
- PRINTK (("raw_close: delete_ip_protocol failed. \n"));
- kfree_s ((void *)sk->pair, sizeof (struct ip_protocol));
- sk->pair = NULL;
- release_sock (sk);
-}
-
-static int
-raw_init (volatile struct sock *sk)
-{
- struct ip_protocol *p;
- p = kmalloc (sizeof (*p), GFP_KERNEL);
- if (p == NULL) return (-ENOMEM);
-
- p->handler = raw_rcv;
- p->protocol = sk->protocol;
- p->data = (void *)sk;
- p->err_handler = raw_err;
- add_ip_protocol (p);
-
- /* we need to remember this somewhere. */
- sk->pair = (volatile struct sock *)p;
-
- PRINTK (("raw init added protocol %d\n", sk->protocol));
-
- 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;
-
- PRINTK (("raw_recvfrom (sk=%X, to=%X, len=%d, noblock=%d, flags=%X,\n"
- " sin=%X, addr_len=%X)\n", sk, to, len, noblock,
- flags, sin, addr_len));
-
- if (len == 0) return (0);
- if (len < 0) return (-EINVAL);
-
- if (sk->shutdown & RCV_SHUTDOWN) return (0);
- if (addr_len)
- {
- verify_area (VERIFY_WRITE, addr_len, sizeof(*addr_len));
- put_fs_long (sizeof (*sin), addr_len);
- }
- sk->inuse = 1;
- while (sk->rqueue == NULL)
- {
- if (noblock)
- {
- release_sock (sk);
- if (copied) return (copied);
- return (-EAGAIN);
- }
- release_sock (sk);
- cli();
- if (sk->rqueue == NULL)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- }
- skb = sk->rqueue;
-
- if (!(flags & MSG_PEEK))
- {
- if (skb->next == skb )
- {
- sk->rqueue = NULL;
- }
- else
- {
- sk->rqueue = (struct sk_buff *)sk->rqueue ->next;
- skb->prev->next = skb->next;
- skb->next->prev = skb->prev;
- }
- }
- copied = min (len, skb->len);
- verify_area (VERIFY_WRITE, 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 (VERIFY_WRITE, sin, sizeof (*sin));
- memcpy_tofs(sin, &addr, sizeof (*sin));
- }
-
- if (!(flags & MSG_PEEK))
- {
- kfree_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,
- NULL,
- 128,
- 0,
- {NULL,},
- "RAW"
-};
diff --git a/net/tcp/sock.c b/net/tcp/sock.c
deleted file mode 100644
index 43ed5aa..0000000
--- a/net/tcp/sock.c
+++ /dev/null
@@ -1,1885 +0,0 @@
-/* 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
-*/
-/* $Id: sock.c,v 0.8.4.16 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: sock.c,v $
- * Revision 0.8.4.16 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.15 1993/01/23 18:00:11 bir7
- * Added volatile keyword
- *
- * Revision 0.8.4.14 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.13 1993/01/22 22:58:08 bir7
- * *** empty log message ***
- *
- * Revision 0.8.4.12 1992/12/12 19:25:04 bir7
- * Made memory leak checking more leanent.
- *
- * Revision 0.8.4.11 1992/12/12 01:50:49 bir7
- * Fixed memory leak in accept.
- *
- * Revision 0.8.4.10 1992/12/08 20:49:15 bir7
- * Added support for -EINPROGRESS
- *
- * Revision 0.8.4.9 1992/12/06 23:29:59 bir7
- * Added mss and support for half completed packets.
- *
- * Revision 0.8.4.8 1992/12/05 21:35:53 bir7
- * changed dev->init to return an int.
- *
- * Revision 0.8.4.7 1992/12/03 19:52:20 bir7
- * added paranoid queue checking
- *
- * Revision 0.8.4.6 1992/11/18 15:38:03 bir7
- * Fixed minor problem in setsockopt.
- *
- * Revision 0.8.4.5 1992/11/17 14:19:47 bir7
- *
- * Revision 0.8.4.4 1992/11/16 16:13:40 bir7
- * Fixed some error returns and undid one of the accept changes.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Added more checking for a packet being on a queue before it's
- * dropped when a socket is closed. Added check to see if it's
- * on the arp_q also.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.5 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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/string.h>
-#include <linux/config.h>
-#include <linux/sock_ioctl.h>
-#include "../kern_sock.h"
-#include "timer.h"
-#include "ip.h"
-#include "tcp.h"
-#include "udp.h"
-#include "sock.h"
-#include "arp.h"
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-#undef ISOCK_DEBUG
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#ifdef ISOCK_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-#ifdef MEM_DEBUG
-#define MPRINTK(x) printk x
-#else
-#define MPRINTK(x) /**/
-#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)
-{
- if (!sk) {
- printk (" print_sk(NULL)\n");
- return;
- }
- 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);
- printk (" urg = %d shutdown=%d\n", sk->urg, sk->shutdown);
-}
-
-void
-print_skb(struct sk_buff *skb)
-{
- if (!skb) {
- printk (" print_skb(NULL)\n");
- return;
- }
- 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
-kfree_skb (struct sk_buff *skb, int rw)
-{
- if (skb == NULL)
- {
- printk ("kfree_skb: skb = NULL\n");
- return;
- }
-
- if (skb->lock)
- {
- skb->free = 1;
- return;
- }
- skb->magic = 0;
- 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
- {
- kfree_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)
- kfree_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->num == 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;
-
- 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)
- {
- start = (i+1+start )%1024;
- PRINTK (("get_new_socknum returning %d, start = %d\n",
- i+base+1,start));
- 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;
- }
- PRINTK (("get_new_socknum returning %d, start = %d\n", best+base+1,start));
- 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));
-
- if (!sk1)
- {
- printk ("sock.c: remove_sock: sk1 == NULL\n");
- return;
- }
-
- if (!sk1->prot)
- {
- printk ("sock.c: remove_sock: sk1->prot == NULL\n");
- return;
- }
-
- /* 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 && sk2->next != sk1)
- sk2 = sk2->next;
-
- if (sk2)
- {
- sk2->next = sk1->next;
- sti();
- return;
- }
- sti();
-
- if (sk1->num != 0)
- PRINTK (("remove_sock: sock not found.\n"));
-}
-
-void
-destroy_sock(volatile struct sock *sk)
-{
-
- struct sk_buff *skb;
- PRINTK (("destroying socket %X\n",sk));
- /* just to be safe. */
- sk->inuse = 1;
-
- /* incase it's sleeping somewhere. */
- if (!sk->dead) wake_up (sk->sleep);
-
- remove_sock (sk);
- /* now we can no longer get new packets. */
-
- delete_timer((struct timer *)&sk->time_wait);
-
- if (sk->send_tmp != NULL) kfree_skb (sk->send_tmp, FREE_WRITE);
-
- /* cleanup up the write buffer. */
- for (skb = sk->wfront; skb != NULL; )
- {
- struct sk_buff *skb2;
- skb2=(struct sk_buff *)skb->next;
- if (skb->magic != TCP_WRITE_QUEUE_MAGIC)
- {
- printk ("sock.c:destroy_sock write queue with bad magic (%X)\n",
- skb->magic);
- break;
- }
- kfree_skb(skb, FREE_WRITE);
- skb=skb2;
- }
-
- sk->wfront = NULL;
- sk->wback = NULL;
-
- if (sk->rqueue != NULL)
- {
- skb = sk->rqueue;
- do {
- struct sk_buff *skb2;
- skb2=(struct sk_buff *)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);
- }
- kfree_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, or
- maybe the arp queue */
- cli();
- /* see if it's in a transmit queue. */
- /* this can be simplified quite a bit. Look
- at tcp.c:tcp_ack to see how. */
-
- if (skb->next != NULL)
- {
- extern volatile struct sk_buff *arp_q;
- int i;
- if (skb->next != skb)
- {
- skb->next->prev = skb->prev;
- skb->prev->next = skb->next;
-
- if (skb == arp_q)
- {
- if (skb->magic != ARP_QUEUE_MAGIC)
- {
- sti();
- printk ("sock.c: destroy_sock skb on arp queue with"
- "bas magic (%X)\n", skb->magic);
- cli();
- arp_q = NULL;
- continue;
- }
- arp_q = skb->next;
- }
- else
- {
- for (i = 0; i < DEV_NUMBUFFS; i++)
- {
- if (skb->dev && skb->dev->buffs[i] == skb)
- {
- if (skb->magic != DEV_QUEUE_MAGIC)
- {
- sti();
- printk ("sock.c: destroy sock skb on dev queue"
- "with bad magic (%X)\n", skb->magic);
- cli();
- break;
- }
- skb->dev->buffs[i]= skb->next;
- break;
- }
- }
- }
- }
- else
- {
- if (skb == arp_q)
- {
- if (skb->magic != ARP_QUEUE_MAGIC)
- {
- sti();
- printk ("sock.c: destroy_sock skb on arp queue with"
- "bas magic (%X)\n", skb->magic);
- cli();
- }
- arp_q = NULL;
- }
- else
- {
- for (i = 0; i < DEV_NUMBUFFS; i++)
- {
- if (skb->dev && skb->dev->buffs[i] == skb)
- {
- if (skb->magic != DEV_QUEUE_MAGIC)
- {
- sti();
- printk ("sock.c: destroy sock skb on dev queue"
- "with bad magic (%X)\n", skb->magic);
- cli();
- break;
- }
- skb->dev->buffs[i]= NULL;
- break;
- }
- }
- }
- }
- }
- skb->dev = NULL;
- sti();
- skb2=(struct sk_buff *)skb->link3;
- kfree_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 = (struct sk_buff *)sk->back_log;
- do {
- struct sk_buff *skb2;
- skb2=(struct sk_buff *)skb->next;
- kfree_skb(skb, FREE_READ);
- skb=skb2;
- } while (skb != sk->back_log);
- sti();
- }
-
- sk->back_log = NULL;
-
- /* Now if it has a half accepted/ closed socket. */
- if (sk->pair)
- {
- sk->pair->dead = 1;
- sk->pair->prot->close (sk->pair, 0);
- sk->pair = 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)
- {
- kfree_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));
- 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);
- }
- PRINTK (("leaving destroy_sock\n"));
-}
-
-
-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:
-
- /* this is a little restrictive, but it's the only way to make
- sure that you can't send a sigurg to another process. */
- if (!suser() && current->pgrp != -arg && current->pid != arg)
- return (-EPERM);
-
- sk->proc = arg;
- return (0);
-
-
-
- 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);
- }
- if (optval == NULL) return (-EINVAL);
-
-/* verify_area (VERIFY_WRITE, 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 = 0;
- 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 (VERIFY_WRITE, optlen, sizeof (int));
- put_fs_long (sizeof(int),(unsigned long *) optlen);
-
- verify_area(VERIFY_WRITE, 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);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- /* we might as well re use these. */
- sk->max_ack_backlog = backlog;
- sk->ack_backlog = 0;
- sk->state = TCP_LISTEN;
- return (0);
-}
-
-/* Hardware should be inited here. */
-static int ip_proto_init(void)
-{
- int i;
- struct device *dev, *dev2;
- 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 = (struct ip_protocol *) p->next;
- add_ip_protocol (p);
- p = tmp;
- }
-
- /* add the devices */
- /* if the call to dev->init fails, the dev is removed
- from the chain disconnecting the device until the
- next reboot. */
-
- dev2 = NULL;
- for (dev = dev_base; dev != NULL; dev=dev->next)
- {
- if (dev->init && dev->init(dev))
- {
- if (dev2 == NULL)
- dev_base = dev->next;
- else
- dev2->next = dev->next;
- }
- else
- {
- dev2 = dev;
- }
- }
- bh_base[INET_BH].routine = inet_bh;
- 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 = kmalloc (sizeof (*sk), GFP_KERNEL);
- if (sk == NULL)
- return (-ENOMEM);
- sk->num = 0;
-
-
- switch (sock->type)
- {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- if (protocol && protocol != IPPROTO_TCP)
- {
- kfree_s ((void *)sk, sizeof (*sk));
- return (-EPROTONOSUPPORT);
- }
- sk->no_check = TCP_NO_CHECK;
- prot = &tcp_prot;
- break;
-
- case SOCK_DGRAM:
- if (protocol && protocol != IPPROTO_UDP)
- {
- kfree_s ((void *)sk, sizeof (*sk));
- return (-EPROTONOSUPPORT);
- }
- sk->no_check = UDP_NO_CHECK;
- prot=&udp_prot;
- break;
-
- case SOCK_RAW:
- if (!suser())
- {
- kfree_s ((void *)sk, sizeof (*sk));
- return (-EPERM);
- }
-
- if (!protocol)
- {
- kfree_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())
- {
- kfree_s ((void *)sk, sizeof (*sk));
- return (-EPERM);
- }
-
- if (!protocol)
- {
- kfree_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:
- kfree_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;
- sk->send_tmp = NULL;
- sk->mss = 0; /* we will try not to send any packets smaller
- than this. */
-
- /* 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 = 0;
- sk->inuse = 0;
- sk->delay_acks = 0;
- 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)
- {
- /* it assumes that any protocol which allows
- the user to assign a number at socket
- creation time automatically
- shares. */
- 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);
- PRINTK (("ip_proto_release (sock = %X, peer = %X)\n", sock, peer));
- 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
- {
- PRINTK (("sk->linger set.\n"));
- 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;
- PRINTK (("ip_proto_release returning\n"));
- return (0);
-}
-
-
-/* this needs to be changed to dissallow
- the rebinding of sockets. What error
- should it return? */
-
-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);
- if (sk->num != 0) return (-EINVAL);
-
-/* verify_area (VERIFY_WRITE, uaddr, addr_len);*/
- memcpy_fromfs (&addr, uaddr, min (sizeof (addr), addr_len));
-
-#if 0
- if (addr.sin_family && addr.sin_family != AF_INET)
- {
- /* this is really a bug in BSD which we need to emulate because
- ftp expects it. */
- return (-EINVAL);
- }
-#endif
-
- snum = net16(addr.sin_port);
- PRINTK (("bind sk =%X to port = %d\n", sk, snum));
- 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 (-EACCES);
-
- 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)]));
-
- /* 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->state == TCP_ESTABLISHED)
- {
- sock->state = SS_CONNECTED;
- return (-EISCONN);
- }
-
- if (sock->state == SS_CONNECTING)
- {
- if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- return (-EINPROGRESS);
-
- if (sk->err) return (-sk->err);
-
- sock->state = SS_CONNECTED;
- return (-EISCONN);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- if (sk->prot->connect == NULL)
- return (-EOPNOTSUPP);
-
- err = sk->prot->connect (sk, (struct sockaddr_in *)uaddr, addr_len);
- if (err < 0) return (err);
-
- sock->state = SS_CONNECTING;
- if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
- return (-EINPROGRESS);
-
- cli(); /* avoid the race condition */
-
- while (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- return (-ERESTARTSYS);
- }
- }
- sti();
- sock->state = SS_CONNECTED;
-
- if (sk->state != TCP_ESTABLISHED && sk->err)
- {
- sock->state = SS_UNCONNECTED;
- 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);
- }
-
- /* we've been passed an extra socket. We need to free it up because
- the tcp module creates it's own when it accepts one. */
-
- if (newsock->data)
- kfree_s (newsock->data, sizeof (struct sock));
-
- 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)
- {
- if (sk1->err <= 0)
- printk ("Warning sock.c:sk1->err <= 0. Returning non-error.\n");
- 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 > 0)
- {
- 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;
- len = get_fs_long(uaddr_len);
- /* check this error. */
- if (len < sizeof (sin)) return (-EINVAL);
- sin.sin_family=AF_INET;
- sk = sock->data;
- if (sk == NULL)
- {
- printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
- return (0);
- }
- if (peer)
- {
- if (!tcp_connected(sk->state))
- return (-ENOTCONN);
- sin.sin_port = sk->dummy_th.dest;
- sin.sin_addr.s_addr = sk->daddr;
- }
- else
- {
- sin.sin_port = sk->dummy_th.source;
- if (sk->saddr == 0)
- sin.sin_addr.s_addr = MY_IP_ADDR;
- else
- sin.sin_addr.s_addr = sk->saddr;
- }
- len = sizeof (sin);
- verify_area (VERIFY_WRITE, uaddr, len);
- memcpy_tofs(uaddr, &sin, sizeof (sin));
- verify_area(VERIFY_WRITE, uaddr_len, sizeof (len));
- 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);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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)
- {
- send_sig (SIGPIPE, current, 1);
- return (-EPIPE);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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)
- {
- send_sig (SIGPIPE, current, 1);
- return (-EPIPE);
- }
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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)
- {
- send_sig (SIGPIPE, current, 1);
- return (-EPIPE);
- }
-
- if (sk->prot->sendto == NULL) return (-EOPNOTSUPP);
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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->prot->recvfrom == NULL) return (-EOPNOTSUPP);
-
- /* we may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum (sk->prot, 0);
- if (sk->num == 0) return (-EAGAIN);
- put_sock (sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
- }
-
- 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 (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
- sock->state = SS_CONNECTED;
-
- if (!tcp_connected(sk->state)) return (-ENOTCONN);
- sk->shutdown |= how;
- if (sk->prot->shutdown)
- sk->prot->shutdown (sk, 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));
-
- case SIOCSARP:
- if (!suser())
- return (-EPERM);
- return (arp_ioctl_set((struct arpreq *)arg));
-
- case SIOCGARP:
- return (arp_ioctl_get((struct arpreq *)arg));
-
- case SIOCDARP:
- if (!suser())
- return (-EPERM);
- return (arp_ioctl_del((struct arpreq *)arg));
-
- case FIOSETOWN:
- case SIOCSPGRP:
- {
- long user;
- user = get_fs_long ((void *) arg);
- sk->proc = user;
- return (0);
- }
-
- case FIOGETOWN:
- case SIOCGPGRP:
- {
- verify_area (VERIFY_WRITE, (void *)arg, sizeof (long));
- put_fs_long (sk->proc, (void *)arg);
- return (0);
- }
-
- default:
- if (!sk->prot->ioctl)
- return (-EINVAL);
- return (sk->prot->ioctl (sk, cmd, arg));
- }
-}
-
-void *
-sock_wmalloc(volatile struct sock *sk, unsigned long size, int force,
- int priority)
-{
- if (sk)
- {
- if (sk->wmem_alloc + size < SK_WMEM_MAX || force)
- {
- cli();
- sk->wmem_alloc+= size;
- sti();
- return (kmalloc (size, priority));
- }
- MPRINTK (("sock_wmalloc(%X,%d,%d,%d) returning NULL\n",
- sk, size, force, priority));
- return (NULL);
- }
- return (kmalloc(size, priority));
-}
-
-void *
-sock_rmalloc(volatile struct sock *sk, unsigned long size, int force,
- int priority)
-{
- if (sk )
- {
- if (sk->rmem_alloc + size < SK_RMEM_MAX || force)
- {
- cli();
- sk->rmem_alloc+= size;
- sti();
- return (kmalloc (size, priority));
- }
- MPRINTK (("sock_rmalloc(%X,%d,%d,%d) returning NULL\n",
- sk,size,force, priority));
- return (NULL);
- }
- return (kmalloc (size, priority));
-}
-
-
-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));
- kfree_s (mem, size);
- if (sk)
- {
- sk->wmem_alloc -= size;
- /* in case it might be waiting for more memory. */
- if (!sk->dead) 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);
- kfree_s ((void *)sk, sizeof (*sk));
- }
- return;
- }
-}
-
-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));
- kfree_s (mem, size);
- if (sk)
- {
- sk->rmem_alloc -= size;
- if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0)
- {
- delete_timer ((struct timer *)&sk->time_wait);
- kfree_s ((void *)sk, sizeof (*sk));
- }
- }
-}
-
-
-/* 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)
- {
- printk ("sock.c: release_sock sk == NULL\n");
- return;
- }
-
- if (!sk->prot)
- {
- printk ("sock.c: release_sock sk->prot == NULL\n");
- return;
- }
-
- 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 = (struct sk_buff *)sk->back_log;
- PRINTK (("release_sock: skb = %X:\n",skb));
- if (skb->next != skb)
- {
- sk->back_log = skb->next;
- skb->prev->next = skb->next;
- skb->next->prev = skb->prev;
- }
- else
- {
- sk->back_log = NULL;
- }
- sti();
- PRINTK (("sk->back_log = %X\n",sk->back_log));
- if (sk->prot->rcv)
- sk->prot->rcv(skb, skb->dev, sk->opt,
- skb->saddr, skb->len, skb->daddr, 1,
- /* only used for/by raw sockets. */
- (struct ip_protocol *)sk->pair);
- cli();
- }
- sk->blog = 0;
- sk->inuse = 0;
- sti();
- if (sk->dead && sk->state == TCP_CLOSE)
- {
- /* should be about 2 rtt's */
- sk->time_wait.len = min (sk->rtt * 2, TCP_DONE_TIME);
- sk->timeout = TIME_DONE;
- reset_timer ((struct timer *)&sk->time_wait);
- }
-}
diff --git a/net/tcp/sock.h b/net/tcp/sock.h
deleted file mode 100644
index b077cef..0000000
--- a/net/tcp/sock.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/* 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
-*/
-/* $Id: sock.h,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: sock.h,v $
- * Revision 0.8.4.7 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.6 1993/01/23 18:00:11 bir7
- * Added volatile keyword
- *
- * Revision 0.8.4.5 1992/12/12 01:50:49 bir7
- * Fixed support for half duplex connections.
- *
- * Revision 0.8.4.4 1992/12/06 23:29:59 bir7
- * Added mss and support for half completed packets.
- *
- * Revision 0.8.4.3 1992/12/03 19:54:12 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.4 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-#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;
- volatile unsigned long wmem_alloc;
- volatile 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;
- /* not all are volatile, but some are, so we might as
- well say they all are. */
- volatile 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;
- volatile struct sk_buff * volatile back_log;
- struct sk_buff *send_tmp;
- 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;
- volatile unsigned short cong_window;
- volatile unsigned short packets_out;
- volatile unsigned short urg;
- volatile unsigned short shutdown;
- unsigned short mss;
- volatile short rtt;
- volatile short err;
- unsigned char protocol;
- volatile unsigned char state;
- volatile unsigned char ack_backlog;
- unsigned char max_ack_backlog;
- unsigned char priority;
- struct tcp_header dummy_th; /* I may be able to get rid of this. */
- volatile struct timer time_wait;
-};
-
-struct proto
-{
- void *(*wmalloc)(volatile struct sock *sk, unsigned long size, int force,
- int priority);
- void *(*rmalloc)(volatile struct sock *sk, unsigned long size, int force,
- int priority);
- 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);
- void (*shutdown) (volatile struct sock *sk, int how);
- unsigned short max_header;
- unsigned long retransmits;
- volatile struct sock *sock_array[SOCK_ARRAY_SIZE];
- char name[80];
-};
-
-#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
-{
- volatile struct sk_buff *next;
- volatile struct sk_buff *prev;
- volatile struct sk_buff *link3;
- volatile struct sock *sk;
- volatile 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;
- int magic;
- volatile 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,
- int priority);
-void *sock_rmalloc(volatile struct sock *sk, unsigned long size, int force,
- int priority);
-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 kfree_skb (struct sk_buff *skb, int rw);
-void lock_skb (struct sk_buff *skb);
-void unlock_skb (struct sk_buff *skb, int rw);
-
-void dummy_routine(void *, ... );
-
-#endif
diff --git a/net/tcp/tcp.c b/net/tcp/tcp.c
deleted file mode 100644
index 3765bba..0000000
--- a/net/tcp/tcp.c
+++ /dev/null
@@ -1,3392 +0,0 @@
- /* 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
-
- Modifications for half-duplex connections base on those by,
- Tim MacKenzie (tym@dibbler.cs.moansh.edu.au)
-
- */
-/* $Id: tcp.c,v 0.8.4.16 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: tcp.c,v $
- * Revision 0.8.4.16 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.15 1993/01/23 18:00:11 bir7
- * added volatile keyword and fixed infinite loop in tcp_write.
- *
- * Revision 0.8.4.14 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.13 1993/01/22 22:58:08 bir7
- * Check in for merge with previous .99 pl 4.
- *
- * Revision 0.8.4.12 1992/12/12 19:25:04 bir7
- * Fixed anti-memory Leak in shutdown.
- *
- * Revision 0.8.4.11 1992/12/12 01:50:49 bir7
- * Fixed several bugs including half-duplex connections.
- *
- * Revision 0.8.4.10 1992/12/08 20:49:15 bir7
- * Fixed minor bugs and checked out MSS.
- *
- * Revision 0.8.4.9 1992/12/06 23:29:59 bir7
- * Added support for mss and half completed packets. Also added
- * support for shrinking windows.
- *
- * Revision 0.8.4.8 1992/12/05 21:35:53 bir7
- *
- * Revision 0.8.4.7 1992/12/03 19:52:20 bir7
- * fixed = <-> == bug.
- *
- * Revision 0.8.4.6 1992/11/18 15:38:03 bir7
- * fixed minor problem in waiting for memory.
- *
- * Revision 0.8.4.4 1992/11/16 16:13:40 bir7
- * Fixed some error returns and undid one of the accept changes.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Fixed problem with accept. It was charging the memory for the initial
- * sock buff to one socket, but freeing it from another.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.3 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/string.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 "arp.h"
-#include <linux/errno.h>
-#include <linux/timer.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <linux/mm.h>
-/* #include <signal.h>*/
-#include <linux/termios.h> /* for ioctl's */
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#undef TCP_DEBUG
-
-#ifdef TCP_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-#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 = (struct sk_buff *)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->shutdown = SHUTDOWN_MASK;
- if (!sk->dead) wake_up (sk->sleep);
- 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;
-
- PRINTK (("tcp_err(err=%d, header=%X, daddr=%X saddr=%X, protocol=%X)\n",
- err, header, daddr, saddr, protocol));
-
- th = (struct tcp_header *)header;
- sk = get_sock (&tcp_prot, net16(th->dest), saddr, th->source, daddr);
- print_th (th);
-
- 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;
- }
-
- PRINTK (("tcp.c: icmp_err got error\n"));
- sk->err = icmp_err_convert[err & 0xff].errno;
- /* if we've already connected we will keep trying
- until we time out, or the user gives up. */
- if (icmp_err_convert[err & 0xff].fatal)
- {
- if (sk->state == TCP_SYN_SENT)
- {
- sk->state = TCP_CLOSE;
- sk->prot->close(sk, 0);
- }
- }
-
- return;
-
-}
-
-static int
-tcp_readable (volatile struct sock *sk)
-{
- unsigned long counted;
- unsigned long amount;
- struct sk_buff *skb;
- int count=0;
- int sum;
-
- PRINTK (("tcp_readable (sk=%X)\n", sk));
-
- if (sk == NULL || sk->rqueue == NULL) return (0);
-
- counted = sk->copied_seq+1;
- amount = 0;
- skb = (struct sk_buff *)sk->rqueue->next;
-
- /* go until a push or until we are out of data. */
- do {
- count ++;
- if (count > 20)
- {
- PRINTK (("tcp_readable, more than 20 packets without a psh\n"));
- PRINTK (("possible read_queue corruption.\n"));
- return (amount);
- }
- if (before (counted, skb->h.th->seq)) break;
- sum = skb->len - ( counted - skb->h.th->seq);
- if (skb->h.th->syn) sum ++;
- if (skb->h.th->urg)
- {
- sum -= net16(skb->h.th->urg_ptr);
- }
- if (sum >= 0)
- {
- amount += sum;
- if (skb->h.th->syn) amount --;
- counted += sum;
- }
- if (amount && skb->h.th->psh) break;
- skb = (struct sk_buff *)skb->next;
- } while (skb != sk->rqueue->next);
- PRINTK (("tcp readable returning %d bytes\n", amount));
- return (amount);
-}
-
-
-static int
-tcp_select (volatile struct sock *sk, int sel_type, select_table *wait)
-{
- sk->inuse = 1;
- PRINTK (("tcp_select (sk=%X, sel_type = %d, wait = %X)\n",
- sk, sel_type, wait));
- switch (sel_type)
- {
- case SEL_IN:
- select_wait (sk->sleep, wait);
- if (sk->rqueue != NULL)
- {
- if (sk->state == TCP_LISTEN || tcp_readable(sk))
- {
- release_sock (sk);
- return (1);
- }
- }
-
- if (sk->shutdown & RCV_SHUTDOWN)
- {
- release_sock (sk);
- return (1);
- }
- else
- {
- release_sock (sk);
- return (0);
- }
-
- case SEL_OUT:
- select_wait (sk->sleep, wait);
-
- if (sk->shutdown & SEND_SHUTDOWN)
- {
- PRINTK (("write select on shutdown socket.\n"));
- /* should this return an error? */
- release_sock (sk);
- return (0);
- }
-
- /* hack so it will probably be able to write something
- if it says it's ok to write. */
- if (sk->prot->wspace(sk) >= sk->mtu)
- {
- release_sock (sk);
- /* This should cause connect to work ok. */
- if (sk->state == TCP_SYN_RECV || sk->state == TCP_SYN_SENT)
- return (0);
- return (1);
- }
-
- PRINTK (("tcp_select: sleeping on write sk->wmem_alloc = %d, "
- "sk->packets_out = %d\n"
- "sk->wback = %X, sk->wfront = %X\n"
- "sk->send_seq = %u, sk->window_seq=%u\n",
- sk->wmem_alloc, sk->packets_out,
- sk->wback, sk->wfront,
- sk->send_seq, sk->window_seq));
-
- release_sock (sk);
- return (0);
-
-
- case SEL_EX:
- select_wait(sk->sleep,wait);
- if (sk->err)
- {
- release_sock (sk);
- return (1);
- }
- release_sock (sk);
- return (0);
- }
-
- release_sock (sk);
- return (0);
-}
-
-static int
-tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
-{
- PRINTK (("tcp_ioctl (sk=%X, cmd = %d, arg=%X)\n", sk, cmd, arg));
- switch (cmd)
- {
- default:
- return (-EINVAL);
-
- case TIOCINQ:
-/* case FIONREAD:*/
- {
- unsigned long amount;
-
- if (sk->state == TCP_LISTEN)
- return (-EINVAL);
-
- amount = 0;
- sk->inuse = 1;
- if (sk->rqueue != NULL)
- {
- amount = tcp_readable(sk);
- }
- release_sock (sk);
- PRINTK (("returning %d\n", amount));
- verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- put_fs_long (amount, (unsigned long *)arg);
- return (0);
- }
-
- case SIOCATMARK:
- {
- struct sk_buff *skb;
- int answ=0;
- /* try to figure out if we need to read some urgent data. */
- sk->inuse = 1;
- if (sk->rqueue != NULL)
- {
- skb = (struct sk_buff *)sk->rqueue->next;
- if (sk->copied_seq+1 == skb->h.th->seq && skb->h.th->urg)
- answ = 1;
- }
- release_sock (sk);
- verify_area (VERIFY_WRITE, (void *) arg, sizeof (unsigned long));
- put_fs_long (answ, (void *) arg);
- return (0);
- }
-
- case TIOCOUTQ:
- {
- unsigned long amount;
- if (sk->state == TCP_LISTEN)
- return (-EINVAL);
- amount = sk->prot->wspace(sk)/2;
- verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- put_fs_long (amount, (unsigned long *)arg);
- return (0);
- }
-
- }
-}
-
-
-/* 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) + IPPROTO_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;
-}
-
-static void
-tcp_send_partial(volatile struct sock *sk)
-{
- struct sk_buff *skb;
-
- if (sk == NULL || sk->send_tmp == NULL) return;
-
- skb = sk->send_tmp;
- /* we need to complete and send the packet. */
- tcp_send_check (skb->h.th, sk->saddr, sk->daddr,
- skb->len-(unsigned long)skb->h.th +
- (unsigned long)(skb+1), sk);
-
- 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;
- skb->magic = TCP_WRITE_QUEUE_MAGIC;
- if (sk->wback == NULL)
- {
- sk->wfront=skb;
- }
- else
- {
- sk->wback->next = skb;
- }
- sk->wback = skb;
- }
- else
- {
- sk->prot->queue_xmit (sk, skb->dev, skb,0);
- }
- sk->send_tmp = NULL;
-}
-
-
-
-/* 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, GFP_ATOMIC);
- if (buff == NULL)
- {
- /* force it to send an ack. */
- sk->ack_backlog++;
- if (sk->timeout != TIME_WRITE && tcp_connected (sk->state))
- {
- 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->lock = 0;
- 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,
- IPPROTO_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 (("tcp_write (sk=%X, from=%X, len=%d, nonblock=%d, flags=%X)\n",
- sk, from, len, nonblock, flags));
-
- prot = sk->prot;
- while (len > 0)
- {
-
- if (sk->err)
- {
- if (copied) return (copied);
- tmp = -sk->err;
- sk->err = 0;
- return (tmp);
- }
-
- /* first thing we do is make sure that we are established. */
-
- sk->inuse = 1; /* no one else will use this socket. */
- if (sk->shutdown & SEND_SHUTDOWN)
- {
- release_sock (sk);
- sk->err = EPIPE;
- if (copied) return (copied);
- sk->err = 0;
- return (-EPIPE);
- }
-
- while (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)
- {
-
- if (sk->err)
- {
- if (copied) return (copied);
- tmp = -sk->err;
- sk->err = 0;
- return (tmp);
- }
-
- if (sk->state != TCP_SYN_SENT &&
- sk->state != TCP_SYN_RECV)
- {
- release_sock (sk);
- PRINTK (("tcp_write: return 1\n"));
- if (copied) return (copied);
-
- if (sk->err)
- {
- tmp = -sk->err;
- sk->err = 0;
- return (tmp);
- }
-
- if (sk->keepopen)
- {
- send_sig (SIGPIPE, current, 0);
- }
- return (-EPIPE);
- }
-
- if (nonblock || copied)
- {
- release_sock (sk);
- PRINTK (("tcp_write: return 2\n"));
- if (copied) return (copied);
- 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 && sk->state != TCP_CLOSE_WAIT &&
- sk->err == 0)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- PRINTK (("tcp_write: return 3\n"));
- if (copied) return (copied);
- return (-ERESTARTSYS);
- }
- }
- sti();
- sk->inuse = 1;
- }
-
- /* Now we need to check if we have a half built packet. */
- if (sk->send_tmp != NULL)
- {
- /* if sk->mss has been changed this could cause problems. */
- /* add more stuff to the end of skb->len*/
- skb = sk->send_tmp;
- if (!(flags & MSG_OOB))
- {
- copy = min (sk->mss - skb->len + 128 + prot->max_header, len);
-
- /* this is really a bug. */
- if (copy <= 0)
- copy = 0;
-
- memcpy_fromfs ((unsigned char *)(skb+1) + skb->len, from, copy);
- skb->len += copy;
- from += copy;
- copied += copy;
- len -= copy;
- sk->send_seq += copy;
- }
-
- if (skb->len - (unsigned long)skb->h.th +
- (unsigned long)(skb+1) >= sk->mss
- || (flags & MSG_OOB))
- {
- tcp_send_partial (sk);
- }
- continue;
-
- }
-
- /* 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));
-
- /* redundent check here. */
- if (copy < 200 || copy > sk->mtu) copy = sk->mtu;
- copy = min (copy, len);
-
- /* we should really check the window here also. */
- if (sk->packets_out && copy < sk->mss && !(flags & MSG_OOB))
- {
- /* we will release the socket incase we sleep here. */
- release_sock (sk);
- skb=prot->wmalloc (sk,
- sk->mss + 128 + prot->max_header + sizeof (*skb),
- 0, GFP_KERNEL);
- sk->inuse = 1;
- sk->send_tmp = skb;
- if (skb != NULL)
- skb->mem_len = sk->mss + 128 + prot->max_header+sizeof (*skb);
- }
- else
- {
- /* we will release the socket incase we sleep here. */
- release_sock (sk);
- skb=prot->wmalloc (sk, copy + prot->max_header+sizeof (*skb),0,
- GFP_KERNEL);
- sk->inuse = 1;
- if (skb != NULL)
- skb->mem_len = copy+prot->max_header+sizeof (*skb);
- }
-
- /* if we didn't get any memory, we need to sleep. */
- if (skb == NULL)
- {
- if (nonblock || copied)
- {
- release_sock (sk);
- PRINTK (("tcp_write: return 4\n"));
- if (copied) return (copied);
- return (-EAGAIN);
- }
-
- /* 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
- && (sk->state == TCP_ESTABLISHED || sk->state == TCP_CLOSE_WAIT )
- && sk->err == 0)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- PRINTK (("tcp_write: return 5\n"));
- if (copied) return (copied);
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- continue;
- }
-
- skb->mem_addr = skb;
- skb->len = 0;
- skb->sk = sk;
- skb->lock = 0;
- skb->free = 0;
-
- 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,
- IPPROTO_TCP, sk->opt, skb->mem_len);
- if (tmp < 0 )
- {
- prot->wfree (sk, skb->mem_addr, skb->mem_len);
- release_sock (sk);
- PRINTK (("tcp_write: return 6\n"));
- if (copied) return (copied);
- return (tmp);
- }
- skb->len += tmp;
- skb->dev = dev;
- buff+=tmp;
- skb->h.th =(struct tcp_header *) buff;
- 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);
- PRINTK (("tcp_write: return 7\n"));
- if (copied) return (copied);
- return (tmp);
- }
-
- if (flags & MSG_OOB)
- {
- ((struct tcp_header *)buff)->urg = 1;
- ((struct tcp_header *)buff)->urg_ptr = net16(copy);
- }
- skb->len += tmp;
- memcpy_fromfs (buff+tmp, from, copy);
-
- from += copy;
- copied += copy;
- len -= copy;
- skb->len += copy;
- skb->free = 0;
- sk->send_seq += copy;
-
- if (sk->send_tmp != NULL)
- {
- continue;
- }
-
- tcp_send_check ((struct tcp_header *)buff, sk->saddr, sk->daddr,
- copy +sizeof (struct tcp_header), sk);
-
-
- 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;
- skb->magic = TCP_WRITE_QUEUE_MAGIC;
- 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);
- PRINTK (("tcp_write: return 8\n"));
- return (copied);
-}
-
-static int
-tcp_sendto (volatile struct sock *sk, unsigned char *from,
- int len, int nonblock, unsigned flags,
- struct sockaddr_in *addr, int addr_len)
-{
- struct sockaddr_in sin;
- if (addr_len < sizeof (sin))
- return (-EINVAL);
- memcpy_fromfs (&sin, addr, sizeof (sin));
- if (sin.sin_family && sin.sin_family != AF_INET)
- return (-EINVAL);
- if (sin.sin_port != sk->dummy_th.dest)
- return (-EINVAL);
- if (sin.sin_addr.s_addr != sk->daddr)
- return (-EINVAL);
- return (tcp_write (sk, from, len, nonblock, flags));
-}
-
-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, GFP_ATOMIC);
- 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->lock = 0;
- 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,
- IPPROTO_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=(struct sk_buff *)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;
- kfree_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)));
-
- /* this area has caused the most trouble. The current strategy
- is to simply do nothing if the other end has room to send atleast
- 3 full packets, because the ack from those will automatically
- update the window. If the other end doesn't think we have much
- space left, but we have room for atleast 1 more complete packet
- than it thinks we do, we will send an ack immediatedly. Otherwise
- we will wait up to .5 seconds in case the user reads some more. */
-
- sk->ack_backlog ++;
- if ((sk->prot->rspace(sk) >
- (sk->window - sk->bytes_rcv + sk->mtu)))
- {
- /* send an ack right now. */
- tcp_read_wakeup (sk);
- }
- else
- {
- /* force it to send an ack soon. */
- if ( before (jiffies + TCP_ACK_TIME, sk->time_wait.when))
- {
- 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, int nonblock,
- 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));
-
- while (len > 0)
- {
- sk->inuse = 1;
- while (sk->urg==0 || sk->rqueue == NULL)
- {
- if (sk->err)
- {
- int tmp;
- release_sock (sk);
- if (copied) return (copied);
- tmp = -sk->err;
- sk->err = 0;
- return (tmp);
- }
-
- if (sk->state == TCP_CLOSE || sk->done)
- {
- release_sock (sk);
- if (copied) return (copied);
- if (!sk->done)
- {
- sk->done = 1;
- return (0);
- }
- return (-ENOTCONN);
- }
-
- if (sk->shutdown & RCV_SHUTDOWN)
- {
- release_sock(sk);
- if (copied == 0)
- sk->done = 1;
- return (copied);
- }
-
- if (nonblock || copied)
- {
- release_sock (sk);
- if (copied) return (copied);
- return (-EAGAIN);
- }
-
- /* now at this point, we may have gotten some data. */
- release_sock (sk);
- cli();
- if ((sk->urg == 0 || sk->rqueue == NULL) && sk->err == 0
- && !(sk->shutdown & RCV_SHUTDOWN) )
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- if (copied) return (copied);
- return (-ERESTARTSYS);
- }
- }
- sti();
- sk->inuse = 1;
- }
-
- skb = (struct sk_buff *)sk->rqueue->next;
- do {
- int amt;
- if (skb->h.th->urg && !skb->urg_used)
- {
- if (skb->h.th->urg_ptr == 0)
- {
- skb->h.th->urg_ptr = net16(skb->len);
- }
- amt = min(net16(skb->h.th->urg_ptr),len);
- verify_area (VERIFY_WRITE, to, amt);
- memcpy_tofs (to, (unsigned char *)(skb->h.th) +
- skb->h.th->doff*4, amt);
-
- if (!(flags & MSG_PEEK))
- {
- skb->urg_used = 1;
- sk->urg --;
- }
- release_sock (sk);
- copied += amt;
- return (copied);
- }
- skb = (struct sk_buff *)skb->next;
- } while (skb != sk->rqueue->next);
- }
- sk->urg = 0;
- release_sock(sk);
- 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, nonblock, to, len, flags));
-
- /* so no-one else will use this socket. */
- sk->inuse = 1;
- if (sk->rqueue != NULL)
- skb=(struct sk_buff *)sk->rqueue->next;
- else
- skb = NULL;
-
- PRINTK(("tcp_read (sk=%X, to=%X, len=%d, nonblock=%d, flags=%X)\n",
- sk, to, len, nonblock, flags));
-
- while ( len > 0)
- {
- 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));
-
- cleanup_rbuf(sk);
-
- if (sk->err)
- {
- int tmp;
- release_sock (sk);
- if (copied)
- {
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
- tmp = -sk->err;
- sk->err = 0;
- return (tmp);
- }
-
- if (sk->state == TCP_CLOSE)
- {
- release_sock (sk);
- if (copied)
- {
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
- if (!sk->done)
- {
- sk->done = 1;
- return (0);
- }
- return (-ENOTCONN);
- }
-
- if (sk->shutdown & RCV_SHUTDOWN)
- {
- release_sock (sk);
- if (copied == 0) sk->done = 1;
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
-
- if (nonblock || copied)
- {
- release_sock (sk);
- if (copied)
- {
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
- return (-EAGAIN);
- }
-
- if ((flags & MSG_PEEK) && copied != 0)
- {
- release_sock (sk);
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
-
- PRINTK (("tcp_read about to sleep. state = %d\n",sk->state));
-
- release_sock (sk); /* now we may have some data waiting. */
- /* or we could have changed state. */
- cli();
- if ( sk->shutdown & RCV_SHUTDOWN || sk->err != 0)
- {
- sk->inuse = 1;
- sti();
- continue;
- }
-
- 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)
- {
- PRINTK (("tcp_read: returing %d\n", copied));
- return (copied);
- }
-
- return (-ERESTARTSYS);
- }
- }
- sti();
- PRINTK (("tcp_read woke up. \n"));
-
- sk->inuse = 1;
-
- if (sk->rqueue != NULL)
- skb=(struct sk_buff *)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)
- {
- sk->copied_seq += net16(skb->h.th->urg_ptr);
- offset += net16(skb->h.th->urg_ptr);
- if (offset >= skb->len)
- {
- skb->used = 1;
- skb = (struct sk_buff *)skb->next;
- continue;
- }
- }
- else
- {
- release_sock (sk);
- if (copied) return (copied);
- return (-EIO);
- }
- }
- used = min(skb->len - offset, len);
-
- verify_area (VERIFY_WRITE, 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=(struct sk_buff *)skb->next;
- }
- cleanup_rbuf (sk);
- release_sock (sk);
- PRINTK (("tcp_read: returing %d\n", copied));
- if (copied == 0 && nonblock) return (-EAGAIN);
- return (copied);
-}
-
-
-/* sends a fin without closing the connection. Not called
- at interrupt time. */
-
-void
-tcp_shutdown (volatile struct sock *sk, int how)
-{
- /* 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;
- struct tcp_header *t1,*th;
- struct proto *prot;
- int tmp;
- struct device *dev=NULL;
-
-/* Written; Tim MacKenzie (tym@dibbler.cs.monash.edu.au) 4 Dec '92.
- * Most of this is guesswork, so maybe it will work...
- */
-
- /* if we've already sent a fin, return. */
- if (sk->state == TCP_FIN_WAIT1 ||
- sk->state == TCP_FIN_WAIT2)
- return;
-
- if (!(how & SEND_SHUTDOWN)) return;
- sk->inuse = 1;
-
- /* clear out any half completed packets. */
- if (sk->send_tmp)
- tcp_send_partial(sk);
-
- prot = (struct proto *)sk->prot;
- th=(struct tcp_header *)&sk->dummy_th;
- release_sock (sk); /* incase the malloc sleeps. */
- buff=prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
- if (buff == NULL)
- {
- return;
- }
- sk->inuse = 1;
-
-
- PRINTK(("tcp_shutdown_send buff = %X\n", buff));
- buff->mem_addr = buff;
- buff->mem_len = MAX_RESET_SIZE;
- buff->lock = 0;
- 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,
- IPPROTO_TCP, sk->opt,
- sizeof(struct tcp_header));
- if (tmp < 0)
- {
- prot->wfree (sk,buff->mem_addr, buff->mem_len);
- release_sock(sk);
- PRINTK (("Unable to build header for fin.\n"));
- 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;
-
- t1->ack_seq = net32(sk->acked_seq);
- t1->window = net16(sk->prot->rspace(sk));
- t1->fin = 1;
- t1->rst = 0;
-
- 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;
- buff->magic = TCP_WRITE_QUEUE_MAGIC;
- }
- else
- {
- sk->prot->queue_xmit (sk, dev, buff,0);
- }
-
- if (sk->state == TCP_ESTABLISHED)
- {
- sk->state = TCP_FIN_WAIT1;
- }
- else
- {
- sk->state = TCP_FIN_WAIT2;
- }
- release_sock(sk);
-}
-
-
-static int
-tcp_recvfrom (volatile struct sock *sk, unsigned char *to,
- int to_len, int nonblock, unsigned flags,
- struct sockaddr_in *addr, int *addr_len)
-{
- int result = tcp_read(sk, to, to_len, nonblock, flags);
- struct sockaddr_in sin;
- int len;
- if (result < 0)
- return (result);
- len = get_fs_long(addr_len);
- if (len > sizeof (sin))
- len = sizeof (sin);
- sin.sin_family = AF_INET;
- sin.sin_port = sk->dummy_th.dest;
- sin.sin_addr.s_addr = sk->daddr;
- verify_area (VERIFY_WRITE, addr, len);
- memcpy_tofs (addr, &sin, len);
- verify_area (VERIFY_WRITE, addr_len, sizeof (len));
- put_fs_long (len, addr_len);
- return (result);
-}
-
-/* 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, GFP_ATOMIC);
- if (buff == NULL) return;
-
- PRINTK(("tcp_reset buff = %X\n", buff));
- buff->mem_addr = buff;
- buff->mem_len = MAX_RESET_SIZE;
- buff->lock = 0;
- 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, IPPROTO_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);
- kfree_skb (skb, FREE_READ);
- return;
- }
-
- /* make sure we can accept more. This will prevent a flurry of
- syns from eating up all our memory. */
- if (sk->ack_backlog >= sk->max_ack_backlog)
- {
- kfree_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 = kmalloc(sizeof (struct sock), GFP_ATOMIC);
- if (newsk == NULL)
- {
- /* just ignore the syn. It will get retransmitted. */
- kfree_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->rtt = TCP_CONNECT_TIME;
- newsk->blog = 0;
- newsk->intr = 0;
- newsk->proc = 0;
- newsk->done = 0;
- newsk->send_tmp = NULL;
- 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=576-HEADER_SIZE;
- }
- else
- {
- ptr = (unsigned char *)(skb->h.th + 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);
- }
- }
-
- buff=newsk->prot->wmalloc(newsk,MAX_SYN_SIZE,1, GFP_ATOMIC);
- if (buff == NULL)
- {
- sk->err = -ENOMEM;
- newsk->dead = 1;
- release_sock (newsk);
- kfree_skb (skb, FREE_READ);
- return;
- }
-
- buff->lock = 0;
- 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,
- IPPROTO_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);
- skb->sk = sk;
- kfree_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 = newsk->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;
- }
- sk->ack_backlog++;
- 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 = 1;
- 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=(struct sk_buff *)skb->next;
- /* if there is some real unread data, send a reset. */
- if (skb->len > 0 &&
- after (skb->h.th->seq + skb->len + 1, sk->copied_seq))
- need_reset = 1;
- kfree_skb (skb, FREE_READ);
- skb=skb2;
- } while (skb != sk->rqueue);
- }
- sk->rqueue = NULL;
-
- /* get rid on any half completed packets. */
- if (sk->send_tmp)
- {
- tcp_send_partial (sk);
- }
-
- switch (sk->state)
- {
-
- case TCP_FIN_WAIT1:
- case TCP_FIN_WAIT2:
- case TCP_LAST_ACK:
- /* start a timer. */
- sk->time_wait.len = 4*sk->rtt;;
- sk->timeout = TIME_CLOSE;
- reset_timer ((struct timer *)&sk->time_wait);
- if (timeout)
- tcp_time_wait(sk);
- release_sock (sk);
- 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_CLOSE_WAIT:
- 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, GFP_ATOMIC);
- if (buff == NULL)
- {
- /* this will force it to try again later. */
- if (sk->state != TCP_CLOSE_WAIT)
- 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->lock = 0;
- 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,
- IPPROTO_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;
- buff->magic = TCP_WRITE_QUEUE_MAGIC;
-
- }
-
- if (sk->state == TCP_CLOSE_WAIT)
- {
- sk->state = TCP_FIN_WAIT2;
- }
- else
- {
- 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;
- PRINTK (("tcp_write_xmit (sk=%X)\n",sk));
- while (sk->wfront != NULL && before (sk->wfront->h.seq, sk->window_seq) &&
- sk->packets_out < sk->cong_window)
- {
- skb = sk->wfront;
- sk->wfront = (struct sk_buff *)skb->next;
- if (sk->wfront == NULL)
- sk->wback = NULL;
- skb->next = NULL;
- if (skb->magic != TCP_WRITE_QUEUE_MAGIC)
- {
- PRINTK (("tcp.c skb with bad magic (%X) on write queue. Squashing "
- "queue\n", skb->magic));
- sk->wfront = NULL;
- sk->wback = NULL;
- return;
- }
- skb->magic = 0;
- PRINTK(("Sending a packet.\n"));
- 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);
-
- PRINTK (("tcp_ack ack=%d, window=%d, "
- "sk->rcv_ack_seq=%d, sk->window_seq = %d\n",
- ack, net16(th->window), sk->rcv_ack_seq, sk->window_seq));
- if (after (ack, sk->send_seq+1) || before (ack, sk->rcv_ack_seq-1))
- {
- if (after (ack, sk->send_seq) || (sk->state != TCP_ESTABLISHED &&
- sk->state != TCP_CLOSE_WAIT))
- {
- return (0);
- }
- if (sk->keepopen)
- reset_timer ((struct timer *)&sk->time_wait);
- sk->retransmits = 0;
- return (1);
- }
-
- /* see if our window has been shrunk. */
- if (after (sk->window_seq, ack+net16(th->window)))
- {
- /* we may need to move packets from the send queue to the
- write queue. if the window has been shrunk on us. */
- /* the rfc says you are not allowed to shrink your window like
- this, but if the other end does, you must be able to deal
- with it. */
-
- struct sk_buff *skb;
- struct sk_buff *skb2=NULL;
- struct sk_buff *wskb=NULL;
-
- sk->window_seq = ack + net16(th->window);
- cli();
- for (skb = sk->send_head; skb != NULL; skb= (struct sk_buff *)skb->link3)
- {
- if (after( skb->h.seq, sk->window_seq))
- {
-
- /* remove it from the send queue. */
- if (skb2 == NULL)
- {
- sk->send_head = (struct sk_buff *)skb->link3;
- }
- else
- {
- skb2->link3 = skb->link3;
- }
- if (sk->send_tail == skb)
- sk->send_tail = skb2;
-
- /* we may need to remove this from the dev send list. */
- if (skb->next != NULL)
- {
- int i;
- if (skb->next != skb)
- {
- skb->next->prev = skb->prev;
- skb->prev->next = skb->next;
- }
- for (i = 0; i < DEV_NUMBUFFS; i++)
- {
- if (skb->dev->buffs[i] == skb)
- {
- if (skb->next == skb)
- skb->dev->buffs[i] = NULL;
- else
- skb->dev->buffs[i] = skb->next;
- break;
- }
- }
- if (arp_q == skb)
- {
- if (skb->next == skb)
- arp_q = NULL;
- else
- arp_q = skb->next;
- }
- }
-
- /* now add it to the write_queue. */
- skb->magic = TCP_WRITE_QUEUE_MAGIC;
- if (wskb == NULL)
- {
- skb->next = sk->wfront;
- sk->wfront = skb;
- }
- else
- {
- skb->next = wskb->next;
- wskb->next = skb;
- }
- wskb = skb;
- }
- else
- {
- skb2 = skb;
- }
- }
- sti();
- }
-
- 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++;
- }
-
- PRINTK (("tcp_ack: Updating rcv ack sequence. \n"));
- 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 --;
- PRINTK (("skb=%X acked\n", sk->send_head));
- /* wake up the process, it can probably
- write more. */
- if (!sk->dead)
- wake_up (sk->sleep);
-
- 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 = (struct sk_buff *)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)
- {
- int i;
- if (oskb->next != oskb)
- {
- oskb->next->prev = oskb->prev;
- oskb->prev->next = oskb->next;
- }
- for (i = 0; i < DEV_NUMBUFFS; i++)
- {
- if (oskb->dev->buffs[i] == oskb)
- {
- if (oskb== oskb->next)
- oskb->dev->buffs[i]= NULL;
- else
- oskb->dev->buffs[i] = oskb->next;
- break;
- }
- }
- if (arp_q == oskb)
- {
- if (oskb == oskb->next)
- arp_q = NULL;
- else
- arp_q = (struct sk_buff *)oskb->next;
- }
- }
- oskb->magic = 0;
- kfree_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)
- {
- PRINTK (("retransmitting\n"));
- 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 && !sk->keepopen)
- {
- PRINTK (("Nothing to do, going to sleep.\n"));
- if (!sk->dead)
- wake_up (sk->sleep);
-
- delete_timer((struct timer *)&sk->time_wait);
- sk->timeout = 0;
- }
- else
- {
- if (sk->state != sk->keepopen)
- {
- sk->timeout = TIME_WRITE;
- sk->time_wait.len = sk->rtt*2;
- reset_timer ((struct timer *)&sk->time_wait);
- }
- if (sk->state == TCP_TIME_WAIT)
- {
- sk->time_wait.len = TCP_TIMEWAIT_LEN;
- reset_timer ((struct timer *)&sk->time_wait);
- sk->timeout = TIME_CLOSE;
- }
- }
- }
-
-
- if (sk->packets_out == 0 && sk->send_tmp != NULL &&
- sk->wfront == NULL && sk->send_head == NULL)
- {
- tcp_send_partial (sk);
- }
-
- /* see if we are done. */
- if ( sk->state == TCP_TIME_WAIT)
- {
- if (!sk->dead) wake_up (sk->sleep);
- if (sk->rcv_ack_seq == sk->send_seq &&
- sk->acked_seq == sk->fin_seq)
- {
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- }
- }
-
- if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2)
- {
- if (!sk->dead) wake_up (sk->sleep);
- if (sk->rcv_ack_seq == sk->send_seq)
- {
- if (sk->acked_seq != sk->fin_seq)
- {
- tcp_time_wait(sk);
- }
- else
- {
- PRINTK (("tcp_ack closing socket - %X\n", sk));
- tcp_send_ack (sk->send_seq, sk->acked_seq, sk, th, sk->daddr);
- sk->shutdown = SHUTDOWN_MASK;
- sk->state = TCP_CLOSE;
- }
- }
- }
-
- PRINTK (("leaving tcp_ack\n"));
-
- 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));
-
- 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);
- kfree_skb(skb, FREE_READ);
- return (0);
- }
-
- if (sk->shutdown & RCV_SHUTDOWN)
- {
- sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
- tcp_reset (sk->saddr, sk->daddr, skb->h.th,
- sk->prot, NULL, skb->dev);
- sk->state = TCP_CLOSE;
- sk->err = EPIPE;
- sk->shutdown = SHUTDOWN_MASK;
- PRINTK (("tcp_data: closing socket - %X\n", sk));
- kfree_skb (skb, FREE_READ);
- if (!sk->dead) wake_up (sk->sleep);
- 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. */
-
- /* this should start at the last one, and then go around forwards. */
- if (sk->rqueue == NULL)
- {
- PRINTK (("tcp_data: skb = %X:\n",skb));
-
- sk->rqueue = skb;
- skb->next = skb;
- skb->prev = skb;
- skb1= NULL;
- }
- else
- {
- PRINTK (("tcp_data adding to chain sk = %X:\n",sk));
-
- for (skb1=sk->rqueue; ; skb1=(struct sk_buff *)skb1->prev)
- {
- PRINTK (("skb1=%X\n",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));
-
- }
-
- 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))
- {
- if (after (th->ack_seq, sk->acked_seq))
- sk->acked_seq = th->ack_seq;
- skb->acked = 1;
-
- /* when we ack the fin, we turn on the RCV_SHUTDOWN flag. */
- if (skb->h.th->fin)
- {
- if (!sk->dead) wake_up (sk->sleep);
- sk->shutdown |= RCV_SHUTDOWN;
- }
-
- for (skb2=(struct sk_buff *)skb->next;
- skb2 !=(struct sk_buff *) sk->rqueue->next;
- skb2=(struct sk_buff *)skb2->next)
- {
- if (before(skb2->h.th->seq, sk->acked_seq+1))
- {
- if (after (skb2->h.th->ack_seq, sk->acked_seq))
- sk->acked_seq = skb2->h.th->ack_seq;
- skb2->acked = 1;
-
- /* when we ack the fin, we turn on the RCV_SHUTDOWN flag. */
- if (skb2->h.th->fin)
- {
- sk->shutdown |= RCV_SHUTDOWN;
- if (!sk->dead) wake_up (sk->sleep);
- }
-
- /* 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->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);
- sk->retransmits = 0;
- }
- }
- }
- 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_FIN_WAIT2 && sk->acked_seq == sk->fin_seq
- && sk->rcv_ack_seq == sk->send_seq)
- {
- PRINTK (("tcp_data: entering last_ack state sk = %X\n", sk));
-
- tcp_send_ack (sk->send_seq, sk->acked_seq, sk, th, saddr);
- sk->shutdown = SHUTDOWN_MASK;
- sk->state = TCP_LAST_ACK;
- if (!sk->dead) wake_up (sk->sleep);
- }
-
- 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);
- }
-
- if (!sk->urg)
- {
- /* so if we get more urgent data, we don't
- signal the user again. */
- if (sk->proc != 0)
- {
- if (sk->proc > 0)
- {
- kill_proc (sk->proc, SIGURG, 1);
- }
- else
- {
- kill_pg (-sk->proc, SIGURG, 1);
- }
- }
- }
- sk->urg++;
- 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)
-{
- PRINTK (("tcp_fin (sk=%X, th=%X, saddr=%X, dev=%X)\n",
- sk, th, saddr, dev));
-
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
-
- switch (sk->state)
- {
- case TCP_SYN_RECV:
- case TCP_SYN_SENT:
- case TCP_ESTABLISHED:
- sk->fin_seq = th->seq+1; /* Contains the one that needs to be acked */
- sk->state = TCP_CLOSE_WAIT;
- if (th->rst) sk->shutdown = SHUTDOWN_MASK;
- break;
-
- case TCP_CLOSE_WAIT:
- case TCP_FIN_WAIT2:
- break; /* we got a retransmit of the fin. */
-
- case TCP_FIN_WAIT1:
- sk->fin_seq = th->seq+1; /* Contains the one that needs to be acked */
- sk->state = TCP_FIN_WAIT2;
- break;
-
- default:
- 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);
-
- }
- /* there is no longer any reason to do this. Just let tcp_data
- deal with it. */
- sk->ack_backlog ++;
-
-#if 0
- /* send an ack */
- buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
- 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,
- IPPROTO_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 = 0;
- 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;
- buff->magic = TCP_WRITE_QUEUE_MAGIC;
- }
- else
- {
- sk->prot->queue_xmit (sk, dev, buff,0);
- }
-#endif
- 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));
- /* 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;
-
- kfree_skb (skb, FREE_READ);
- sk->ack_backlog--;
- 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 (VERIFY_WRITE, usin, addr_len);*/
- memcpy_fromfs (&sin,usin, min(sizeof (sin), addr_len));
-
- if (sin.sin_family && sin.sin_family != AF_INET) return (-EAFNOSUPPORT);
- sk->inuse = 1;
- 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;
- release_sock (sk);
-
- buff=sk->prot->wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
- if (buff == NULL)
- {
- return (-ENOMEM);
- }
- sk->inuse = 1;
- buff->lock = 0;
- 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,
- IPPROTO_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;
- sk->rtt = TCP_CONNECT_TIME;
- reset_timer ((struct timer *)&sk->time_wait);
- sk->retransmits = TCP_RETR2 - 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. */
-
- PRINTK (("tcp_sequence (sk=%X, th=%X, len = %d, opt=%d, saddr=%X)\n",
- sk, th, len, opt, saddr));
-
- if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)||
- between(th->seq + len-(th->doff * 4), sk->acked_seq + 1,
- sk->acked_seq + sk->window) ||
- (before (th->seq, sk->acked_seq) &&
- after (th->seq + len - (th->doff * 4), sk->acked_seq + sk->window)))
- {
- return (1);
- }
-
- PRINTK (("tcp_sequence: rejecting packet. \n"));
-
- /* 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);
- }
-
- /* in case it's just a late ack, let it through */
- if (th->ack && len == (th->doff * 4) && after (th->seq, sk->acked_seq - 32767) &&
- !th->fin && !th->syn) return (1);
-
- if (!th->rst)
- {
- /* try to resync things. */
- tcp_send_ack (net32(th->ack_seq), sk->acked_seq, sk, th, saddr);
- }
-
-
- 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;
-
- if (!skb)
- {
- PRINTK (("tcp.c: tcp_rcv skb = NULL\n"));
- return (0);
- }
-#if 0 /* it's ok for protocol to be NULL */
- if (!protocol)
- {
- PRINTK (("tcp.c: tcp_rcv protocol = NULL\n"));
- return (0);
- }
-
- if (!opt) /* it's ok for opt to be NULL */
- {
- PRINTK (("tcp.c: tcp_rcv opt = NULL\n"));
- }
-#endif
- if (!dev)
- {
- PRINTK (("tcp.c: tcp_rcv dev = NULL\n"));
- return (0);
- }
-
- 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));
- }
-
- if (!redo)
- {
- if (th->check && tcp_check (th, len, saddr, daddr ))
- {
- skb->sk = NULL;
- PRINTK (("packet dropped with bad checksum.\n"));
- kfree_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;
- kfree_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();
- }
- else
- {
- if (!sk)
- {
- PRINTK (("tcp.c: tcp_rcv bug sk=NULL redo = 1\n"));
- return (0);
- }
- }
-
- if (!sk->prot)
- {
- PRINTK (("tcp.c: tcp_rcv sk->prot = NULL \n"));
- return (0);
- }
-
- /* charge the memory to the socket. */
- if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX)
- {
- skb->sk = NULL;
- PRINTK (("dropping packet due to lack of buffer space.\n"));
- kfree_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;
- sk->shutdown = SHUTDOWN_MASK;
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-
- case TCP_ESTABLISHED:
- case TCP_CLOSE_WAIT:
- case TCP_FIN_WAIT1:
- case TCP_FIN_WAIT2:
- case TCP_TIME_WAIT:
-
- if (!tcp_sequence (sk, th, len, opt, saddr))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-
- if (th->rst)
- {
- /* this means the thing should really be closed. */
- sk->err = ECONNRESET;
-
- if (sk->state == TCP_CLOSE_WAIT)
- {
- sk->err = EPIPE;
- }
-
- /* a reset with a fin just means that the
- data was not all read. */
- if (!th->fin)
- {
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
- }
-#if 0
- if (opt && (opt->security != 0 || opt->compartment != 0 || th->syn))
- {
- sk->err = ECONNRESET;
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- tcp_reset (daddr, saddr, th, sk->prot, opt,dev);
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-#endif
- if (th->ack)
- {
- if(!tcp_ack (sk, th, saddr))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
- }
- if (th->urg)
- {
- if (tcp_urg (sk, th, saddr))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
- }
-
- if (th->fin && tcp_fin (sk, th, saddr, dev))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-
- if ( tcp_data (skb, sk, saddr, len))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-
- release_sock(sk);
- return (0);
-
- case TCP_CLOSE:
-
- if (sk->dead || sk->daddr)
- {
- PRINTK (("packet received for closed,dead socket\n"));
- kfree_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);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
-
- case TCP_LISTEN:
- if (th->rst)
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
- if (th->ack)
- {
- tcp_reset (daddr, saddr, th, sk->prot, opt,dev );
- kfree_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);
- }
-
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
-
- default:
- if (!tcp_sequence (sk, th, len, opt, saddr))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-
- case TCP_SYN_SENT:
- if (th->rst)
- {
- sk->err = ECONNREFUSED ;
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
-#if 0
- if (opt->security != 0 || opt->compartment != 0 )
- {
- sk->err = ECONNRESET;
- sk->state = TCP_CLOSE;
- sk->shutdown = SHUTDOWN_MASK;
- tcp_reset (daddr, saddr, th, sk->prot, opt, dev);
- if (!sk->dead)
- {
- wake_up (sk->sleep);
- }
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- } */
-#endif
- if (!th->ack)
- {
- if (th->syn)
- {
- sk->state = TCP_SYN_RECV;
- }
-
- kfree_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);
- kfree_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)
- {
- kfree_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);
- kfree_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))
- {
- kfree_skb (skb, FREE_READ);
- release_sock(sk);
- return (0);
- }
- if (tcp_data (skb, sk, saddr, len))
- kfree_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))
- {
- kfree_skb (skb, FREE_READ);
- release_sock (sk);
- return (0);
- }
- }
-
- if (tcp_data (skb, sk, saddr, len))
- {
- kfree_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 && sk->state != TCP_CLOSE_WAIT) return;
-
- buff=sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
- /* no big loss. */
- if (buff == NULL) return;
-
- buff->lock = 0;
- 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,
- IPPROTO_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,
- tcp_sendto,
- tcp_recvfrom,
- 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,
- tcp_shutdown,
- 128,
- 0,
- {NULL,},
- "TCP"
-};
-
-
-
-
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
deleted file mode 100644
index 123cdba..0000000
--- a/net/tcp/tcp.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* 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
-*/
-/* $Id: tcp.h,v 0.8.4.7 1993/01/22 22:58:08 bir7 Exp $ */
-/* $Log: tcp.h,v $
- * Revision 0.8.4.7 1993/01/22 22:58:08 bir7
- * Check in for merge with previous .99 pl 4.
- *
- * Revision 0.8.4.6 1992/12/12 19:25:04 bir7
- * Fixed anti-memory Leak in shutdown.
- *
- * Revision 0.8.4.5 1992/12/12 01:50:49 bir7
- * Fixed several bugs including half-duplex connections.
- *
- * Revision 0.8.4.4 1992/12/08 20:49:15 bir7
- * Fixed minor bugs and checked out MSS.
- *
- * Revision 0.8.4.3 1992/12/06 23:29:59 bir7
- * Added support for mss and half completed packets. Also added
- * support for shrinking windows.
- *
- * Revision 0.8.4.2 1992/12/03 19:54:12 bir7
- * Added paranoid queue checking.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- *
- */
-
-#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,
-#if 0
- TCP_CLOSING, /* not a valid state, just a seperator so we can use
- < tcp_closing or > tcp_closing for checks. */
-#endif
- TCP_FIN_WAIT1,
- TCP_FIN_WAIT2,
- TCP_TIME_WAIT,
- TCP_CLOSE,
- TCP_CLOSE_WAIT,
- 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 10 /* this is howmany retries it does
- before it tries to figure out
- if the gateway is down. */
-
-#define TCP_RETR2 25 /* this should take at least
- 90 minutes to time out. */
-
-
-#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 250 /* 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_PROBEWAIT_LEN 250 /* time to wait between probes when I've got
- something to write and there is no window. */
-
-#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 /* Maximum header size we need to deal with. */
-
-#define TCP_WRITE_QUEUE_MAGIC 0xa5f23477
-
- /* 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));
- }
-
-static inline const int
-tcp_connected (const int state)
-{
- return (state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
- state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2);
-}
-
-#endif
diff --git a/net/tcp/timer.c b/net/tcp/timer.c
deleted file mode 100644
index a2c4d08..0000000
--- a/net/tcp/timer.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/* 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
- */
-
-/* $Id: timer.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: timer.c,v $
- * Revision 0.8.4.8 1993/01/23 18:00:11 bir7
- * added volatile keyword.
- *
- * Revision 0.8.4.7 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.6 1993/01/22 22:58:08 bir7
- * Check in for merge with previous .99 pl 4.
- *
- * Revision 0.8.4.5 1992/12/12 19:25:04 bir7
- * cleaned up Log messages.
- *
- * Revision 0.8.4.4 1992/12/12 01:50:49 bir7
- * Fixed timeouts.
- *
- * Revision 0.8.4.3 1992/12/06 23:29:59 bir7
- * Fixed bugs in timeout.
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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"
-
-#undef TIMER_DEBUG
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-
-#ifdef TIMER_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-static volatile 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 || t == 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 = (struct timer *)timer_base;
- tm->next != NULL ;
- tm=(struct timer *)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 = (struct timer *)timer_base; ; tm=(struct timer *)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();
- why = sk->timeout;
-
- PRINTK (("net_timer: found sk=%X why = %d\n",sk, why));
-
- if (sk->keepopen)
- {
- sk->time_wait.len = TCP_TIMEOUT_LEN;
- sk->timeout = TIME_KEEPOPEN;
- reset_timer ((struct timer *)timer_base);
- }
- else
- {
- sk->timeout = 0;
- delete_timer((struct 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));
- 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. */
- /* it could be we got here because we needed
- to send an ack. So we need to check for that. */
- if (sk->send_head != NULL)
- {
- if (before (jiffies, sk->send_head->when + 2*sk->rtt))
- {
- sk->time_wait.len = 2*sk->rtt;
- sk->timeout = TIME_WRITE;
- reset_timer ((struct timer *)&sk->time_wait);
- release_sock (sk);
- break;
- }
- PRINTK (("retransmitting.\n"));
- sk->prot->retransmit (sk, 0);
-
- if (sk->retransmits > TCP_RETR1)
- {
- PRINTK (("timer.c TIME_WRITE time-out 1\n"));
- arp_destroy (sk->daddr);
- ip_route_check (sk->daddr);
- }
-
- if (sk->retransmits > TCP_RETR2)
- {
- PRINTK (("timer.c TIME_WRITE time-out 2\n"));
- sk->err = ETIMEDOUT;
- 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;
- }
- }
- release_sock (sk);
- break;
- }
- release_sock (sk);
- break;
-
- case TIME_KEEPOPEN: /* send something to keep the
- connection open. */
-
- if (sk->prot->write_wakeup != NULL)
- sk->prot->write_wakeup(sk);
- sk->retransmits ++;
- if (sk->shutdown == SHUTDOWN_MASK)
- {
- sk->prot->close (sk,1);
- sk->state = TCP_CLOSE;
- }
-
- if (sk->retransmits > TCP_RETR1)
- {
- PRINTK (("timer.c TIME_KEEPOPEN time-out 1\n"));
- arp_destroy (sk->daddr);
- ip_route_check (sk->daddr);
- release_sock (sk);
- break;
- }
- if (sk->retransmits > TCP_RETR2)
- {
- PRINTK (("timer.c TIME_KEEPOPEN time-out 2\n"));
- 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;
- }
- release_sock (sk);
- break;
-
- default:
- release_sock(sk);
- break;
- } /* switch */
- } /* while (timer_base != ... */
-
- /* Now we need to reset the timer. */
- if (timer_base != NULL)
- {
- timer_table[NET_TIMER].expires = timer_base->when;
- timer_active |= 1 << NET_TIMER;
- }
-}
-
-
diff --git a/net/tcp/timer.h b/net/tcp/timer.h
deleted file mode 100644
index 29df38d..0000000
--- a/net/tcp/timer.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* 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
-*/
-/* $Id: timer.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $ */
-/* $Log: timer.h,v $
- * Revision 0.8.4.2 1993/01/23 18:00:11 bir7
- * added volatile keyword.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: timer.h,v $
- * Revision 0.8.4.2 1993/01/23 18:00:11 bir7
- * added volatile keyword.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-#ifndef _TCP_TIMER_H
-#define _TCP_TIMER_H
-
-struct timer
-{
- unsigned long len;
- volatile struct sock *sk;
- unsigned long when;
- volatile struct timer *next;
-};
-
-
-void delete_timer (struct timer *);
-void reset_timer (struct timer *);
-void net_timer (void);
-
-#define SEQ_TICK 3
-#define timer_seq jiffies
-extern unsigned long seq_offset;
-#endif
diff --git a/net/tcp/udp.c b/net/tcp/udp.c
deleted file mode 100644
index 4c5d0d1..0000000
--- a/net/tcp/udp.c
+++ /dev/null
@@ -1,783 +0,0 @@
-/* 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
-*/
-/* $Id: udp.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $ */
-/* $Log: udp.c,v $
- * Revision 0.8.4.12 1993/01/26 22:04:00 bir7
- * Added support for proc fs.
- *
- * Revision 0.8.4.11 1993/01/23 18:00:11 bir7
- * added volatile keyword.
- *
- * Revision 0.8.4.10 1993/01/22 23:21:38 bir7
- * Merged with 99 pl4
- *
- * Revision 0.8.4.9 1992/12/12 19:25:04 bir7
- * cleaned up Log messages.
- *
- * Revision 0.8.4.8 1992/12/12 01:50:49 bir7
- * Changed connect.
- *
- * Revision 0.8.4.7 1992/12/05 21:35:53 bir7
- * Added more debuggin code.
- *
- * Revision 0.8.4.6 1992/12/03 19:52:20 bir7
- * fixed problems in udp_error.
- *
- * Revision 0.8.4.5 1992/11/18 15:38:03 bir7
- * fixed minor problem in waiting for memory.
- *
- * Revision 0.8.4.3 1992/11/15 14:55:30 bir7
- * Fixed ctrl-h and added NULL checking to print_uh
- *
- * Revision 0.8.4.2 1992/11/10 10:38:48 bir7
- * Change free_s to kfree_s and accidently changed free_skb to kfree_skb.
- *
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.5 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added Id and Log
- * */
-
-#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 <linux/termios.h> /* for ioctl's */
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <linux/mm.h>
-#include "../kern_sock.h" /* for PRINTK */
-#include "udp.h"
-#include "icmp.h"
-
-#undef UDP_DEBUG
-
-#ifdef PRINTK
-#undef PRINTK
-#endif
-
-#ifdef UDP_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x) /**/
-#endif
-
-#define min(a,b) ((a)<(b)?(a):(b))
-
-
-
-static void
-print_uh(struct udp_header *uh)
-{
- if (uh == NULL)
- {
- PRINTK (("(NULL)\n"));
- return;
- }
- 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 udp_header *th;
- volatile struct sock *sk;
-
- PRINTK (("udp_err (err=%d, header=%X, daddr=%X, saddr=%X, ip_protocl=%X)\n"));
-
- th = (struct udp_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;
- /* it's only fatal if we have connected to them. */
- if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED)
- {
- 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) + IPPROTO_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, GFP_KERNEL);
-
- /* if we didn't get the memory, just drop the packet. */
- if (skb == NULL) return (len);
- skb->lock = 0;
- 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 (VERIFY_WRITE, 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 (VERIFY_WRITE, 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,
- GFP_KERNEL);
- /* this should never happen, but it is possible. */
-
- if (skb == NULL)
- {
- tmp = sk->wmem_alloc;
- release_sock (sk);
- if (copied) return (copied);
- if (noblock) return (-EAGAIN);
- cli();
- if (tmp <= sk->wmem_alloc)
- {
- interruptible_sleep_on (sk->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- if (copied) return (copied);
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- continue;
- }
-
- skb->lock = 0;
- 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,
- IPPROTO_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);
- if (amt < 0)
- {
- printk ("udp.c: amt = %d < 0\n",amt);
- release_sock (sk);
- return (copied);
- }
-
-/* verify_area (VERIFY_WRITE, 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));
-}
-
-
-static int
-udp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
-{
- switch (cmd)
- {
- default:
- return (-EINVAL);
-
- case TIOCOUTQ:
- {
- unsigned long amount;
- if (sk->state == TCP_LISTEN)
- return (-EINVAL);
- amount = sk->prot->wspace(sk)/2;
- verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- put_fs_long (amount, (unsigned long *)arg);
- return (0);
- }
-
-
- case TIOCINQ:
-/* case FIONREAD:*/
- {
- struct sk_buff *skb;
- unsigned long amount;
- if (sk->state == TCP_LISTEN)
- return (-EINVAL);
- amount = 0;
- skb = sk->rqueue;
- if (skb != NULL)
- {
- /* we will only return the amount of this packet since that is all
- that will be read. */
- amount = skb->len;
- }
-
- verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- put_fs_long (amount, (unsigned long *)arg);
- return (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);
-
- /* this will pick up errors that occured
- while the program was doing something
- else. */
- if (sk->err)
- {
- int err;
- err = -sk->err;
- sk->err = 0;
- return (err);
- }
- if (addr_len)
- {
- verify_area (VERIFY_WRITE, addr_len, sizeof(*addr_len));
- put_fs_long (sizeof (*sin), addr_len);
- }
- sk->inuse = 1;
- while (sk->rqueue == NULL)
- {
- if (sk->shutdown & RCV_SHUTDOWN)
- {
- return (0);
- }
-
- 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)
- {
- sti();
- return (-ERESTARTSYS);
- }
- }
- sk->inuse = 1;
- sti();
- }
- skb = sk->rqueue;
-
- if (!(flags & MSG_PEEK))
- {
- if (skb->next == skb )
- {
- sk->rqueue = NULL;
- }
- else
- {
- sk->rqueue = (struct sk_buff *)sk->rqueue ->next;
- skb->prev->next = skb->next;
- skb->next->prev = skb->prev;
- }
- }
- copied = min (len, skb->len);
- verify_area (VERIFY_WRITE, 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 (VERIFY_WRITE, sin, sizeof (*sin));
- memcpy_tofs(sin, &addr, sizeof (*sin));
- }
-
- if (!(flags & MSG_PEEK))
- {
- kfree_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 (VERIFY_WRITE, 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)
- {
- if ((daddr & 0xff000000 != 0) &&
- (daddr & 0xff000000 != 0xff000000))
- {
- icmp_reply (skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, dev);
- }
- skb->sk = NULL;
- kfree_skb (skb, 0);
- return (0);
- }
-
-
- if (!redo)
- {
- if (uh->check && udp_check (uh, len, saddr, daddr))
- {
- PRINTK (("bad udp checksum\n"));
- skb->sk = NULL;
- kfree_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;
- kfree_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"));
-
- /* 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,
- udp_ioctl,
- NULL,
- NULL,
- 128,
- 0,
- {NULL,},
- "UDP"
-};
-
diff --git a/net/tcp/udp.h b/net/tcp/udp.h
deleted file mode 100644
index 1cd8114..0000000
--- a/net/tcp/udp.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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
-*/
-/* $Id: udp.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $ */
-/* $Log: udp.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: udp.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-#ifndef _TCP_UDP_H
-#define _TCP_UDP_H
-
-struct udp_header
-{
- unsigned short source;
- unsigned short dest;
- unsigned short len;
- unsigned short check;
-};
-
-extern struct proto udp_prot;
-#define UDP_NO_CHECK 1
-
-#endif
diff --git a/net/tcp/wereg.h b/net/tcp/wereg.h
deleted file mode 100644
index 029c5c0..0000000
--- a/net/tcp/wereg.h
+++ /dev/null
@@ -1,490 +0,0 @@
-/* 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
-*/
-/* $Id: wereg.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $ */
-/* $Log: wereg.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *
- * Revision 0.8.3.2 1992/11/10 00:14:47 bir7
- * Changed malloc to kmalloc and added $iId$ and $Log: wereg.h,v $
- * Revision 0.8.4.1 1992/11/10 00:17:18 bir7
- * version change only.
- *.
- * */
-
-/* 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 */
-
-
-
-
-
diff --git a/net/unix.c b/net/unix.c
deleted file mode 100644
index 05cbd42..0000000
--- a/net/unix.c
+++ /dev/null
@@ -1,773 +0,0 @@
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/socket.h>
-#include <linux/un.h>
-#include <linux/fcntl.h>
-#include <linux/termios.h>
-#include <linux/mm.h>
-#include <linux/config.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-
-#include "kern_sock.h"
-
-static struct unix_proto_data {
- int refcnt; /* cnt of reference 0=free */
- struct socket *socket; /* socket we're bound to */
- int protocol;
- struct sockaddr_un sockaddr_un;
- short sockaddr_len; /* >0 if name bound */
- char *buf;
- int bp_head, bp_tail;
- struct inode *inode;
- struct unix_proto_data *peerupd;
-} unix_datas[NSOCKETS];
-#define last_unix_data (unix_datas + NSOCKETS - 1)
-
-#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data)
-#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0)->sun_path)
-
-/*
- * buffer size must be power of 2. buffer mgmt inspired by pipe code.
- * note that buffer contents can wraparound, and we can write one byte less
- * than full size to discern full vs empty.
- */
-#define BUF_SIZE PAGE_SIZE
-#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & (BUF_SIZE-1))
-#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
-
-static int unix_proto_init(void);
-static int unix_proto_create(struct socket *sock, int protocol);
-static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);
-static int unix_proto_release(struct socket *sock, struct socket *peer);
-static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
- int sockaddr_len);
-static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags);
-static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2);
-static int unix_proto_accept(struct socket *sock, struct socket *newsock,
- int flags);
-static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
- int *usockaddr_len, int peer);
-static int unix_proto_read(struct socket *sock, char *ubuf, int size,
- int nonblock);
-static int unix_proto_write(struct socket *sock, char *ubuf, int size,
- int nonblock);
-static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait);
-static int unix_proto_ioctl(struct socket *sock, unsigned int cmd,
- unsigned long arg);
-static int unix_proto_listen(struct socket *sock, int backlog);
-static int unix_proto_send (struct socket *sock, void *buff, int len,
- int nonblock, unsigned flags);
-static int unix_proto_recv (struct socket *sock, void *buff, int len,
- int nonblock, unsigned flags);
-static int unix_proto_sendto (struct socket *sock, void *buff, int len,
- int nonblock, unsigned flags,
- struct sockaddr *addr, int addr_len);
-static int unix_proto_recvfrom (struct socket *sock, void *buff, int len,
- int nonblock, unsigned flags,
- struct sockaddr *addr, int *addr_len);
-
-static int unix_proto_shutdown (struct socket *sock, int how);
-
-static int unix_proto_setsockopt (struct socket *sock, int level, int optname,
- char *optval, int optlen);
-static int unix_proto_getsockopt (struct socket *sock, int level, int optname,
- char *optval, int *optlen);
-
-struct proto_ops unix_proto_ops = {
- unix_proto_init,
- unix_proto_create,
- unix_proto_dup,
- unix_proto_release,
- unix_proto_bind,
- unix_proto_connect,
- unix_proto_socketpair,
- unix_proto_accept,
- unix_proto_getname,
- unix_proto_read,
- unix_proto_write,
- unix_proto_select,
- unix_proto_ioctl,
- unix_proto_listen,
- unix_proto_send,
- unix_proto_recv,
- unix_proto_sendto,
- unix_proto_recvfrom,
- unix_proto_shutdown,
- unix_proto_setsockopt,
- unix_proto_getsockopt,
- NULL, /* unix_proto_fcntl. */
-};
-
-static inline int
-min (int a, int b)
-{
- if (a < b) return (a);
- return (b);
-}
-
-#ifdef SOCK_DEBUG
-void
-sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
-{
- char buf[sizeof(sockun->sun_path) + 1];
-
- sockaddr_len -= UN_PATH_OFFSET;
- if (sockun->sun_family != AF_UNIX)
- printk("sockaddr_un: <BAD FAMILY: %d>\n", sockun->sun_family);
- else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf))
- printk("sockaddr_un: <BAD LENGTH: %d>\n", sockaddr_len);
- else {
- memcpy(buf, sockun->sun_path, sockaddr_len);
- buf[sockaddr_len] = '\0';
- printk("sockaddr_un: '%s'[%d]\n", buf,
- sockaddr_len + UN_PATH_OFFSET);
- }
-}
-#endif /* SOCK_DEBUG */
-
-/* don't have to do anything. */
-static int
-unix_proto_listen (struct socket *sock, int backlog)
-{
- return (0);
-}
-
-static int
-unix_proto_setsockopt(struct socket *sock, int level, int optname,
- char *optval, int optlen)
-{
- return (-EOPNOTSUPP);
-}
-
-static int
-unix_proto_getsockopt(struct socket *sock, int level, int optname,
- char *optval, int *optlen)
-{
- return (-EOPNOTSUPP);
-}
-
-static int
-unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *addr, int addr_len)
-{
- return (-EOPNOTSUPP);
-}
-
-static int
-unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags, struct sockaddr *addr, int *addr_len)
-{
- return (-EOPNOTSUPP);
-}
-
-static int
-unix_proto_shutdown (struct socket *sock, int how)
-{
- return (-EOPNOTSUPP);
-}
-
-static int
-unix_proto_send(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags)
-{
- /* this error needs to be checked. */
- if (flags != 0)
- return (-EINVAL);
- return (unix_proto_write (sock, buff, len, nonblock));
-}
-
-static int
-unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock,
- unsigned flags)
-{
- /* this error needs to be checked. */
- if (flags != 0)
- return (-EINVAL);
- return (unix_proto_read (sock, buff, len, nonblock));
-}
-
-static struct unix_proto_data *
-unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len,
- struct inode *inode)
-{
- struct unix_proto_data *upd;
-
- for (upd = unix_datas; upd <= last_unix_data; ++upd) {
- if (upd->refcnt && upd->socket &&
- upd->socket->state == SS_UNCONNECTED &&
- upd->sockaddr_un.sun_family == sockun->sun_family &&
- upd->inode == inode)
- return upd;
- }
- return NULL;
-}
-
-static struct unix_proto_data *
-unix_data_alloc(void)
-{
- struct unix_proto_data *upd;
-
- cli();
- for (upd = unix_datas; upd <= last_unix_data; ++upd) {
- if (!upd->refcnt) {
- upd->refcnt = 1;
- sti();
- upd->socket = NULL;
- upd->sockaddr_len = 0;
- upd->buf = NULL;
- upd->bp_head = upd->bp_tail = 0;
- upd->inode = NULL;
- upd->peerupd = NULL;
- return upd;
- }
- }
- sti();
- return NULL;
-}
-
-static inline void
-unix_data_ref(struct unix_proto_data *upd)
-{
- ++upd->refcnt;
- PRINTK(("unix_data_ref: refing data 0x%x (%d)\n", upd, upd->refcnt));
-}
-
-static void
-unix_data_deref(struct unix_proto_data *upd)
-{
- if (upd->refcnt == 1) {
- PRINTK(("unix_data_deref: releasing data 0x%x\n", upd));
- if (upd->buf) {
- free_page((unsigned long)upd->buf);
- upd->buf = NULL;
- upd->bp_head = upd->bp_tail = 0;
- }
- }
- --upd->refcnt;
-}
-
-/*
- * upon a create, we allocate an empty protocol data, and grab a page to
- * buffer writes
- */
-static int
-unix_proto_create(struct socket *sock, int protocol)
-{
- struct unix_proto_data *upd;
-
- PRINTK(("unix_proto_create: socket 0x%x, proto %d\n", sock, protocol));
- if (protocol != 0) {
- PRINTK(("unix_proto_create: protocol != 0\n"));
- return -EINVAL;
- }
- if (!(upd = unix_data_alloc())) {
- printk("unix_proto_create: can't allocate buffer\n");
- return -ENOMEM;
- }
- if (!(upd->buf = (char *)get_free_page(GFP_USER))) {
- printk("unix_proto_create: can't get page!\n");
- unix_data_deref(upd);
- return -ENOMEM;
- }
- upd->protocol = protocol;
- upd->socket = sock;
- UN_DATA(sock) = upd;
- PRINTK(("unix_proto_create: allocated data 0x%x\n", upd));
- return 0;
-}
-
-static int
-unix_proto_dup(struct socket *newsock, struct socket *oldsock)
-{
- struct unix_proto_data *upd = UN_DATA(oldsock);
-
- return unix_proto_create(newsock, upd->protocol);
-}
-
-static int
-unix_proto_release(struct socket *sock, struct socket *peer)
-{
- struct unix_proto_data *upd = UN_DATA(sock);
-
- PRINTK(("unix_proto_release: socket 0x%x, unix_data 0x%x\n",
- sock, upd));
- if (!upd)
- return 0;
- if (upd->socket != sock) {
- printk("unix_proto_release: socket link mismatch!\n");
- return -EINVAL;
- }
- if (upd->inode) {
- PRINTK(("unix_proto_release: releasing inode 0x%x\n",
- upd->inode));
- iput(upd->inode);
- upd->inode = NULL;
- }
- UN_DATA(sock) = NULL;
- upd->socket = NULL;
- if (upd->peerupd)
- unix_data_deref(upd->peerupd);
- unix_data_deref(upd);
- return 0;
-}
-
-/*
- * bind a name to a socket. this is where much of the work is done. we
- * allocate a fresh page for the buffer, grab the appropriate inode and
- * set things up.
- *
- * XXX what should we do if an address is already bound? here we return
- * EINVAL, but it may be necessary to re-bind. i think thats what bsd does
- * in the case of datagram sockets
- */
-static int
-unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
- int sockaddr_len)
-{
- struct unix_proto_data *upd = UN_DATA(sock);
- char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
- int i;
-
- PRINTK(("unix_proto_bind: socket 0x%x, len=%d\n", sock,
- sockaddr_len));
- if (sockaddr_len <= UN_PATH_OFFSET ||
- sockaddr_len > sizeof(struct sockaddr_un)) {
- PRINTK(("unix_proto_bind: bad length %d\n", sockaddr_len));
- return -EINVAL;
- }
- if (upd->sockaddr_len || upd->inode) {
- printk("unix_proto_bind: already bound!\n");
- return -EINVAL;
- }
- verify_area(VERIFY_WRITE,umyaddr, sockaddr_len);
- memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
- if (upd->sockaddr_un.sun_family != AF_UNIX) {
- PRINTK(("unix_proto_bind: family is %d, not AF_UNIX (%d)\n",
- upd->sockaddr_un.sun_family, AF_UNIX));
- return -EINVAL;
- }
-
- memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
- fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
- i = do_mknod(fname, S_IFSOCK | 0777, 0);
- if (i == 0)
- i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
- if (i < 0) {
- printk("unix_proto_bind: can't open socket %s\n", fname);
- return i;
- }
-
- upd->sockaddr_len = sockaddr_len; /* now its legal */
- PRINTK(("unix_proto_bind: bound socket address: "));
-#ifdef SOCK_DEBUG
- sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
-#endif
- PRINTK(("to inode 0x%x\n", upd->inode));
- return 0;
-}
-
-/*
- * perform a connection. we can only connect to unix sockets (i can't for
- * the life of me find an application where that wouldn't be the case!)
- */
-static int
-unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags)
-{
- int i;
- struct unix_proto_data *serv_upd;
- struct sockaddr_un sockun;
- char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
- struct inode *inode;
-
- PRINTK(("unix_proto_connect: socket 0x%x, servlen=%d\n", sock,
- sockaddr_len));
-
- if (sockaddr_len <= UN_PATH_OFFSET ||
- sockaddr_len > sizeof(struct sockaddr_un)) {
- PRINTK(("unix_proto_connect: bad length %d\n", sockaddr_len));
- return -EINVAL;
- }
-
- if (sock->state == SS_CONNECTING)
- return (-EINPROGRESS);
-
- if (sock->state == SS_CONNECTED)
- return (-EISCONN);
-
- verify_area(VERIFY_WRITE,uservaddr, sockaddr_len);
- memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
- if (sockun.sun_family != AF_UNIX) {
- PRINTK(("unix_proto_connect: family is %d, not AF_UNIX (%d)\n",
- sockun.sun_family, AF_UNIX));
- return -EINVAL;
- }
-
- /*
- * try to open the name in the filesystem - this is how we
- * identify ourselves and our server. Note that we don't
- * hold onto the inode that long, just enough to find our
- * server. When we're connected, we mooch off the server.
- */
- memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
- fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
- i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
- if (i < 0) {
- PRINTK(("unix_proto_connect: can't open socket %s\n", fname));
- return i;
- }
- serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
- iput(inode);
- if (!serv_upd) {
- PRINTK(("unix_proto_connect: can't locate peer %s at inode 0x%x\n",
- fname, inode));
- return -EINVAL;
- }
- if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
- PRINTK(("unix_proto_connect: can't await connection\n"));
- return i;
- }
- unix_data_ref(UN_DATA(sock->conn));
- UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
- return 0;
-}
-
-/*
- * to do a socketpair, we just connect the two datas, easy! since we
- * always wait on the socket inode, they're no contention for a wait area,
- * and deadlock prevention in the case of a process writing to itself is,
- * ignored, in true unix fashion!
- */
-static int
-unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
-{
- struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
-
- unix_data_ref(upd1);
- unix_data_ref(upd2);
- upd1->peerupd = upd2;
- upd2->peerupd = upd1;
- return 0;
-}
-
-/*
- * on accept, we ref the peer's data for safe writes
- */
-static int
-unix_proto_accept(struct socket *sock, struct socket *newsock, int flags)
-{
- struct socket *clientsock;
-
- PRINTK(("unix_proto_accept: socket 0x%x accepted via socket 0x%x\n",
- sock, newsock));
-
- /*
- * if there aren't any sockets awaiting connection, then wait for
- * one, unless nonblocking
- */
- while (!(clientsock = sock->iconn)) {
- if (flags & O_NONBLOCK)
- return -EAGAIN;
- interruptible_sleep_on(sock->wait);
- if (current->signal & ~current->blocked) {
- PRINTK(("sys_accept: sleep was interrupted\n"));
- return -ERESTARTSYS;
- }
- }
-
- /*
- * great. finish the connection relative to server and client,
- * wake up the client and return the new fd to the server
- */
- sock->iconn = clientsock->next;
- clientsock->next = NULL;
- newsock->conn = clientsock;
- clientsock->conn = newsock;
- clientsock->state = SS_CONNECTED;
- newsock->state = SS_CONNECTED;
- wake_up(clientsock->wait);
- unix_data_ref (UN_DATA(newsock->conn));
- UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn);
- UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
- UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
- UN_DATA(newsock->conn)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
- UN_DATA(newsock->conn)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
- return 0;
-}
-
-/*
- * gets the current name or the name of the connected socket.
- */
-static int
-unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
- int *usockaddr_len, int peer)
-{
- struct unix_proto_data *upd;
- int len;
-
- PRINTK(("unix_proto_getname: socket 0x%x for %s\n", sock,
- peer ? "peer" : "self"));
- if (peer) {
- if (sock->state != SS_CONNECTED) {
- PRINTK(("unix_proto_getname: socket not connected\n"));
- return -EINVAL;
- }
- upd = UN_DATA(sock->conn);
- }
- else
- upd = UN_DATA(sock);
- verify_area(VERIFY_WRITE,usockaddr_len, sizeof(*usockaddr_len));
- if ((len = get_fs_long(usockaddr_len)) <= 0)
- return -EINVAL;
- if (len > upd->sockaddr_len)
- len = upd->sockaddr_len;
- if (len) {
- verify_area(VERIFY_WRITE,usockaddr, len);
- memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
- }
- put_fs_long(len, usockaddr_len);
- return 0;
-}
-
-/*
- * we read from our own buf.
- */
-static int
-unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
-{
- struct unix_proto_data *upd;
- int todo, avail;
-
- if ((todo = size) <= 0)
- return 0;
- upd = UN_DATA(sock);
- while (!(avail = UN_BUF_AVAIL(upd))) {
- if (sock->state != SS_CONNECTED) {
- PRINTK(("unix_proto_read: socket not connected\n"));
- return (sock->state == SS_DISCONNECTING) ? 0 : -EINVAL;
- }
- PRINTK(("unix_proto_read: no data available...\n"));
- if (nonblock)
- return -EAGAIN;
- interruptible_sleep_on(sock->wait);
- if (current->signal & ~current->blocked) {
- PRINTK(("unix_proto_read: interrupted\n"));
- return -ERESTARTSYS;
- }
- }
-
- /*
- * copy from the read buffer into the user's buffer, watching for
- * wraparound. then we wake up the writer
- */
- do {
- int part, cando;
-
- if (avail <= 0) {
- PRINTK(("unix_proto_read: AVAIL IS NEGATIVE!!!\n"));
- send_sig(SIGKILL,current,1);
- return -EINTR;
- }
-
- if ((cando = todo) > avail)
- cando = avail;
- if (cando > (part = BUF_SIZE - upd->bp_tail))
- cando = part;
- PRINTK(("unix_proto_read: avail=%d, todo=%d, cando=%d\n",
- avail, todo, cando));
- verify_area(VERIFY_WRITE,ubuf, cando);
- memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
- upd->bp_tail = (upd->bp_tail + cando) & (BUF_SIZE-1);
- ubuf += cando;
- todo -= cando;
- if (sock->state == SS_CONNECTED)
- wake_up(sock->conn->wait);
- avail = UN_BUF_AVAIL(upd);
- } while (todo && avail);
- return size - todo;
-}
-
-/*
- * we write to our peer's buf. when we connected we ref'd this peer so we
- * are safe that the buffer remains, even after the peer has disconnected,
- * which we check other ways.
- */
-static int
-unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
-{
- struct unix_proto_data *pupd;
- int todo, space;
-
- if ((todo = size) <= 0)
- return 0;
- if (sock->state != SS_CONNECTED) {
- PRINTK(("unix_proto_write: socket not connected\n"));
- if (sock->state == SS_DISCONNECTING) {
- send_sig(SIGPIPE,current,1);
- return -EINTR;
- }
- return -EINVAL;
- }
- pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */
-
- while (!(space = UN_BUF_SPACE(pupd))) {
- PRINTK(("unix_proto_write: no space left...\n"));
- if (nonblock)
- return -EAGAIN;
- interruptible_sleep_on(sock->wait);
- if (current->signal & ~current->blocked) {
- PRINTK(("unix_proto_write: interrupted\n"));
- return -ERESTARTSYS;
- }
- if (sock->state == SS_DISCONNECTING) {
- PRINTK(("unix_proto_write: disconnected (SIGPIPE)\n"));
- send_sig(SIGPIPE,current,1);
- return -EINTR;
- }
- }
-
- /*
- * copy from the user's buffer to the write buffer, watching for
- * wraparound. then we wake up the reader
- */
- do {
- int part, cando;
-
- if (space <= 0) {
- PRINTK(("unix_proto_write: SPACE IS NEGATIVE!!!\n"));
- send_sig(SIGKILL,current,1);
- return -EINTR;
- }
-
- /*
- * we may become disconnected inside this loop, so watch
- * for it (peerupd is safe until we close)
- */
- if (sock->state == SS_DISCONNECTING) {
- send_sig(SIGPIPE,current,1);
- return -EINTR;
- }
- if ((cando = todo) > space)
- cando = space;
- if (cando > (part = BUF_SIZE - pupd->bp_head))
- cando = part;
- PRINTK(("unix_proto_write: space=%d, todo=%d, cando=%d\n",
- space, todo, cando));
- verify_area(VERIFY_WRITE,ubuf, cando);
- memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
- pupd->bp_head = (pupd->bp_head + cando) & (BUF_SIZE-1);
- ubuf += cando;
- todo -= cando;
- if (sock->state == SS_CONNECTED)
- wake_up(sock->conn->wait);
- space = UN_BUF_SPACE(pupd);
- } while (todo && space);
- return size - todo;
-}
-
-static int
-unix_proto_select(struct socket *sock, int sel_type, select_table * wait)
-{
- struct unix_proto_data *upd, *peerupd;
-
- /*
- * handle server sockets specially
- */
- if (sock->flags & SO_ACCEPTCON) {
- if (sel_type == SEL_IN) {
- PRINTK(("sock_select: %sconnections pending\n",
- sock->iconn ? "" : "no "));
- if (sock->iconn)
- return 1;
- select_wait(sock->wait, wait);
- return sock->iconn ? 1 : 0;
- }
- PRINTK(("sock_select: nothing else for server socket\n"));
- select_wait(sock->wait, wait);
- return 0;
- }
-
- if (sel_type == SEL_IN) {
- upd = UN_DATA(sock);
- PRINTK(("unix_proto_select: there is%s data available\n",
- UN_BUF_AVAIL(upd) ? "" : " no"));
- if (UN_BUF_AVAIL(upd)) /* even if disconnected */
- return 1;
- else if (sock->state != SS_CONNECTED) {
- PRINTK(("unix_proto_select: socket not connected (read EOF)\n"));
- return 1;
- }
- select_wait(sock->wait,wait);
- return 0;
- }
- if (sel_type == SEL_OUT) {
- if (sock->state != SS_CONNECTED) {
- PRINTK(("unix_proto_select: socket not connected (write EOF)\n"));
- return 1;
- }
- peerupd = UN_DATA(sock->conn);
- PRINTK(("unix_proto_select: there is%s space available\n",
- UN_BUF_SPACE(peerupd) ? "" : " no"));
- if (UN_BUF_SPACE(peerupd) > 0)
- return 1;
- select_wait(sock->wait,wait);
- return 0;
- }
- /* SEL_EX */
- PRINTK(("unix_proto_select: there are no exceptions here?!\n"));
- return 0;
-}
-
-static int
-unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- struct unix_proto_data *upd, *peerupd;
-
- upd = UN_DATA(sock);
- peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
-
- switch (cmd) {
-
- case TIOCINQ:
- if (sock->flags & SO_ACCEPTCON)
- return -EINVAL;
- verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
- if (UN_BUF_AVAIL(upd) || peerupd)
- put_fs_long(UN_BUF_AVAIL(upd), (unsigned long *)arg);
- else
- put_fs_long(1, (unsigned long *)arg); /* read EOF */
- break;
-
- case TIOCOUTQ:
- if (sock->flags & SO_ACCEPTCON)
- return -EINVAL;
- verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
- if (peerupd)
- put_fs_long(UN_BUF_SPACE(peerupd),
- (unsigned long *)arg);
- else
- put_fs_long(0, (unsigned long *)arg);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int
-unix_proto_init(void)
-{
- struct unix_proto_data *upd;
-
- PRINTK(("unix_proto_init: initializing...\n"));
- for (upd = unix_datas; upd <= last_unix_data; ++upd)
- upd->refcnt = 0;
- return 0;
-}
diff --git a/net/tcp/Makefile b/net/unix/Makefile
index faf3fdf..3a2d10c 100644
--- a/net/tcp/Makefile
+++ b/net/unix/Makefile
@@ -1,5 +1,5 @@
#
-# Makefile for the linux networking.
+# Makefile for the UNIX Protocol Family.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
@@ -16,11 +16,10 @@
$(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
+OBJS = sock.o proc.o
-tcpip.a: $(OBJS)
- $(AR) rcs tcpip.a $(OBJS)
+unix.o: $(OBJS)
+ $(LD) -r -o unix.o $(OBJS)
subdirs: dummy
for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
diff --git a/net/unix/proc.c b/net/unix/proc.c
new file mode 100644
index 0000000..ac6644c
--- /dev/null
+++ b/net/unix/proc.c
@@ -0,0 +1,75 @@
+/*
+ * UNIX An implementation of the AF_UNIX network domain for the
+ * LINUX operating system. UNIX is implemented using the
+ * BSD Socket interface as the means of communication with
+ * the user level.
+ *
+ * The functions in this file provide an interface between
+ * the PROC file system and the "unix" family of networking
+ * protocols. It is mainly used for debugging and statistics.
+ *
+ * Version: @(#)proc.c 1.0.4 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
+ * Fred Baumgarten, <dc6iq@insu1.etec.uni-kalrsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/autoconf.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/ddi.h>
+#include <linux/un.h>
+#include <sys/param.h>
+#include "unix.h"
+
+
+/* Called from PROCfs. */
+int unix_get_info(char *buffer)
+{
+ char *pos;
+ int i;
+
+ pos = buffer;
+ pos += sprintf(pos, "Num RefCount Protocol Flags Type St Path\n");
+
+ for(i = 0; i < NSOCKETS; i++) {
+ if (unix_datas[i].refcnt) {
+ pos += sprintf(pos, "%2d: %08X %08X %08X %04X %02X", i,
+ unix_datas[i].refcnt,
+ unix_datas[i].protocol,
+ unix_datas[i].socket->flags,
+ unix_datas[i].socket->type,
+ unix_datas[i].socket->state
+ );
+
+ /* If socket is bound to a filename, we'll print it. */
+ if(unix_datas[i].sockaddr_len>0) {
+ pos += sprintf(pos, " %s\n",
+ unix_datas[i].sockaddr_un.sun_path);
+ } else { /* just add a newline */
+ *pos='\n';
+ pos++;
+ *pos='\0';
+ }
+
+ /*
+ * Check wether buffer _may_ overflow in the next loop.
+ * Since sockets may have very very long paths, we make
+ * MAXPATHLEN+100 the minimum space left for a new line.
+ */
+ if (pos > buffer+PAGE_SIZE-80-MAXPATHLEN) {
+ printk("UNIX: netinfo: oops, too many sockets.\n");
+ return(pos - buffer);
+ }
+ }
+ }
+ return(pos - buffer);
+}
diff --git a/net/unix/sock.c b/net/unix/sock.c
new file mode 100644
index 0000000..c8e13b5
--- /dev/null
+++ b/net/unix/sock.c
@@ -0,0 +1,860 @@
+/*
+ * UNIX An implementation of the AF_UNIX network domain for the
+ * LINUX operating system. UNIX is implemented using the
+ * BSD Socket interface as the means of communication with
+ * the user level.
+ *
+ * Version: @(#)sock.c 1.0.5 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or(at your option) any later version.
+ */
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/un.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fs.h>
+#include <linux/ddi.h>
+#include <stdarg.h>
+#include "unix.h"
+
+
+struct unix_proto_data unix_datas[NSOCKETS];
+static int unix_debug = 0;
+
+
+static int unix_proto_create(struct socket *sock, int protocol);
+static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);
+static int unix_proto_release(struct socket *sock, struct socket *peer);
+static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags);
+static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2);
+static int unix_proto_accept(struct socket *sock, struct socket *newsock,
+ int flags);
+static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+ int *usockaddr_len, int peer);
+static int unix_proto_read(struct socket *sock, char *ubuf, int size,
+ int nonblock);
+static int unix_proto_write(struct socket *sock, char *ubuf, int size,
+ int nonblock);
+static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait);
+static int unix_proto_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+static int unix_proto_listen(struct socket *sock, int backlog);
+static int unix_proto_send(struct socket *sock, void *buff, int len,
+ int nonblock, unsigned flags);
+static int unix_proto_recv(struct socket *sock, void *buff, int len,
+ int nonblock, unsigned flags);
+static int unix_proto_sendto(struct socket *sock, void *buff, int len,
+ int nonblock, unsigned flags,
+ struct sockaddr *addr, int addr_len);
+static int unix_proto_recvfrom(struct socket *sock, void *buff, int len,
+ int nonblock, unsigned flags,
+ struct sockaddr *addr, int *addr_len);
+
+static int unix_proto_shutdown(struct socket *sock, int how);
+
+static int unix_proto_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+static int unix_proto_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+
+
+static void
+dprintf(int level, char *fmt, ...)
+{
+ va_list args;
+ char *buff;
+ extern int vsprintf(char * buf, const char * fmt, va_list args);
+
+ if (level != unix_debug) return;
+
+ buff = kmalloc(256, GFP_KERNEL);
+ if (buff != NULL) {
+ va_start(args, fmt);
+ vsprintf(buff, fmt, args);
+ va_end(args);
+ printk(buff);
+ kfree(buff);
+ }
+}
+
+
+static inline int
+min(int a, int b)
+{
+ if (a < b) return(a);
+ return(b);
+}
+
+
+void
+sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
+{
+ char buf[sizeof(sockun->sun_path) + 1];
+
+ if (unix_debug == 0) return;
+
+ sockaddr_len -= UN_PATH_OFFSET;
+ if (sockun->sun_family != AF_UNIX)
+ printk("UNIX: Badd addr family %d>\n", sockun->sun_family);
+ else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf))
+ printk("UNIX: Bad addr len %d>\n", sockaddr_len);
+ else {
+ memcpy(buf, sockun->sun_path, sockaddr_len);
+ buf[sockaddr_len] = '\0';
+ printk("\"%s\"[%d]\n", buf, sockaddr_len + UN_PATH_OFFSET);
+ }
+}
+
+
+/* don't have to do anything. */
+static int
+unix_proto_listen(struct socket *sock, int backlog)
+{
+ return(0);
+}
+
+
+static int
+unix_proto_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ return(-EOPNOTSUPP);
+}
+
+
+static int
+unix_proto_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ return(-EOPNOTSUPP);
+}
+
+static int
+unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags, struct sockaddr *addr, int addr_len)
+{
+ return(-EOPNOTSUPP);
+}
+
+static int
+unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags, struct sockaddr *addr, int *addr_len)
+{
+ return(-EOPNOTSUPP);
+}
+
+
+static int
+unix_proto_shutdown(struct socket *sock, int how)
+{
+ return(-EOPNOTSUPP);
+}
+
+
+/* This error needs to be checked. */
+static int
+unix_proto_send(struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags)
+{
+ if (flags != 0) return(-EINVAL);
+ return(unix_proto_write(sock, buff, len, nonblock));
+}
+
+
+/* This error needs to be checked. */
+static int
+unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock,
+ unsigned flags)
+{
+ if (flags != 0) return(-EINVAL);
+ return(unix_proto_read(sock, buff, len, nonblock));
+}
+
+
+static struct unix_proto_data *
+unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len,
+ struct inode *inode)
+{
+ struct unix_proto_data *upd;
+
+ for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ if (upd->refcnt && upd->socket &&
+ upd->socket->state == SS_UNCONNECTED &&
+ upd->sockaddr_un.sun_family == sockun->sun_family &&
+ upd->inode == inode) return(upd);
+ }
+ return(NULL);
+}
+
+
+static struct unix_proto_data *
+unix_data_alloc(void)
+{
+ struct unix_proto_data *upd;
+
+ cli();
+ for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ if (!upd->refcnt) {
+ upd->refcnt = 1;
+ sti();
+ upd->socket = NULL;
+ upd->sockaddr_len = 0;
+ upd->sockaddr_un.sun_family = 0;
+ upd->buf = NULL;
+ upd->bp_head = upd->bp_tail = 0;
+ upd->inode = NULL;
+ upd->peerupd = NULL;
+ return(upd);
+ }
+ }
+ sti();
+ return(NULL);
+}
+
+
+static inline void
+unix_data_ref(struct unix_proto_data *upd)
+{
+ ++upd->refcnt;
+ dprintf(1, "UNIX: data_ref: refing data 0x%x(%d)\n", upd, upd->refcnt);
+}
+
+
+static void
+unix_data_deref(struct unix_proto_data *upd)
+{
+ if (upd->refcnt == 1) {
+ dprintf(1, "UNIX: data_deref: releasing data 0x%x\n", upd);
+ if (upd->buf) {
+ free_page((unsigned long)upd->buf);
+ upd->buf = NULL;
+ upd->bp_head = upd->bp_tail = 0;
+ }
+ }
+ --upd->refcnt;
+}
+
+
+/*
+ * Upon a create, we allocate an empty protocol data,
+ * and grab a page to buffer writes.
+ */
+static int
+unix_proto_create(struct socket *sock, int protocol)
+{
+ struct unix_proto_data *upd;
+
+ dprintf(1, "UNIX: create: socket 0x%x, proto %d\n", sock, protocol);
+ if (protocol != 0) {
+ dprintf(1, "UNIX: create: protocol != 0\n");
+ return(-EINVAL);
+ }
+ if (!(upd = unix_data_alloc())) {
+ printk("UNIX: create: can't allocate buffer\n");
+ return(-ENOMEM);
+ }
+ if (!(upd->buf =(char *)get_free_page(GFP_USER))) {
+ printk("UNIX: create: can't get page!\n");
+ unix_data_deref(upd);
+ return(-ENOMEM);
+ }
+ upd->protocol = protocol;
+ upd->socket = sock;
+ UN_DATA(sock) = upd;
+ dprintf(1, "UNIX: create: allocated data 0x%x\n", upd);
+ return(0);
+}
+
+
+static int
+unix_proto_dup(struct socket *newsock, struct socket *oldsock)
+{
+ struct unix_proto_data *upd = UN_DATA(oldsock);
+
+ return(unix_proto_create(newsock, upd->protocol));
+}
+
+
+static int
+unix_proto_release(struct socket *sock, struct socket *peer)
+{
+ struct unix_proto_data *upd = UN_DATA(sock);
+
+ dprintf(1, "UNIX: release: socket 0x%x, unix_data 0x%x\n", sock, upd);
+ if (!upd) return(0);
+ if (upd->socket != sock) {
+ printk("UNIX: release: socket link mismatch!\n");
+ return(-EINVAL);
+ }
+ if (upd->inode) {
+ dprintf(1, "UNIX: release: releasing inode 0x%x\n", upd->inode);
+ iput(upd->inode);
+ upd->inode = NULL;
+ }
+ UN_DATA(sock) = NULL;
+ upd->socket = NULL;
+ if (upd->peerupd) unix_data_deref(upd->peerupd);
+ unix_data_deref(upd);
+ return(0);
+}
+
+
+/*
+ * Bind a name to a socket.
+ * This is where much of the work is done: we allocate a fresh page for
+ * the buffer, grab the appropriate inode and set things up.
+ *
+ * FIXME: what should we do if an address is already bound?
+ * Here we return EINVAL, but it may be necessary to re-bind.
+ * I think thats what BSD does in the case of datagram sockets...
+ */
+static int
+unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len)
+{
+ char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
+ struct unix_proto_data *upd = UN_DATA(sock);
+ unsigned long old_fs;
+ int i;
+
+ dprintf(1, "UNIX: bind: socket 0x%x, len=%d\n", sock, sockaddr_len);
+ if (sockaddr_len <= UN_PATH_OFFSET ||
+ sockaddr_len > sizeof(struct sockaddr_un)) {
+ dprintf(1, "UNIX: bind: bad length %d\n", sockaddr_len);
+ return(-EINVAL);
+ }
+ if (upd->sockaddr_len || upd->inode) {
+ printk("UNIX: bind: already bound!\n");
+ return(-EINVAL);
+ }
+ verify_area(VERIFY_WRITE, umyaddr, sockaddr_len);
+ memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
+ upd->sockaddr_un.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
+ if (upd->sockaddr_un.sun_family != AF_UNIX) {
+ dprintf(1, "UNIX: bind: family is %d, not AF_UNIX(%d)\n",
+ upd->sockaddr_un.sun_family, AF_UNIX);
+ return(-EINVAL);
+ }
+
+ memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
+ fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
+ old_fs = get_fs();
+ set_fs(get_ds());
+ i = do_mknod(fname, S_IFSOCK | 0777, 0);
+ if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
+ set_fs(old_fs);
+ if (i < 0) {
+ printk("UNIX: bind: can't open socket %s\n", fname);
+ return(i);
+ }
+ upd->sockaddr_len = sockaddr_len; /* now its legal */
+
+ dprintf(1, "UNIX: bind: bound socket address: ");
+ sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
+ dprintf(1, "to inode 0x%x\n", upd->inode);
+ return(0);
+}
+
+
+/*
+ * Perform a connection. we can only connect to unix sockets
+ * (I can't for the life of me find an application where that
+ * wouldn't be the case!)
+ */
+static int
+unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags)
+{
+ char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
+ struct sockaddr_un sockun;
+ struct unix_proto_data *serv_upd;
+ struct inode *inode;
+ unsigned long old_fs;
+ int i;
+
+ dprintf(1, "UNIX: connect: socket 0x%x, servlen=%d\n", sock, sockaddr_len);
+
+ if (sockaddr_len <= UN_PATH_OFFSET ||
+ sockaddr_len > sizeof(struct sockaddr_un)) {
+ dprintf(1, "UNIX: connect: bad length %d\n", sockaddr_len);
+ return(-EINVAL);
+ }
+ if (sock->state == SS_CONNECTING) return(-EINPROGRESS);
+ if (sock->state == SS_CONNECTED) return(-EISCONN);
+
+ verify_area(VERIFY_WRITE, uservaddr, sockaddr_len);
+ memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
+ sockun.sun_path[sockaddr_len-UN_PATH_OFFSET] = '\0';
+ if (sockun.sun_family != AF_UNIX) {
+ dprintf(1, "UNIX: connect: family is %d, not AF_UNIX(%d)\n",
+ sockun.sun_family, AF_UNIX);
+ return(-EINVAL);
+ }
+
+ /*
+ * Try to open the name in the filesystem - this is how we
+ * identify ourselves and our server. Note that we don't
+ * hold onto the inode that long, just enough to find our
+ * server. When we're connected, we mooch off the server.
+ */
+ memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
+ fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
+ old_fs = get_fs();
+ set_fs(get_ds());
+ i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
+ set_fs(old_fs);
+ if (i < 0) {
+ dprintf(1, "UNIX: connect: can't open socket %s\n", fname);
+ return(i);
+ }
+ serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
+ iput(inode);
+ if (!serv_upd) {
+ dprintf(1, "UNIX: connect: can't locate peer %s at inode 0x%x\n",
+ fname, inode);
+ return(-EINVAL);
+ }
+ if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
+ dprintf(1, "UNIX: connect: can't await connection\n");
+ return(i);
+ }
+ if (sock->conn) {
+ unix_data_ref(UN_DATA(sock->conn));
+ UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
+ }
+ return(0);
+}
+
+
+/*
+ * To do a socketpair, we just connect the two datas, easy!
+ * Since we always wait on the socket inode, they're no contention
+ * for a wait area, and deadlock prevention in the case of a process
+ * writing to itself is, ignored, in true unix fashion!
+ */
+static int
+unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
+
+ unix_data_ref(upd1);
+ unix_data_ref(upd2);
+ upd1->peerupd = upd2;
+ upd2->peerupd = upd1;
+ return(0);
+}
+
+
+/* On accept, we ref the peer's data for safe writes. */
+static int
+unix_proto_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct socket *clientsock;
+
+ dprintf(1, "UNIX: accept: socket 0x%x accepted via socket 0x%x\n",
+ sock, newsock);
+
+ /*
+ * If there aren't any sockets awaiting connection,
+ * then wait for one, unless nonblocking.
+ */
+ while(!(clientsock = sock->iconn)) {
+ if (flags & O_NONBLOCK) return(-EAGAIN);
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ dprintf(1, "UNIX: accept: sleep was interrupted\n");
+ return(-ERESTARTSYS);
+ }
+ }
+
+ /*
+ * Great. Finish the connection relative to server and client,
+ * wake up the client and return the new fd to the server.
+ */
+ sock->iconn = clientsock->next;
+ clientsock->next = NULL;
+ newsock->conn = clientsock;
+ clientsock->conn = newsock;
+ clientsock->state = SS_CONNECTED;
+ newsock->state = SS_CONNECTED;
+ unix_data_ref(UN_DATA(clientsock));
+ UN_DATA(newsock)->peerupd = UN_DATA(clientsock);
+ UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
+ UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
+ wake_up(clientsock->wait);
+ return(0);
+}
+
+
+/* Gets the current name or the name of the connected socket. */
+static int
+unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+ int *usockaddr_len, int peer)
+{
+ struct unix_proto_data *upd;
+ int len;
+
+ dprintf(1, "UNIX: getname: socket 0x%x for %s\n", sock, peer?"peer":"self");
+ if (peer) {
+ if (sock->state != SS_CONNECTED) {
+ dprintf(1, "UNIX: getname: socket not connected\n");
+ return(-EINVAL);
+ }
+ upd = UN_DATA(sock->conn);
+ } else
+ upd = UN_DATA(sock);
+
+ verify_area(VERIFY_WRITE, usockaddr_len, sizeof(*usockaddr_len));
+ if ((len = get_fs_long(usockaddr_len)) <= 0) return(-EINVAL);
+ if (len > upd->sockaddr_len) len = upd->sockaddr_len;
+ if (len) {
+ verify_area(VERIFY_WRITE, usockaddr, len);
+ memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
+ }
+ put_fs_long(len, usockaddr_len);
+ return(0);
+}
+
+
+/* We read from our own buf. */
+static int
+unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
+{
+ struct unix_proto_data *upd;
+ int todo, avail;
+
+ if ((todo = size) <= 0) return(0);
+ upd = UN_DATA(sock);
+ while(!(avail = UN_BUF_AVAIL(upd))) {
+ if (sock->state != SS_CONNECTED) {
+ dprintf(1, "UNIX: read: socket not connected\n");
+ return((sock->state == SS_DISCONNECTING) ? 0 : -EINVAL);
+ }
+ dprintf(1, "UNIX: read: no data available...\n");
+ if (nonblock) return(-EAGAIN);
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ dprintf(1, "UNIX: read: interrupted\n");
+ return(-ERESTARTSYS);
+ }
+ if (sock->state == SS_DISCONNECTING) {
+ dprintf(1, "UNIX: read: disconnected\n");
+ return(0);
+ }
+ }
+
+ /*
+ * Copy from the read buffer into the user's buffer,
+ * watching for wraparound. Then we wake up the writer.
+ */
+ do {
+ int part, cando;
+
+ if (avail <= 0) {
+ printk("UNIX: read: AVAIL IS NEGATIVE!!!\n");
+ send_sig(SIGKILL, current, 1);
+ return(-EINTR);
+ }
+
+ if ((cando = todo) > avail) cando = avail;
+ if (cando >(part = BUF_SIZE - upd->bp_tail)) cando = part;
+ dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n",
+ avail, todo, cando);
+ verify_area(VERIFY_WRITE, ubuf, cando);
+ memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
+ upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1);
+ ubuf += cando;
+ todo -= cando;
+ if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait);
+ avail = UN_BUF_AVAIL(upd);
+ } while(todo && avail);
+ return(size - todo);
+}
+
+
+/*
+ * We write to our peer's buf. When we connected we ref'd this
+ * peer so we are safe that the buffer remains, even after the
+ * peer has disconnected, which we check other ways.
+ */
+static int
+unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
+{
+ struct unix_proto_data *pupd;
+ int todo, space;
+
+ if ((todo = size) <= 0) return(0);
+ if (sock->state != SS_CONNECTED) {
+ dprintf(1, "UNIX: write: socket not connected\n");
+ if (sock->state == SS_DISCONNECTING) {
+ send_sig(SIGPIPE, current, 1);
+ return(-EINTR);
+ }
+ return(-EINVAL);
+ }
+ pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */
+
+ while(!(space = UN_BUF_SPACE(pupd))) {
+ dprintf(1, "UNIX: write: no space left...\n");
+ if (nonblock) return(-EAGAIN);
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ dprintf(1, "UNIX: write: interrupted\n");
+ return(-ERESTARTSYS);
+ }
+ if (sock->state == SS_DISCONNECTING) {
+ dprintf(1, "UNIX: write: disconnected(SIGPIPE)\n");
+ send_sig(SIGPIPE, current, 1);
+ return(-EINTR);
+ }
+ }
+
+ /*
+ * Copy from the user's buffer to the write buffer,
+ * watching for wraparound. Then we wake up the reader.
+ */
+ do {
+ int part, cando;
+
+ if (space <= 0) {
+ printk("UNIX: write: SPACE IS NEGATIVE!!!\n");
+ send_sig(SIGKILL, current, 1);
+ return(-EINTR);
+ }
+
+ /*
+ * We may become disconnected inside this loop, so watch
+ * for it (peerupd is safe until we close).
+ */
+ if (sock->state == SS_DISCONNECTING) {
+ send_sig(SIGPIPE, current, 1);
+ return(-EINTR);
+ }
+ if ((cando = todo) > space) cando = space;
+ if (cando >(part = BUF_SIZE - pupd->bp_head)) cando = part;
+ dprintf(1, "UNIX: write: space=%d, todo=%d, cando=%d\n",
+ space, todo, cando);
+ verify_area(VERIFY_WRITE, ubuf, cando);
+ memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
+ pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1);
+ ubuf += cando;
+ todo -= cando;
+ if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait);
+ space = UN_BUF_SPACE(pupd);
+ } while(todo && space);
+ return(size - todo);
+}
+
+
+static int
+unix_proto_select(struct socket *sock, int sel_type, select_table * wait)
+{
+ struct unix_proto_data *upd, *peerupd;
+
+ /* Handle server sockets specially. */
+ if (sock->flags & SO_ACCEPTCON) {
+ if (sel_type == SEL_IN) {
+ dprintf(1, "UNIX: select: %sconnections pending\n",
+ sock->iconn ? "" : "no ");
+ if (sock->iconn) return(1);
+ select_wait(sock->wait, wait);
+ return(sock->iconn ? 1 : 0);
+ }
+ dprintf(1, "UNIX: select: nothing else for server socket\n");
+ select_wait(sock->wait, wait);
+ return(0);
+ }
+
+ if (sel_type == SEL_IN) {
+ upd = UN_DATA(sock);
+ dprintf(1, "UNIX: select: there is%s data available\n",
+ UN_BUF_AVAIL(upd) ? "" : " no");
+ if (UN_BUF_AVAIL(upd)) /* even if disconnected */
+ return(1);
+ else if (sock->state != SS_CONNECTED) {
+ dprintf(1, "UNIX: select: socket not connected(read EOF)\n");
+ return(1);
+ }
+ select_wait(sock->wait,wait);
+ return(0);
+ }
+ if (sel_type == SEL_OUT) {
+ if (sock->state != SS_CONNECTED) {
+ dprintf(1, "UNIX: select: socket not connected(write EOF)\n");
+ return(1);
+ }
+ peerupd = UN_DATA(sock->conn);
+ dprintf(1, "UNIX: select: there is%s space available\n",
+ UN_BUF_SPACE(peerupd) ? "" : " no");
+ if (UN_BUF_SPACE(peerupd) > 0) return(1);
+ select_wait(sock->wait,wait);
+ return(0);
+ }
+
+ /* SEL_EX */
+ dprintf(1, "UNIX: select: there are no exceptions here?!\n");
+ return(0);
+}
+
+
+static int
+unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct unix_proto_data *upd, *peerupd;
+
+ upd = UN_DATA(sock);
+ peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
+
+ switch(cmd) {
+ case TIOCINQ:
+ if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+ if (UN_BUF_AVAIL(upd) || peerupd)
+ put_fs_long(UN_BUF_AVAIL(upd),(unsigned long *)arg);
+ else
+ put_fs_long(1,(unsigned long *)arg); /* read EOF */
+ break;
+ case TIOCOUTQ:
+ if (sock->flags & SO_ACCEPTCON) return(-EINVAL);
+ verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
+ if (peerupd) put_fs_long(UN_BUF_SPACE(peerupd),
+ (unsigned long *)arg);
+ else
+ put_fs_long(0,(unsigned long *)arg);
+ break;
+ default:
+ return(-EINVAL);
+ }
+ return(0);
+}
+
+
+static int
+unix_open(struct inode * inode, struct file * file)
+{
+ int minor;
+
+ dprintf(1, "UNIX: open\n");
+ minor = MINOR(inode->i_rdev);
+ if (minor != 0) return(-ENODEV);
+
+ return(0);
+}
+
+
+static void
+unix_close(struct inode * inode, struct file * file)
+{
+ dprintf(1, "UNIX: close\n");
+}
+
+
+static int
+unix_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int minor, ret;
+
+ dprintf(1, "UNIX: ioctl(0x%X, 0x%X)\n", cmd, arg);
+ minor = MINOR(inode->i_rdev);
+ if (minor != 0) return(-ENODEV);
+
+ ret = -EINVAL;
+ switch(cmd) {
+ case DDIOCSDBG:
+ verify_area(VERIFY_WRITE,(void *)arg, sizeof(int));
+ unix_debug = get_fs_long((void *)arg);
+ if (unix_debug != 0 && unix_debug != 1) {
+ unix_debug = 0;
+ return(-EINVAL);
+ }
+ return(0);
+ case SIOCSIFLINK:
+ printk("UNIX: cannot link streams!\n");
+ break;
+ default:
+ break;
+ }
+ return(ret);
+}
+
+
+static struct file_operations unix_fops = {
+ NULL, /* LSEEK */
+ NULL, /* READ */
+ NULL, /* WRITE */
+ NULL, /* READDIR */
+ NULL, /* SELECT */
+ unix_ioctl, /* IOCTL */
+ NULL, /* MMAP */
+ unix_open, /* OPEN */
+ unix_close /* CLOSE */
+};
+
+
+static struct proto_ops unix_proto_ops = {
+ AF_UNIX,
+ unix_proto_create,
+ unix_proto_dup,
+ unix_proto_release,
+ unix_proto_bind,
+ unix_proto_connect,
+ unix_proto_socketpair,
+ unix_proto_accept,
+ unix_proto_getname,
+ unix_proto_read,
+ unix_proto_write,
+ unix_proto_select,
+ unix_proto_ioctl,
+ unix_proto_listen,
+ unix_proto_send,
+ unix_proto_recv,
+ unix_proto_sendto,
+ unix_proto_recvfrom,
+ unix_proto_shutdown,
+ unix_proto_setsockopt,
+ unix_proto_getsockopt,
+ NULL /* unix_proto_fcntl */
+};
+
+
+void
+unix_proto_init(struct ddi_proto *pro)
+{
+ struct unix_proto_data *upd;
+
+ dprintf(1, "%s: init: initializing...\n", pro->name);
+ if (register_chrdev(AF_UNIX_MAJOR, "af_unix", &unix_fops) < 0) {
+ printk("%s: cannot register major device %d!\n",
+ pro->name, AF_UNIX_MAJOR);
+ return;
+ }
+
+ /* Tell SOCKET that we are alive... */
+ (void) sock_register(unix_proto_ops.family, &unix_proto_ops);
+
+ for(upd = unix_datas; upd <= last_unix_data; ++upd) {
+ upd->refcnt = 0;
+ }
+}
diff --git a/net/unix/unix.h b/net/unix/unix.h
new file mode 100644
index 0000000..a83c081
--- /dev/null
+++ b/net/unix/unix.h
@@ -0,0 +1,66 @@
+/*
+ * UNIX An implementation of the AF_UNIX network domain for the
+ * LINUX operating system. UNIX is implemented using the
+ * BSD Socket interface as the means of communication with
+ * the user level.
+ *
+ * This file descibes some things of the UNIX protocol family
+ * module. It is mainly used for the "proc" sub-module now,
+ * but it may be useful for cleaning up the UNIX module as a
+ * whole later.
+ *
+ * Version: @(#)unix.h 1.0.3 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#define AF_UNIX_MAJOR 17 /* UNIX VFS major number */
+
+
+#ifdef _LINUX_UN_H
+
+
+struct unix_proto_data {
+ int refcnt; /* cnt of reference 0=free */
+ struct socket *socket; /* socket we're bound to */
+ int protocol;
+ struct sockaddr_un sockaddr_un;
+ short sockaddr_len; /* >0 if name bound */
+ char *buf;
+ int bp_head, bp_tail;
+ struct inode *inode;
+ struct unix_proto_data *peerupd;
+};
+
+extern struct unix_proto_data unix_datas[NSOCKETS];
+
+
+#define last_unix_data (unix_datas + NSOCKETS - 1)
+
+
+#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data)
+#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0) \
+ ->sun_path)
+
+/*
+ * Buffer size must be power of 2. buffer mgmt inspired by pipe code.
+ * note that buffer contents can wraparound, and we can write one byte less
+ * than full size to discern full vs empty.
+ */
+#define BUF_SIZE PAGE_SIZE
+#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & \
+ (BUF_SIZE-1))
+#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
+
+#endif /* _LINUX_UN_H */
+
+
+extern void unix_proto_init(struct ddi_proto *pro);
diff --git a/tools/build.c b/tools/build.c
index a15ee3b..7576c45 100644
--- a/tools/build.c
+++ b/tools/build.c
@@ -190,8 +190,12 @@ int main(int argc, char ** argv)
die("Unable to read header of 'system'");
if (N_MAGIC(*ex) != ZMAGIC)
die("Non-GCC header of 'system'");
+ fprintf(stderr,"System is %d kB (%d kB code, %d kB data and %d kB bss)\n",
+ (ex->a_text+ex->a_data+ex->a_bss)/1024,
+ ex->a_text /1024,
+ ex->a_data /1024,
+ ex->a_bss /1024);
sz = N_SYMOFF(*ex) - GCC_HEADER + 4;
- fprintf(stderr,"System is %d bytes.\n", sz);
sys_size = (sz + 15) / 16;
if (sys_size > SYS_SIZE)
die("System is too big");
diff --git a/tools/version.c b/tools/version.c
index 2f2e168..190b93c 100644
--- a/tools/version.c
+++ b/tools/version.c
@@ -12,9 +12,11 @@
#include "./version.h"
struct new_utsname system_utsname = {
- UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE
+ UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION,
+ UTS_MACHINE, UTS_DOMAINNAME
};
char *linux_banner =
"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
- LINUX_COMPILE_HOST ") " UTS_VERSION " " LINUX_COMPILE_TIME " \n";
+ LINUX_COMPILE_HOST "." LINUX_COMPILE_DOMAIN ") "
+ UTS_VERSION ": " LINUX_COMPILE_TIME "\n";
diff --git a/zBoot/head.S b/zBoot/head.S
index eb27b2b..6481069 100644
--- a/zBoot/head.S
+++ b/zBoot/head.S
@@ -1,11 +1,11 @@
/*
- * linux/boot/head.s
+ * linux/boot/head.S
*
* Copyright (C) 1991, 1992, 1993 Linus Torvalds
*/
/*
- * head.s contains the 32-bit startup code.
+ * head.S contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00000000, which is also where
* the page directory will exist. The startup code will be overwritten by
@@ -13,10 +13,12 @@
*/
.text
+#include <linux/segment.h>
+
startup_32:
cld
cli
- movl $0x10,%eax
+ movl $KERNEL_DS,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
@@ -34,5 +36,18 @@ startup_32:
*/
pushl $0
popfl
+/*
+ * Clear BSS
+ */
+ xorl %eax,%eax
+ movl $__edata,%edi
+ movl $__end,%ecx
+ subl %edi,%ecx
+ cld
+ rep
+ stosb
+/*
+ * Do the decompression, and jump to the new kernel..
+ */
call _decompress_kernel
- ljmp $0x08, $0x100000
+ ljmp $KERNEL_CS, $0x100000
diff --git a/zBoot/misc.c b/zBoot/misc.c
index ae883b9..0ba96b6 100644
--- a/zBoot/misc.c
+++ b/zBoot/misc.c
@@ -4,12 +4,14 @@
* This is a collection of several routines from gzip-1.0.3
* adapted for Linux.
*
- * malloc and printk by Hannu Savolainen 1993
+ * malloc and puts by Hannu Savolainen 1993
*/
#include "gzip.h"
#include "lzw.h"
+#include <linux/segment.h>
+
/*
* These are set up by the setup-routine at boot-time:
*/
@@ -70,6 +72,8 @@ int hard_math = 0;
void (*work)(int inf, int outf);
void makecrc(void);
+local int get_method(int);
+
char *vidmem = (char *)0xb8000;
int vidp = 0;
int lines, cols;
@@ -87,7 +91,7 @@ void *malloc(int size)
free_mem_ptr += size;
- if (free_mem_ptr >= (640*1024)) error("\nOut of memory\n");
+ if (free_mem_ptr > 0x90000) error("\nOut of memory\n");
if (p == NULL) error("malloc = NULL\n");
return p;
@@ -97,10 +101,10 @@ void free(void *where)
{ /* Don't care */
}
-int printk(char *s)
+static void puts(char *s)
{
int i,n;
- n = strlen(s);
+ for (n = 0; s [n] != '\0'; n++);
if (!n) n = 10;
for (i=0;i<n;i++)
@@ -111,8 +115,6 @@ int printk(char *s)
vidmem[vidp] = s[i];
vidp = vidp + 2;
}
-
- return 1;
}
__ptr_t memset(__ptr_t s, int c, size_t n)
@@ -255,9 +257,9 @@ makecrc(void)
void error(char *x)
{
- printk("\n\n");
- printk(x);
- printk("\n\n -- System halted");
+ puts("\n\n");
+ puts(x);
+ puts("\n\n -- System halted");
while(1); /* Halt */
}
@@ -269,7 +271,7 @@ long user_stack [STACK_SIZE];
struct {
long * a;
short b;
- } stack_start = { & user_stack [STACK_SIZE] , 0x10 };
+ } stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS };
void decompress_kernel()
{
@@ -296,15 +298,15 @@ void decompress_kernel()
clear_bufs();
makecrc();
- printk("Uncompressing Linux...");
+ puts("Uncompressing Linux...");
method = get_method(0);
work(0, 0);
- printk("done.\n\n");
+ puts("done.\n\n");
- printk("Now booting the kernel\n");
+ puts("Now booting the kernel\n");
}
/* ========================================================================