aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@cc.helsinki.fi>1993-08-16 08:50:03 -0400
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:17 -0400
commitf1e7ea6f72640d1033eeeb2ff0c4daf9d12256ab (patch)
tree973041669cc09e5ee3554edbb17b8babad829bf8
parent35214e687d47d66b771c59a34632792dc04c7587 (diff)
downloadarchive-f1e7ea6f72640d1033eeeb2ff0c4daf9d12256ab.tar.gz
Linux 0.99 patchlevel 12 releasedv0.99-pl12
As promised, the 0.99.12 kernel made it out this weekend: it's essentially the last ALPHA-pl12 with some minor changes that shouldn't break anything (famous last words) while they should be a boon especially for NFS users. Nic.funet.fi: pub/OS/Linux/PEOPLE/Linus now contains the 0.99.12 kernel as both full source (linux-0.99.12.tar.gz) and as patches against pl11 (linux-0.99.patch12.gz). It's usually easier to get the full sources: expecially due to some cosmetic fixes the patches are pretty large. Note that kbd.tar.gz (at the same place as the kernel) has been updated yet again to fix some problems with the Swiss keyboard mappings. Hope that caught the last of these problems. Also note that the manual pages in kbd.tar.gz aren't up-to-date but that the format of the keyboard files shouldn't really pose any problems as they are pretty self- explanatory (the man-pages will be fixed eventually, probabably not by me). Also note that the pl12 kernel is more strict about routing entries, and that there is a bug in the 4.4.1 library which may make adding routes extremely difficult (especially if you try to add a C-net route that has been subnetted from a B-net). Libc-4.4.2 fixes the problem, but if you don't use subnetting or any other special netmasks, you'll never see the bug anyway. I'm including some of the README so that people can see what's new.. Linus PS. The network card configs are now in the main "make config", but you should check the net/inet/CONFIG file as well anyway. Also, the 3c509 driver is probably not functional yet, so don't get too excited. ---------- Linux kernel release 0.99 patchlevel 12 These are the release notes for linux version 0.99.12. Read them carefully, as they tell you what's new, explain how to install the kernel, and what to do if something goes wrong. NOTE! There has been some indication that gcc versions older than 2.4.5 result in bad kernels being built: 2.3.3 will fail even to build the kernel, and I have at least one report of trouble with a 2.4.3-built kernel that went away when the kernel was recompiled with 2.4.5. CHANGES since 0.99 patchlevel 11 and earlier: - The memory manager cleanup has continued, and seems to be mostly ready, as proven by the ease of adding mmap() over NFS with the new routines. So yes, the pl12 kernel will demand-load your binaries over NFS, sharing code and clean data, as well as running shared libraries over NFS. Memory management by Eric and me, while the NFS mmap code was written by Jon Tombs, - ** IMPORTANT **: The keyboard driver has been enhanced even further, and almost everything is completely re-mappable. This means that there is a new version of 'loadkeys' and 'dumpkeys' that you must use with this kernel or you'll have problems. The default keyboard is still the US mapping, but if you want to create your own mappings you'll have to load them with the new binaries. Get the 'kbd.tar.gz' archive from the same place you get the kernel. The new keymappings allow things like function key string changes, remapping of the control keys, and freedom to remap any of the normal keyboard functions: including special features like rebooting, console switching etc. The keyboard remapping code has been done mostly by Risto Kankkunen (Risto.Kankku...@Helsinki.FI). - updated network drivers by Donald Becker - updated serial drivers - ty...@Athena.mit.edu - updated 387 emulation (Bill Metzenthen). The updated emulator code has more exact trigonometric functions and improved exception handling. It now behaves very much like a real 486, with only small changes (greater accuracy, slightly different denormal NaN handling etc - hard to detect the differences even if you are looking for them). - network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c and some bad race-conditions fixed). - Scsi code updates by Eric Youngdale and others - Sony CDU-31A CDROM driver by Corey Minyard added to the standard kernel distribution. - The Mitsumi CDROM driver is now part of the standard kernel. Driver by Martin Harriss with patches by stu...@cc4.kuleuven.ac.be (yes, he probably has a real name, but no, I haven't found it) and Jon Tombs. - various other minor patches (preliminary ldt support etc) [ rest deleted ]
-rw-r--r--Makefile42
-rw-r--r--README300
-rw-r--r--boot/head.S69
-rw-r--r--config.in37
-rw-r--r--fs/block_dev.c4
-rw-r--r--fs/buffer.c68
-rw-r--r--fs/exec.c451
-rw-r--r--fs/ext2/acl.c3
-rw-r--r--fs/ext2/balloc.c44
-rw-r--r--fs/ext2/dcache.c69
-rw-r--r--fs/ext2/dir.c3
-rw-r--r--fs/ext2/file.c5
-rw-r--r--fs/ext2/fsync.c56
-rw-r--r--fs/ext2/ialloc.c49
-rw-r--r--fs/ext2/inode.c177
-rw-r--r--fs/ext2/namei.c39
-rw-r--r--fs/ext2/symlink.c10
-rw-r--r--fs/ext2/truncate.c4
-rw-r--r--fs/fifo.c2
-rw-r--r--fs/file_table.c27
-rw-r--r--fs/inode.c25
-rw-r--r--fs/isofs/inode.c34
-rw-r--r--fs/locks.c2
-rw-r--r--fs/minix/inode.c5
-rw-r--r--fs/msdos/inode.c12
-rw-r--r--fs/msdos/misc.c4
-rw-r--r--fs/namei.c5
-rw-r--r--fs/nfs/Makefile2
-rw-r--r--fs/nfs/dir.c2
-rw-r--r--fs/nfs/file.c4
-rw-r--r--fs/nfs/mmap.c159
-rw-r--r--fs/nfs/proc.c50
-rw-r--r--fs/nfs/sock.c2
-rw-r--r--fs/open.c38
-rw-r--r--fs/proc/array.c29
-rw-r--r--fs/proc/inode.c26
-rw-r--r--fs/proc/mem.c30
-rw-r--r--fs/proc/net.c3
-rw-r--r--fs/select.c5
-rw-r--r--fs/super.c25
-rw-r--r--fs/xiafs/namei.c4
-rw-r--r--include/asm/bitops.h13
-rw-r--r--include/asm/segment.h4
-rw-r--r--include/asm/system.h8
-rw-r--r--include/linux/a.out.h2
-rw-r--r--include/linux/binfmts.h36
-rw-r--r--include/linux/busmouse.h4
-rw-r--r--include/linux/cdu31a.h313
-rw-r--r--include/linux/config.h4
-rw-r--r--include/linux/ext2_fs.h110
-rw-r--r--include/linux/ext2_fs_i.h24
-rw-r--r--include/linux/ext2_fs_sb.h7
-rw-r--r--include/linux/fs.h8
-rw-r--r--include/linux/ioctl.h27
-rw-r--r--include/linux/kd.h7
-rw-r--r--include/linux/kernel.h5
-rw-r--r--include/linux/keyboard.h95
-rw-r--r--include/linux/ldt.h28
-rw-r--r--include/linux/mcd.h101
-rw-r--r--include/linux/mm.h4
-rw-r--r--include/linux/msdos_fs.h4
-rw-r--r--include/linux/mtio.h2
-rw-r--r--include/linux/nfs.h2
-rw-r--r--include/linux/nfs_fs.h5
-rw-r--r--include/linux/page.h34
-rw-r--r--include/linux/param.h2
-rw-r--r--include/linux/resource.h8
-rw-r--r--include/linux/sched.h57
-rw-r--r--include/linux/serial.h10
-rw-r--r--include/linux/shm.h47
-rw-r--r--include/linux/stat.h10
-rw-r--r--include/linux/sys.h3
-rw-r--r--include/linux/termios.h2
-rw-r--r--include/linux/timer.h5
-rw-r--r--include/linux/tty.h15
-rw-r--r--include/linux/unistd.h1
-rw-r--r--init/main.c22
-rw-r--r--ipc/msg.c15
-rw-r--r--ipc/sem.c21
-rw-r--r--ipc/shm.c55
-rw-r--r--ipc/util.c10
-rw-r--r--kernel/FPU-emu/README151
-rw-r--r--kernel/FPU-emu/errors.c168
-rw-r--r--kernel/FPU-emu/exception.h4
-rw-r--r--kernel/FPU-emu/fpu_arith.c99
-rw-r--r--kernel/FPU-emu/fpu_aux.c30
-rw-r--r--kernel/FPU-emu/fpu_emu.h35
-rw-r--r--kernel/FPU-emu/fpu_entry.c274
-rw-r--r--kernel/FPU-emu/fpu_etc.c30
-rw-r--r--kernel/FPU-emu/fpu_proto.h25
-rw-r--r--kernel/FPU-emu/fpu_system.h5
-rw-r--r--kernel/FPU-emu/fpu_trig.c957
-rw-r--r--kernel/FPU-emu/get_address.c10
-rw-r--r--kernel/FPU-emu/load_store.c142
-rw-r--r--kernel/FPU-emu/poly_2xm1.c39
-rw-r--r--kernel/FPU-emu/poly_atan.c11
-rw-r--r--kernel/FPU-emu/poly_sin.c36
-rw-r--r--kernel/FPU-emu/poly_tan.c43
-rw-r--r--kernel/FPU-emu/polynomial.S134
-rw-r--r--kernel/FPU-emu/reg_add_sub.c119
-rw-r--r--kernel/FPU-emu/reg_compare.c189
-rw-r--r--kernel/FPU-emu/reg_constant.c11
-rw-r--r--kernel/FPU-emu/reg_constant.h1
-rw-r--r--kernel/FPU-emu/reg_div.S20
-rw-r--r--kernel/FPU-emu/reg_ld_str.c800
-rw-r--r--kernel/FPU-emu/reg_mul.c41
-rw-r--r--kernel/FPU-emu/reg_round.S60
-rw-r--r--kernel/FPU-emu/reg_u_add.S4
-rw-r--r--kernel/FPU-emu/reg_u_sub.S44
-rw-r--r--kernel/FPU-emu/status_w.h9
-rw-r--r--kernel/FPU-emu/version.h2
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/blk_drv/Makefile2
-rw-r--r--kernel/blk_drv/blk.h19
-rw-r--r--kernel/blk_drv/cdu31a.c1705
-rw-r--r--kernel/blk_drv/floppy.c8
-rw-r--r--kernel/blk_drv/hd.c7
-rw-r--r--kernel/blk_drv/ll_rw_blk.c22
-rw-r--r--kernel/blk_drv/mcd.c1196
-rw-r--r--kernel/blk_drv/ramdisk.c6
-rw-r--r--kernel/blk_drv/scsi/Makefile18
-rw-r--r--kernel/blk_drv/scsi/aha1542.c6
-rw-r--r--kernel/blk_drv/scsi/aha1740.c2
-rw-r--r--kernel/blk_drv/scsi/constants.c460
-rw-r--r--kernel/blk_drv/scsi/constants.h6
-rw-r--r--kernel/blk_drv/scsi/fdomain.c157
-rw-r--r--kernel/blk_drv/scsi/hosts.c2
-rw-r--r--kernel/blk_drv/scsi/scsi.c149
-rw-r--r--kernel/blk_drv/scsi/scsi.h27
-rw-r--r--kernel/blk_drv/scsi/scsi_debug.c4
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.c17
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.h2
-rw-r--r--kernel/blk_drv/scsi/sd.c22
-rw-r--r--kernel/blk_drv/scsi/sd_ioctl.c7
-rw-r--r--kernel/blk_drv/scsi/seagate.c375
-rw-r--r--kernel/blk_drv/scsi/seagate.h24
-rw-r--r--kernel/blk_drv/scsi/sr.c16
-rw-r--r--kernel/blk_drv/scsi/st.c282
-rw-r--r--kernel/blk_drv/scsi/st.h2
-rw-r--r--kernel/blk_drv/scsi/ultrastor.c9
-rw-r--r--kernel/blk_drv/scsi/ultrastor.h2
-rw-r--r--kernel/blk_drv/xd.c7
-rw-r--r--kernel/chr_drv/Makefile4
-rw-r--r--kernel/chr_drv/atixlmouse.c12
-rw-r--r--kernel/chr_drv/busmouse.c3
-rw-r--r--kernel/chr_drv/console.c23
-rw-r--r--kernel/chr_drv/defkeymap.c351
-rw-r--r--kernel/chr_drv/defkeymap.map246
-rw-r--r--kernel/chr_drv/keyboard.c382
-rw-r--r--kernel/chr_drv/mem.c8
-rw-r--r--kernel/chr_drv/msbusmouse.c15
-rw-r--r--kernel/chr_drv/serial.c339
-rw-r--r--kernel/chr_drv/tpqic02.c6
-rw-r--r--kernel/chr_drv/tty_io.c85
-rw-r--r--kernel/chr_drv/tty_ioctl.c87
-rw-r--r--kernel/chr_drv/vt.c55
-rw-r--r--kernel/exit.c107
-rw-r--r--kernel/fork.c18
-rw-r--r--kernel/ldt.c96
-rw-r--r--kernel/printk.c22
-rw-r--r--kernel/ptrace.c84
-rw-r--r--kernel/sched.c40
-rw-r--r--kernel/signal.c2
-rw-r--r--kernel/sys.c13
-rw-r--r--kernel/sys_call.S6
-rw-r--r--lib/malloc.c42
-rw-r--r--mm/memory.c304
-rw-r--r--mm/mmap.c36
-rw-r--r--mm/swap.c186
-rw-r--r--net/inet/3c509.c594
-rw-r--r--net/inet/8390.c137
-rw-r--r--net/inet/8390.h54
-rw-r--r--net/inet/CONFIG15
-rw-r--r--net/inet/LICENSE.SRC15
-rw-r--r--net/inet/Makefile13
-rw-r--r--net/inet/README.839075
-rw-r--r--net/inet/README.DRIVERS1
-rw-r--r--net/inet/Space.c124
-rw-r--r--net/inet/arp.c14
-rw-r--r--net/inet/auto_irq.c5
-rw-r--r--net/inet/d_link.c1
-rw-r--r--net/inet/dev.c34
-rw-r--r--net/inet/dev.h18
-rw-r--r--net/inet/el2.c428
-rw-r--r--net/inet/el2reg.h59
-rw-r--r--net/inet/eth.c1
-rw-r--r--net/inet/hp.c77
-rw-r--r--net/inet/icmp.c1
-rw-r--r--net/inet/iow.h126
-rw-r--r--net/inet/ip.c15
-rw-r--r--net/inet/lance.c630
-rw-r--r--net/inet/loopback.c1
-rw-r--r--net/inet/ne.c178
-rw-r--r--net/inet/packet.c1
-rw-r--r--net/inet/plip.c759
-rw-r--r--net/inet/proc.c25
-rw-r--r--net/inet/protocol.c1
-rw-r--r--net/inet/raw.c1
-rw-r--r--net/inet/route.c78
-rw-r--r--net/inet/slhc.c1
-rw-r--r--net/inet/slip.c69
-rw-r--r--net/inet/sock.c29
-rw-r--r--net/inet/sock.h17
-rw-r--r--net/inet/tcp.c96
-rw-r--r--net/inet/timer.c420
-rw-r--r--net/inet/timer.h43
-rw-r--r--net/inet/udp.c1
-rw-r--r--net/inet/utils.c2
-rw-r--r--net/inet/wd.c284
-rw-r--r--net/unix/sock.c4
-rw-r--r--zBoot/misc.c54
211 files changed, 13603 insertions, 4581 deletions
diff --git a/Makefile b/Makefile
index 7ee2bf5..e8a3c95 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ all: Version zImage
.EXPORT_ALL_VARIABLES:
-BASH = $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
@@ -96,13 +96,8 @@ KERNELHDRS =/usr/src/linux/include
Version: dummy
rm -f tools/version.h
-lilo: $(CONFIGURE) Image
- if [ -f /vmlinux ]; then mv /vmlinux /vmlinux.old; fi
- cat Image > /vmlinux
- /etc/lilo/install
-
config:
- $(BASH) Configure $(OPTS) < config.in
+ $(CONFIG_SHELL) Configure $(OPTS) < config.in
mv .config~ .config
$(MAKE) soundconf
@@ -116,20 +111,13 @@ tools/./version.h: tools/version.h
tools/version.h: $(CONFIGURE) Makefile
@./makever.sh
- @echo \#define UTS_RELEASE \"0.99.11\" > tools/version.h
+ @echo \#define UTS_RELEASE \"0.99.12\" > 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
- sync
-
-disk: Image
- dd bs=8192 if=Image of=/dev/fd0
-
tools/build: $(CONFIGURE) tools/build.c
$(HOSTCC) $(CFLAGS) \
-o tools/build tools/build.c
@@ -246,3 +234,27 @@ else
dummy:
endif
+
+#
+# Leave these dummy entries for now to tell people that they are going away..
+#
+lilo:
+ @echo
+ @echo Uncompressed kernel images no longer supported. Use
+ @echo \"make zlilo\" instead.
+ @echo
+ @exit 1
+
+Image:
+ @echo
+ @echo Uncompressed kernel images no longer supported. Use
+ @echo \"make zImage\" instead.
+ @echo
+ @exit 1
+
+disk:
+ @echo
+ @echo Uncompressed kernel images no longer supported. Use
+ @echo \"make zdisk\" instead.
+ @echo
+ @exit 1
diff --git a/README b/README
index b10f98a..dfd94ed 100644
--- a/README
+++ b/README
@@ -1,109 +1,249 @@
- DON'T PANIC
+ Linux kernel release 0.99 patchlevel 12
-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, but it shouldn't really be that hard to compile the kernel.
-I hope.
+These are the release notes for linux version 0.99.12. Read them
+carefully, as they tell you what's new, explain how to install the
+kernel, and what to do if something goes wrong.
-In order to compile this version of the kernel you need GCC 2.4.3 or
-newer (older compiler versions have problems - no guarantees it will
-even compile much less work). 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.
+NOTE! There has been some indication that gcc versions older than 2.4.5
+result in bad kernels being built: 2.3.3 will fail even to build the
+kernel, and I have at least one report of trouble with a 2.4.3-built
+kernel that went away when the kernel was recompiled with 2.4.5.
-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.
-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.
+CHANGES since 0.99 patchlevel 11 and earlier:
-* Basic configuration
+ - The memory manager cleanup has continued, and seems to be mostly
+ ready, as proven by the ease of adding mmap() over NFS with the new
+ routines. So yes, the pl12 kernel will demand-load your binaries
+ over NFS, sharing code and clean data, as well as running shared
+ libraries over NFS. Memory management by Eric and me, while the NFS
+ mmap code was written by Jon Tombs,
- * SETUP
+ - ** IMPORTANT **: The keyboard driver has been enhanced even further,
+ and almost everything is completely re-mappable. This means that
+ there is a new version of 'loadkeys' and 'dumpkeys' that you must use
+ with this kernel or you'll have problems. The default keyboard is
+ still the US mapping, but if you want to create your own mappings
+ you'll have to load them with the new binaries. Get the 'kbd.tar.gz'
+ archive from the same place you get the kernel.
-1. make sure /usr/include/asm and /usr/include/linux are symlinks to
-the linux source tree include files. The output of
+ The new keymappings allow things like function key string changes,
+ remapping of the control keys, and freedom to remap any of the normal
+ keyboard functions: including special features like rebooting,
+ console switching etc. The keyboard remapping code has been done
+ mostly by Risto Kankkunen (Risto.Kankkunen@Helsinki.FI).
- # ls -ld /usr/include/asm /usr/include/linux
+ - updated network drivers by Donald Becker
-should look like this:
+ - updated serial drivers - tytso@Athena.mit.edu
- 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
+ - updated 387 emulation (Bill Metzenthen). The updated emulator code
+ has more exact trigonometric functions and improved exception
+ handling. It now behaves very much like a real 486, with only small
+ changes (greater accuracy, slightly different denormal NaN handling
+ etc - hard to detect the differences even if you are looking for
+ them).
-If it doesn't, create the appropriate symlinks with
+ - network timer fixes by Florian La Roche (much cleaned up net/inet/timer.c
+ and some bad race-conditions fixed).
- # cd /usr/include
- # rm -rf linux asm
- # ln -s /usr/src/linux/include/linux .
- # ln -s /usr/src/linux/include/asm .
+ - Scsi code updates by Eric Youngdale and others
-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.
+ - Sony CDU-31A CDROM driver by Corey Minyard added to the standard
+ kernel distribution.
-2. Edit Makefile: Check the definitions of macros ROOTDEV, RAMDISK and
-SVGA_MODE before you run make. They are explained in the Makefile.
+ - The Mitsumi CDROM driver is now part of the standard kernel. Driver
+ by Martin Harriss with patches by stud11@cc4.kuleuven.ac.be (yes, he
+ probably has a real name, but no, I haven't found it) and Jon Tombs.
-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
-of the flags for your system.
+ - various other minor patches (preliminary ldt support etc)
-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. 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.
+NOTABLE changes since patchlevel 10 or earlier:
-* Running make
+ - The memory manager has been cleaned up substantially, and mmap()
+ works for MAP_PRIVATE. MAP_SHARED is still not supported for
+ anything else than /dev/mem, but even so it actually is usable for a
+ lot of applications. The shared library routines have been rewritten
+ to use mmap() instead of the old hardcoded behaviour.
-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.
+ - The kernel is now compiled with C++ instead of plain C. Very few
+ actual C++ features are used, but even so C++ allows for more
+ type-checking and type-safe linkage.
-The following targets all apply for the makefile at the root of the
-kernel source tree.
+ - The filesystem routines have been cleaned up for multiple block
+ sizes. None of the filesystems use it yet, but people are working on
+ it.
-"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.
+ - named pipes and normal pipes should hopefully have the right select()
+ semantics in the presense/absense of writers.
-"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).
+ - QIC-02 tape driver by Hennus Bergman
-"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.
+ - selection patches in the default kernel
-"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.
+ - fixed a bug in the pty code which led to busy waiting in some
+ circumstances instead of sleeping.
-"make dep" updates all dependencies. This requires sed. It modifies
-the makefiles directly (the end of them, starting at the ###Dependencies
--line at the end). "make dep" is required after patching, or the kernel
-may not compile cleanly.
+ - Compressed SLIP support (Charles Hedrick). See net/inet/CONFIG
-"make clean" will remove all object files and other files created by the
-compilation. This requires basename.
+ - the 'clear_bit()' function was changed to return the previous setting
+ of the bit instead of the old "error-code". This makes use of the
+ bit operations more logical.
-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
-Bash with:
+ - udelay() function for short delays (busy-waiting) added. Used
+ currently only by the QIC driver.
- make something 2>&1 | tee make.out
+ - fork() and sheduler changes to make task switches happen only from
+ kernel mode to kernel mode. Cleaner and more portable than the old
+ code which counted on being able to task-switch directly into user
+ mode.
-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.
+ - debugging malloc code.
+
+INSTALLING the kernel:
+
+ - if you install by patching, you need a *clean* 0.99.11 source tree,
+ which presumably exists in /usr/src/linux. If so, to get the kernel
+ patched, just do a
+
+ cd /usr/src
+ patch -p0 < linux-0.99.patch12
+
+ and you should be ok. You may want to remove the backup files (xxx~
+ or xxx.orig), and make sure that there are no failed patches (xxx# or
+ xxx.rej).
+
+ - If you install the full sources, do a
+
+ cd /usr/src
+ tar xvf linux-0.99.12.tar
+
+ to get it all put in place.
+
+ - make sure your /usr/include/linux and /usr/include/asm directories
+ are just symlinks to the kernel sources:
+
+ cd /usr/include
+ rm -rf linux
+ rm -rf asm
+ ln -s /usr/src/linux/include/linux .
+ ln -s /usr/src/linux/include/asm .
+
+ - make sure you have no stale .o files and dependencies lying around:
+
+ cd /usr/src/linux
+ make mrproper
+
+ You should now have the sources correctly installed.
+
+CONFIGURING the kernel:
+
+ - do a "make config" to configure the basic kernel. "make config"
+ needs bash to work: it will search for bash in $BASH, /bin/bash and
+ /bin/sh (in that order), so hopefully one of those is correct.
+
+ NOTES on "make config":
+ - compiling the kernel with "-m486" for a number of 486-specific
+ will result in a kernel that still works on a 386: it may be
+ slightly larger and possibly slower by an insignificant amount,
+ but it should not hurt performance.
+ - A kernel with math-emulation compiled in will still use the
+ coprocessor if one is present: the math emulation will just
+ never get used in that case. The kernel will be slighly larger,
+ but will work on different machines regardless of whether they
+ have a math coprocessor or not.
+ - the "kernel hacking" configuration details usually result in a
+ bigger or slower kernel (or both), and can even make the kernel
+ less stable by configuring some routines to actively try to
+ break bad code to find kernel problems (kmalloc()). Thus you
+ should probably answer 'n' to the questions for a "production"
+ kernel.
+
+ - edit net/inet/CONFIG to configure the networking parts of the kernel.
+ The comments should hopefully clarify it all.
+
+ - Check the top Makefile for further site-dependent configuration
+ (default SVGA mode etc).
+
+ - Finally, do a "make dep" to set up all the dependencies correctly.
+
+COMPILING the kernel:
+
+ - make sure you have gcc-2.4.5 or newer available with g++. It seems
+ older gcc versions can have problems compiling linux 0.99.10 and
+ newer versions. If you upgrade, remember to get the new binutils
+ package too (for as/ld/nm and company)
+
+ - do a "make zImage" to create a compressed kernel image. If you want
+ to make a bootdisk (without root filesystem or lilo), insert a floppy
+ in your A: drive, and do a "make zdisk". It is also possible to do
+ "make zlilo" if you have lilo installed to suit the kernel makefiles,
+ but you may want to check your particular lilo setup first.
+
+ - keep a backup kernel handy in case something goes wrong.
+
+ - reboot with the new kernel and enjoy.
+
+IF SOMETHING GOES WRONG:
+
+ - if you have problems that seem to be due to kernel bugs, please mail
+ them to me (Linus.Torvalds@Helsinki.FI), and possibly to any other
+ relevant mailing-list or to the newsgroup. The mailing-lists are
+ useful especially for SCSI and NETworking problems, as I can't test
+ either of those personally anyway.
+
+ - In all bug-reports, *please* tell what kernel you are talking about,
+ how to duplicate the problem, and what your setup is (use your common
+ sense). If the problem is new, tell me so, and if the problem is
+ old, please try to tell me when you first noticed it.
+
+ - if the bug results in a message like
+
+ unable to handle kernel paging request at address C0000010
+ Oops: 0002
+ EIP: 0010:xxxxxxxx
+ eax: xxxxxxxx ebx: xxxxxxxx ecx: xxxxxxxx edx: xxxxxxxx
+ esi: xxxxxxxx edi: xxxxxxxx ebp: xxxxxxxx
+ ds: xxxx es: xxxx fs: xxxx gs: xxxx
+ Pid: xx, process nr: xx
+ xx xx xx xx xx xx xx xx xx xx
+
+ or similar kernel debugging information on your screen or in your
+ system log, please duplicate it *exactly*. The dump may look
+ incomprehensible to you, but it does contain information that may
+ help debugging the problem. The text above the dump is also
+ important: it tells something about why the kernel dumped code (in
+ the above example it's due to a bad kernel pointer)
+
+ - in debugging dumps like the above, it helps enourmously if you can
+ look up what the EIP value means. The hex value as such doesn't help
+ me or anybody else very much: it will depend on your particular
+ kernel setup. What you should do is take the hex value from the EIP
+ line (ignore the "0010:"), and look it up in the kernel namelist to
+ see which kernel function contains the offending address.
+
+ To find out the kernel function name, you'll need to find the system
+ binary associated with the kernel that exhibited the symptom. In the
+ case of compressed kernels, this will be 'linux/tools/zSystem', while
+ uncompressed kernels use the file 'tools/system'. To extract the
+ namelist and match it against the EIP from the kernel crash, do:
+
+ nm tools/zSystem | sort | less
+
+ This will give you a list of kernel addresses sorted in ascending
+ order, from which it is simple to find the function that contains the
+ offending address. Note that the address given by the kernel
+ debugging messages will not necessarily match exactly with the
+ function addresses (in fact, that is very unlikely), so you can't
+ just 'grep' the list: the list will, however, give you the starting
+ point of each kernel function, so by looking for the function that
+ has a starting address lower than the one you are searching for but
+ is followed by a function with a higher address you will find the one
+ you want. In fact, it may be a good idea to include a bit of
+ "context" in your problem report, giving a few lines around the
+ interesting one.
+
+ If you for some reason cannot do the above (you have a pre-compiled
+ kernel image or similar), telling me as much about your setup as
+ possible will help.
- Lars Wirzenius & Linus Torvalds
diff --git a/boot/head.S b/boot/head.S
index ed12f94..322f198 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -35,6 +35,20 @@ startup_32:
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
+/*
+ * Clear BSS first so that there are no surprises...
+ */
+ xorl %eax,%eax
+ movl $__edata,%edi
+ movl $__end,%ecx
+ subl %edi,%ecx
+ cld
+ rep
+ stosb
+/*
+ * start system 32-bit setup. We need to re-do some of the things done
+ * in 16-bit mode for the "real" operations.
+ */
call setup_idt
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
@@ -72,16 +86,6 @@ startup_32:
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,6 +94,7 @@ startup_32:
*/
movl %esp,%edi # save stack pointer
andl $0xfffffffc,%esp # align stack to avoid AC fault
+ movl $3,_x86
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
@@ -100,28 +105,31 @@ startup_32:
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
- jnz 1f # 486
- pushl %ecx # restore original EFLAGS
+ je is386
+ movl $4,_x86
+ movl %ecx,%eax
+ xorl $0x200000,%eax # check ID flag
+ pushl %eax
+ popfl # if we are on a 486,
+ pushfl # we can't change it
+ popl %eax
+ xorl %ecx,%eax
+ andl $0x200000,%eax
+ je is486
+ movl $5,_x86 # 586 setup same as 486 at least for now
+is486: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
- movl %cr0,%eax # 386
+ movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
- orl $2,%eax # set MP
- jmp 2f
-/*
- * NOTE! 486 should set bit 16, to check for write-protect in supervisor
- * mode. Then it would be unnecessary with the "verify_area()"-calls.
- * 486 users probably want to set the NE (#5) bit also, so as to use
- * int 16 for math errors.
- * XXX - the above is out of date. We set all the bits, but don't take
- * advantage of WP (26 Dec 92).
- */
-1: pushl %ecx # restore original EFLAGS
+ orl $0x50022,%eax # set AM, WP, NE and MP
+ jmp 2f
+is386: pushl %ecx # restore original EFLAGS
popfl
movl %edi,%esp # restore esp
- movl %cr0,%eax # 486
+ movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
- orl $0x50022,%eax # set AM, WP, NE and MP
+ orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
call setup_paging
@@ -134,9 +142,11 @@ startup_32:
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
- pushl $0 # These are the parameters to main :-)
- pushl $0
- pushl $0
+ xorl %eax,%eax
+ lldt %ax
+ pushl %eax # These are the parameters to main :-)
+ pushl %eax
+ pushl %eax
cld # gcc2 wants the direction flag cleared at all times
call _start_kernel
L6:
@@ -148,6 +158,7 @@ L6:
*/
check_x87:
movl $0,_hard_math
+ clts
fninit
fstsw %ax
cmpb $0,%al
diff --git a/config.in b/config.in
index 5620e2c..e122208 100644
--- a/config.in
+++ b/config.in
@@ -5,7 +5,7 @@
*
* General setup
*
-bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'TCP/IP networking' CONFIG_INET y
@@ -18,7 +18,7 @@ bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
bool 'SCSI support?' CONFIG_SCSI n
if [ "$CONFIG_SCSI" = "n" ]
:
-: Skipping SCSI onfiguration options...
+: Skipping SCSI configuration options...
:
else
*
@@ -38,6 +38,32 @@ bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n
bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
fi
*
+* Network device support
+*
+bool 'Network device support?' CONFIG_ETHERCARDS y
+if [ "$CONFIG_ETHERCARDS" = "n" ]
+:
+: Skipping ethercard configuration options...
+:
+else
+bool 'SLIP (serial line) support' CONFIG_SLIP n
+bool 'PLIP (parallel port) support' CONFIG_PLIP n
+bool 'NE2000/NE1000 support' CONFIG_NE2000 y
+bool 'WD80*3 support' CONFIG_WD80x3 y
+#bool '3c501 support' CONFIG_EL1 n
+bool '3c503 support' CONFIG_EL2 y
+#bool '3c509 support' CONFIG_EL3 n
+bool 'HP PCLAN support' CONFIG_HPLAN y
+bool 'AT1500 and NE2100 support' CONFIG_AT1500 y
+#bool 'DEPCA support' CONFIG_DEPCA n
+bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 y
+#bool 'AT-LAN-TEC pocket adaptor support' CONFIG_ATP n
+#bool 'EtherExpress support' CONFIG_EEXPRESS n
+fi
+*
+bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
+bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+*
* Filesystems
*
bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
@@ -46,12 +72,13 @@ bool 'Second extended fs support' CONFIG_EXT2_FS n
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
bool '/proc filesystem support' CONFIG_PROC_FS y
-bool 'NFS filesystem support' CONFIG_NFS_FS n
+bool 'NFS filesystem support' CONFIG_NFS_FS y
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
*
* character devices
*
bool 'Keyboard meta-key sends ESC-prefix' CONFIG_KBD_META y
+bool 'Keyboard Num Lock on by default' CONFIG_KBD_NUML y
bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
@@ -67,4 +94,6 @@ bool 'Sound card support (distributed separately)' CONFIG_SOUND n
*
bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
bool 'Kernel profiling support' CONFIG_PROFILE n
-
+if [ "$CONFIG_SCSI" = "y" ]
+bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS n
+fi
diff --git a/fs/block_dev.c b/fs/block_dev.c
index c50b9b8..4fdf3a8 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -44,7 +44,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
if (blk_size[MAJOR(dev)])
size = (blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
else
- size = 0x7fffffff;
+ size = INT_MAX;
while (count>0) {
if (block >= size)
return written;
@@ -106,7 +106,7 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
if (blk_size[MAJOR(dev)])
size = blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
else
- size = 0x7fffffff;
+ size = INT_MAX;
if (offset > size)
left = 0;
diff --git a/fs/buffer.c b/fs/buffer.c
index e3eb636..dc767ac 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -6,7 +6,7 @@
/*
* 'buffer.c' implements the buffer-cache functions. Race-conditions have
- * been avoided by NEVER letting a interrupt change a buffer (except for the
+ * been avoided by NEVER letting an interrupt change a buffer (except for the
* data, of course), but instead letting the caller do it.
*/
@@ -38,6 +38,12 @@ extern int check_scsidisk_media_change(int, int);
extern int revalidate_scsidisk(int, int);
#endif
#endif
+#ifdef CONFIG_CDU31A
+extern int check_cdu31a_media_change(int, int);
+#endif
+#ifdef CONFIG_MCD
+extern int check_mcd_media_change(int, int);
+#endif
static struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list = NULL;
@@ -233,6 +239,18 @@ void check_disk_change(dev_t dev)
break;
#endif
+#if defined(CONFIG_CDU31A)
+ case 15: /* Sony CDROM */
+ i = check_cdu31a_media_change(dev, 0);
+ break;
+#endif
+
+#if defined(CONFIG_MCD)
+ case 23: /* Sony CDROM */
+ i = check_mcd_media_change(dev, 0);
+ break;
+#endif
+
default:
return;
};
@@ -379,13 +397,15 @@ void set_blocksize(dev_t dev, int size)
if (!blksize_size[MAJOR(dev)])
return;
- if (size != 512 && size != 1024 && size != 2048 && size != 4096)
- panic("Invalid blocksize passed to set_blocksize");
+ switch(size) {
+ default: panic("Invalid blocksize passed to set_blocksize");
+ case 512: case 1024: case 2048: case 4096:;
+ }
if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
blksize_size[MAJOR(dev)][MINOR(dev)] = size;
return;
- };
+ }
if (blksize_size[MAJOR(dev)][MINOR(dev)] == size)
return;
sync_buffers(dev, 2);
@@ -437,7 +457,7 @@ repeat:
grow_size -= size;
if (nr_free_pages > min_free_pages && grow_size <= 0) {
grow_buffers(size);
- grow_size = 4096;
+ grow_size = PAGE_SIZE;
}
buffers = nr_buffers;
bh = NULL;
@@ -588,19 +608,18 @@ static void put_unused_buffer_head(struct buffer_head * bh)
static void get_more_buffer_heads(void)
{
- unsigned long page;
+ int i;
struct buffer_head * bh;
if (unused_list)
return;
- page = get_free_page(GFP_BUFFER);
- if (!page)
+
+ if(! (bh = (struct buffer_head*) get_free_page(GFP_BUFFER)))
return;
- bh = (struct buffer_head *) page;
- while ((unsigned long) (bh+1) <= page+4096) {
- put_unused_buffer_head(bh);
- bh++;
- nr_buffer_heads++;
+
+ for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) {
+ bh->b_next_free = unused_list; /* only make link */
+ unused_list = bh++;
}
}
@@ -632,8 +651,8 @@ static struct buffer_head * create_buffers(unsigned long page, unsigned long siz
unsigned long offset;
head = NULL;
- offset = 4096;
- while ((offset -= size) < 4096) {
+ offset = PAGE_SIZE;
+ while ((offset -= size) < PAGE_SIZE) {
bh = get_unused_buffer_head();
if (!bh)
goto no_grow;
@@ -685,14 +704,14 @@ static unsigned long check_aligned(struct buffer_head * first, unsigned long add
int nrbuf;
page = (unsigned long) first->b_data;
- if (page & 0xfff) {
+ if (page & ~PAGE_MASK) {
brelse(first);
return 0;
}
mem_map[MAP_NR(page)]++;
bh[0] = first;
nrbuf = 1;
- for (offset = size ; offset < 4096 ; offset += size) {
+ for (offset = size ; offset < PAGE_SIZE ; offset += size) {
block = *++b;
if (!block)
goto no_go;
@@ -728,7 +747,7 @@ static unsigned long try_to_load_aligned(unsigned long address,
if (!bh)
return 0;
p = b;
- for (offset = 0 ; offset < 4096 ; offset += size) {
+ for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
block = *(p++);
if (!block)
goto not_aligned;
@@ -755,7 +774,7 @@ static unsigned long try_to_load_aligned(unsigned long address,
else
break;
}
- buffermem += 4096;
+ buffermem += PAGE_SIZE;
bh->b_this_page = tmp;
mem_map[MAP_NR(address)]++;
read_buffers(arr,block);
@@ -847,12 +866,11 @@ void grow_buffers(int size)
unsigned long page;
struct buffer_head *bh, *tmp;
- if ((size & 511) || (size > 4096)) {
+ if ((size & 511) || (size > PAGE_SIZE)) {
printk("VFS: grow_buffers: size = %d\n",size);
return;
}
- page = get_free_page(GFP_BUFFER);
- if (!page)
+ if(!(page = __get_free_page(GFP_BUFFER)))
return;
bh = create_buffers(page, size);
if (!bh) {
@@ -878,7 +896,7 @@ void grow_buffers(int size)
break;
}
tmp->b_this_page = bh;
- buffermem += 4096;
+ buffermem += PAGE_SIZE;
return;
}
@@ -893,7 +911,7 @@ static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
*bhp = bh;
page = (unsigned long) bh->b_data;
- page &= 0xfffff000;
+ page &= PAGE_MASK;
tmp = bh;
do {
if (!tmp)
@@ -912,7 +930,7 @@ static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
remove_from_queues(p);
put_unused_buffer_head(p);
} while (tmp != bh);
- buffermem -= 4096;
+ buffermem -= PAGE_SIZE;
free_page(page);
return !mem_map[MAP_NR(page)];
}
diff --git a/fs/exec.c b/fs/exec.c
index 74451c8..10c63e7 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -15,6 +15,12 @@
*
* Once more I can proudly say that linux stood up to being changed: it
* was less than 2 hours work to get demand-loading completely implemented.
+ *
+ * Demand loading changed July 1993 by Eric Youngdale. Use mmap instead,
+ * current->executable is only used by the procfs. This allows a dispatch
+ * table to check for several different types of binary formats. We keep
+ * trying until we recognize the file or we run out of supported binary
+ * formats.
*/
#include <linux/fs.h>
@@ -31,17 +37,56 @@
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/segment.h>
+#include <asm/system.h>
+
+#include <linux/binfmts.h>
#include <asm/segment.h>
+#include <asm/system.h>
+
+extern "C" int sys_exit(int exit_code);
+extern "C" int sys_close(unsigned fd);
+extern "C" int sys_open(const char *, int, int);
extern void shm_exit (void);
-/*
- * MAX_ARG_PAGES defines the number of pages allocated for arguments
- * and envelope for the new program. 32 should suffice, this gives
- * a maximum env+arg of 128kB !
- */
-#define MAX_ARG_PAGES 32
+static int open_inode(struct inode * inode, int mode)
+{
+ int error, fd;
+ struct file *f, **fpp;
+
+ if (!inode->i_op || !inode->i_op->default_file_ops)
+ return -EINVAL;
+ f = get_empty_filp();
+ if (!f)
+ return -EMFILE;
+ fd = 0;
+ fpp = current->filp;
+ for (;;) {
+ if (!*fpp)
+ break;
+ if (++fd > NR_OPEN)
+ return -ENFILE;
+ fpp++;
+ }
+ *fpp = f;
+ f->f_flags = mode;
+ f->f_mode = (mode+1) & O_ACCMODE;
+ f->f_inode = inode;
+ f->f_pos = 0;
+ f->f_reada = 0;
+ f->f_op = inode->i_op->default_file_ops;
+ if (f->f_op->open) {
+ error = f->f_op->open(inode,f);
+ if (error) {
+ *fpp = NULL;
+ f->f_count--;
+ return error;
+ }
+ }
+ inode->i_count++;
+ return fd;
+}
/*
* These are the only things you should do on a core-file: use only these
@@ -78,9 +123,6 @@ int core_dump(long signr, struct pt_regs * regs)
return 0;
current->dumpable = 0;
- if (current->elf_executable)
- return 0;
-
/* See if we have enough room to write the upage. */
if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
return 0;
@@ -185,54 +227,27 @@ end_coredump:
*/
extern "C" int sys_uselib(const char * library)
{
- int fd, error;
+ int fd, retval;
struct file * file;
- struct inode * inode;
- struct exec ex;
- unsigned int len;
- unsigned int bss;
+ struct linux_binfmt * fmt;
fd = sys_open(library, 0, 0);
if (fd < 0)
return fd;
file = current->filp[fd];
- if (!file || !(inode = file->f_inode) || !file->f_op || !file->f_op->read) {
- sys_close(fd);
- return -ENOEXEC;
- }
- set_fs(KERNEL_DS);
- if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
- sys_close(fd);
- return -EACCES;
- }
- set_fs(USER_DS);
-
- /* We come in here for the regular a.out style of shared libraries */
- if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize ||
- ex.a_drsize || ex.a_entry & 0xfff ||
- inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
- sys_close(fd);
- return -ENOEXEC;
- }
- if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
- (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
- printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
- sys_close(fd);
- return -ENOEXEC;
+ retval = -ENOEXEC;
+ if (file && file->f_inode && file->f_op && file->f_op->read) {
+ fmt = formats;
+ do {
+ int (*fn)(int) = fmt->load_shlib;
+ if (!fn)
+ break;
+ retval = fn(fd);
+ fmt++;
+ } while (retval == -ENOEXEC);
}
-
- /* Now use mmap to map the library into memory. */
- error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data,
- PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
- N_TXTOFF(ex));
sys_close(fd);
- if (error != ex.a_entry)
- return error;
- len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000;
- bss = ex.a_text + ex.a_data + ex.a_bss;
- if (bss > len)
- zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY);
- return 0;
+ return retval;
}
/*
@@ -240,7 +255,7 @@ extern "C" int sys_uselib(const char * library)
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
-static unsigned long * create_tables(char * p,int argc,int envc)
+unsigned long * create_tables(char * p,int argc,int envc)
{
unsigned long *argv,*envp;
unsigned long * sp;
@@ -351,7 +366,7 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
return p;
}
-static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
+unsigned long change_ldt(unsigned long text_size,unsigned long * page)
{
unsigned long code_limit,data_limit,code_base,data_base;
int i;
@@ -363,8 +378,10 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
data_base += data_limit;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
- if (page[i])
+ if (page[i]) {
+ current->rss++;
put_dirty_page(current,page[i],data_base);
+ }
}
return data_limit;
}
@@ -374,7 +391,7 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
* that aren't on a block boundary, and for files on filesystems
* without bmap support.
*/
-static int read_exec(struct inode *inode, unsigned long offset,
+int read_exec(struct inode *inode, unsigned long offset,
char * addr, unsigned long count)
{
struct file file;
@@ -413,14 +430,16 @@ end_readexec:
* that a new one can be started
*/
-static void flush_old_exec(struct inode * inode, char * filename, int e_uid, int e_gid)
+void flush_old_exec(struct linux_binprm * bprm)
{
int i;
int ch;
+ char * name;
struct vm_area_struct * mpnt, *mpnt1;
current->dumpable = 1;
- for (i=0; (ch = get_fs_byte(filename++)) != '\0';) {
+ name = bprm->filename;
+ for (i=0; (ch = *(name++)) != '\0';) {
if (ch == '/')
i = 0;
else
@@ -445,7 +464,22 @@ static void flush_old_exec(struct inode * inode, char * filename, int e_uid, int
kfree(mpnt);
mpnt = mpnt1;
}
- if (e_uid != current->euid || e_gid != current->egid || !permission(inode,MAY_READ))
+
+ /* Flush the old ldt stuff... */
+ if (current->ldt) {
+ free_page((unsigned long) current->ldt);
+ current->ldt = NULL;
+ for (i=1 ; i<NR_TASKS ; i++) {
+ if (task[i] == current) {
+ set_ldt_desc(gdt+(i<<1)+
+ FIRST_LDT_ENTRY,&default_ldt, 1);
+ load_ldt(i);
+ }
+ }
+ }
+
+ if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
+ !permission(bprm->inode,MAY_READ))
current->dumpable = 0;
current->signal = 0;
for (i=0 ; i<32 ; i++) {
@@ -468,78 +502,72 @@ static void flush_old_exec(struct inode * inode, char * filename, int e_uid, int
/*
* sys_execve() executes a new program.
*/
-extern "C" int sys_execve(struct pt_regs regs)
+static int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
- char * filename = (char *) regs.ebx;
- char ** argv = (char **) regs.ecx;
- char ** envp = (char **) regs.edx;
- struct inode * inode;
- char buf[128];
+ struct linux_binprm bprm;
+ struct linux_binfmt * fmt;
unsigned long old_fs;
- struct exec ex;
- unsigned long page[MAX_ARG_PAGES];
- int i,argc,envc;
- int e_uid, e_gid;
+ int i;
int retval;
int sh_bang = 0;
- unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
- if (regs.cs != USER_CS)
- panic("VFS: execve called from supervisor mode");
+ if (regs->cs != USER_CS)
+ return -EINVAL;
+ bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4;
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
- page[i]=0;
- retval = namei(filename,&inode); /* get executable inode */
+ bprm.page[i] = 0;
+ retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
if (retval)
return retval;
- argc = count(argv);
- envc = count(envp);
+ bprm.filename = filename;
+ bprm.argc = count(argv);
+ bprm.envc = count(envp);
restart_interp:
- if (!S_ISREG(inode->i_mode)) { /* must be regular file */
+ if (!S_ISREG(bprm.inode->i_mode)) { /* must be regular file */
retval = -EACCES;
goto exec_error2;
}
- if (IS_NOEXEC(inode)) { /* FS mustn't be mounted noexec */
+ if (IS_NOEXEC(bprm.inode)) { /* FS mustn't be mounted noexec */
retval = -EPERM;
goto exec_error2;
}
- if (!inode->i_sb) {
+ if (!bprm.inode->i_sb) {
retval = -EACCES;
goto exec_error2;
}
- i = inode->i_mode;
- if (IS_NOSUID(inode) && (((i & S_ISUID) && inode->i_uid != current->
- euid) || ((i & S_ISGID) && !in_group_p(inode->i_gid))) &&
+ i = bprm.inode->i_mode;
+ if (IS_NOSUID(bprm.inode) && (((i & S_ISUID) && bprm.inode->i_uid != current->
+ euid) || ((i & S_ISGID) && !in_group_p(bprm.inode->i_gid))) &&
!suser()) {
retval = -EPERM;
goto exec_error2;
}
/* make sure we don't let suid, sgid files be ptraced. */
if (current->flags & PF_PTRACED) {
- e_uid = current->euid;
- e_gid = current->egid;
+ bprm.e_uid = current->euid;
+ bprm.e_gid = current->egid;
} else {
- e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
- e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
+ bprm.e_uid = (i & S_ISUID) ? bprm.inode->i_uid : current->euid;
+ bprm.e_gid = (i & S_ISGID) ? bprm.inode->i_gid : current->egid;
}
- if (current->euid == inode->i_uid)
+ if (current->euid == bprm.inode->i_uid)
i >>= 6;
- else if (in_group_p(inode->i_gid))
+ else if (in_group_p(bprm.inode->i_gid))
i >>= 3;
if (!(i & 1) &&
- !((inode->i_mode & 0111) && suser())) {
+ !((bprm.inode->i_mode & 0111) && suser())) {
retval = -EACCES;
goto exec_error2;
}
- memset(buf,0,sizeof(buf));
+ memset(bprm.buf,0,sizeof(bprm.buf));
old_fs = get_fs();
set_fs(get_ds());
- retval = read_exec(inode,0,buf,128);
+ retval = read_exec(bprm.inode,0,bprm.buf,128);
set_fs(old_fs);
if (retval < 0)
goto exec_error2;
- ex = *((struct exec *) buf); /* exec-header */
- if ((buf[0] == '#') && (buf[1] == '!') && (!sh_bang)) {
+ if ((bprm.buf[0] == '#') && (bprm.buf[1] == '!') && (!sh_bang)) {
/*
* This section does the #! interpretation.
* Sorta complicated, but hopefully it will work. -TYT
@@ -547,19 +575,19 @@ restart_interp:
char *cp, *interp, *i_name, *i_arg;
- iput(inode);
- buf[127] = '\0';
- if ((cp = strchr(buf, '\n')) == NULL)
- cp = buf+127;
+ iput(bprm.inode);
+ bprm.buf[127] = '\0';
+ if ((cp = strchr(bprm.buf, '\n')) == NULL)
+ cp = bprm.buf+127;
*cp = '\0';
- while (cp > buf) {
+ while (cp > bprm.buf) {
cp--;
if ((*cp == ' ') || (*cp == '\t'))
*cp = '\0';
else
break;
}
- for (cp = buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
+ for (cp = bprm.buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
if (!cp || *cp == '\0') {
retval = -ENOEXEC; /* No interpreter name found */
goto exec_error1;
@@ -579,8 +607,8 @@ restart_interp:
* (optional) argument.
*/
if (sh_bang++ == 0) {
- p = copy_strings(envc, envp, page, p, 0);
- p = copy_strings(--argc, argv+1, page, p, 0);
+ bprm.p = copy_strings(bprm.envc, envp, bprm.page, bprm.p, 0);
+ bprm.p = copy_strings(--bprm.argc, argv+1, bprm.page, bprm.p, 0);
}
/*
* Splice in (1) the interpreter's name for argv[0]
@@ -590,15 +618,15 @@ restart_interp:
* This is done in reverse order, because of how the
* user environment and arguments are stored.
*/
- p = copy_strings(1, &filename, page, p, 1);
- argc++;
+ bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
+ bprm.argc++;
if (i_arg) {
- p = copy_strings(1, &i_arg, page, p, 2);
- argc++;
+ bprm.p = copy_strings(1, &i_arg, bprm.page, bprm.p, 2);
+ bprm.argc++;
}
- p = copy_strings(1, &i_name, page, p, 2);
- argc++;
- if (!p) {
+ bprm.p = copy_strings(1, &i_name, bprm.page, bprm.p, 2);
+ bprm.argc++;
+ if (!bprm.p) {
retval = -E2BIG;
goto exec_error1;
}
@@ -607,71 +635,198 @@ restart_interp:
* Note that we use open_namei() as the name is now in kernel
* space, and we don't need to copy it.
*/
- retval = open_namei(interp,0,0,&inode,NULL);
+ retval = open_namei(interp, 0, 0, &bprm.inode, NULL);
if (retval)
goto exec_error1;
goto restart_interp;
}
+ if (!sh_bang) {
+ bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
+ bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0);
+ if (!bprm.p) {
+ retval = -E2BIG;
+ goto exec_error2;
+ }
+ }
+
+ fmt = formats;
+ do {
+ int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+ if (!fn)
+ break;
+ retval = fn(&bprm, regs);
+ if (retval == 0) {
+ iput(bprm.inode);
+ return 0;
+ }
+ fmt++;
+ } while (retval == -ENOEXEC);
+exec_error2:
+ iput(bprm.inode);
+exec_error1:
+ for (i=0 ; i<MAX_ARG_PAGES ; i++)
+ free_page(bprm.page[i]);
+ return(retval);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+extern "C" int sys_execve(struct pt_regs regs)
+{
+ int error;
+ char * filename;
+
+ error = getname((char *) regs.ebx, &filename);
+ if (error)
+ return error;
+ error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
+ putname(filename);
+ return error;
+}
+
+/*
+ * These are the prototypes for the functions in the dispatch table, as
+ * well as the dispatch table itself.
+ */
+
+extern int load_aout_binary(struct linux_binprm *,
+ struct pt_regs * regs);
+extern int load_aout_library(int fd);
+
+/* Here are the actual binaries that will be accepted */
+struct linux_binfmt formats[] = {
+ {load_aout_binary, load_aout_library},
+ {NULL, NULL}
+};
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct exec ex;
+ struct file * file;
+ int fd, error;
+ unsigned long p = bprm->p;
+
+ ex = *((struct exec *) bprm->buf); /* exec-header */
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) ||
- ex.a_trsize || ex.a_drsize ||
- inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
- retval = -ENOEXEC;
- goto exec_error2;
+ ex.a_trsize || ex.a_drsize ||
+ bprm->inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+ return -ENOEXEC;
}
if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
- (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+ (N_TXTOFF(ex) < bprm->inode->i_sb->s_blocksize)) {
printk("N_TXTOFF < BLOCK_SIZE. Please convert binary.");
- retval = -ENOEXEC;
- goto exec_error2;
+ return -ENOEXEC;
}
if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) {
printk("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
- retval = -ENOEXEC;
- goto exec_error2;
- }
- if (!sh_bang) {
- p = copy_strings(envc,envp,page,p,0);
- p = copy_strings(argc,argv,page,p,0);
- if (!p) {
- retval = -E2BIG;
- goto exec_error2;
- }
+ return -ENOEXEC;
}
-/* OK, This is the point of no return */
- flush_old_exec(inode, filename, e_uid, e_gid);
- p += change_ldt(ex.a_text,page);
- p -= MAX_ARG_PAGES*PAGE_SIZE;
- p = (unsigned long) create_tables((char *)p,argc,envc);
- current->brk = ex.a_bss +
+
+ /* OK, This is the point of no return */
+ flush_old_exec(bprm);
+ current->start_brk = current->brk = ex.a_bss +
(current->end_data = ex.a_data +
- (current->end_code = ex.a_text));
- current->start_stack = p;
- current->rss = (TASK_SIZE - p + PAGE_SIZE-1) / PAGE_SIZE;
- current->suid = current->euid = e_uid;
+ (current->end_code = ex.a_text));
+ current->rss = 0;
+ current->suid = current->euid = bprm->e_uid;
current->mmap = NULL;
- current->sgid = current->egid = e_gid;
+ current->executable = NULL; /* for OMAGIC files */
+ current->sgid = current->egid = bprm->e_gid;
if (N_MAGIC(ex) == OMAGIC) {
- read_exec(inode, 32, (char *) 0, ex.a_text+ex.a_data);
- iput(inode);
- } else if (!inode->i_op || !inode->i_op->bmap) {
- read_exec(inode, 1024, (char *) 0, ex.a_text+ex.a_data);
- iput(inode);
+ read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data);
} else {
if (ex.a_text & 0xfff || ex.a_data & 0xfff)
printk("%s: executable not page aligned\n", current->comm);
- current->executable = inode;
+
+ fd = open_inode(bprm->inode, O_RDONLY);
+
+ if (fd < 0)
+ return fd;
+ file = current->filp[fd];
+ if (!file->f_op || !file->f_op->mmap) {
+ sys_close(fd);
+ read_exec(bprm->inode, 1024, (char *) 0, ex.a_text+ex.a_data);
+ goto beyond_if;
+ }
+ error = do_mmap(file, 0, ex.a_text,
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_SHARED, N_TXTOFF(ex));
+ if (error != 0) {
+ sys_close(fd);
+ send_sig(SIGSEGV, current, 0);
+ return 0;
+ };
+
+ error = do_mmap(file, ex.a_text, ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, N_TXTOFF(ex) + ex.a_text);
+ sys_close(fd);
+ if (error != ex.a_text) {
+ send_sig(SIGSEGV, current, 0);
+ return 0;
+ };
+ current->executable = bprm->inode;
+ bprm->inode->i_count++;
}
+beyond_if:
zeromap_page_range((ex.a_text + ex.a_data + 0xfff) & 0xfffff000,ex.a_bss, PAGE_COPY);
- regs.eip = ex.a_entry; /* eip, magic happens :-) */
- regs.esp = p; /* stack pointer */
+ p += change_ldt(ex.a_text,bprm->page);
+ p -= MAX_ARG_PAGES*PAGE_SIZE;
+ p = (unsigned long) create_tables((char *)p,bprm->argc,bprm->envc);
+ current->start_stack = p;
+ regs->eip = ex.a_entry; /* eip, magic happens :-) */
+ regs->esp = p; /* stack pointer */
if (current->flags & PF_PTRACED)
send_sig(SIGTRAP, current, 0);
return 0;
-exec_error2:
- iput(inode);
-exec_error1:
- for (i=0 ; i<MAX_ARG_PAGES ; i++)
- free_page(page[i]);
- return(retval);
}
+
+int load_aout_library(int fd)
+{
+ struct file * file;
+ struct exec ex;
+ struct inode * inode;
+ unsigned int len;
+ unsigned int bss;
+ int error;
+
+ file = current->filp[fd];
+ inode = file->f_inode;
+
+ set_fs(KERNEL_DS);
+ if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
+ return -EACCES;
+ }
+ set_fs(USER_DS);
+
+ /* We come in here for the regular a.out style of shared libraries */
+ if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize ||
+ ex.a_drsize || ex.a_entry & 0xfff ||
+ inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+ if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
+ (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+ printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+ return -ENOEXEC;
+ }
+
+ /* Now use mmap to map the library into memory. */
+ error = do_mmap(file, ex.a_entry, ex.a_text + ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
+ N_TXTOFF(ex));
+ if (error != ex.a_entry)
+ return error;
+ len = (ex.a_text + ex.a_data + 0xfff) & 0xfffff000;
+ bss = ex.a_text + ex.a_data + ex.a_bss;
+ if (bss > len)
+ zeromap_page_range(ex.a_entry + len, bss-len, PAGE_COPY);
+ return 0;
+}
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index adf834a..ae4d89b 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/ext2_fs.h>
#include <linux/errno.h>
+#include <linux/stat.h>
/*
* ext2_permission ()
@@ -30,7 +31,7 @@ int ext2_permission (struct inode * inode, int mask)
mode >>= 6;
else if (in_group_p (inode->i_gid))
mode >>= 3;
- if (((mode & mask & 0007) == mask))
+ if (((mode & mask & S_IRWXO) == mask))
return 1;
else
return 0;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 7f8b1c8..be9b8b7 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -35,7 +35,7 @@
"rep\n\t" \
"stosl" \
: \
- :"a" (0), "c" (size/4), "D" ((long) (addr)) \
+ :"a" (0), "c" (size / 4), "D" ((long) (addr)) \
:"cx", "di")
static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
@@ -58,7 +58,7 @@ static inline int find_first_zero_bit (unsigned long * addr, unsigned size)
shll $3,%%edi
addl %%edi,%%edx"
:"=d" (res)
- :"c" ((size+31)>>5), "D" (addr), "b" (addr)
+ :"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
:"ax", "bx", "cx", "di");
return res;
}
@@ -239,13 +239,12 @@ void ext2_free_block (struct super_block * sb, unsigned long block)
return;
}
lock_super (sb);
- if (block < sb->u.ext2_sb.s_first_data_block ||
- block >= sb->u.ext2_sb.s_blocks_count) {
+ es = sb->u.ext2_sb.s_es;
+ if (block < es->s_first_data_block || block >= es->s_blocks_count) {
printk ("ext2_free_block: block not in datazone\n");
unlock_super (sb);
return;
}
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
#ifdef EXT2FS_DEBUG
printk ("ext2_free_block: freeing block %d\n", block);
#endif
@@ -253,10 +252,9 @@ void ext2_free_block (struct super_block * sb, unsigned long block)
if (bh)
bh->b_dirt = 0;
brelse (bh);
- block_group = (block - sb->u.ext2_sb.s_first_data_block) /
+ block_group = (block - es->s_first_data_block) /
EXT2_BLOCKS_PER_GROUP(sb);
- bit = (block - sb->u.ext2_sb.s_first_data_block) %
- EXT2_BLOCKS_PER_GROUP(sb);
+ bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
bitmap_nr = load_block_bitmap (sb, block_group);
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
if (!bh) {
@@ -275,11 +273,11 @@ void ext2_free_block (struct super_block * sb, unsigned long block)
panic ("ext2_free_block: Group descriptor not loaded");
}
gdp = (struct ext2_group_desc *) bh2->b_data;
- gdp[desc].bg_free_blocks_count ++;
+ gdp[desc].bg_free_blocks_count++;
bh2->b_dirt = 1;
}
bh->b_dirt = 1;
- es->s_free_blocks_count ++;
+ es->s_free_blocks_count++;
sb->u.ext2_sb.s_sbh->b_dirt = 1;
sb->s_dirt = 1;
unlock_super (sb);
@@ -313,7 +311,7 @@ int ext2_new_block (struct super_block * sb, unsigned long goal)
return 0;
}
lock_super (sb);
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es = sb->u.ext2_sb.s_es;
if (es->s_free_blocks_count <= es->s_r_blocks_count && !suser()) {
unlock_super (sb);
return 0;
@@ -325,8 +323,7 @@ int ext2_new_block (struct super_block * sb, unsigned long goal)
repeat:
/* First, test whether the goal block is free. */
- i = ((goal - sb->u.ext2_sb.s_first_data_block) /
- EXT2_BLOCKS_PER_GROUP(sb));
+ i = ((goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb));
group_desc = i / EXT2_DESC_PER_BLOCK(sb);
desc = i % EXT2_DESC_PER_BLOCK(sb);
gdp = (struct ext2_group_desc *)
@@ -335,7 +332,7 @@ repeat:
panic ("ext2_new_block: Descriptor not loaded");
}
if (gdp[desc].bg_free_blocks_count > 0) {
- j = ((goal - sb->u.ext2_sb.s_first_data_block) %
+ j = ((goal - es->s_first_data_block) %
EXT2_BLOCKS_PER_GROUP(sb));
#ifdef EXT2FS_DEBUG
if (j)
@@ -475,8 +472,8 @@ got_block:
#ifdef EXT2FS_DEBUG
printk ("ext2_new_block: found bit %d\n", j);
#endif
- j += i * EXT2_BLOCKS_PER_GROUP(sb) + sb->u.ext2_sb.s_first_data_block;
- if (j >= sb->u.ext2_sb.s_blocks_count) {
+ j += i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
+ if (j >= es->s_blocks_count) {
printk ("block_group = %d,block=%d\n", i, j);
printk ("ext2_new_block: block >= blocks count");
unlock_super (sb);
@@ -495,9 +492,9 @@ got_block:
printk("ext2_new_block: allocating block %d. "
"Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
#endif
- gdp[desc].bg_free_blocks_count --;
+ gdp[desc].bg_free_blocks_count--;
sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1;
- es->s_free_blocks_count --;
+ es->s_free_blocks_count--;
sb->u.ext2_sb.s_sbh->b_dirt = 1;
sb->s_dirt = 1;
unlock_super (sb);
@@ -506,8 +503,8 @@ got_block:
unsigned long ext2_count_free_blocks (struct super_block *sb)
{
- struct ext2_super_block * es;
#ifdef EXT2FS_DEBUG
+ struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
unsigned long group_desc;
unsigned long desc;
@@ -516,7 +513,7 @@ unsigned long ext2_count_free_blocks (struct super_block *sb)
int i;
lock_super (sb);
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
group_desc = 0;
@@ -545,9 +542,9 @@ unsigned long ext2_count_free_blocks (struct super_block *sb)
printk ("group %d: stored = %d, counted = %d\n",
i, gdp[desc].bg_free_blocks_count, x);
bitmap_count += x;
- desc ++;
+ desc++;
if (desc == EXT2_DESC_PER_BLOCK(sb)) {
- group_desc ++;
+ group_desc++;
desc = 0;
gdp = NULL;
}
@@ -557,7 +554,6 @@ unsigned long ext2_count_free_blocks (struct super_block *sb)
unlock_super (sb);
return bitmap_count;
#else
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
- return es->s_free_blocks_count;
+ return sb->u.ext2_sb.s_es->s_free_blocks_count;
#endif
}
diff --git a/fs/ext2/dcache.c b/fs/ext2/dcache.c
index a74be03..4d61691 100644
--- a/fs/ext2/dcache.c
+++ b/fs/ext2/dcache.c
@@ -25,15 +25,15 @@ struct dir_cache_entry {
unsigned long ino;
char name[EXT2_NAME_LEN];
int len;
- struct dir_cache_entry *queue_prev;
- struct dir_cache_entry *queue_next;
- struct dir_cache_entry *prev;
- struct dir_cache_entry *next;
+ struct dir_cache_entry * queue_prev;
+ struct dir_cache_entry * queue_next;
+ struct dir_cache_entry * prev;
+ struct dir_cache_entry * next;
};
-static struct dir_cache_entry *first = NULL;
-static struct dir_cache_entry *last = NULL;
-static struct dir_cache_entry *first_free = NULL;
+static struct dir_cache_entry * first = NULL;
+static struct dir_cache_entry * last = NULL;
+static struct dir_cache_entry * first_free = NULL;
static int cache_initialized = 0;
#ifdef EXT2FS_DEBUG_CACHE
static int hits = 0;
@@ -46,8 +46,8 @@ static struct dir_cache_entry dcache[CACHE_SIZE];
#define HASH_QUEUES 16
-static struct dir_cache_entry *queue_head[HASH_QUEUES];
-static struct dir_cache_entry *queue_tail[HASH_QUEUES];
+static struct dir_cache_entry * queue_head[HASH_QUEUES];
+static struct dir_cache_entry * queue_tail[HASH_QUEUES];
#define hash(dev,dir) ((dev ^ dir) % HASH_QUEUES)
@@ -78,14 +78,15 @@ static void init_cache (void)
/*
* Find a name in the cache
*/
-static struct dir_cache_entry *find_name (int queue, unsigned short dev,
- unsigned long dir, const char *name,
- int len)
+static struct dir_cache_entry * find_name (int queue, unsigned short dev,
+ unsigned long dir,
+ const char * name, int len)
{
- struct dir_cache_entry *p;
+ struct dir_cache_entry * p;
- for (p = queue_head[queue]; p!= NULL && (p->dev != dev ||
- p->dir != dir || p->len != len || strncmp (name, p->name, p->len));
+ for (p = queue_head[queue]; p != NULL && (p->dev != dev ||
+ p->dir != dir || p->len != len ||
+ strncmp (name, p->name, p->len) != 0);
p = p->queue_next)
;
return p;
@@ -95,9 +96,9 @@ static struct dir_cache_entry *find_name (int queue, unsigned short dev,
/*
* List the cache entries for debugging
*/
-static void show_cache (const char *func_name)
+static void show_cache (const char * func_name)
{
- struct dir_cache_entry *p;
+ struct dir_cache_entry * p;
printk ("%s: cache status\n", func_name);
for (p = first; p != NULL; p = p->next)
@@ -109,7 +110,7 @@ static void show_cache (const char *func_name)
/*
* Add an entry at the beginning of the cache
*/
-static void add_to_cache (struct dir_cache_entry *p)
+static void add_to_cache (struct dir_cache_entry * p)
{
p->prev = NULL;
p->next = first;
@@ -123,7 +124,7 @@ static void add_to_cache (struct dir_cache_entry *p)
/*
* Add an entry at the beginning of a queue
*/
-static void add_to_queue (int queue, struct dir_cache_entry *p)
+static void add_to_queue (int queue, struct dir_cache_entry * p)
{
p->queue_prev = NULL;
p->queue_next = queue_head[queue];
@@ -137,7 +138,7 @@ static void add_to_queue (int queue, struct dir_cache_entry *p)
/*
* Remove an entry from the cache
*/
-static void remove_from_cache (struct dir_cache_entry *p)
+static void remove_from_cache (struct dir_cache_entry * p)
{
if (p->prev)
p->prev->next = p->next;
@@ -152,7 +153,7 @@ static void remove_from_cache (struct dir_cache_entry *p)
/*
* Remove an entry from a queue
*/
-static void remove_from_queue (int queue, struct dir_cache_entry *p)
+static void remove_from_queue (int queue, struct dir_cache_entry * p)
{
if (p->queue_prev)
p->queue_prev->queue_next = p->queue_next;
@@ -170,8 +171,8 @@ static void remove_from_queue (int queue, struct dir_cache_entry *p)
*/
void ext2_dcache_invalidate (unsigned short dev)
{
- struct dir_cache_entry *p;
- struct dir_cache_entry *p2;
+ struct dir_cache_entry * p;
+ struct dir_cache_entry * p2;
if (!cache_initialized)
init_cache ();
@@ -193,11 +194,11 @@ void ext2_dcache_invalidate (unsigned short dev)
* Lookup a directory entry in the cache
*/
unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
- const char *name, int len)
+ const char * name, int len)
{
char our_name[EXT2_NAME_LEN];
int queue;
- struct dir_cache_entry *p;
+ struct dir_cache_entry * p;
if (!cache_initialized)
init_cache ();
@@ -208,7 +209,7 @@ unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
#ifdef EXT2FS_DEBUG_CACHE
printk ("dcache_lookup (%04x, %d, %s, %d)\n", dev, dir, our_name, len);
#endif
- queue = hash(dev, dir);
+ queue = hash (dev, dir);
if ((p = find_name (queue, dev, dir, our_name, len))) {
if (p != first) {
remove_from_cache (p);
@@ -219,7 +220,7 @@ unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
add_to_queue (queue, p);
}
#ifdef EXT2FS_DEBUG_CACHE
- hits ++;
+ hits++;
printk ("dcache_lookup: %s,hit,inode=%d,hits=%d,misses=%d\n",
our_name, p->ino, hits, misses);
show_cache ("dcache_lookup");
@@ -227,7 +228,7 @@ unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
return p->ino;
} else {
#ifdef EXT2FS_DEBUG_CACHE
- misses ++;
+ misses++;
printk ("dcache_lookup: %s,miss,hits=%d,misses=%d\n",
our_name, hits, misses);
show_cache ("dcache_lookup");
@@ -242,10 +243,10 @@ 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
*/
-void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
+void ext2_dcache_add (unsigned short dev, unsigned long dir, const char * name,
int len, int ino)
{
- struct dir_cache_entry *p;
+ struct dir_cache_entry * p;
int queue;
if (!cache_initialized)
@@ -256,7 +257,7 @@ void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
#endif
if (len > EXT2_NAME_LEN)
len = EXT2_NAME_LEN;
- queue = hash(dev, dir);
+ queue = hash (dev, dir);
if ((p = find_name (queue, dev, dir, name, len))) {
p->dir = dir;
p->ino = ino;
@@ -303,9 +304,9 @@ void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
* This function is called by the functions which remove directory entries
*/
void ext2_dcache_remove (unsigned short dev, unsigned long dir,
- const char *name, int len)
+ const char * name, int len)
{
- struct dir_cache_entry *p;
+ struct dir_cache_entry * p;
int queue;
if (!cache_initialized)
@@ -315,7 +316,7 @@ void ext2_dcache_remove (unsigned short dev, unsigned long dir,
#endif
if (len > EXT2_NAME_LEN)
len = EXT2_NAME_LEN;
- queue = hash(dev, dir);
+ queue = hash (dev, dir);
if ((p = find_name (queue, dev, dir, name, len))) {
remove_from_cache (p);
remove_from_queue (queue, p);
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index a828ace..4634b7e 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -104,7 +104,8 @@ static int ext2_readdir (struct inode * inode, struct file * filp,
sb = inode->i_sb;
while (filp->f_pos < inode->i_size) {
offset = filp->f_pos & (sb->s_blocksize - 1);
- bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err);
+ bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb),
+ 0, &err);
if (!bh) {
filp->f_pos += sb->s_blocksize - offset;
continue;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index a8a7f3e..ee7b273 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -44,7 +44,7 @@ static struct file_operations ext2_file_operations = {
ext2_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
- ext2_ioctl, /* ioctl - default */
+ ext2_ioctl, /* ioctl */
generic_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
@@ -228,9 +228,6 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
while (written < count) {
bh = ext2_getblk (inode, pos / sb->s_blocksize, 1, &err);
if (!bh) {
-#ifdef EXT2FS_DEBUG
- printk ("ext2_file_write: ext2_getblk returned NULL\n");
-#endif
if (!written)
written = err;
break;
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index 3c85c4e..08513c3 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -35,7 +35,7 @@ static int sync_block (struct inode * inode, unsigned long * block, int wait)
if (!*block)
return 0;
tmp = *block;
- bh = get_hash_table(inode->i_dev, *block, blocksize);
+ bh = get_hash_table (inode->i_dev, *block, blocksize);
if (!bh)
return 0;
if (*block != tmp) {
@@ -43,21 +43,20 @@ static int sync_block (struct inode * inode, unsigned long * block, int wait)
return 1;
}
if (wait && bh->b_req && !bh->b_uptodate) {
- brelse(bh);
+ brelse (bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt)
- {
- brelse(bh);
+ if (wait || !bh->b_uptodate || !bh->b_dirt) {
+ brelse (bh);
return 0;
}
- ll_rw_block(WRITE, 1, &bh);
+ 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)
+ struct buffer_head ** bh, int wait)
{
int rc, tmp;
@@ -68,9 +67,9 @@ static int sync_iblock (struct inode * inode, unsigned long * iblock,
rc = sync_block (inode, iblock, wait);
if (rc)
return rc;
- *bh = bread(inode->i_dev, tmp, blocksize);
+ *bh = bread (inode->i_dev, tmp, blocksize);
if (tmp != *iblock) {
- brelse(*bh);
+ brelse (*bh);
*bh = NULL;
return 1;
}
@@ -80,7 +79,7 @@ static int sync_iblock (struct inode * inode, unsigned long * iblock,
}
-static int sync_direct(struct inode *inode, int wait)
+static int sync_direct (struct inode * inode, int wait)
{
int i;
int rc, err = 0;
@@ -95,7 +94,8 @@ static int sync_direct(struct inode *inode, int wait)
return err;
}
-static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+static int sync_indirect (struct inode * inode, unsigned long * iblock,
+ int wait)
{
int i;
struct buffer_head * ind_bh;
@@ -114,12 +114,12 @@ static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
if (rc)
err = rc;
}
- brelse(ind_bh);
+ brelse (ind_bh);
return err;
}
-static int sync_dindirect(struct inode *inode, unsigned long *diblock,
- int wait)
+static int sync_dindirect (struct inode * inode, unsigned long * diblock,
+ int wait)
{
int i;
struct buffer_head * dind_bh;
@@ -138,12 +138,12 @@ static int sync_dindirect(struct inode *inode, unsigned long *diblock,
if (rc)
err = rc;
}
- brelse(dind_bh);
+ brelse (dind_bh);
return err;
}
-static int sync_tindirect(struct inode *inode, unsigned long *tiblock,
- int wait)
+static int sync_tindirect (struct inode * inode, unsigned long * tiblock,
+ int wait)
{
int i;
struct buffer_head * tind_bh;
@@ -162,11 +162,11 @@ static int sync_tindirect(struct inode *inode, unsigned long *tiblock,
if (rc)
err = rc;
}
- brelse(tind_bh);
+ brelse (tind_bh);
return err;
}
-int ext2_sync_file(struct inode * inode, struct file *file)
+int ext2_sync_file (struct inode * inode, struct file * file)
{
int wait, err = 0;
@@ -179,16 +179,16 @@ int ext2_sync_file(struct inode * inode, struct file *file)
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,
+ 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);
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 02f768e..de0046f 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -221,12 +221,13 @@ void ext2_free_inode (struct inode * inode)
#endif
sb = inode->i_sb;
lock_super (sb);
- if (inode->i_ino < 1 || inode->i_ino > sb->u.ext2_sb.s_inodes_count) {
+ if (inode->i_ino < 1 ||
+ inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
printk("free_inode: inode 0 or nonexistent inode\n");
unlock_super (sb);
return;
}
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es = sb->u.ext2_sb.s_es;
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
@@ -247,14 +248,14 @@ void ext2_free_inode (struct inode * inode)
panic ("ext2_free_inode: Group descriptor not loaded");
}
gdp = (struct ext2_group_desc *) bh2->b_data;
- gdp[desc].bg_free_inodes_count ++;
+ gdp[desc].bg_free_inodes_count++;
if (S_ISDIR(inode->i_mode))
- gdp[desc].bg_used_dirs_count --;
+ gdp[desc].bg_used_dirs_count--;
bh2->b_dirt = 1;
set_inode_dtime (inode, gdp, desc);
}
bh->b_dirt = 1;
- es->s_free_inodes_count ++;
+ es->s_free_inodes_count++;
sb->u.ext2_sb.s_sbh->b_dirt = 1;
sb->s_dirt = 1;
unlock_super (sb);
@@ -295,10 +296,11 @@ static void inc_inode_version (struct inode * inode,
brelse (bh);
}
-static struct ext2_group_desc * get_group_desc(struct super_block * sb,
- int group)
+static struct ext2_group_desc * get_group_desc (struct super_block * sb,
+ int group)
{
struct ext2_group_desc * gdp;
+
if (group >= sb->u.ext2_sb.s_groups_count || group < 0 )
panic ("ext2: get_group_desc: Invalid group\n");
if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)])
@@ -335,7 +337,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode)
inode->i_sb = sb;
inode->i_flags = sb->s_flags;
lock_super (sb);
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es = sb->u.ext2_sb.s_es;
repeat:
gdp = NULL; i=0;
@@ -345,7 +347,7 @@ repeat:
/* I am not yet convinced that this next bit is necessary.
i = dir->u.ext2_i.i_block_group;
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
- tmp = get_group_desc(sb, i);
+ tmp = get_group_desc (sb, i);
if ((tmp->bg_used_dirs_count << 8) <
tmp->bg_free_inodes_count) {
gdp = tmp;
@@ -357,7 +359,7 @@ repeat:
*/
if (!gdp) {
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
- tmp = get_group_desc(sb, j);
+ tmp = get_group_desc (sb, j);
if (tmp->bg_free_inodes_count &&
tmp->bg_free_inodes_count >= avefreei) {
if (!gdp ||
@@ -373,7 +375,7 @@ repeat:
else
{ /* Try to place the inode in it\'s parent directory */
i = dir->u.ext2_i.i_block_group;
- tmp = get_group_desc(sb, i);
+ tmp = get_group_desc (sb, i);
if (tmp->bg_free_inodes_count)
gdp = tmp;
else
@@ -382,7 +384,7 @@ repeat:
i += j;
if (i >= sb->u.ext2_sb.s_groups_count)
i -= sb->u.ext2_sb.s_groups_count;
- tmp = get_group_desc(sb, i);
+ tmp = get_group_desc (sb, i);
if (tmp->bg_free_inodes_count) {
gdp = tmp;
break;
@@ -395,7 +397,7 @@ repeat:
for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) {
if (++i >= sb->u.ext2_sb.s_groups_count)
i = 0;
- tmp = get_group_desc(sb,i);
+ tmp = get_group_desc (sb,i);
if (tmp->bg_free_inodes_count) {
gdp = tmp;
break;
@@ -426,16 +428,18 @@ repeat:
} else
goto repeat;
j += i * EXT2_INODES_PER_GROUP(sb) + 1;
- if (j > sb->u.ext2_sb.s_inodes_count) {
+ if (j > es->s_inodes_count) {
printk ("block_group = %d,inode=%d\n", i, j);
printk ("ext2_new_inode: inode > inodes count");
+ unlock_super (sb);
+ iput (inode);
return NULL;
}
- gdp->bg_free_inodes_count --;
+ gdp->bg_free_inodes_count--;
if (S_ISDIR(mode))
- gdp->bg_used_dirs_count ++;
+ gdp->bg_used_dirs_count++;
sb->u.ext2_sb.s_group_desc[i / EXT2_DESC_PER_BLOCK(sb)]->b_dirt = 1;
- es->s_free_inodes_count --;
+ es->s_free_inodes_count--;
sb->u.ext2_sb.s_sbh->b_dirt = 1;
sb->s_dirt = 1;
inode->i_mode = mode;
@@ -470,8 +474,8 @@ repeat:
unsigned long ext2_count_free_inodes (struct super_block *sb)
{
- struct ext2_super_block * es;
#ifdef EXT2FS_DEBUG
+ struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
unsigned long group_desc;
unsigned long desc;
@@ -480,7 +484,7 @@ unsigned long ext2_count_free_inodes (struct super_block *sb)
int i;
lock_super (sb);
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
group_desc = 0;
@@ -507,9 +511,9 @@ unsigned long ext2_count_free_inodes (struct super_block *sb)
printk ("group %d: stored = %d, counted = %d\n",
i, gdp[desc].bg_free_inodes_count, x);
bitmap_count += x;
- desc ++;
+ desc++;
if (desc == EXT2_DESC_PER_BLOCK(sb)) {
- group_desc ++;
+ group_desc++;
desc = 0;
gdp = NULL;
}
@@ -519,7 +523,6 @@ unsigned long ext2_count_free_inodes (struct super_block *sb)
unlock_super (sb);
return desc_count;
#else
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
- return es->s_free_inodes_count;
+ return sb->u.ext2_sb.s_es->s_free_inodes_count;
#endif
}
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 95bc2e6..78006de 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -37,13 +37,13 @@ void ext2_put_inode (struct inode * inode)
void ext2_put_super (struct super_block * sb)
{
- struct ext2_super_block * es;
int i;
lock_super (sb);
- es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
- es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
- sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ if ((sb->s_flags & MS_RDONLY) == 0) {
+ sb->u.ext2_sb.s_es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ }
#ifndef DONT_USE_DCACHE
ext2_dcache_invalidate (sb->s_dev);
#endif
@@ -57,6 +57,7 @@ void ext2_put_super (struct super_block * sb)
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
if (sb->u.ext2_sb.s_block_bitmap[i])
brelse (sb->u.ext2_sb.s_block_bitmap[i]);
+ brelse (sb->u.ext2_sb.s_sbh);
unlock_super (sb);
return;
}
@@ -127,7 +128,7 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
#endif
lock_super (s);
- set_blocksize(dev, BLOCK_SIZE);
+ set_blocksize (dev, BLOCK_SIZE);
if (!(bh = bread (dev, 1, BLOCK_SIZE))) {
s->s_dev = 0;
unlock_super (s);
@@ -135,40 +136,59 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
return NULL;
}
es = (struct ext2_super_block *) bh->b_data;
+ /* Note: s_es must be initialized s_es as soon as possible because
+ some ext2 macro-instructions depend on its value */
+ s->u.ext2_sb.s_es = es;
s->s_magic = es->s_magic;
+ if (s->s_magic != EXT2_SUPER_MAGIC
+#ifdef EXT2FS_PRE_02B_COMPAT
+ && s->s_magic != EXT2_OLD_SUPER_MAGIC
+#endif
+ ) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ if (!silent)
+ printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+ dev);
+ return NULL;
+ }
s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
- s->u.ext2_sb.s_log_block_size = es->s_log_block_size;
s->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(s);
if (s->s_blocksize != BLOCK_SIZE &&
(s->s_blocksize == 1024 || s->s_blocksize == 2048 ||
s->s_blocksize == 4096)) {
- brelse(bh);
- set_blocksize(dev, s->s_blocksize);
- bh = bread (dev, 0, s->s_blocksize);
+ brelse (bh);
+ set_blocksize (dev, s->s_blocksize);
+ bh = bread (dev, 0, s->s_blocksize);
if(!bh)
return NULL;
- es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE) ;
- };
- s->u.ext2_sb.s_inodes_count = es->s_inodes_count;
- s->u.ext2_sb.s_blocks_count = es->s_blocks_count;
- s->u.ext2_sb.s_r_blocks_count = es->s_r_blocks_count;
- s->u.ext2_sb.s_first_data_block = es->s_first_data_block;
- s->u.ext2_sb.s_log_frag_size = es->s_log_frag_size;
+ es = (struct ext2_super_block *) (((char *)bh->b_data) + BLOCK_SIZE);
+ s->u.ext2_sb.s_es = es;
+ if (es->s_magic != EXT2_SUPER_MAGIC) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("ext2-FS: Magic mismatch, very weird !\n");
+ return NULL;
+ }
+ }
s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
- es->s_log_frag_size;
+ es->s_log_frag_size;
if (s->u.ext2_sb.s_frag_size)
s->u.ext2_sb.s_frags_per_block = s->s_blocksize /
- s->u.ext2_sb.s_frag_size;
+ s->u.ext2_sb.s_frag_size;
else
s->s_magic = 0;
s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
s->u.ext2_sb.s_inodes_per_block = s->s_blocksize /
- sizeof (struct ext2_inode);
+ sizeof (struct ext2_inode);
s->u.ext2_sb.s_desc_per_block = s->s_blocksize /
- sizeof (struct ext2_group_desc);
+ sizeof (struct ext2_group_desc);
s->u.ext2_sb.s_sbh = bh;
+ s->u.ext2_sb.s_es = es;
s->u.ext2_sb.s_was_mounted_valid = es->s_valid;
s->u.ext2_sb.s_rename_lock = 0;
s->u.ext2_sb.s_rename_wait = NULL;
@@ -208,8 +228,8 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
unlock_super (s);
brelse (bh);
if (!silent)
- printk("VFS: Can't find an ext2fs filesystem on dev 0x%04x.\n",
- dev);
+ printk ("VFS: Can't find an ext2 filesystem on dev 0x%04x.\n",
+ dev);
return NULL;
}
if (s->s_blocksize != bh->b_size) {
@@ -217,45 +237,48 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
unlock_super (s);
brelse (bh);
if (!silent)
- printk("VFS: Unsupported blocksize on dev 0x%04x.\n",
- dev);
+ printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
+ dev);
return NULL;
}
+
if (s->s_blocksize != s->u.ext2_sb.s_frag_size) {
s->s_dev = 0;
unlock_super (s);
brelse (bh);
- printk ("EXT2-fs: fragsize != blocksize (not supported yet)\n");
+ printk ("EXT2-fs: fragsize %d != blocksize %d (not supported yet)\n",
+ s->u.ext2_sb.s_frag_size, s->s_blocksize);
return NULL;
}
+
if (!es->s_valid)
printk ("EXT2-fs warning: mounting unchecked file system, "
"running e2fsck is recommended\n");
- s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count -
- s->u.ext2_sb.s_first_data_block +
+ s->u.ext2_sb.s_groups_count = (es->s_blocks_count -
+ es->s_first_data_block +
EXT2_BLOCKS_PER_GROUP(s) - 1) /
EXT2_BLOCKS_PER_GROUP(s);
for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
s->u.ext2_sb.s_group_desc[i] = NULL;
- bh_count = (s->u.ext2_sb.s_groups_count +
- EXT2_DESC_PER_BLOCK(s) - 1) /
+ 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");
+ printk ("EXT2-fs: file system too big\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);
+ s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2,
+ s->s_blocksize);
if (!s->u.ext2_sb.s_group_desc[i]) {
s->s_dev = 0;
unlock_super (s);
for (j = 0; j < i; j++)
brelse (s->u.ext2_sb.s_group_desc[i]);
brelse (bh);
- printk ("ext2_read_super: unable to read group descriptors\n");
+ printk ("ext2-FS: unable to read group descriptors\n");
return NULL;
}
}
@@ -271,7 +294,7 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
/* set up enough so that it can read an inode */
s->s_dev = dev;
s->s_op = &ext2_sops;
- if (!(s->s_mounted = iget(s, EXT2_ROOT_INO))) {
+ if (!(s->s_mounted = iget (s, EXT2_ROOT_INO))) {
s->s_dev = 0;
for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
if (s->u.ext2_sb.s_group_desc[i])
@@ -309,51 +332,13 @@ struct super_block * ext2_read_super (struct super_block * s, void * data,
* flags to 0. We need to set this flag to 0 since the fs
* may have been checked while mounted and e2fsck may have
* set s_valid to 1 after some corrections.
- *
- * Note that this function also writes backups of the super block and
- * of the group descriptors in each group.
*/
-static void ext2_commit_super (struct super_block *sb,
- struct ext2_super_block *es)
+static void ext2_commit_super (struct super_block * sb,
+ struct ext2_super_block * es)
{
- struct buffer_head * bh;
- unsigned long block;
- unsigned long bh_count;
- int i, j;
-
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_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_commit_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_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);
- }
- }
- }
sb->s_dirt = 0;
}
@@ -362,7 +347,7 @@ 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;
+ es = sb->u.ext2_sb.s_es;
#ifdef EXT2FS_DEBUG
printk ("ext2_write_super: setting valid to 0\n");
#endif
@@ -372,11 +357,11 @@ void ext2_write_super (struct super_block * sb)
sb->s_dirt = 0;
}
-int ext2_remount(struct super_block *sb, int *flags)
+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;
+ es = sb->u.ext2_sb.s_es;
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
if (*flags & MS_RDONLY) {
@@ -387,13 +372,16 @@ int ext2_remount(struct super_block *sb, int *flags)
again. */
sb->s_flags |= MS_RDONLY;
es->s_valid = sb->u.ext2_sb.s_was_mounted_valid;
- ext2_commit_super(sb, es);
+ 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.) */
+ by e2fsck since we originally mounted the partition.) */
sb->u.ext2_sb.s_was_mounted_valid = es->s_valid;
+ if (!es->s_valid)
+ printk ("EXT2-fs warning: remounting unchecked fs, "
+ "running e2fsck is recommended\n");
}
return 0;
}
@@ -404,17 +392,16 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf)
put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type);
put_fs_long (sb->s_blocksize, &buf->f_bsize);
- put_fs_long (sb->u.ext2_sb.s_blocks_count >> sb->u.ext2_sb.s_log_block_size,
- &buf->f_blocks);
+ put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count, &buf->f_blocks);
tmp = ext2_count_free_blocks (sb);
put_fs_long (tmp, &buf->f_bfree);
- if (tmp >= sb->u.ext2_sb.s_r_blocks_count)
- put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count,
+ if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count)
+ put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count,
&buf->f_bavail);
else
put_fs_long (0, &buf->f_bavail);
- put_fs_long (sb->u.ext2_sb.s_inodes_count, &buf->f_files);
- put_fs_long (ext2_count_free_inodes(sb), &buf->f_ffree);
+ put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files);
+ put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree);
put_fs_long (EXT2_NAME_LEN, &buf->f_namelen);
/* Don't know what value to put in buf->f_fsid */
}
@@ -517,7 +504,7 @@ repeat:
printk ("ext2 inode_getblk: hint = %d,", goal);
#endif
if (!goal) {
- for (tmp = nr-1; tmp>=0; tmp--) {
+ for (tmp = nr - 1; tmp >= 0; tmp--) {
if (inode->u.ext2_i.i_data[tmp]) {
goal = inode->u.ext2_i.i_data[tmp];
break;
@@ -525,8 +512,8 @@ repeat:
}
if (!goal)
goal = (inode->u.ext2_i.i_block_group *
- EXT2_BLOCKS_PER_GROUP(inode->i_sb))
- + inode->i_sb->u.ext2_sb.s_first_data_block;
+ EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+ inode->i_sb->u.ext2_sb.s_es->s_first_data_block;
}
#ifdef EXT2FS_DEBUG
@@ -592,7 +579,7 @@ repeat:
if (inode->u.ext2_i.i_next_alloc_block == new_block)
goal = inode->u.ext2_i.i_next_alloc_goal;
if (!goal) {
- for (tmp = nr-1; tmp>=0; tmp--) {
+ for (tmp = nr - 1; tmp >= 0; tmp--) {
if (((unsigned long *) bh->b_data)[tmp]) {
goal = ((unsigned long *)bh->b_data)[tmp];
break;
@@ -714,8 +701,8 @@ void ext2_read_inode (struct inode * inode)
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_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+ printk ("ext2_read_inode: bad inode number on dev %0x04: %d\n",
inode->i_dev, inode->i_ino);
return;
}
@@ -790,7 +777,7 @@ static struct buffer_head * ext2_update_inode (struct inode * inode)
struct ext2_group_desc * gdp;
if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
- inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
+ inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n",
inode->i_dev, inode->i_ino);
return 0;
@@ -840,7 +827,7 @@ static struct buffer_head * ext2_update_inode (struct inode * inode)
void ext2_write_inode (struct inode * inode)
{
struct buffer_head * bh;
- bh = ext2_update_inode(inode);
+ bh = ext2_update_inode (inode);
brelse (bh);
}
@@ -849,11 +836,11 @@ int ext2_sync_inode (struct inode *inode)
int err = 0;
struct buffer_head *bh;
- bh = ext2_update_inode(inode);
+ bh = ext2_update_inode (inode);
if (bh && bh->b_dirt)
{
- ll_rw_block(WRITE, 1, &bh);
- wait_on_buffer(bh);
+ 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",
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index b0edb54..49e06cc 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -27,10 +27,6 @@
/* #define NO_TRUNCATE */
/*
- * ok, we cannot use strncmp, as the name is not in our data space.
- * Thus we'll have to use ext2_match. No big problem. ext2_match also makes
- * some sanity tests.
- *
* NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
*/
static int ext2_match (int len, const char * const name,
@@ -261,6 +257,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
for (i = 0; i < namelen ; i++)
de->name[i] = name [i];
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_dirt = 1;
bh->b_dirt = 1;
*res_dir = de;
*err = 0;
@@ -325,7 +322,7 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode,
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
@@ -388,7 +385,7 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
@@ -436,7 +433,7 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
dir_block = ext2_bread (inode, 0, 1, &err);
if (!dir_block) {
iput (dir);
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
return err;
@@ -455,7 +452,7 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
inode->i_nlink = 2;
dir_block->b_dirt = 1;
brelse (dir_block);
- inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+ inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
inode->i_dirt = 1;
@@ -472,7 +469,7 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
de->inode);
#endif
bh->b_dirt = 1;
- dir->i_nlink ++;
+ dir->i_nlink++;
dir->i_dirt = 1;
iput (dir);
iput (inode);
@@ -596,7 +593,7 @@ repeat:
bh->b_dirt = 1;
inode->i_nlink = 0;
inode->i_dirt = 1;
- dir->i_nlink --;
+ dir->i_nlink--;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
end_rmdir:
@@ -651,7 +648,7 @@ repeat:
bh->b_dirt = 1;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
retval = 0;
@@ -677,7 +674,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
iput (dir);
return -ENOSPC;
}
- inode->i_mode = S_IFLNK | 0777;
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &ext2_symlink_inode_operations;
for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
symname [l]; l++)
@@ -689,7 +686,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
name_block = ext2_bread (inode, 0, 1, &err);
if (!name_block) {
iput (dir);
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
return err;
@@ -702,7 +699,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
#endif
}
i = 0;
- while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname ++)))
+ while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
link[i++] = c;
link[i] = 0;
if (name_block) {
@@ -713,7 +710,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
inode->i_dirt = 1;
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
brelse (bh);
@@ -722,7 +719,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
}
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
- inode->i_nlink --;
+ inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
@@ -778,7 +775,7 @@ int ext2_link (struct inode * oldinode, struct inode * dir,
bh->b_dirt = 1;
brelse (bh);
iput (dir);
- oldinode->i_nlink ++;
+ oldinode->i_nlink++;
oldinode->i_ctime = CURRENT_TIME;
oldinode->i_dirt = 1;
iput (oldinode);
@@ -790,7 +787,7 @@ static int subdir (struct inode * new_inode, struct inode * old_inode)
int ino;
int result;
- new_inode->i_count ++;
+ new_inode->i_count++;
result = 0;
for (;;) {
if (new_inode == old_inode) {
@@ -937,7 +934,7 @@ start_up:
if (retval)
goto end_rename;
if (new_inode) {
- new_inode->i_nlink --;
+ new_inode->i_nlink--;
new_inode->i_dirt = 1;
}
old_bh->b_dirt = 1;
@@ -945,8 +942,8 @@ start_up:
if (dir_bh) {
PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
dir_bh->b_dirt = 1;
- old_dir->i_nlink --;
- new_dir->i_nlink ++;
+ old_dir->i_nlink--;
+ new_dir->i_nlink++;
old_dir->i_dirt = 1;
new_dir->i_dirt = 1;
}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 2c255eb..e802842 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -38,7 +38,7 @@ struct inode_operations ext2_symlink_inode_operations = {
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
- ext2_readlink, /* readlink */
+ ext2_readlink, /* readlink */
ext2_follow_link, /* follow_link */
NULL, /* bmap */
NULL, /* truncate */
@@ -55,7 +55,7 @@ static int ext2_follow_link(struct inode * dir, struct inode * inode,
*res_inode = NULL;
if (!dir) {
dir = current->root;
- dir->i_count ++;
+ dir->i_count++;
}
if (!inode) {
iput (dir);
@@ -80,9 +80,9 @@ static int ext2_follow_link(struct inode * dir, struct inode * inode,
link = bh->b_data;
} else
link = (char *) inode->u.ext2_i.i_data;
- current->link_count ++;
+ current->link_count++;
error = open_namei (link, flag, mode, res_inode, dir);
- current->link_count --;
+ current->link_count--;
iput (inode);
if (bh)
brelse (bh);
@@ -114,7 +114,7 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
link = (char *) inode->u.ext2_i.i_data;
i = 0;
while (i < buflen && (c = link[i])) {
- i ++;
+ i++;
put_fs_byte (c, buffer++);
}
iput (inode);
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index 0028845..92fc244 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -161,7 +161,7 @@ static int trunc_dindirect (struct inode * inode, int offset,
return 0;
}
repeat:
- for (i = dindirect_block ; i < addr_per_block ; i ++) {
+ for (i = dindirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < dindirect_block)
@@ -218,7 +218,7 @@ static int trunc_tindirect (struct inode * inode)
return 0;
}
repeat:
- for (i = tindirect_block ; i < addr_per_block ; i ++) {
+ for (i = tindirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < tindirect_block)
diff --git a/fs/fifo.c b/fs/fifo.c
index 2e63550..c899f8a 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -99,7 +99,7 @@ static int fifo_open(struct inode * inode,struct file * filp)
}
if (retval || PIPE_BASE(*inode))
return retval;
- page = get_free_page(GFP_KERNEL);
+ page = __get_free_page(GFP_KERNEL);
if (PIPE_BASE(*inode)) {
free_page(page);
return 0;
diff --git a/fs/file_table.c b/fs/file_table.c
index e23d9da..8542dd9 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -42,26 +42,21 @@ static void put_last_free(struct file *file)
void grow_files(void)
{
- unsigned long page;
struct file * file;
int i;
- page = get_free_page(GFP_BUFFER);
- if (!page)
+ file = (struct file*) __get_free_page(GFP_BUFFER);
+
+ if (!file)
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;
+
+ nr_files+=i= PAGE_SIZE/sizeof(struct file);
+
+ if (!first_file)
+ file->f_next = file->f_prev = first_file = file++, i--;
+
+ for (; i ; i--)
+ insert_file_free(file++);
}
unsigned long file_table_init(unsigned long start, unsigned long end)
diff --git a/fs/inode.c b/fs/inode.c
index 1c0f8b1..fcd1825 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -17,7 +17,7 @@ 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)
+static inline int const hashfn(dev_t dev, unsigned int i)
{
return (dev ^ i) % NR_IHASH;
}
@@ -84,24 +84,21 @@ static void put_last_free(struct inode *inode)
void grow_inodes(void)
{
- unsigned long page;
struct inode * inode;
int i;
- page = get_free_page(GFP_BUFFER);
- if (!page)
+ if(!(inode = (struct inode*) get_free_page(GFP_KERNEL)))
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);
- }
+
+ i=PAGE_SIZE / sizeof(struct inode);
nr_inodes += i;
nr_free_inodes += i;
+
+ if (!first_inode)
+ inode->i_next = inode->i_prev = first_inode = inode++, i--;
+
+ for ( ; i ; i-- )
+ insert_inode_free(inode++);
}
unsigned long inode_init(unsigned long start, unsigned long end)
@@ -400,7 +397,7 @@ struct inode * get_pipe_inode(void)
if (!(inode = get_empty_inode()))
return NULL;
- if (!(PIPE_BASE(*inode) = (char *) get_free_page(GFP_USER))) {
+ if (!(PIPE_BASE(*inode) = (char*) __get_free_page(GFP_USER))) {
iput(inode);
return NULL;
}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 23310cd..7c9777e 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -18,7 +18,15 @@
#include <asm/segment.h>
#include <linux/errno.h>
+#if defined(CONFIG_BLK_DEV_SR)
extern int check_cdrom_media_change(int, int);
+#endif
+#if defined(CONFIG_CDU31A)
+extern int check_cdu31a_media_change(int, int);
+#endif
+#if defined(CONFIG_MCD)
+extern int check_mcd_media_change(int, int);
+#endif
#ifdef LEAK_CHECK
static int check_malloc = 0;
@@ -254,11 +262,27 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
printk("get root inode failed\n");
return NULL;
}
+#if defined(CONFIG_BLK_DEV_SR)
if(MAJOR(s->s_dev) == 11) {
- /* Chech this one more time. */
+ /* Check this one more time. */
if(check_cdrom_media_change(s->s_dev, 0))
goto out;
};
+#endif
+#if defined(CONFIG_CDU31A)
+ if(MAJOR(s->s_dev) == 15) {
+ /* Check this one more time. */
+ if(check_cdu31a_media_change(s->s_dev, 0))
+ goto out;
+ };
+#endif
+#if defined(CONFIG_MCD)
+ if(MAJOR(s->s_dev) == 23) {
+ /* Check this one more time. */
+ if(check_mcd_media_change(s->s_dev, 0))
+ goto out;
+ };
+#endif
return s;
out: /* Kick out for various error conditions */
brelse(bh);
@@ -321,15 +345,15 @@ void isofs_read_inode(struct inode * inode)
raw_inode = ((struct iso_directory_record *) pnt);
};
- inode->i_mode = 0444; /* Everybody gets to read the file. */
+ inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */
inode->i_nlink = 1;
if (raw_inode->flags[-high_sierra] & 2) {
- inode->i_mode = 0555 | S_IFDIR;
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
inode->i_nlink = 2; /* There are always at least 2. It is
hard to figure out what is correct*/
} else {
- inode->i_mode = 0444; /* Everybody gets to read the file. */
+ inode->i_mode = S_IRUGO; /* Everybody gets to read the file. */
inode->i_nlink = 1;
inode->i_mode |= S_IFREG;
/* If there are no periods in the name, then set the execute permission bit */
@@ -337,7 +361,7 @@ void isofs_read_inode(struct inode * inode)
if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
break;
if(i == raw_inode->name_len[0] || raw_inode->name[i] == ';')
- inode->i_mode |= 0111; /* execute permission */
+ inode->i_mode |= S_IXUGO; /* execute permission */
};
inode->i_uid = 0;
inode->i_gid = 0;
diff --git a/fs/locks.c b/fs/locks.c
index 43dc61e..3a5e4b6 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -20,7 +20,7 @@
#include <linux/stat.h>
#include <linux/fcntl.h>
-#define OFFSET_MAX 0x7fffffff /* FIXME: move elsewhere? */
+#define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */
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);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 717cbf6..206e5ec 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -14,6 +14,7 @@
#include <asm/system.h>
#include <asm/segment.h>
+#include <asm/bitops.h>
void minix_put_inode(struct inode *inode)
{
@@ -116,8 +117,8 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
return NULL;
}
- s->u.minix_sb.s_imap[0]->b_data[0] |= 1;
- s->u.minix_sb.s_zmap[0]->b_data[0] |= 1;
+ set_bit(0,s->u.minix_sb.s_imap[0]->b_data);
+ set_bit(0,s->u.minix_sb.s_zmap[0]->b_data);
/* set up enough so that it can read an inode */
s->s_dev = dev;
s->s_op = &minix_sops;
diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c
index 9289e4b..1739403 100644
--- a/fs/msdos/inode.c
+++ b/fs/msdos/inode.c
@@ -315,7 +315,7 @@ void msdos_read_inode(struct inode *inode)
inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid;
inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid;
if (inode->i_ino == MSDOS_ROOT_INO) {
- inode->i_mode = (0777 & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+ inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) |
S_IFDIR;
inode->i_op = &msdos_dir_inode_operations;
inode->i_nlink = msdos_subdirs(inode)+2;
@@ -339,7 +339,7 @@ void msdos_read_inode(struct inode *inode)
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
[inode->i_ino & (MSDOS_DPB-1)];
if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
- inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0777 &
+ inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR;
inode->i_op = &msdos_dir_inode_operations;
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
@@ -363,7 +363,7 @@ void msdos_read_inode(struct inode *inode)
}
else {
inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode)
- ? 0666 : 0777) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
S_IFREG;
inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ?
&msdos_file_inode_operations :
@@ -436,10 +436,10 @@ int msdos_notify_change(int flags,struct inode *inode)
error = -EPERM;
}
if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
- inode->i_mode &= S_IFMT | 0666;
- else inode->i_mode |= 0111;
+ inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
+ else inode->i_mode |= S_IXUGO;
inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
- & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*0111)) &
+ & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
~MSDOS_SB(inode->i_sb)->fs_umask;
return MSDOS_SB(inode->i_sb)->quiet ? 0 : error;
}
diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c
index 2eb2abd..edd704f 100644
--- a/fs/msdos/misc.c
+++ b/fs/msdos/misc.c
@@ -15,7 +15,7 @@
/* Well-known binary file extensions */
static char bin_extensions[] =
- "EXECOMAPPSYSOVLOBJLIB" /* program code */
+ "EXECOMBINAPPSYSDRVOVLOVROBJLIBDLLPIF" /* program code */
"ARCZIPLHALZHZOOTARZ ARJ" /* common archivers */
"TZ TAZTZPTPZ" /* abbreviations of tar.Z and tar.zip */
"GIFBMPTIFGL JPGPCX" /* graphics */
@@ -141,7 +141,7 @@ printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
#endif
last = 0;
if ((current = MSDOS_I(inode)->i_start) != 0) {
- cache_lookup(inode,0x7fffffff,&last,&current);
+ cache_lookup(inode,INT_MAX,&last,&current);
while (current && current != -1)
if (!(current = fat_access(inode->i_sb,
last = current,-1))) {
diff --git a/fs/namei.c b/fs/namei.c
index c864777..6c8945e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -44,8 +44,7 @@ int getname(const char * filename, char **result)
c = get_fs_byte(filename++);
if (!c)
return -ENOENT;
- page = __get_free_page(GFP_KERNEL);
- if (!page)
+ if(!(page = __get_free_page(GFP_KERNEL)))
return -ENOMEM;
*result = tmp = (char *) page;
while (--i) {
@@ -283,7 +282,7 @@ int open_namei(const char * pathname, int flag, int mode,
struct inode * dir, *inode;
struct task_struct ** p;
- mode &= 07777 & ~current->umask;
+ mode &= S_IALLUGO & ~current->umask;
mode |= S_IFREG;
error = dir_namei(pathname,&namelen,&basename,base,&dir);
if (error)
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index f4fdd2f..c51d7f7 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -15,7 +15,7 @@
$(AS) -o $*.o $<
OBJS= proc.o sock.o inode.o file.o dir.o \
- symlink.o
+ symlink.o mmap.o
nfs.o: $(OBJS)
$(LD) -r -o nfs.o $(OBJS)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 4f5f0c0..f4e567d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -477,7 +477,7 @@ static int nfs_symlink(struct inode *dir, const char *name, int len,
iput(dir);
return -ENAMETOOLONG;
}
- sattr.mode = S_IFLNK | 0777; /* SunOS 4.1.2 crashes without this! */
+ sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index cbf27b3..a77d0e4 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -19,6 +19,8 @@
static int nfs_file_read(struct inode *, struct file *, char *, int);
static int nfs_file_write(struct inode *, struct file *, char *, int);
+extern int nfs_mmap(struct inode * inode, struct file * file,
+ unsigned long addr, size_t len, int prot, unsigned long off);
static struct file_operations nfs_file_operations = {
NULL, /* lseek - default */
@@ -27,7 +29,7 @@ static struct file_operations nfs_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- NULL, /* mmap */
+ nfs_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
NULL /* fsync */
diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c
new file mode 100644
index 0000000..e1b13fb
--- /dev/null
+++ b/fs/nfs/mmap.c
@@ -0,0 +1,159 @@
+/*
+ * fs/nfs/mmap.c by Jon Tombs 15 Aug 1993
+ *
+ * This code is from
+ * linux/mm/mmap.c which was written by obz, Linus and Eric
+ * and
+ * linux/mm/memory.c by Linus Torvalds and others
+ *
+ * Copyright (C) 1993
+ *
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/nfs_fs.h>
+
+extern int share_page(struct vm_area_struct * area, struct task_struct * tsk,
+ struct inode * inode, unsigned long address, unsigned long error_code,
+ unsigned long newpage);
+
+extern unsigned long put_page(struct task_struct * tsk,unsigned long page,
+ unsigned long address,int prot);
+
+static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
+ unsigned long address);
+
+extern void file_mmap_free(struct vm_area_struct * area);
+extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to,
+ unsigned long address);
+
+struct vm_operations_struct nfs_file_mmap = {
+ NULL, /* open */
+ file_mmap_free, /* close */
+ nfs_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ file_mmap_share, /* share */
+};
+
+
+/* This is used for a general mmap of a nfs file */
+int nfs_mmap(struct inode * inode, struct file * file,
+ unsigned long addr, size_t len, int prot, unsigned long off)
+{
+ struct vm_area_struct * mpnt;
+
+ if (off & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (len > high_memory || off > high_memory - len) /* avoid overflow */
+ return -ENXIO;
+ if (get_limit(USER_DS) != TASK_SIZE)
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+ if (!mpnt)
+ return -ENOMEM;
+
+ unmap_page_range(addr, len);
+ mpnt->vm_task = current;
+ mpnt->vm_start = addr;
+ mpnt->vm_end = addr + len;
+ mpnt->vm_page_prot = prot;
+ mpnt->vm_share = NULL;
+ mpnt->vm_inode = inode;
+ inode->i_count++;
+ mpnt->vm_offset = off;
+ mpnt->vm_ops = &nfs_file_mmap;
+ mpnt->vm_next = current->mmap;
+ current->mmap = mpnt;
+#if 0
+ printk("VFS: Loaded mmap at %08x - %08x\n",
+ mpnt->vm_start, mpnt->vm_end);
+#endif
+ return 0;
+}
+
+
+static void nfs_file_mmap_nopage(int error_code, struct vm_area_struct * area,
+ unsigned long address)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned int clear;
+ unsigned long page;
+ unsigned long tmp;
+ int n;
+ int i;
+ int pos;
+ struct nfs_fattr fattr;
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ page = get_free_page(GFP_KERNEL);
+ if (share_page(area, area->vm_task, inode, address, error_code, page)) {
+ ++area->vm_task->min_flt;
+ return;
+ }
+
+ ++area->vm_task->maj_flt;
+ if (!page) {
+ oom(current);
+ put_page(area->vm_task, BAD_PAGE, address, PAGE_PRIVATE);
+ return;
+ }
+
+ clear = 0;
+ if (address + PAGE_SIZE > area->vm_end) {
+ clear = address + PAGE_SIZE - area->vm_end;
+ }
+
+ n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
+
+ for (i = 0; i < (PAGE_SIZE - clear); i += n) {
+ int hunk, result;
+
+ hunk = PAGE_SIZE - i;
+ if (hunk > n)
+ hunk = n;
+ result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+ pos, hunk, (char *) (page + i), &fattr);
+ if (result < 0)
+ break;
+ pos += result;
+ if (result < n) {
+ i += result;
+ break;
+ }
+ }
+
+#ifdef doweneedthishere
+ nfs_refresh_inode(inode, &fattr);
+#endif
+
+ if (!(error_code & PAGE_RW)) {
+ if (share_page(area, area->vm_task, inode, address, error_code, page))
+ return;
+ }
+
+ tmp = page + PAGE_SIZE;
+ while (clear--) {
+ *(char *)--tmp = 0;
+ }
+ if (put_page(area->vm_task,page,address,area->vm_page_prot))
+ return;
+ free_page(page);
+ oom(current);
+}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index df5bfc4..a9f699c 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -33,6 +33,11 @@ static int proc_debug = 0;
#define PRINTK if (0) printk
#endif
+#define PREP_PAGE_RPC(code) \
+ if (!(p0 = (int*)__get_free_page(GFP_KERNEL)))\
+ return NFSERR_IO;\
+ p=nfs_rpc_header(p0,code)
+
static int *nfs_rpc_header(int *p, int procedure);
static int *nfs_rpc_verify(int *p);
static int nfs_stat_to_errno(int stat);
@@ -168,8 +173,7 @@ int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call getattr\n");
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_GETATTR);
+ PREP_PAGE_RPC(NFSPROC_GETATTR);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
free_page((long) p0);
@@ -194,8 +198,7 @@ int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call setattr\n");
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_SETATTR);
+ PREP_PAGE_RPC(NFSPROC_SETATTR);
p = xdr_encode_fhandle(p, fhandle);
p = xdr_encode_sattr(p, sattr);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -225,8 +228,7 @@ int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *n
if (!strcmp(name, "xyzzy"))
proc_debug = 1 - proc_debug;
#endif
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_LOOKUP);
+ PREP_PAGE_RPC(NFSPROC_LOOKUP);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -253,8 +255,7 @@ int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call readlink\n");
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_READLINK);
+ PREP_PAGE_RPC(NFSPROC_READLINK);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
free_page((long) p0);
@@ -284,8 +285,7 @@ int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
int len = 0; /* = 0 is for gcc */
PRINTK("NFS call read %d @ %d\n", count, offset);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_READ);
+ PREP_PAGE_RPC(NFSPROC_READ);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(offset);
*p++ = htonl(count);
@@ -318,8 +318,7 @@ int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call write %d @ %d\n", count, offset);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_WRITE);
+ PREP_PAGE_RPC(NFSPROC_WRITE);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(offset); /* traditional, could be any value */
*p++ = htonl(offset);
@@ -349,8 +348,7 @@ int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
int status;
PRINTK("NFS call create %s\n", name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_CREATE);
+ PREP_PAGE_RPC(NFSPROC_CREATE);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_sattr(p, sattr);
@@ -377,8 +375,7 @@ int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *n
int status;
PRINTK("NFS call remove %s\n", name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_REMOVE);
+ PREP_PAGE_RPC(NFSPROC_REMOVE);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -404,8 +401,7 @@ int nfs_proc_rename(struct nfs_server *server,
int status;
PRINTK("NFS call rename %s -> %s\n", old_name, new_name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_RENAME);
+ PREP_PAGE_RPC(NFSPROC_RENAME);
p = xdr_encode_fhandle(p, old_dir);
p = xdr_encode_string(p, old_name);
p = xdr_encode_fhandle(p, new_dir);
@@ -432,8 +428,7 @@ int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call link %s\n", name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_LINK);
+ PREP_PAGE_RPC(NFSPROC_LINK);
p = xdr_encode_fhandle(p, fhandle);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
@@ -459,8 +454,7 @@ int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
int status;
PRINTK("NFS call symlink %s -> %s\n", name, path);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_SYMLINK);
+ PREP_PAGE_RPC(NFSPROC_SYMLINK);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_string(p, path);
@@ -488,8 +482,7 @@ int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
int status;
PRINTK("NFS call mkdir %s\n", name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_MKDIR);
+ PREP_PAGE_RPC(NFSPROC_MKDIR);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
p = xdr_encode_sattr(p, sattr);
@@ -516,8 +509,7 @@ int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *na
int status;
PRINTK("NFS call rmdir %s\n", name);
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_RMDIR);
+ PREP_PAGE_RPC(NFSPROC_RMDIR);
p = xdr_encode_fhandle(p, dir);
p = xdr_encode_string(p, name);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
@@ -546,8 +538,7 @@ int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
PRINTK("NFS call readdir %d @ %d\n", count, cookie);
size = server->rsize;
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_READDIR);
+ PREP_PAGE_RPC(NFSPROC_READDIR);
p = xdr_encode_fhandle(p, fhandle);
*p++ = htonl(cookie);
*p++ = htonl(size);
@@ -588,8 +579,7 @@ int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
int status;
PRINTK("NFS call statfs\n");
- p = p0 = (int *) get_free_page(GFP_KERNEL);
- p = nfs_rpc_header(p, NFSPROC_STATFS);
+ PREP_PAGE_RPC(NFSPROC_STATFS);
p = xdr_encode_fhandle(p, fhandle);
if ((status = nfs_rpc_call(server, p0, p)) < 0) {
free_page((long) p0);
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index 2f97999..ec64064 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -120,7 +120,7 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
remove_wait_queue(entry.wait_address, &entry.wait);
current->state = TASK_RUNNING;
addrlen = 0;
- result = sock->ops->recvfrom(sock, (void *) start, 4096, 1, 0,
+ result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0,
NULL, &addrlen);
if (result < 0) {
if (result == -EAGAIN) {
diff --git a/fs/open.c b/fs/open.c
index 914a546..2fb689e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -153,24 +153,24 @@ extern "C" int sys_utime(char * filename, struct utimbuf * times)
/*
* XXX we should use the real ids for checking _all_ components of the
- * path. Now we only use them for the final compenent of the path.
+ * path. Now we only use them for the final component of the path.
*/
extern "C" int sys_access(const char * filename,int mode)
{
struct inode * inode;
int res, i_mode;
- if (mode != (mode & 0007)) /* where's F_OK, X_OK, W_OK, R_OK? */
+ if (mode != (mode & S_IRWXO)) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
res = namei(filename,&inode);
if (res)
return res;
i_mode = inode->i_mode;
- res = i_mode & 0777;
+ res = i_mode & S_IRWXUGO;
if (current->uid == inode->i_uid)
- res >>= 6;
+ res >>= 6; /* needs cleaning? */
else if (in_group_p(inode->i_gid))
- res >>= 3;
+ res >>= 3; /* needs cleaning? */
iput(inode);
if ((res & mode) == mode)
return 0;
@@ -184,7 +184,7 @@ extern "C" int sys_access(const char * filename,int mode)
* decomposing the path would be racy.
*/
if ((!current->uid) &&
- (S_ISDIR(i_mode) || !(mode & 1) || (i_mode & 0111)))
+ (S_ISDIR(i_mode) || !(mode & S_IXOTH) || (i_mode & S_IXUGO)))
return 0;
return -EACCES;
}
@@ -244,7 +244,7 @@ extern "C" int sys_fchmod(unsigned int fd, mode_t mode)
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
- inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+ inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
if (!suser() && !in_group_p(inode->i_gid))
inode->i_mode &= ~S_ISGID;
inode->i_ctime = CURRENT_TIME;
@@ -268,7 +268,7 @@ extern "C" int sys_chmod(const char * filename, mode_t mode)
iput(inode);
return -EROFS;
}
- inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+ inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
if (!suser() && !in_group_p(inode->i_gid))
inode->i_mode &= ~S_ISGID;
inode->i_ctime = CURRENT_TIME;
@@ -350,11 +350,10 @@ extern "C" int sys_chown(const char * filename, uid_t user, gid_t group)
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-extern "C" int sys_open(const char * filename,int flags,int mode)
+int do_open(const char * filename,int flags,int mode)
{
struct inode * inode;
struct file * f;
- char * tmp;
int flag,error,fd;
for(fd=0 ; fd<NR_OPEN ; fd++)
@@ -373,11 +372,7 @@ extern "C" int sys_open(const char * filename,int flags,int mode)
flag++;
if (flag & (O_TRUNC | O_CREAT))
flag |= 2;
- error = getname(filename,&tmp);
- if (!error) {
- error = open_namei(tmp,flag,mode,&inode,NULL);
- putname(tmp);
- }
+ error = open_namei(filename,flag,mode,&inode,NULL);
if (error) {
current->filp[fd]=NULL;
f->f_count--;
@@ -414,6 +409,19 @@ extern "C" int sys_open(const char * filename,int flags,int mode)
return (fd);
}
+extern "C" int sys_open(const char * filename,int flags,int mode)
+{
+ char * tmp;
+ int error;
+
+ error = getname(filename, &tmp);
+ if (error)
+ return error;
+ error = do_open(tmp,flags,mode);
+ putname(tmp);
+ return error;
+}
+
extern "C" int sys_creat(const char * pathname, int mode)
{
return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index bc43cac..fe1d4e6 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -89,18 +89,16 @@ static unsigned long get_phys_addr(struct task_struct ** p, unsigned long ptr)
if (!p || !*p || ptr >= TASK_SIZE)
return 0;
- page = (*p)->tss.cr3;
- page += (ptr >> 20) & 0xffc;
- page = *(unsigned long *) page;
+ page = *PAGE_DIR_OFFSET((*p)->tss.cr3,ptr);
if (!(page & 1))
return 0;
- page &= 0xfffff000;
- page += (ptr >> 10) & 0xffc;
+ page &= PAGE_MASK;
+ page += PAGE_PTR(ptr);
page = *(unsigned long *) page;
if (!(page & 1))
return 0;
- page &= 0xfffff000;
- page += ptr & 0xfff;
+ page &= PAGE_MASK;
+ page += ptr & ~PAGE_MASK;
return page;
}
@@ -128,7 +126,7 @@ static int get_array(struct task_struct ** p, unsigned long start, unsigned long
start++;
if (start >= end)
return result;
- } while (!(addr & 0xfff));
+ } while (!(addr & ~PAGE_MASK));
}
}
@@ -196,7 +194,7 @@ static int get_stat(int pid, char * buffer)
if (vsize) {
eip = KSTK_EIP(vsize);
esp = KSTK_ESP(vsize);
- vsize = (*p)->brk + 4095;
+ vsize = (*p)->brk + PAGE_SIZE-1;
if (esp)
vsize += TASK_SIZE - esp;
}
@@ -266,14 +264,14 @@ static int get_statm(int pid, char * buffer)
return 0;
tpag = (*p)->end_code / PAGE_SIZE;
if ((*p)->state != TASK_ZOMBIE) {
- pagedir = (unsigned long *)((*p)->tss.cr3 + ((*p)->start_code >> 20));
+ pagedir = PAGE_DIR_OFFSET((*p)->tss.cr3,(*p)->start_code);
for (i = 0; i < 0x300; ++i) {
if ((ptbl = pagedir[i]) == 0) {
- tpag -= 1024;
+ tpag -= PTRS_PER_PAGE;
continue;
}
- buf = (unsigned long *)(ptbl & 0xfffff000);
- for (pte = buf; pte < (buf + 1024); ++pte) {
+ buf = (unsigned long *)(ptbl & PAGE_MASK);
+ for (pte = buf; pte < (buf + PTRS_PER_PAGE); ++pte) {
if (*pte != 0) {
++size;
if (*pte & 1) {
@@ -290,7 +288,7 @@ static int get_statm(int pid, char * buffer)
--drs;
}
map_nr = MAP_NR(*pte);
- if (map_nr < (high_memory / 4096) && mem_map[map_nr] > 1)
+ if (map_nr < (high_memory / PAGE_SIZE) && mem_map[map_nr] > 1)
++share;
}
}
@@ -311,8 +309,7 @@ static int array_read(struct inode * inode, struct file * file,char * buf, int c
if (count < 0)
return -EINVAL;
- page = (char *) get_free_page(GFP_KERNEL);
- if (!page)
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
type = inode->i_ino;
pid = type >> 16;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index cb2353f..97cbe2a 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -61,7 +61,7 @@ struct super_block *proc_read_super(struct super_block *s,void *data,
void proc_statfs(struct super_block *sb, struct statfs *buf)
{
put_fs_long(PROC_SUPER_MAGIC, &buf->f_type);
- put_fs_long(1024, &buf->f_bsize);
+ put_fs_long(PAGE_SIZE/sizeof(long), &buf->f_bsize);
put_fs_long(0, &buf->f_blocks);
put_fs_long(0, &buf->f_bfree);
put_fs_long(0, &buf->f_bavail);
@@ -94,7 +94,7 @@ void proc_read_inode(struct inode * inode)
if (!p || i >= NR_TASKS)
return;
if (ino == PROC_ROOT_INO) {
- inode->i_mode = S_IFDIR | 0555;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
inode->i_nlink = 2;
for (i = 1 ; i < NR_TASKS ; i++)
if (task[i])
@@ -103,23 +103,23 @@ void proc_read_inode(struct inode * inode)
return;
}
if ((ino >= 128) && (ino <= 160)) { /* files within /proc/net */
- inode->i_mode = S_IFREG | 0444;
+ inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_net_inode_operations;
return;
}
if (!pid) {
switch (ino) {
case 5:
- inode->i_mode = S_IFREG | 0444;
+ inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_kmsg_inode_operations;
break;
case 8: /* for the net directory */
- inode->i_mode = S_IFDIR | 0555;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
inode->i_nlink = 2;
inode->i_op = &proc_net_inode_operations;
break;
default:
- inode->i_mode = S_IFREG | 0444;
+ inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_array_inode_operations;
break;
}
@@ -131,23 +131,23 @@ void proc_read_inode(struct inode * inode)
switch (ino) {
case 2:
inode->i_nlink = 4;
- inode->i_mode = S_IFDIR | 0555;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
inode->i_op = &proc_base_inode_operations;
return;
case 3:
inode->i_op = &proc_mem_inode_operations;
- inode->i_mode = S_IFREG | 0600;
+ inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
return;
case 4:
case 5:
case 6:
inode->i_op = &proc_link_inode_operations;
inode->i_size = 64;
- inode->i_mode = S_IFLNK | 0700;
+ inode->i_mode = S_IFLNK | S_IRWXU;
return;
case 7:
case 8:
- inode->i_mode = S_IFDIR | 0500;
+ inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
inode->i_op = &proc_fd_inode_operations;
inode->i_nlink = 2;
return;
@@ -155,7 +155,7 @@ void proc_read_inode(struct inode * inode)
case 10:
case 11:
case 12:
- inode->i_mode = S_IFREG | 0444;
+ inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_array_inode_operations;
return;
}
@@ -166,7 +166,7 @@ void proc_read_inode(struct inode * inode)
return;
inode->i_op = &proc_link_inode_operations;
inode->i_size = 64;
- inode->i_mode = S_IFLNK | 0700;
+ inode->i_mode = S_IFLNK | S_IRWXU;
return;
case 2:
ino &= 0xff;
@@ -181,7 +181,7 @@ void proc_read_inode(struct inode * inode)
}
inode->i_op = &proc_link_inode_operations;
inode->i_size = 64;
- inode->i_mode = S_IFLNK | 0700;
+ inode->i_mode = S_IFLNK | S_IRWXU;
return;
}
return;
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index 368e28f..28fd4be 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -24,7 +24,7 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou
{
unsigned long addr, pid, cr3;
char *tmp;
- unsigned long pde, pte, page;
+ unsigned long pte, page;
int i;
if (count < 0)
@@ -44,18 +44,17 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou
while (count > 0) {
if (current->signal & ~current->blocked)
break;
- pde = cr3 + (addr >> 20 & 0xffc);
- pte = *(unsigned long *) pde;
+ pte = *PAGE_DIR_OFFSET(cr3,addr);
if (!(pte & PAGE_PRESENT))
break;
- pte &= 0xfffff000;
- pte += (addr >> 10) & 0xffc;
+ pte &= PAGE_MASK;
+ pte += PAGE_PTR(addr);
page = *(unsigned long *) pte;
if (!(page & 1))
break;
- page &= 0xfffff000;
- page += addr & 0xfff;
- i = 4096-(addr & 0xfff);
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
+ i = PAGE_SIZE-(addr & ~PAGE_MASK);
if (i > count)
i = count;
memcpy_tofs(tmp,(void *) page,i);
@@ -73,7 +72,7 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co
{
unsigned long addr, pid, cr3;
char *tmp;
- unsigned long pde, pte, page;
+ unsigned long pte, page;
int i;
if (count < 0)
@@ -93,12 +92,11 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co
while (count > 0) {
if (current->signal & ~current->blocked)
break;
- pde = cr3 + (addr >> 20 & 0xffc);
- pte = *(unsigned long *) pde;
+ pte = *PAGE_DIR_OFFSET(cr3,addr);
if (!(pte & PAGE_PRESENT))
break;
- pte &= 0xfffff000;
- pte += (addr >> 10) & 0xffc;
+ pte &= PAGE_MASK;
+ pte += PAGE_PTR(addr);
page = *(unsigned long *) pte;
if (!(page & PAGE_PRESENT))
break;
@@ -106,9 +104,9 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co
do_wp_page(0,addr,current,0);
continue;
}
- page &= 0xfffff000;
- page += addr & 0xfff;
- i = 4096-(addr & 0xfff);
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
+ i = PAGE_SIZE-(addr & ~PAGE_MASK);
if (i > count)
i = count;
memcpy_fromfs((void *) page,tmp,i);
diff --git a/fs/proc/net.c b/fs/proc/net.c
index 466163d..9972dde 100644
--- a/fs/proc/net.c
+++ b/fs/proc/net.c
@@ -164,8 +164,7 @@ static int proc_readnet(struct inode * inode, struct file * file,
if (count < 0)
return -EINVAL;
- page = (char *) get_free_page(GFP_KERNEL);
- if (!page)
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
ino = inode->i_ino;
switch (ino) {
diff --git a/fs/select.c b/fs/select.c
index 0311289..61066ef 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -104,8 +104,7 @@ int do_select(int n, fd_set *in, fd_set *out, fd_set *ex,
}
end_check:
n = max + 1;
- entry = (struct select_table_entry *) __get_free_page(GFP_KERNEL);
- if (!entry)
+ if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
FD_ZERO(res_in);
FD_ZERO(res_out);
@@ -219,7 +218,7 @@ extern "C" int sys_select( unsigned long *buffer )
if ((i = get_fd_set(n, inp, &in)) ||
(i = get_fd_set(n, outp, &out)) ||
(i = get_fd_set(n, exp, &ex))) return i;
- timeout = 0xffffffff;
+ timeout = ~0UL;
if (tvp) {
i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
if (i)
diff --git a/fs/super.c b/fs/super.c
index 420d3b7..fbc6c5b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -176,7 +176,7 @@ static dev_t get_unnamed_dev(void)
memset(unnamed_dev_in_use, 0, sizeof(unnamed_dev_in_use));
unnamed_dev_in_use[0] = 1; /* minor 0 (nodev) is special */
}
- for (i = 0; i < 256; i++) {
+ for (i = 0; i < sizeof unnamed_dev_in_use/sizeof unnamed_dev_in_use[0]; i++) {
if (!unnamed_dev_in_use[i]) {
unnamed_dev_in_use[i] = 1;
return (UNNAMED_MAJOR << 8) | i;
@@ -254,15 +254,18 @@ extern "C" int sys_umount(char * name)
if (!suser())
return -EPERM;
retval = namei(name,&inode);
- if (retval)
- return retval;
+ if (retval) {
+ retval = lnamei(name,&inode);
+ if (retval)
+ return retval;
+ }
if (S_ISBLK(inode->i_mode)) {
dev = inode->i_rdev;
if (IS_NODEV(inode)) {
iput(inode);
return -EACCES;
}
- } else if (S_ISDIR(inode->i_mode)) {
+ } else {
if (!inode || !inode->i_sb || inode != inode->i_sb->s_mounted) {
iput(inode);
return -EINVAL;
@@ -272,9 +275,6 @@ extern "C" int sys_umount(char * name)
memset(&dummy_inode, 0, sizeof(dummy_inode));
dummy_inode.i_rdev = dev;
inode = &dummy_inode;
- } else {
- iput(inode);
- return -EINVAL;
}
if (MAJOR(dev) >= MAX_BLKDEV) {
iput(inode);
@@ -382,7 +382,7 @@ static int do_remount(const char *dir,int flags)
* Flags is a 16-bit value that allows up to 16 non-fs dependent flags to
* be given to the mount() call (ie: read-only, no-dev, no-suid etc).
*
- * data is a (void *) that can point to any structure up to 4095 bytes, which
+ * data is a (void *) that can point to any structure up to PAGE_SIZE-1 bytes, which
* can contain arbitrary fs-dependent information (or be NULL).
*
* NOTE! As old versions of mount() didn't use this setup, the flags has to have
@@ -458,10 +458,13 @@ extern "C" int sys_mount(char * dev_name, char * dir_name, char * type,
iput(inode);
return -EFAULT;
}
- page = get_free_page(GFP_KERNEL);
+ if (!(page = __get_free_page(GFP_KERNEL))) {
+ iput(inode);
+ return -ENOMEM;
+ }
i = TASK_SIZE - (unsigned long) data;
- if (i < 0 || i > 4095)
- i = 4095;
+ if ((unsigned long) i >= PAGE_SIZE)
+ i = PAGE_SIZE-1;
memcpy_fromfs((void *) page,data,i);
}
}
diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c
index 2f2839f..761d88d 100644
--- a/fs/xiafs/namei.c
+++ b/fs/xiafs/namei.c
@@ -373,7 +373,7 @@ int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode)
inode->i_nlink = 2;
dir_block->b_dirt = 1;
brelse(dir_block);
- inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+ inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
inode->i_dirt = 1;
@@ -594,7 +594,7 @@ int xiafs_symlink(struct inode * dir, const char * name,
iput(dir);
return -ENOSPC;
}
- inode->i_mode = S_IFLNK | 0777;
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &xiafs_symlink_inode_operations;
name_block = xiafs_bread(inode,0,1);
if (!name_block) {
diff --git a/include/asm/bitops.h b/include/asm/bitops.h
index f8191b9..41b4f29 100644
--- a/include/asm/bitops.h
+++ b/include/asm/bitops.h
@@ -1,4 +1,5 @@
#ifndef _ASM_BITOPS_H
+#define _ASM_BITOPS_H
/*
* Copyright 1992, Linus Torvalds.
*/
@@ -18,7 +19,7 @@
struct __dummy { unsigned long a[100]; };
#define ADDR (*(struct __dummy *) addr)
-extern inline int set_bit(int nr, void * addr)
+extern __inline__ int set_bit(int nr, void * addr)
{
int oldbit;
@@ -28,7 +29,7 @@ extern inline int set_bit(int nr, void * addr)
return oldbit;
}
-extern inline int clear_bit(int nr, void * addr)
+extern __inline__ int clear_bit(int nr, void * addr)
{
int oldbit;
@@ -42,7 +43,7 @@ extern inline int clear_bit(int nr, void * addr)
* This routine doesn't need to be atomic, but it's faster to code it
* this way.
*/
-extern inline int test_bit(int nr, void * addr)
+extern __inline__ int test_bit(int nr, void * addr)
{
int oldbit;
@@ -68,7 +69,7 @@ extern inline int test_bit(int nr, void * addr)
* C language equivalents written by Theodore Ts'o, 9/26/92
*/
-extern inline int set_bit(int nr,int * addr)
+extern __inline__ int set_bit(int nr,int * addr)
{
int mask, retval;
@@ -81,7 +82,7 @@ extern inline int set_bit(int nr,int * addr)
return retval;
}
-extern inline int clear_bit(int nr, int * addr)
+extern __inline__ int clear_bit(int nr, int * addr)
{
int mask, retval;
@@ -94,7 +95,7 @@ extern inline int clear_bit(int nr, int * addr)
return retval;
}
-extern inline int test_bit(int nr, int * addr)
+extern __inline__ int test_bit(int nr, int * addr)
{
int mask;
diff --git a/include/asm/segment.h b/include/asm/segment.h
index 5e471f9..4e8ef17 100644
--- a/include/asm/segment.h
+++ b/include/asm/segment.h
@@ -1,3 +1,6 @@
+#ifndef _ASM_SEGMENT_H
+#define _ASM_SEGMENT_H
+
static inline unsigned char get_fs_byte(const char * addr)
{
register unsigned char _v;
@@ -164,3 +167,4 @@ static inline void set_fs(unsigned long val)
__asm__ __volatile__("mov %w0,%%fs": /* no output */ :"r" (val));
}
+#endif /* _ASM_SEGMENT_H */
diff --git a/include/asm/system.h b/include/asm/system.h
index 7888381..b88e28a 100644
--- a/include/asm/system.h
+++ b/include/asm/system.h
@@ -76,13 +76,13 @@ __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
_set_gate(a,12,3,addr)
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
- *(gate_addr) = ((base) & 0xff000000) | \
+ *((gate_addr)+1) = ((base) & 0xff000000) | \
(((base) & 0x00ff0000)>>16) | \
((limit) & 0xf0000) | \
((dpl)<<13) | \
(0x00408000) | \
((type)<<8); \
- *((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \
+ *(gate_addr) = (((base) & 0x0000ffff)<<16) | \
((limit) & 0x0ffff); }
#define _set_tssldt_desc(n,addr,limit,type) \
@@ -100,6 +100,8 @@ __asm__ __volatile__ ("movw $" #limit ",%1\n\t" \
)
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),235,"0x89")
-#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),23,"0x82")
+#define set_ldt_desc(n,addr,size) \
+ _set_tssldt_desc(((char *) (n)),((int)(addr)),((size << 3) - 1),"0x82")
+
#endif
diff --git a/include/linux/a.out.h b/include/linux/a.out.h
index 69bf01f..2c6b8c8 100644
--- a/include/linux/a.out.h
+++ b/include/linux/a.out.h
@@ -134,7 +134,7 @@ enum machine_type {
#endif
#ifdef linux
-#define PAGE_SIZE 4096
+#include <linux/page.h>
#define SEGMENT_SIZE 1024
#endif
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
new file mode 100644
index 0000000..35823c8
--- /dev/null
+++ b/include/linux/binfmts.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_BINFMTS_H
+#define _LINUX_BINFMTS_H
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB !
+ */
+#define MAX_ARG_PAGES 32
+
+/*
+ * This structure is used to hold the arguments that are used when loading binaries.
+ */
+struct linux_binprm{
+ char buf[128];
+ unsigned long page[MAX_ARG_PAGES];
+ unsigned long p;
+ struct inode * inode;
+ int e_uid, e_gid;
+ int argc, envc;
+ char * filename; /* Name of binary */
+};
+
+/* This structure defines the functions that are used to load the binary formats that
+ * linux accepts. */
+
+struct linux_binfmt{
+ int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
+ int (*load_shlib)(int fd);
+};
+
+extern struct linux_binfmt formats[];
+
+
+
+#endif
diff --git a/include/linux/busmouse.h b/include/linux/busmouse.h
index 2cd45b8..33c6ec7 100644
--- a/include/linux/busmouse.h
+++ b/include/linux/busmouse.h
@@ -83,8 +83,8 @@
struct mouse_status {
- char buttons;
- char latch_buttons;
+ unsigned char buttons;
+ unsigned char latch_buttons;
int dx;
int dy;
int present;
diff --git a/include/linux/cdu31a.h b/include/linux/cdu31a.h
new file mode 100644
index 0000000..56ad5ad
--- /dev/null
+++ b/include/linux/cdu31a.h
@@ -0,0 +1,313 @@
+/*
+ * Definitions for a Sony interface CDROM drive.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Copyright (C) 1993 Corey Minyard
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Offsets (from the base address) and bits for the various write registers
+ * of the drive.
+ */
+#define SONY_CMD_REG_OFFSET 0
+#define SONY_PARAM_REG_OFFSET 1
+#define SONY_WRITE_REG_OFFSET 2
+#define SONY_CONTROL_REG_OFFSET 3
+# define SONY_ATTN_CLR_BIT 0x01
+# define SONY_RES_RDY_CLR_BIT 0x02
+# define SONY_DATA_RDY_CLR_BIT 0x04
+# define SONY_ATTN_INT_EN_BIT 0x08
+# define SONY_RES_RDY_INT_EN_BIT 0x10
+# define SONY_DATA_RDY_INT_EN_BIT 0x20
+# define SONY_PARAM_CLR_BIT 0x40
+# define SONY_DRIVE_RESET_BIT 0x80
+
+/*
+ * Offsets (from the base address) and bits for the various read registers
+ * of the drive.
+ */
+#define SONY_STATUS_REG_OFFSET 0
+# define SONY_ATTN_BIT 0x01
+# define SONY_RES_RDY_BIT 0x02
+# define SONY_DATA_RDY_BIT 0x04
+# define SONY_ATTN_INT_ST_BIT 0x08
+# define SONY_RES_RDY_INT_ST_BIT 0x10
+# define SONY_DATA_RDY_INT_ST_BIT 0x20
+# define SONY_DATA_REQUEST_BIT 0x40
+# define SONY_BUSY_BIT 0x80
+#define SONY_RESULT_REG_OFFSET 1
+#define SONY_READ_REG_OFFSET 2
+#define SONY_FIFOST_REG_OFFSET 3
+# define SONY_PARAM_WRITE_RDY_BIT 0x01
+# define SONY_PARAM_REG_EMPTY_BIT 0x02
+# define SONY_RES_REG_NOT_EMP_BIT 0x04
+# define SONY_RES_REG_FULL_BIT 0x08
+
+#define LOG_START_OFFSET 150 /* Offset of first logical sector */
+
+#define SONY_DETECT_TIMEOUT 80 /* Maximum amount of time
+ that drive detection code
+ will wait for response
+ from drive (in 1/100th's
+ of seconds). */
+
+#define SONY_JIFFIES_TIMEOUT 500 /* Maximum number of times the
+ drive will wait/try for an
+ operation */
+#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the
+ drive will wait/try a reset
+ operation */
+#define SONY_READY_RETRIES 20000 /* How many times to retry a
+ spin waiting for a register
+ to come ready */
+
+#define MAX_CDU31A_RETRIES 3 /* How many times to retry an
+ operation */
+
+/* Commands to request or set drive control parameters and disc information */
+#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */
+#define SONY_REQ_DRIVE_MODE_CMD 0x01
+#define SONY_REQ_DRIVE_PARAM_CMD 0x02
+#define SONY_REQ_MECH_STATUS_CMD 0x03
+#define SONY_REQ_AUDIO_STATUS_CMD 0x04
+#define SONY_SET_DRIVE_PARAM_CMD 0x10
+#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */
+#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */
+#define SONY_REQ_UPC_EAN_CMD 0x22
+#define SONY_REQ_ISRC_CMD 0x23
+#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24
+
+/* Commands to request information from the drive */
+#define SONY_READ_TOC_CMD 0x30
+#define SONY_SEEK_CMD 0x31
+#define SONY_READ_CMD 0x32
+#define SONY_READ_BLKERR_STAT_CMD 0x34
+#define SONY_ABORT_CMD 0x35
+#define SONY_READ_TOC_SPEC_CMD 0x36
+
+/* Commands to control audio */
+#define SONY_AUDIO_PLAYBACK_CMD 0x40
+#define SONY_AUDIO_STOP_CMD 0x41
+#define SONY_AUDIO_SCAN_CMD 0x42
+
+/* Miscellaneous control commands */
+#define SONY_EJECT_CMD 0x50
+#define SONY_SPIN_UP_CMD 0x51
+#define SONY_SPIN_DOWN_CMD 0x52
+
+/* Diagnostic commands */
+#define SONY_WRITE_BUFFER_CMD 0x60
+#define SONY_READ_BUFFER_CMD 0x61
+#define SONY_DIAGNOSTICS_CMD 0x62
+
+
+/*
+ * The following are command paramters for the set drive parameter command
+ */
+#define SONY_SD_DECODE_PARAM 0x00
+#define SONY_SD_INTERFACE_PARAM 0x01
+#define SONY_SD_BUFFERING_PARAM 0x02
+#define SONY_SD_AUDIO_PARAM 0x03
+#define SONY_SD_AUDIO_VOLUME 0x04
+#define SONY_SD_MECH_CONTROL 0x05
+#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06
+
+/*
+ * The following extract information from the drive configuration about
+ * the drive itself.
+ */
+#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03)
+#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04)
+#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08)
+#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6)
+#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01)
+#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02)
+#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
+
+#define SONY_HWC_CADDY_LOAD_MECH 0x00
+#define SONY_HWC_TRAY_LOAD_MECH 0x01
+#define SONY_HWC_POPUP_LOAD_MECH 0x02
+#define SONY_HWC_UNKWN_LOAD_MECH 0x03
+
+#define SONY_HWC_8KB_BUFFER 0x00
+#define SONY_HWC_32KB_BUFFER 0x01
+#define SONY_HWC_64KB_BUFFER 0x02
+#define SONY_HWC_UNKWN_BUFFER 0x03
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s_sony_drive_config
+{
+ unsigned char exec_status[2];
+ char vendor_id[8];
+ char product_id[16];
+ char product_rev_level[8];
+ unsigned char hw_config[2];
+};
+
+/* The following is returned from the request subcode address command */
+struct s_sony_subcode
+{
+ unsigned char exec_status[2];
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track_num;
+ unsigned char index_num;
+ unsigned char rel_msf[3];
+ unsigned char reserved1;
+ unsigned char abs_msf[3];
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s_sony_toc
+{
+ unsigned char exec_status[2];
+ unsigned char address0 :4;
+ unsigned char control0 :4;
+ unsigned char point0;
+ unsigned char first_track_num;
+ unsigned char disk_type;
+ unsigned char dummy0;
+ unsigned char address1 :4;
+ unsigned char control1 :4;
+ unsigned char point1;
+ unsigned char last_track_num;
+ unsigned char dummy1;
+ unsigned char dummy2;
+ unsigned char address2 :4;
+ unsigned char control2 :4;
+ unsigned char point2;
+ unsigned char lead_out_start_msf[3];
+ struct
+ {
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track;
+ unsigned char track_start_msf[3];
+ } tracks[100];
+
+ unsigned int lead_out_start_lba;
+};
+
+
+/*
+ * The following are errors returned from the drive.
+ */
+
+/* Command error group */
+#define SONY_ILL_CMD_ERR 0x10
+#define SONY_ILL_PARAM_ERR 0x11
+
+/* Mechanism group */
+#define SONY_NOT_LOAD_ERR 0x20
+#define SONY_NO_DISK_ERR 0x21
+#define SONY_NOT_SPIN_ERR 0x22
+#define SONY_SPIN_ERR 0x23
+#define SONY_SPINDLE_SERVO_ERR 0x25
+#define SONY_FOCUS_SERVO_ERR 0x26
+#define SONY_EJECT_MECH_ERR 0x29
+#define SONY_AUDIO_PLAYING_ERR 0x2a
+#define SONY_EMERGENCY_EJECT_ERR 0x2c
+
+/* Seek error group */
+#define SONY_FOCUS_ERR 0x30
+#define SONY_FRAME_SYNC_ERR 0x31
+#define SONY_SUBCODE_ADDR_ERR 0x32
+#define SONY_BLOCK_SYNC_ERR 0x33
+#define SONY_HEADER_ADDR_ERR 0x34
+
+/* Read error group */
+#define SONY_ILL_TRACK_R_ERR 0x40
+#define SONY_MODE_0_R_ERR 0x41
+#define SONY_ILL_MODE_R_ERR 0x42
+#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43
+#define SONY_MODE_R_ERR 0x44
+#define SONY_FORM_R_ERR 0x45
+#define SONY_LEAD_OUT_R_ERR 0x46
+#define SONY_BUFFER_OVERRUN_R_ERR 0x47
+
+/* Data error group */
+#define SONY_UNREC_CIRC_ERR 0x53
+#define SONY_UNREC_LECC_ERR 0x57
+
+/* Subcode error group */
+#define SONY_NO_TOC_ERR 0x60
+#define SONY_SUBCODE_DATA_NVAL_ERR 0x61
+#define SONY_FOCUS_ON_TOC_READ_ERR 0x63
+#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
+#define SONY_TOC_DATA_ERR 0x65
+
+/* Hardware failure group */
+#define SONY_HW_FAILURE_ERR 0x70
+#define SONY_LEAD_IN_A_ERR 0x91
+#define SONY_LEAD_OUT_A_ERR 0x92
+#define SONY_DATA_TRACK_A_ERR 0x93
+
+/*
+ * The following are returned from the Read With Block Error Status command.
+ * They are not errors but information (Errors from the 0x5x group above may
+ * also be returned
+ */
+#define SONY_NO_CIRC_ERR_BLK_STAT 0x50
+#define SONY_NO_LECC_ERR_BLK_STAT 0x54
+#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55
+#define SONY_NO_ERR_DETECTION_STAT 0x59
+
+/*
+ * The following is not an error returned by the drive, but by the code
+ * that talks to the drive. It is returned because of a timeout.
+ */
+#define SONY_TIMEOUT_OP_ERR 0x01
+#define SONY_SIGNAL_OP_ERR 0x02
+
+
+/*
+ * The following are attention code for asyncronous events from the drive.
+ */
+
+/* Standard attention group */
+#define SONY_EMER_EJECT_ATTN 0x2c
+#define SONY_HW_FAILURE_ATTN 0x70
+#define SONY_MECH_LOADED_ATTN 0x80
+#define SONY_EJECT_PUSHED_ATTN 0x81
+
+/* Audio attention group */
+#define SONY_AUDIO_PLAY_DONE_ATTN 0x90
+#define SONY_LEAD_IN_ERR_ATTN 0x91
+#define SONY_LEAD_OUT_ERR_ATTN 0x92
+#define SONY_DATA_TRACK_ERR_ATTN 0x93
+#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94
+
+/* Auto spin up group */
+#define SONY_SPIN_UP_COMPLETE_ATTN 0x24
+#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25
+#define SONY_FOCUS_SERVO_ERR_ATTN 0x26
+#define SONY_TOC_READ_DONE_ATTN 0x62
+#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
+#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65
+
+/* Auto eject group */
+#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27
+#define SONY_EJECT_COMPLETE_ATTN 0x28
+#define SONY_EJECT_MECH_ERR_ATTN 0x29
diff --git a/include/linux/config.h b/include/linux/config.h
index b21ca68..81fe479 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -13,9 +13,7 @@
#define UTS_NODENAME "(none)" /* set by sethostname() */
#endif
-#ifdef CONFIG_M486
-#define UTS_MACHINE "i486" /* hardware type */
-#else
+#ifndef UTS_MACHINE
#define UTS_MACHINE "i386" /* hardware type */
#endif
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
index 93d458c..00c9806 100644
--- a/include/linux/ext2_fs.h
+++ b/include/linux/ext2_fs.h
@@ -28,8 +28,8 @@
/*
* The second extended file system version
*/
-#define EXT2FS_DATE "93/06/06"
-#define EXT2FS_VERSION "0.3a"
+#define EXT2FS_DATE "93/08/05"
+#define EXT2FS_VERSION "0.3c"
/*
* Special inodes numbers
@@ -38,6 +38,7 @@
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_FIRST_INO 11 /* First non reserved inode */
/*
@@ -65,7 +66,7 @@
#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__
-# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_log_block_size + 10)
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_es->s_log_block_size + 10)
#else
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
#endif
@@ -98,12 +99,12 @@ struct ext2_acl_header /* Header of Access Control Lists */
struct ext2_acl_entry /* Access Control List Entry */
{
- unsigned long acle_size;
+ 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_next; /* Pointer on next entry for the */
+ unsigned long acle_next; /* Pointer on next entry for the */
/* same inode or on next free entry */
};
@@ -112,23 +113,23 @@ struct ext2_acl_entry /* Access Control List Entry */
*/
struct ext2_old_group_desc
{
- unsigned long bg_block_bitmap; /* Blocks bitmap block */
- unsigned long bg_inode_bitmap; /* Inodes bitmap block */
- unsigned long bg_inode_table; /* Inodes table block */
+ unsigned long bg_block_bitmap; /* Blocks bitmap block */
+ unsigned long bg_inode_bitmap; /* Inodes bitmap block */
+ unsigned long bg_inode_table; /* Inodes table block */
unsigned short bg_free_blocks_count; /* Free blocks count */
unsigned short bg_free_inodes_count; /* Free inodes count */
};
struct ext2_group_desc
{
- unsigned long bg_block_bitmap; /* Blocks bitmap block */
- unsigned long bg_inode_bitmap; /* Inodes bitmap block */
- unsigned long bg_inode_table; /* Inodes table block */
+ unsigned long bg_block_bitmap; /* Blocks bitmap block */
+ unsigned long bg_inode_bitmap; /* Inodes bitmap block */
+ unsigned long bg_inode_table; /* Inodes table block */
unsigned short bg_free_blocks_count; /* Free blocks count */
unsigned short bg_free_inodes_count; /* Free inodes count */
unsigned short bg_used_dirs_count; /* Directories count */
unsigned short bg_pad;
- unsigned long bg_reserved[3];
+ unsigned long bg_reserved[3];
};
/*
@@ -147,11 +148,11 @@ struct ext2_group_desc
/*
* Constants relative to the data blocks
*/
-#define EXT2_NDIR_BLOCKS 12
-#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
-#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
-#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
-#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
/*
* Structure of an inode on the disk
@@ -159,50 +160,50 @@ struct ext2_group_desc
struct ext2_inode {
unsigned short i_mode; /* File mode */
unsigned short i_uid; /* Owner Uid */
- unsigned long i_size; /* Size in bytes */
- unsigned long i_atime; /* Access time */
- unsigned long i_ctime; /* Creation time */
- unsigned long i_mtime; /* Modification time */
- unsigned long i_dtime; /* Deletion Time */
+ unsigned long i_size; /* Size in bytes */
+ unsigned long i_atime; /* Access time */
+ unsigned long i_ctime; /* Creation time */
+ unsigned long i_mtime; /* Modification time */
+ unsigned long i_dtime; /* Deletion Time */
unsigned short i_gid; /* Group Id */
unsigned short i_links_count; /* Links count */
- unsigned long i_blocks; /* Blocks count */
- unsigned long i_flags; /* File flags */
- unsigned long i_reserved1;
- unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
- unsigned long i_version; /* File version (for NFS) */
- unsigned long i_file_acl; /* File ACL */
- unsigned long i_dir_acl; /* Directory ACL */
- unsigned long i_faddr; /* Fragment address */
- unsigned char i_frag; /* Fragment number */
- unsigned char i_fsize; /* Fragment size */
+ unsigned long i_blocks; /* Blocks count */
+ unsigned long i_flags; /* File flags */
+ unsigned long i_reserved1;
+ unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ unsigned long i_version; /* File version (for NFS) */
+ unsigned long i_file_acl; /* File ACL */
+ unsigned long i_dir_acl; /* Directory ACL */
+ unsigned long i_faddr; /* Fragment address */
+ unsigned char i_frag; /* Fragment number */
+ unsigned char i_fsize; /* Fragment size */
unsigned short i_pad1;
- unsigned long i_reserved2[2];
+ unsigned long i_reserved2[2];
};
/*
* Structure of the super block
*/
struct ext2_super_block {
- unsigned long s_inodes_count; /* Inodes count */
- unsigned long s_blocks_count; /* Blocks count */
- unsigned long s_r_blocks_count; /* Reserved blocks count */
- unsigned long s_free_blocks_count;/* Free blocks count */
- unsigned long s_free_inodes_count;/* Free inodes count */
- unsigned long s_first_data_block;/* First Data Block */
- unsigned long s_log_block_size; /* Block size */
- long s_log_frag_size; /* Fragment size */
- unsigned long s_blocks_per_group;/* # Blocks per group */
- unsigned long s_frags_per_group;/* # Fragments per group */
- unsigned long s_inodes_per_group;/* # Inodes per group */
- unsigned long s_mtime; /* Mount time */
- unsigned long s_wtime; /* Write time */
- unsigned long s_pad; /* Padding to get the magic signature*/
+ unsigned long s_inodes_count; /* Inodes count */
+ unsigned long s_blocks_count; /* Blocks count */
+ unsigned long s_r_blocks_count;/* Reserved blocks count */
+ unsigned long s_free_blocks_count;/* Free blocks count */
+ unsigned long s_free_inodes_count;/* Free inodes count */
+ unsigned long s_first_data_block;/* First Data Block */
+ unsigned long s_log_block_size;/* Block size */
+ long s_log_frag_size; /* Fragment size */
+ unsigned long s_blocks_per_group;/* # Blocks per group */
+ unsigned long s_frags_per_group;/* # Fragments per group */
+ unsigned long s_inodes_per_group;/* # Inodes per group */
+ unsigned long s_mtime; /* Mount time */
+ unsigned long s_wtime; /* Write time */
+ unsigned long s_pad; /* Padding to get the magic signature*/
/* at the same offset as in the */
/* previous ext fs */
unsigned short s_magic; /* Magic signature */
unsigned short s_valid; /* Flag */
- unsigned long s_reserved[243]; /* Padding to the end of the block */
+ unsigned long s_reserved[243]; /* Padding to the end of the block */
};
/*
@@ -211,10 +212,10 @@ struct ext2_super_block {
#define EXT2_NAME_LEN 255
struct ext2_dir_entry {
- unsigned long inode; /* Inode number */
- unsigned short rec_len; /* Directory entry length */
- unsigned short name_len; /* Name length */
- char name[EXT2_NAME_LEN]; /* File name */
+ unsigned long inode; /* Inode number */
+ unsigned short rec_len; /* Directory entry length */
+ unsigned short name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
};
/*
@@ -227,6 +228,7 @@ struct ext2_dir_entry {
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
+#ifdef __KERNEL__
/*
* Function prototypes
*/
@@ -261,7 +263,7 @@ 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 *);
+extern int ext2_sync_file (struct inode *, struct file *);
/* ialloc.c */
extern struct inode * ext2_new_inode (const struct inode *, int);
@@ -320,3 +322,5 @@ extern struct inode_operations ext2_file_inode_operations;
extern struct inode_operations ext2_symlink_inode_operations;
#endif
+
+#endif
diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h
index c3bb5c1..ffb1ba6 100644
--- a/include/linux/ext2_fs_i.h
+++ b/include/linux/ext2_fs_i.h
@@ -5,19 +5,19 @@
* second extended file system inode data in memory
*/
struct ext2_inode_info {
- unsigned long i_data[15];
- unsigned long i_flags;
- unsigned long i_faddr;
- unsigned char i_frag;
- unsigned char i_fsize;
+ unsigned long i_data[15];
+ unsigned long i_flags;
+ unsigned long i_faddr;
+ unsigned char i_frag;
+ unsigned char i_fsize;
unsigned short i_pad1;
- unsigned long i_file_acl;
- unsigned long i_dir_acl;
- unsigned long i_dtime;
- unsigned long i_version;
- unsigned long i_block_group;
- unsigned long i_next_alloc_block;
- unsigned long i_next_alloc_goal;
+ unsigned long i_file_acl;
+ unsigned long i_dir_acl;
+ unsigned long i_dtime;
+ unsigned long i_version;
+ unsigned long i_block_group;
+ unsigned long i_next_alloc_block;
+ unsigned long i_next_alloc_goal;
};
#endif
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index 59870b1..f004e14 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -8,12 +8,6 @@
* second extended-fs super-block data in memory
*/
struct ext2_sb_info {
- unsigned long s_inodes_count; /* Inodes count */
- unsigned long s_blocks_count; /* Blocks count */
- unsigned long s_r_blocks_count; /* Reserved blocks count */
- unsigned long s_first_data_block;/* First data block */
- unsigned long s_log_block_size; /* Log of block size */
- long s_log_frag_size; /* Log of fragment size */
unsigned long s_frag_size; /* Size of a fragment in bytes */
unsigned long s_frags_per_block;/* Number of fragments per block */
unsigned long s_inodes_per_block;/* Number of inodes per block */
@@ -23,6 +17,7 @@ struct ext2_sb_info {
unsigned long s_desc_per_block; /* Number of group descriptors per block */
unsigned long s_groups_count; /* Number of groups in the fs */
struct buffer_head * s_sbh; /* Buffer containing the super block */
+ struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
struct buffer_head * s_group_desc[EXT2_MAX_GROUP_DESC];
unsigned short s_loaded_inode_bitmaps;
unsigned short s_loaded_block_bitmaps;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e25ceab..dada0e8 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -28,7 +28,7 @@
#define NR_SUPER 32
#define NR_HASH 997
#define NR_IHASH 131
-#define NR_FILE_LOCKS 32
+#define NR_FILE_LOCKS 64
#define BLOCK_SIZE 1024
#define BLOCK_SIZE_BITS 10
#define MAX_CHRDEV 32
@@ -124,6 +124,12 @@ extern unsigned long file_table_init(unsigned long start, unsigned long end);
#define BLKROGET 4702 /* get read-only status (0 = read_write) */
#define BLKRRPART 4703 /* re-read partition table */
#define BLKGETSIZE 4704 /* return device size */
+#define BLKFLSBUF 4705 /* flush buffer cache */
+
+/* These are a few other constants only used by scsi devices */
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
#define FIBMAP 1 /* bmap access */
diff --git a/include/linux/ioctl.h b/include/linux/ioctl.h
index 9ecbca4..ec03d79 100644
--- a/include/linux/ioctl.h
+++ b/include/linux/ioctl.h
@@ -1,4 +1,4 @@
-/* $Id: ioctl.h,v 1.2 1992/11/18 01:31:16 root Exp root $
+/* $Id: ioctl.h,v 1.5 1993/07/19 21:53:50 root Exp root $
*
* linux/ioctl.h for Linux by H.H. Bergman.
*/
@@ -8,11 +8,13 @@
/* ioctl command encoding: 32 bits total, command in lower 16 bits,
- * size of the parameter structure in the upper 14 bits.
- * Encoding size in ioctl request is useful for catching old versions
+ * size of the parameter structure in the lower 14 bits of the
+ * upper 16 bits.
+ * Encoding the size of the parameter structure in the ioctl request
+ * is useful for catching programs compiled with old versions
* and to avoid overwriting user space outside the user buffer area.
- * The highest 2 bits are reserved.
- * NOTE: This limits the max blocksize to 16kB -1 !
+ * The highest 2 bits are reserved for indicating the ``access mode''.
+ * NOTE: This limits the max parameter size to 16kB -1 !
*/
#define IOC_VOID 0x00000000 /* param in size field */
@@ -24,17 +26,22 @@
#define IOCCMD_MASK 0x0000ffff /* command code */
#define IOCCMD_SHIFT 0
-#define _IO(c,d) (IOC_VOID | ((d)<<16) | c) /* param encoded */
-/* use _IOXX(magic, subcode, arg_t) where arg_t is the type of the
+
+/* _IO(magic, subcode); size field is zero and the
+ * subcode determines the command.
+ */
+#define _IO(c,d) (IOC_VOID | ((c)<<8) | (d)) /* param encoded */
+
+/* _IOXX(magic, subcode, arg_t); where arg_t is the type of the
* (last) argument field in the ioctl call, if present.
*/
#define _IOW(c,d,t) (IOC_IN | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
- (c<<8) | d)
+ ((c)<<8) | (d))
#define _IOR(c,d,t) (IOC_OUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
- (c<<8) | d)
+ ((c)<<8) | (d))
/* WR rather than RW to avoid conflict with stdio.h */
#define _IOWR(c,d,t) (IOC_INOUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
- (c<<8) | d)
+ ((c)<<8) | (d))
#endif /* _LINUX_IOCTL_H */
diff --git a/include/linux/kd.h b/include/linux/kd.h
index 9ef695a..2f8c740 100644
--- a/include/linux/kd.h
+++ b/include/linux/kd.h
@@ -176,4 +176,11 @@ struct kbentry {
#define KDGKBENT 0x4B46 /* gets one entry in translation table */
#define KDSKBENT 0x4B47 /* sets one entry in translation table */
+struct kbsentry {
+ u_char kb_func;
+ u_char kb_string[512]; /* FUNC_BUFSIZE from keyboard.h */
+};
+#define KDGKBSENT 0x4B48 /* gets one function key string entry */
+#define KDSKBSENT 0x4B49 /* sets one function key string entry */
+
#endif /* _LINUX_KD_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index c0a86d1..69b1899 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -9,6 +9,11 @@
#include <linux/config.h>
+#define INT_MAX ((int)(~0U>>1))
+#define UINT_MAX (~0U)
+#define LONG_MAX ((long)(~0UL>>1))
+#define ULONG_MAX (~0UL)
+
#define VERIFY_READ 0
#define VERIFY_WRITE 1
diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h
index 51e2aee..5151bd3 100644
--- a/include/linux/keyboard.h
+++ b/include/linux/keyboard.h
@@ -5,31 +5,6 @@
#define set_leds() mark_bh(KEYBOARD_BH)
/*
- * Global flags: things that don't change between virtual consoles.
- * This includes things like "key-down" flags - if the shift key is
- * down when you change a console, it's down in both.
- *
- * Note that the KG_CAPSLOCK flags is NOT the flag that decides if
- * capslock is on or not: it's just a flag about the key being
- * physically down. The actual capslock status is in the local flags.
- */
-extern unsigned long kbd_flags;
-
-/*
- * These are the hardcoded global flags - use the numbers beyond 16
- * for non-standard or keyboard-dependent flags
- */
-#define KG_LSHIFT 0
-#define KG_RSHIFT 1
-#define KG_LCTRL 2
-#define KG_RCTRL 3
-#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
* character code (sticky shift, E0/E1 special scancodes, diacriticals)
*/
@@ -72,26 +47,6 @@ extern struct kbd_struct kbd_table[];
extern unsigned long kbd_init(unsigned long);
-extern inline int kbd_flag(int flag)
-{
- return kbd_flags & (1 << flag);
-}
-
-extern inline void set_kbd_flag(int flag)
-{
- kbd_flags |= 1 << flag;
-}
-
-extern inline void clr_kbd_flag(int flag)
-{
- kbd_flags &= ~(1 << flag);
-}
-
-extern inline void chg_kbd_flag(int flag)
-{
- kbd_flags ^= 1 << flag;
-}
-
extern inline int kbd_dead(int flag)
{
return kbd_prev_dead_keys & (1 << flag);
@@ -132,12 +87,17 @@ 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
+#define NR_KEYS 128
+#define NR_KEYMAPS 16
extern const int NR_TYPES;
extern const int max_vals[];
extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
+#define NR_FUNC 32
+#define FUNC_BUFSIZE 512
+extern char func_buf[FUNC_BUFSIZE];
+extern char *func_table[NR_FUNC];
+
#define KT_LATIN 0 /* we depend on this being zero */
#define KT_FN 1
#define KT_SPEC 2
@@ -146,6 +106,8 @@ extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
#define KT_CONS 5
#define KT_CUR 6
#define KT_SHIFT 7
+#define KT_META 8
+#define KT_ASCII 9
#define K(t,v) (((t)<<8)|(v))
#define KTYP(x) ((x) >> 8)
@@ -188,6 +150,10 @@ extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
#define K_CAPS K(KT_SPEC,7)
#define K_NUM K(KT_SPEC,8)
#define K_HOLD K(KT_SPEC,9)
+#define K_SCROLLFORW K(KT_SPEC,10)
+#define K_SCROLLBACK K(KT_SPEC,11)
+#define K_BOOT K(KT_SPEC,12)
+#define K_CAPSON K(KT_SPEC,13)
#define K_P0 K(KT_PAD,0)
#define K_P1 K(KT_PAD,1)
@@ -218,16 +184,29 @@ extern unsigned short key_map[NR_KEYMAPS][NR_KEYS];
#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
+#define KG_SHIFT 0
+#define KG_CTRL 2
+#define KG_ALT 3
+#define KG_ALTGR 1
+
+#define K_SHIFT K(KT_SHIFT,KG_SHIFT)
+#define K_CTRL K(KT_SHIFT,KG_CTRL)
+#define K_ALT K(KT_SHIFT,KG_ALT)
+#define K_ALTGR K(KT_SHIFT,KG_ALTGR)
+
+#define NR_SHIFT 16
+
+#define K_CAPSSHIFT K(KT_SHIFT,NR_SHIFT)
+
+#define K_ASC0 K(KT_ASCII,0)
+#define K_ASC1 K(KT_ASCII,1)
+#define K_ASC2 K(KT_ASCII,2)
+#define K_ASC3 K(KT_ASCII,3)
+#define K_ASC4 K(KT_ASCII,4)
+#define K_ASC5 K(KT_ASCII,5)
+#define K_ASC6 K(KT_ASCII,6)
+#define K_ASC7 K(KT_ASCII,7)
+#define K_ASC8 K(KT_ASCII,8)
+#define K_ASC9 K(KT_ASCII,9)
#endif
diff --git a/include/linux/ldt.h b/include/linux/ldt.h
new file mode 100644
index 0000000..7ab13e8
--- /dev/null
+++ b/include/linux/ldt.h
@@ -0,0 +1,28 @@
+/*
+ * ldt.h
+ *
+ * Definitions of structures used with the modify_ldt system call.
+ */
+#ifndef _LINUX_LDT_H
+#define _LINUX_LDT_H
+
+struct modify_ldt_ldt_s {
+ unsigned int entry_number;
+ unsigned long base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+};
+
+#define MODIFY_LDT_CONTENTS_DATA 0
+#define MODIFY_LDT_CONTENTS_STACK 1
+#define MODIFY_LDT_CONTENTS_CODE 2
+
+extern int get_ldt(void *buffer);
+extern int set_ldt_entry(int entry, unsigned long base, unsigned int limit,
+ int seg_32bit_flag, int contents, int read_only_flag,
+ int limit_in_pages_flag);
+
+#endif
diff --git a/include/linux/mcd.h b/include/linux/mcd.h
new file mode 100644
index 0000000..4736f2a
--- /dev/null
+++ b/include/linux/mcd.h
@@ -0,0 +1,101 @@
+/*
+ * Definitions for a Mitsumi CD-ROM interface
+ *
+ * Copyright (C) 1992 Martin Harriss
+ *
+ * martin@bdsi.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/* *** change this to set the I/O port address */
+#define MCDPORT(x) (0x320 + (x))
+
+/* *** change this to set the interrupt number */
+#define MCD_INTR_NR 11
+
+
+
+
+
+/* status bits */
+
+#define MST_CMD_CHECK 0x01 /* command error */
+#define MST_BUSY 0x02 /* now playing */
+#define MST_READ_ERR 0x04 /* read error */
+#define MST_DSK_TYPE 0x08
+#define MST_SERVO_CHECK 0x10
+#define MST_DSK_CHG 0x20 /* disk removed or changed */
+#define MST_READY 0x40 /* disk in the drive */
+#define MST_DOOR_OPEN 0x80 /* door is open */
+
+/* flag bits */
+
+#define MFL_DATA 0x02 /* data available */
+#define MFL_STATUS 0x04 /* status available */
+
+/* commands */
+
+#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */
+#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */
+#define MCMD_GET_STATUS 0x40
+#define MCMD_SET_MODE 0x50
+#define MCMD_SOFT_RESET 0x60
+#define MCMD_STOP 0x70 /* stop play */
+#define MCMD_CONFIG_DRIVE 0x90
+#define MCMD_SET_VOLUME 0xAE /* set audio level */
+#define MCMD_PLAY_READ 0xC0 /* play or read data */
+#define MCMD_GET_VERSION 0xDC
+
+/* borrowed from hd.c */
+
+#define READ_DATA(port, buf, nr) \
+__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","di")
+
+#define SET_TIMER(func, jifs) \
+ ((timer_table[MCD_TIMER].expires = jiffies + jifs), \
+ (timer_table[MCD_TIMER].fn = func), \
+ (timer_active |= 1<<MCD_TIMER))
+
+#define CLEAR_TIMER timer_active &= ~(1<<MCD_TIMER)
+
+#define MAX_TRACKS 104
+
+struct msf {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+};
+
+struct mcd_Play_msf {
+ struct msf start;
+ struct msf end;
+};
+
+struct mcd_DiskInfo {
+ unsigned char first;
+ unsigned char last;
+ struct msf diskLength;
+ struct msf firstTrack;
+};
+
+struct mcd_Toc {
+ unsigned char ctrl_addr;
+ unsigned char track;
+ unsigned char pointIndex;
+ struct msf trackTime;
+ struct msf diskTime;
+};
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a225b29..ff66453 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1,9 +1,7 @@
#ifndef _LINUX_MM_H
#define _LINUX_MM_H
-#define PAGE_SIZE 4096
-#define PAGE_SHIFT 12
-
+#include <linux/page.h>
#include <linux/fs.h>
#include <linux/kernel.h>
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index 3e3197e..756abaa 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -103,11 +103,11 @@ struct fat_cache {
/* Convert attribute bits and a mask to the UNIX mode. */
-#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0555 : 0777))
+#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO))
/* Convert the UNIX mode to MS-DOS attribute bits. */
-#define MSDOS_MKATTR(m) ((m & 0200) ? ATTR_NONE : ATTR_RO)
+#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
static inline struct buffer_head *msdos_sread(int dev,int sector,void **start)
diff --git a/include/linux/mtio.h b/include/linux/mtio.h
index 990f830..446667e 100644
--- a/include/linux/mtio.h
+++ b/include/linux/mtio.h
@@ -48,6 +48,8 @@ struct mtop {
#define MTSETDENSITY 21 /* set tape density (SCSI) */
#define MTSEEK 22 /* seek to block (Tandberg, etc.) */
#define MTTELL 23 /* tell block (Tandber, etc.) */
+#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
+ /* ordinary buffered operation with code 1 */
/* structure for MTIOCGET - mag tape get status command */
diff --git a/include/linux/nfs.h b/include/linux/nfs.h
index 60bc293..cc290ef 100644
--- a/include/linux/nfs.h
+++ b/include/linux/nfs.h
@@ -8,7 +8,7 @@
#define NFS_MAXGROUPS 16
#define NFS_FHSIZE 32
#define NFS_COOKIESIZE 4
-#define NFS_FIFO_DEV -1
+#define NFS_FIFO_DEV (-1)
#define NFSMODE_FMT 0170000
#define NFSMODE_DIR 0040000
#define NFSMODE_CHR 0020000
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 1c5db08..367a9fb 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -116,4 +116,9 @@ extern struct inode_operations nfs_dir_inode_operations;
extern struct inode_operations nfs_symlink_inode_operations;
+/* linux/fs/nfs/mmap.c */
+
+extern int nfs_mmap(struct inode * inode, struct file * file,
+ unsigned long addr, size_t len, int prot, unsigned long off);
+
#endif
diff --git a/include/linux/page.h b/include/linux/page.h
new file mode 100644
index 0000000..97796bd
--- /dev/null
+++ b/include/linux/page.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_PAGE_H
+#define _LINUX_PAGE_H
+
+ /* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE ((unsigned long)1<<PAGE_SHIFT)
+
+#ifdef __KERNEL__
+
+ /* number of bits that fit into a memory pointer */
+#define BITS_PER_PTR (8*sizeof(unsigned long))
+ /* to mask away the intra-page address bits */
+#define PAGE_MASK (~(PAGE_SIZE-1))
+ /* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
+ /* to align the pointer to a pointer address */
+#define PTR_MASK (~(sizeof(void*)-1))
+
+ /* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
+ /* 64-bit machines, beware! SRB. */
+#define SIZEOF_PTR_LOG2 2
+
+ /* to find an entry in a page-table-directory */
+#define PAGE_DIR_OFFSET(base,address) ((unsigned long*)((base)+\
+ ((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)*2&PTR_MASK&~PAGE_MASK)))
+ /* to find an entry in a page-table */
+#define PAGE_PTR(address) \
+ ((unsigned long)(address)>>PAGE_SHIFT-SIZEOF_PTR_LOG2&PTR_MASK&~PAGE_MASK)
+ /* the no. of pointers that fit on a page */
+#define PTRS_PER_PAGE (PAGE_SIZE/sizeof(void*))
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_PAGE_H */
diff --git a/include/linux/param.h b/include/linux/param.h
index ec03686..c634b1e 100644
--- a/include/linux/param.h
+++ b/include/linux/param.h
@@ -12,7 +12,7 @@
#endif
#ifndef NOGROUP
-#define NOGROUP -1
+#define NOGROUP (-1)
#endif
#define MAXHOSTNAMELEN 64 /* max length of hostname */
diff --git a/include/linux/resource.h b/include/linux/resource.h
index 6626c33..86c15c7 100644
--- a/include/linux/resource.h
+++ b/include/linux/resource.h
@@ -13,8 +13,8 @@
* structure will lose. This reduces the chances of that happening.
*/
#define RUSAGE_SELF 0
-#define RUSAGE_CHILDREN -1
-#define RUSAGE_BOTH -2 /* sys_wait4() uses this */
+#define RUSAGE_CHILDREN (-1)
+#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */
struct rusage {
struct timeval ru_utime; /* user time used */
@@ -54,14 +54,14 @@ struct rusage {
#define RLIM_NLIMITS 6
-#define RLIM_INFINITY 0x7fffffff
+#define RLIM_INFINITY LONG_MAX
struct rlimit {
int rlim_cur;
int rlim_max;
};
-#define PRIO_MIN -99
+#define PRIO_MIN (-99)
#define PRIO_MAX 14
#define PRIO_PROCESS 0
diff --git a/include/linux/sched.h b/include/linux/sched.h
index cf6473a..bda86e8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1,6 +1,8 @@
#ifndef _LINUX_SCHED_H
#define _LINUX_SCHED_H
+#define NEW_SWAP
+
/*
* define DEBUG if you want the wait-queues to have some extra
* debugging code. It's not normally used, but might catch some
@@ -165,7 +167,7 @@ struct task_struct {
int elf_executable:1;
int dumpable:1;
int swappable:1;
- unsigned long start_code,end_code,end_data,brk,start_stack,start_mmap;
+ unsigned long start_code,end_code,end_data,start_brk,brk,start_stack,start_mmap;
unsigned long arg_start, arg_end, env_start, env_end;
long pid,pgrp,session,leader;
int groups[NGROUPS];
@@ -206,10 +208,17 @@ struct task_struct {
struct sem_undo *semun;
struct file * filp[NR_OPEN];
fd_set close_on_exec;
-/* ldt for this task - not currently used */
- struct desc_struct ldt[32];
+/* ldt for this task - used by Wine. If NULL, default_ldt is used */
+ struct desc_struct *ldt;
/* tss for this task */
struct tss_struct tss;
+#ifdef NEW_SWAP
+ unsigned long old_maj_flt; /* old value of maj_flt */
+ unsigned long dec_flt; /* page fault count of the last time */
+ unsigned long swap_cnt; /* number of pages to swap on next pass */
+ short swap_table; /* current page table */
+ short swap_page; /* current page */
+#endif NEW_SWAP
};
/*
@@ -236,7 +245,7 @@ struct task_struct {
/* schedlink */ &init_task,&init_task, \
/* signals */ {{ 0, },}, \
/* stack */ 0,0, \
-/* ec,brk... */ 0,0,0,0,0,0,0,0,0,0,0, \
+/* ec,brk... */ 0,0,0,0,0,0,0,0,0,0,0,0, \
/* argv.. */ 0,0,0,0, \
/* pid etc.. */ 0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
@@ -244,9 +253,9 @@ struct task_struct {
/* 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, \
-/* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
- {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
- {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \
+/* rlimits */ { {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
+ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \
+ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}}, \
/* math */ 0, \
/* rss */ 2, \
/* comm */ "swapper", \
@@ -255,9 +264,7 @@ struct task_struct {
/* ipc */ NULL, NULL, \
/* filp */ {NULL,}, \
/* cloe */ {{ 0, }}, \
- { \
-/* ldt */ {0,0}, \
- }, \
+/* ldt */ NULL, \
/*tss*/ {0,0, \
sizeof(init_kernel_stack) + (long) &init_kernel_stack, KERNEL_DS, 0, \
0,0,0,0,0,0, \
@@ -266,7 +273,7 @@ struct task_struct {
USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \
_LDT(0),0, \
0, 0x8000, \
-/* ioperm */ {0xffffffff, }, \
+/* ioperm */ {~0, }, \
_TSS(0), \
/* 387 state */ { { 0, }, } \
} \
@@ -280,8 +287,11 @@ extern unsigned long volatile jiffies;
extern unsigned long startup_time;
extern int jiffies_offset;
extern int need_resched;
+
extern int hard_math;
+extern int x86;
extern int ignore_irq13;
+extern int wp_works_ok;
#define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)
@@ -345,8 +355,6 @@ __asm__("cmpl %%ecx,_current\n\t" \
"c" (tsk) \
:"cx")
-#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)
-
#define _set_base(addr,base) \
__asm__("movw %%dx,%0\n\t" \
"rorl $16,%%edx\n\t" \
@@ -483,24 +491,39 @@ static inline unsigned long get_limit(unsigned long segment)
return __limit+1;
}
-#define REMOVE_LINKS(p) \
+#define REMOVE_LINKS(p) do { unsigned long flags; \
+ save_flags(flags) ; cli(); \
(p)->next_task->prev_task = (p)->prev_task; \
(p)->prev_task->next_task = (p)->next_task; \
+ restore_flags(flags); \
if ((p)->p_osptr) \
(p)->p_osptr->p_ysptr = (p)->p_ysptr; \
if ((p)->p_ysptr) \
(p)->p_ysptr->p_osptr = (p)->p_osptr; \
else \
- (p)->p_pptr->p_cptr = (p)->p_osptr
+ (p)->p_pptr->p_cptr = (p)->p_osptr; \
+ } while (0)
-#define SET_LINKS(p) \
+#define SET_LINKS(p) do { unsigned long flags; \
+ save_flags(flags); cli(); \
(p)->next_task = &init_task; \
(p)->prev_task = init_task.prev_task; \
init_task.prev_task->next_task = (p); \
init_task.prev_task = (p); \
+ restore_flags(flags); \
(p)->p_ysptr = NULL; \
if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
(p)->p_osptr->p_ysptr = p; \
- (p)->p_pptr->p_cptr = p
+ (p)->p_pptr->p_cptr = p; \
+ } while (0)
+
+#define for_each_task(p) \
+ for (p = &init_task ; (p = p->next_task) != &init_task ; )
+
+/*
+ * This is the ldt that every process will get unless we need
+ * something other than this.
+ */
+extern struct desc_struct default_ldt;
#endif
diff --git a/include/linux/serial.h b/include/linux/serial.h
index db5fd47..18244b1 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -15,12 +15,15 @@
*
* For definitions of the flags field, see tty.h
*/
+#ifndef _LINUX_SERIAL_H
+#define _LINUX_SERIAL_H
struct async_struct {
int baud_base;
int port;
int irq;
int flags; /* defined in tty.h */
+ int hub6; /* HUB6 plus one */
int type; /* UART type */
struct tty_struct *tty;
int read_status_mask;
@@ -29,11 +32,14 @@ struct async_struct {
int custom_divisor;
int x_char; /* xon/xoff characater */
int close_delay;
+ int IER; /* Interrupt Enable Register */
int event;
int line;
int count; /* # of fd on device */
int blocked_open; /* # of blocked opens */
- struct wait_queue *open_wait;
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ struct wait_queue *open_wait;
struct async_struct *next_port; /* For the linked list */
struct async_struct *prev_port;
};
@@ -150,3 +156,5 @@ struct async_struct {
#define UART_MSR_DDSR 0x02 /* Delta DSR */
#define UART_MSR_DCTS 0x01 /* Delta CTS */
#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+#endif /* _LINUX_SERIAL_H */
diff --git a/include/linux/shm.h b/include/linux/shm.h
index cd6c767..f61d6d6 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -34,13 +34,41 @@ struct shminfo {
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 */
+#define SHM_RANGE_START 0x40000000
+#define SHM_RANGE_END 0x60000000
+
+ /* _SHM_ID_BITS is a variable you can adjust to */
+ /* tune the kernel. It determines the value of */
+ /* SHMMNI, which specifies the maximum no. of */
+ /* shared segments (system wide). SRB. */
+#define _SHM_ID_BITS 7 /* keep as low as possible */
+ /* a static array is declared */
+ /* using SHMMNI */
+
+#define __SHM_IDX_BITS (BITS_PER_PTR-2-SHM_IDX_SHIFT)
+
+/* !!!!!!!?????
+ * Why reserve the two (2) high bits of the signature (shm_sgn) field?
+ * Since, as far as I can see, only the high bit is used (SHM_READ_ONLY).
+ * SRB.
+ */
+
+#define _SHM_IDX_BITS (__SHM_IDX_BITS+PAGE_SHIFT>=BITS_PER_PTR?\
+ BITS_PER_PTR-PAGE_SHIFT-1:__SHM_IDX_BITS) /* sanity check */
+/* not present page table entry format bit 0 is 0, low byte defined in mm.h */
+#define SHM_ID_SHIFT 8
+#define SHM_ID_MASK ((1<<_SHM_ID_BITS)-1)
+#define SHM_IDX_SHIFT (SHM_ID_SHIFT+_SHM_ID_BITS)
+#define SHM_IDX_MASK ((1<<_SHM_IDX_BITS)-1)
+#define SHM_READ_ONLY (1<<BITS_PER_PTR-1)
+
+#define SHMMAX (1<<PAGE_SHIFT+_SHM_IDX_BITS) /* max shared seg size (bytes) */
+#define SHMMIN 1 /* really PAGE_SIZE */ /* min shared seg size (bytes)*/
+#define SHMMNI (1<<_SHM_ID_BITS) /* max num of segs system wide */
+#define SHMALL (1<<_SHM_IDX_BITS+_SHM_ID_BITS) /* max shm system wide (pages) */
+#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */
+#define SHMSEG SHMMNI /* max shared segs per process */
#ifdef __KERNEL__
@@ -74,13 +102,6 @@ struct shm_desc {
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/stat.h b/include/linux/stat.h
index 3d8b4a9..86fbd82 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -1,7 +1,7 @@
#ifndef _LINUX_STAT_H
#define _LINUX_STAT_H
-#ifndef __NOT_KERNEL
+#ifdef __KERNEL__
struct old_stat {
unsigned short st_dev;
@@ -77,4 +77,12 @@ struct new_stat {
#define S_IWOTH 00002
#define S_IXOTH 00001
+#ifdef __KERNEL__
+#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
+#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
+#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+#endif
+
#endif
diff --git a/include/linux/sys.h b/include/linux/sys.h
index 94635ee..7ff8045 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -129,6 +129,7 @@ extern int sys_sigreturn();
extern int sys_setdomainname();
extern int sys_olduname();
extern int sys_old_syscall();
+extern int sys_modify_ldt();
/*
* These are system calls that will be removed at some time
@@ -167,7 +168,7 @@ sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall,
sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat,
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_setdomainname, sys_newuname};
+sys_clone, sys_setdomainname, sys_newuname, sys_modify_ldt};
}
diff --git a/include/linux/termios.h b/include/linux/termios.h
index 5dcc243..8fd2bde 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -47,6 +47,8 @@
#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
#define TIOCSERSWILD 0x5455
+#define TIOCGLCKTRMIOS 0x5456
+#define TIOCSLCKTRMIOS 0x5457
/* Used for packet mode */
#define TIOCPKT_FLUSHREAD 1
diff --git a/include/linux/timer.h b/include/linux/timer.h
index dcca17f..4cf602f 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -26,6 +26,8 @@
* COPRO_TIMER 387 timeout for buggy hardware..
*
* TAPE_QIC02_TIMER timer for QIC-02 tape driver (it's not hardcoded)
+ *
+ * MCD_TIMER Mitsumi CD-ROM Timer
*/
#define BLANK_TIMER 0
@@ -40,6 +42,7 @@
#define COPRO_TIMER 21
#define TAPE_QIC02_TIMER 22 /* hhb */
+#define MCD_TIMER 23
struct timer_struct {
unsigned long expires;
@@ -70,6 +73,6 @@ struct timer_list {
};
extern void add_timer(struct timer_list * timer);
-extern void del_timer(struct timer_list * timer);
+extern int del_timer(struct timer_list * timer);
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f735fc9..5f43cb3 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -80,7 +80,8 @@ struct serial_struct {
int baud_base;
char close_delay;
char reserved_char[3];
- int reserved[6];
+ int hub6;
+ int reserved[5];
};
/*
@@ -99,6 +100,7 @@ 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_TERMIOS_RESTORE 0x0008 /* Restore termios when dialin unblocks */
#define ASYNC_SPD_MASK 0x0030
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
@@ -107,8 +109,13 @@ struct serial_struct {
#define ASYNC_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
#define ASYNC_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
-#define ASYNC_FLAGS 0x00F7 /* Possible legal async flags */
+#define ASYNC_FLAGS 0x0FFF /* Possible legal async flags */
+#define ASYNC_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset */
/* Internal flags used only by kernel/chr_drv/serial.c */
#define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */
@@ -223,6 +230,8 @@ struct tty_struct {
unsigned int cmd, unsigned long arg);
void (*throttle)(struct tty_struct * tty, int status);
void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ void (*stop)(struct tty_struct *tty);
+ void (*start)(struct tty_struct *tty);
struct tty_struct *link;
unsigned char *write_data_ptr;
int write_data_cnt;
@@ -318,6 +327,8 @@ extern void tty_write_flush(struct tty_struct *);
extern void tty_read_flush(struct tty_struct *);
extern struct tty_struct *tty_table[];
+extern struct termios *tty_termios[];
+extern struct termios *termios_locked[];
extern int tty_check_write[];
extern struct tty_struct * redirect;
extern struct tty_ldisc ldiscs[];
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
index 1056a21..dbb0295 100644
--- a/include/linux/unistd.h
+++ b/include/linux/unistd.h
@@ -129,6 +129,7 @@
#define __NR_clone 120
#define __NR_setdomainname 121
#define __NR_uname 122
+#define __NR_modify_ldt 123
extern int errno;
diff --git a/init/main.c b/init/main.c
index ed00c52..6d0b17b 100644
--- a/init/main.c
+++ b/init/main.c
@@ -22,11 +22,14 @@
#include <linux/fs.h>
#include <linux/ctype.h>
#include <linux/delay.h>
+#include <linux/utsname.h>
extern unsigned long * prof_buffer;
extern unsigned long prof_len;
extern char edata, end;
extern char *linux_banner;
+extern "C" void lcall7(void);
+struct desc_struct default_ldt;
/*
* we need this inline - forking from kernel space will result
@@ -61,7 +64,7 @@ static inline pid_t wait(int * wait_stat)
static char printbuf[1024];
-extern char empty_zero_page[4096];
+extern char empty_zero_page[PAGE_SIZE];
extern int vsprintf(char *,const char *,va_list);
extern void init(void);
extern void init_IRQ(void);
@@ -332,12 +335,13 @@ extern "C" void start_kernel(void)
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
+ set_call_gate(&default_ldt,lcall7);
ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
screen_info = SCREEN_INFO;
aux_device_present = AUX_DEVICE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
- memory_end &= 0xfffff000;
+ memory_end &= PAGE_MASK;
ramdisk_size = RAMDISK_SIZE;
strcpy(command_line,COMMAND_LINE);
#ifdef CONFIG_MAX_16M
@@ -348,13 +352,12 @@ extern "C" void start_kernel(void)
root_mountflags |= MS_RDONLY;
if ((unsigned long)&end >= (1024*1024)) {
memory_start = (unsigned long) &end;
- low_memory_start = 4096;
+ low_memory_start = PAGE_SIZE;
} else {
memory_start = 1024*1024;
low_memory_start = (unsigned long) &end;
}
- low_memory_start += 0xfff;
- low_memory_start &= 0xfffff000;
+ low_memory_start = PAGE_ALIGN(low_memory_start);
memory_start = paging_init(memory_start,memory_end);
trap_init();
init_IRQ();
@@ -409,6 +412,13 @@ extern "C" void start_kernel(void)
printk("Ok, fpu using %s error reporting.\n",
ignore_irq13?"exception 16":"irq13");
}
+#ifndef CONFIG_MATH_EMULATION
+ else {
+ printk("No coprocessor found and no math emulation present.\n");
+ printk("Giving up.\n");
+ for (;;) ;
+ }
+#endif
move_to_user_mode();
if (!fork()) /* we count on this going ok */
init();
@@ -445,7 +455,9 @@ void init(void)
(void) dup(0);
(void) dup(0);
+ system_utsname.machine[1] = '0' + x86;
printf(linux_banner);
+
execve("/etc/init",argv_init,envp_init);
execve("/bin/init",argv_init,envp_init);
execve("/sbin/init",argv_init,envp_init);
diff --git a/ipc/msg.c b/ipc/msg.c
index 2becdde..b8f5f12 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -7,6 +7,7 @@
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/msg.h>
+#include <linux/stat.h>
extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
@@ -59,7 +60,7 @@ int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
slept:
if (ipcp->seq != (msqid / MSGMNI))
return -EIDRM;
- if (ipcperms(ipcp, 0222))
+ if (ipcperms(ipcp, S_IWUGO))
return -EACCES;
if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
@@ -137,7 +138,7 @@ int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
while (!nmsg) {
if(ipcp->seq != msqid / MSGMNI)
return -EIDRM;
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
if (msgtyp == 0)
nmsg = msq->msg_first;
@@ -243,7 +244,7 @@ found:
return -ENOMEM;
}
ipcp = &msq->msg_perm;
- ipcp->mode = (msgflg & 0x01FF);
+ ipcp->mode = (msgflg & S_IRWXUGO);
ipcp->key = key;
ipcp->cuid = ipcp->uid = current->euid;
ipcp->gid = ipcp->cgid = current->egid;
@@ -359,7 +360,7 @@ int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
msq = msgque[msqid];
if (msq == IPC_UNUSED || msq == IPC_NOID)
return -EINVAL;
- if (ipcperms (&msq->msg_perm, 0444))
+ if (ipcperms (&msq->msg_perm, S_IRUGO))
return -EACCES;
id = msqid + msq->msg_perm.seq * MSGMNI;
memcpy_tofs (buf, msq, sizeof(*msq));
@@ -388,7 +389,7 @@ int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
switch (cmd) {
case IPC_STAT:
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
memcpy_tofs (buf, msq, sizeof (*msq));
return 0;
@@ -406,8 +407,8 @@ int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
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);
+ ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
+ (S_IRWXUGO & tbuf.msg_perm.mode);
msq->msg_ctime = CURRENT_TIME;
break;
default:
diff --git a/ipc/sem.c b/ipc/sem.c
index 7795843..a258716 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -9,6 +9,7 @@
#include <linux/sched.h>
#include <linux/sem.h>
#include <linux/ipc.h>
+#include <linux/stat.h>
extern int ipcperms (struct ipc_perm *ipcp, short semflg);
static int newary (key_t, int, int);
@@ -79,7 +80,7 @@ found:
memset (sma, 0, size);
sma->sem_base = (struct sem *) &sma[1];
ipcp = &sma->sem_perm;
- ipcp->mode = (semflg & 0x01FF);
+ ipcp->mode = (semflg & S_IRWXUGO);
ipcp->key = key;
ipcp->cuid = ipcp->uid = current->euid;
ipcp->gid = ipcp->cgid = current->egid;
@@ -198,7 +199,7 @@ int sys_semctl (int semid, int semnum, int cmd, void *arg)
sma = semary[semid];
if (sma == IPC_UNUSED || sma == IPC_NOID)
return -EINVAL;
- if (ipcperms (&sma->sem_perm, 0444))
+ if (ipcperms (&sma->sem_perm, S_IRUGO))
return -EACCES;
id = semid + sma->sem_perm.seq * SEMMNI;
memcpy_tofs (buf, sma, sizeof(*sma));
@@ -223,7 +224,7 @@ int sys_semctl (int semid, int semnum, int cmd, void *arg)
case GETNCNT:
case GETZCNT:
case GETALL:
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
switch (cmd) {
case GETVAL : return curr->semval;
@@ -283,14 +284,14 @@ int sys_semctl (int semid, int semnum, int cmd, void *arg)
switch (cmd) {
case GETALL:
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
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))
+ if (ipcperms (ipcp, S_IWUGO))
return -EACCES;
for (un = sma->undo; un; un = un->id_next)
if (semnum == un->sem_num)
@@ -307,19 +308,19 @@ int sys_semctl (int semid, int semnum, int cmd, void *arg)
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);
+ ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
+ | (tbuf.sem_perm.mode & S_IRWXUGO);
sma->sem_ctime = CURRENT_TIME;
return 0;
}
return -EPERM;
case IPC_STAT:
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
memcpy_tofs (buf, sma, sizeof (*sma));
break;
case SETALL:
- if (ipcperms (ipcp, 0222))
+ if (ipcperms (ipcp, S_IWUGO))
return -EACCES;
for (i=0; i<nsems; i++)
sma->sem_base[i].semval = sem_io[i];
@@ -368,7 +369,7 @@ int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
semncnt ++;
}
}
- if (ipcperms(&sma->sem_perm, alter ? 0222 : 0444))
+ if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
return -EACCES;
/*
* ensure every sop with undo gets an undo structure
diff --git a/ipc/shm.c b/ipc/shm.c
index fe590a4..2e2c1b3 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -2,7 +2,7 @@
* linux/ipc/shm.c
* Copyright (C) 1992, 1993 Krishna Balasubramanian
* Many improvements/fixes by Bruno Haible.
- * assume user segments start at 0x00
+ * assume user segments start at 0x0
*/
#include <linux/errno.h>
@@ -10,6 +10,7 @@
#include <linux/sched.h>
#include <linux/ipc.h>
#include <linux/shm.h>
+#include <linux/stat.h>
extern int ipcperms (struct ipc_perm *ipcp, short semflg);
extern unsigned int get_swap_page(void);
@@ -99,7 +100,7 @@ found:
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.mode = (shmflg & S_IRWXUGO);
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;
@@ -177,7 +178,7 @@ static void killseg (int id)
if (!(page = shp->shm_pages[i]))
continue;
if (page & 1) {
- free_page (page & ~0xfff);
+ free_page (page & PAGE_MASK);
shm_rss--;
} else {
swap_free (page);
@@ -252,7 +253,7 @@ int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
shp = shm_segs[shmid];
if (shp == IPC_UNUSED || shp == IPC_NOID)
return -EINVAL;
- if (ipcperms (&shp->shm_perm, 0444))
+ if (ipcperms (&shp->shm_perm, S_IRUGO))
return -EACCES;
id = shmid + shp->shm_perm.seq * SHMMNI;
memcpy_tofs (buf, shp, sizeof(*shp));
@@ -285,7 +286,7 @@ int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
ipcp->mode |= SHM_LOCKED;
break;
case IPC_STAT:
- if (ipcperms (ipcp, 0444))
+ if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
if (!buf)
return -EFAULT;
@@ -299,8 +300,8 @@ int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
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);
+ ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
+ | (tbuf.shm_perm.mode & S_IRWXUGO);
shp->shm_ctime = CURRENT_TIME;
break;
}
@@ -334,16 +335,16 @@ static int shm_map (struct shm_desc *shmd, int remap)
/* 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));
+ page_table = PAGE_DIR_OFFSET(page_dir,tmp);
if (*page_table & PAGE_PRESENT) {
- page_table = (ulong *) (0xfffff000 & *page_table);
- page_table += ((tmp >> PAGE_SHIFT) & 0x3ff);
+ page_table = (ulong *) (PAGE_MASK & *page_table);
+ page_table += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1);
if (*page_table) {
if (!remap)
return -EINVAL;
if (*page_table & PAGE_PRESENT) {
--current->rss;
- free_page (*page_table & ~0xfff);
+ free_page (*page_table & PAGE_MASK);
}
else
swap_free (*page_table);
@@ -352,11 +353,11 @@ static int shm_map (struct shm_desc *shmd, int remap)
continue;
}
{
- unsigned long new_pt = get_free_page(GFP_KERNEL);
- if (!new_pt)
+ unsigned long new_pt;
+ if(!(new_pt = get_free_page(GFP_KERNEL))) /* clearing needed? SRB. */
return -ENOMEM;
*page_table = new_pt | PAGE_TABLE;
- tmp = ((tmp + (PAGE_SIZE << 10) - 1) & 0xff400000) -PAGE_SIZE;
+ tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE);
}}
if (invalid)
invalidate();
@@ -365,9 +366,9 @@ static int shm_map (struct shm_desc *shmd, int remap)
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 = PAGE_DIR_OFFSET(page_dir,tmp);
+ page_table = (ulong *) (PAGE_MASK & *page_table);
+ page_table += (tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
*page_table = shm_sgn;
}
return 0;
@@ -393,9 +394,6 @@ int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
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;
@@ -407,7 +405,7 @@ int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
if (addr >= shmd->start)
addr = shmd->start;
}
- addr = (addr - shp->shm_segsz) & ~0xfff;
+ addr = (addr - shp->shm_segsz) & PAGE_MASK;
} else if (addr & (SHMLBA-1)) {
if (shmflg & SHM_RND)
addr &= ~(SHMLBA-1); /* round down */
@@ -425,7 +423,7 @@ int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
return -EINVAL;
}
- if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? 0444 : 0666))
+ if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
return -EACCES;
if (shp->shm_perm.seq != shmid / SHMMNI)
return -EIDRM;
@@ -556,7 +554,7 @@ int shm_fork (struct task_struct *p1, struct task_struct *p2)
}
p2->shm = new_desc;
for (shmd = new_desc; shmd; shmd = shmd->task_next) {
- id = (shmd->shm_sgn >> SHM_ID_SHIFT) & 0xfff;
+ id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK;
shp = shm_segs[id];
if (shp == IPC_UNUSED) {
printk("shm_fork: unused id=%d PANIC\n", id);
@@ -598,8 +596,7 @@ void shm_no_page (unsigned long *ptent)
}
if (!(shp->shm_pages[idx] & PAGE_PRESENT)) {
- page = get_free_page(GFP_KERNEL);
- if (!page) {
+ if(!(page = __get_free_page(GFP_KERNEL))) {
oom(current);
*ptent = BAD_PAGE | PAGE_ACCESSED | 7;
return;
@@ -692,15 +689,15 @@ int shm_swap (int prio)
printk ("shm_swap: too large idx=%d id=%d PANIC\n",idx, id);
continue;
}
- pte = (ulong *) (shmd->task->tss.cr3 + ((tmp>>20) & 0xffc));
+ pte = PAGE_DIR_OFFSET(shmd->task->tss.cr3,tmp);
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);
+ pte = (ulong *) (PAGE_MASK & *pte);
+ pte += ((tmp >> PAGE_SHIFT) & PTRS_PER_PAGE-1);
tmp = *pte;
if (!(tmp & PAGE_PRESENT))
continue;
@@ -717,7 +714,7 @@ int shm_swap (int prio)
if (mem_map[MAP_NR(page)] != 1)
goto check_table;
- page &= ~0xfff;
+ page &= PAGE_MASK;
shp->shm_pages[idx] = swap_nr;
if (invalid)
invalidate();
diff --git a/ipc/util.c b/ipc/util.c
index 700d137..46dc00d 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -10,6 +10,7 @@
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
+#include <linux/stat.h>
void ipc_init (void);
extern "C" int sys_ipc (uint call, int first, int second, int third, void *ptr);
@@ -45,16 +46,19 @@ void ipc_init (void)
*/
int ipcperms (struct ipc_perm *ipcp, short flag)
{
- int i, perm = 0007, euid = current->euid, egid;
+ int i; mode_t perm; uid_t euid; int egid;
if (suser())
return 0;
+
+ perm = S_IRWXO; euid = current->euid;
+
if (euid == ipcp->cuid || euid == ipcp->uid)
- perm = 0700;
+ perm = S_IRWXU;
else {
for (i = 0; (egid = current->groups[i]) != NOGROUP; i++)
if ((egid == ipcp->cgid) || (egid == ipcp->gid)) {
- perm = 0070;
+ perm = S_IRWXG;
break;
}
}
diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README
index f34b3ff..a000e4f 100644
--- a/kernel/FPU-emu/README
+++ b/kernel/FPU-emu/README
@@ -29,24 +29,23 @@ The interface to the Linux kernel is based upon the original Linux
math emulator by Linus Torvalds.
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. 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.
+Programmer's Reference Manual (1992 edition). Unfortunately, numerous
+facets of the functioning of the FPU are not well covered in the
+Reference Manual. The information in the manual has been supplemented
+with measurements on real 80486's. Unfortunately, it is simply not
+possible to be sure that all of the peculiarities of the 80486 have
+been discovered, so there is always likely to be obscure differences
+in the detailed behaviour of the emulator and a real 80486.
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
+See "Limitations" later in this file for a list of some differences.
Please report bugs, etc to me at:
apm233m@vaxc.cc.monash.edu.au
--Bill Metzenthen
- May 1993
+ July 1993
----------------------- Internals of wm-FPU-emu -----------------------
@@ -66,6 +65,12 @@ Numeric algorithms:
(4) The trig, log, and exp functions are based in each case upon quasi-
"optimal" polynomial approximations. My definition of "optimal" was
based upon getting good accuracy with reasonable speed.
+(5) The argument reducing code for the trig function effectively uses
+ a value of pi which is accurate to more than 128 bits. As a consequence,
+ the reduced argument is accurate to more than 64 bits for arguments up
+ to a few pi, and accurate to more than 64 bits for most arguments,
+ even for arguments approaching 2^63. This is far superior to an
+ 80486, which uses a value of pi which is accurate to 66 bits.
The code of the emulator is complicated slightly by the need to
account for a limited form of re-entrancy. Normally, the emulator will
@@ -85,27 +90,53 @@ 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.4) and the 80486 FPU (apart from bugs). Some of the
+(version beta 1.5) and the 80486 FPU (apart from bugs). Some of the
more important differences are listed below:
+Segment overrides don't do anything yet.
+
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.
+and the results rounded etc as required by the PC bits of the FPU
+control word. Under the crt0 version for Linux current at June 1993,
+the FPU PC bits specify 64 bits precision.
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.
+(C1 of the status word) are now implemented. Does anyone write code
+which uses these features? The Roundup flag does not have much meaning
+for the transcendental functions and its 80486 value with these
+functions is likely to differ from its emulator value.
+
+In a few rare cases the Underflow flag obtained with the emulator will
+be different from that obtained with an 80486. This occurs when the
+following conditions apply simultaneously:
+(a) the operands have a higher precision than the current setting of the
+ precision control (PC) flags.
+(b) the underflow exception is masked.
+(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
+(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
+(e) the magnitude of the exact result would be exactly 2^-16382 if the
+ operands were rounded to the current precision before the arithmetic
+ operation was performed.
+If all of these apply, the emulator will set the Underflow flag but a real
+80486 will not.
+
+NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
+unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
+and Unnormals. None of these will be generated by an 80486 or by the
+emulator. Do not use them. The emulator treats them differently in
+detail from the way an 80486 does.
+
+The emulator treats PseudoDenormals differently from an 80486. These
+numbers are in fact properly normalised numbers with the exponent
+offset by 1, and the emulator treats them as such. Unlike the 80486,
+the emulator does not generate a Denormal Operand exception for these
+numbers. The arithmetical results produced when using such a number as
+an operand are the same for the emulator and a real 80486 (apart from
+any slight precision difference for the transcendental functions).
+Neither the emulator nor an 80486 produces one of these numbers as the
+result of any arithmetic operation. An 80486 can keep one of these
+numbers in an FPU register with its identity as a PseudoDenormal, but
+the emulator will not; they are always converted to a valid number.
----------------------- Performance of wm-FPU-emu -----------------------
@@ -186,7 +217,8 @@ accurate to 64 bits can result in a relative accuracy in cos() of about
in the last column.
-Function Tested x range Worst result (bits) Turbo C
+Function Tested x range Worst result Turbo C
+ (relative bits)
sqrt(x) 1 .. 2 64.1 63.2
atan(x) 1e-10 .. 200 62.6 62.8
@@ -213,31 +245,64 @@ 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).
+For version 1.5, the accuracy of fprem and fprem1 has been improved.
+These functions now produce exact results. The code for reducing the
+argument for the trig functions (fsin, fcos, fptan and fsincos) has
+been improved and now effectively uses a value for pi which is
+accurate to more than 128 bits precision. As a consquence, the
+accuracy of these functions for large arguments has been dramatically
+improved (and is now very much better than an 80486 FPU). There is
+also now no degradation of accuracy for fcos and ftan for operands
+close to pi/2. Measured results are (note that the definition of
+accuracy has changed slightly from that used for the above table):
+
+Function Tested x range Worst result
+ (absolute bits)
+
+cos(x) 0 .. 9.22e+18 62.0
+sin(x) 1e-16 .. 9.22e+18 62.1
+tan(x) 1e-16 .. 9.22e+18 61.8
+
+It is possible with some effort to find very large arguments which
+give much degraded precision. For example, the integer number
+ 8227740058411162616.0
+is within about 10e-7 of a multiple of pi. To find the tan (for
+example) of this number to 64 bits precision it would be necessary to
+have a value of pi which had about 150 bits precision. The FPU
+emulator computes the result to about 42.6 bits precision (the correct
+result is about -9.739715e-8). On the other hand, an 80486 FPU returns
+0.01059, which in relative terms is hopelessly inaccurate.
+
+For arguments close to critical angles (which occur at multiples of
+pi/2) the emulator is more accurate than an 80486 FPU. For very large
+arguments, the emulator is far more accurate.
+
------------------------- 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):
+emulator, often by just reporting bugs, sometimes with suggested
+fixes, 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 may have forgotten, please forgive 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
+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
+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
+Dan Russel, russed@rpi.edu
+Daniel Carosone, danielce@ee.mu.oz.au
cae@jpmorgan.com
-Hamish Coleman t933093@minyos.xx.rmit.oz.au
+Hamish Coleman, t933093@minyos.xx.rmit.oz.au
+Bruce Evans, bde@kralizec.zeta.org.au
...and numerous others who responded to my request for help with
a real 80486.
diff --git a/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c
index b933594..d7619ce 100644
--- a/kernel/FPU-emu/errors.c
+++ b/kernel/FPU-emu/errors.c
@@ -68,26 +68,24 @@ void emu_printall()
RE_ENTRANT_CHECK_OFF
byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP);
FPU_modrm = get_fs_byte(1 + (unsigned char *) FPU_ORIG_EIP);
+ partial_status = status_word();
#ifdef DEBUGGING
-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");
+if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n");
+if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n");
+if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n");
+if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n");
+if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n");
+if ( partial_status & SW_Summary ) printk("SW: exception summary\n");
+if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
+if ( partial_status & SW_Precision ) printk("SW: loss of precision\n");
+if ( partial_status & SW_Underflow ) printk("SW: underflow\n");
+if ( partial_status & SW_Overflow ) printk("SW: overflow\n");
+if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n");
+if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n");
+if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n");
#endif DEBUGGING
- 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)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
@@ -96,15 +94,15 @@ if ( status_word & SW_Invalid ) printk("SW: invalid operation\n");
(FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
- status_word & 0x8000 ? 1 : 0, /* busy */
- (status_word & 0x3800) >> 11, /* stack top pointer */
- status_word & 0x80 ? 1 : 0, /* Error summary status */
- 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_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);
+ partial_status & 0x8000 ? 1 : 0, /* busy */
+ (partial_status & 0x3800) >> 11, /* stack top pointer */
+ partial_status & 0x80 ? 1 : 0, /* Error summary status */
+ partial_status & 0x40 ? 1 : 0, /* Stack flag */
+ partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
+ partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
+ partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
+ partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
+ partial_status & SW_Denorm_Op?1:0, partial_status & 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,
@@ -124,12 +122,14 @@ printk(" CW: ic=%d rc=%d%d pc=%d%d iem=%d ef=%d%d%d%d%d%d\n",
continue;
break;
case TW_Zero:
+#if 0
printk("st(%d) %c .0000 0000 0000 0000 ",
i, r->sign ? '-' : '+');
break;
+#endif
case TW_Valid:
case TW_NaN:
- case TW_Denormal:
+/* case TW_Denormal: */
case TW_Infinity:
printk("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i,
r->sign ? '-' : '+',
@@ -179,21 +179,22 @@ static struct {
error was detected.
Internal error types:
- 0x14 in e14.c
+ 0 in load_store.c
+ 0x14 in fpu_etc.c
0x1nn in a *.c file:
0x101 in reg_add_sub.c
0x102 in reg_mul.c
0x103 in poly_sin.c
- 0x104 in poly_tan.c
+ 0x104 in poly_atan.c
0x105 in reg_mul.c
- 0x106 in reg_mov.c
+ 0x106 in reg_ld_str.c
0x107 in fpu_trig.c
0x108 in reg_compare.c
0x109 in reg_compare.c
0x110 in reg_add_sub.c
- 0x111 in interface.c
+ 0x111 in fpe_entry.c
0x112 in fpu_trig.c
- 0x113 in reg_add_sub.c
+ 0x113 in errors.c
0x114 in reg_ld_str.c
0x115 in fpu_trig.c
0x116 in fpu_trig.c
@@ -204,8 +205,11 @@ static struct {
0x121 in reg_compare.c
0x122 in reg_compare.c
0x123 in reg_compare.c
+ 0x125 in fpu_trig.c
+ 0x126 in fpu_entry.c
+ 0x127 in poly_2xm1.c
0x2nn in an *.s file:
- 0x201 in reg_u_add.S
+ 0x201 in reg_u_add.S, reg_round.S
0x202 in reg_u_div.S
0x203 in reg_u_div.S
0x204 in reg_u_div.S
@@ -225,7 +229,7 @@ static struct {
0x218 in reg_round.S
*/
-extern "C" void exception(int n)
+void exception(int n)
{
int i, int_type;
@@ -235,22 +239,23 @@ extern "C" void exception(int n)
int_type = n - EX_INTERNAL;
n = EX_INTERNAL;
/* Set lots of exception bits! */
- status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
+ partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
}
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;
- if ( status_word & ~control_word & CW_Exceptions )
- status_word |= SW_Summary;
+ partial_status |= n;
+ /* Set summary bits iff exception isn't masked */
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
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;
+ partial_status &= ~SW_C1;
}
}
@@ -305,12 +310,15 @@ extern "C" void exception(int n)
}
-/* Real operation attempted on two operands, one a NaN */
-extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
+/* Real operation attempted on two operands, one a NaN. */
+/* Returns nz if the exception is unmasked */
+extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
{
FPU_REG *x;
int signalling;
+ /* The default result for the case of two "equal" NaNs (signs may
+ differ) is chosen to reproduce 80486 behaviour */
x = a;
if (a->tag == TW_NaN)
{
@@ -349,7 +357,7 @@ extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
x = &CONST_QNaN;
reg_move(x, dest);
- return;
+ return 0;
}
if ( control_word & CW_Invalid )
@@ -364,28 +372,30 @@ extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
EXCEPTION(EX_Invalid);
- return;
+ return !(control_word & CW_Invalid);
}
+
/* Invalid arith operation on Valid registers */
-extern "C" void arith_invalid(FPU_REG *dest)
+/* Returns nz if the exception is unmasked */
+extern "C" int arith_invalid(FPU_REG *dest)
{
+
+ EXCEPTION(EX_Invalid);
if ( control_word & CW_Invalid )
{
/* The masked response */
reg_move(&CONST_QNaN, dest);
}
-
- EXCEPTION(EX_Invalid);
- return;
+ return !(control_word & CW_Invalid);
}
/* Divide a finite number by zero */
-extern "C" void divide_by_zero(int sign, FPU_REG *dest)
+extern "C" int divide_by_zero(int sign, FPU_REG *dest)
{
if ( control_word & CW_ZeroDiv )
@@ -397,8 +407,25 @@ extern "C" void divide_by_zero(int sign, FPU_REG *dest)
EXCEPTION(EX_ZeroDiv);
- return;
+ return !(control_word & CW_ZeroDiv);
+
+}
+
+/* This may be called often, so keep it lean */
+int set_precision_flag(int flags)
+{
+ if ( control_word & CW_Precision )
+ {
+ partial_status &= ~(SW_C1 & flags);
+ partial_status |= flags; /* The masked response */
+ return 0;
+ }
+ else
+ {
+ exception(flags);
+ return 1;
+ }
}
@@ -406,7 +433,7 @@ extern "C" void divide_by_zero(int sign, FPU_REG *dest)
extern "C" void set_precision_flag_up(void)
{
if ( control_word & CW_Precision )
- status_word |= (SW_Precision | SW_C1); /* The masked response */
+ partial_status |= (SW_Precision | SW_C1); /* The masked response */
else
exception(EX_Precision | SW_C1);
@@ -418,8 +445,8 @@ extern "C" void set_precision_flag_down(void)
{
if ( control_word & CW_Precision )
{ /* The masked response */
- status_word &= ~SW_C1;
- status_word |= SW_Precision;
+ partial_status &= ~SW_C1;
+ partial_status |= SW_Precision;
}
else
exception(EX_Precision);
@@ -430,7 +457,7 @@ extern "C" int denormal_operand(void)
{
if ( control_word & CW_Denormal )
{ /* The masked response */
- status_word |= SW_Denorm_Op;
+ partial_status |= SW_Denorm_Op;
return 0;
}
else
@@ -441,14 +468,14 @@ extern "C" int denormal_operand(void)
}
-extern "C" void arith_overflow(FPU_REG *dest)
+extern "C" int arith_overflow(FPU_REG *dest)
{
if ( control_word & CW_Overflow )
{
char sign;
/* The masked response */
-/* **** The response here depends upon the rounding mode */
+/* ###### The response here depends upon the rounding mode */
sign = dest->sign;
reg_move(&CONST_INF, dest);
dest->sign = sign;
@@ -459,33 +486,50 @@ extern "C" void arith_overflow(FPU_REG *dest)
dest->exp -= (3 * (1 << 13));
}
- /* By definition, precision is lost.
- It appears that the roundup bit (C1) is also set by convention. */
- EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
+ EXCEPTION(EX_Overflow);
+ if ( control_word & CW_Overflow )
+ {
+ /* The overflow exception is masked. */
+ /* By definition, precision is lost.
+ The roundup bit (C1) is also set because we have
+ "rounded" upwards to Infinity. */
+ EXCEPTION(EX_Precision | SW_C1);
+ return !(control_word & CW_Precision);
+ }
- return;
+ return !(control_word & CW_Overflow);
}
-extern "C" void arith_underflow(FPU_REG *dest)
+extern "C" int arith_underflow(FPU_REG *dest)
{
if ( control_word & CW_Underflow )
{
/* The masked response */
if ( dest->exp <= EXP_UNDER - 63 )
- reg_move(&CONST_Z, dest);
+ {
+ reg_move(&CONST_Z, dest);
+ partial_status &= ~SW_C1; /* Round down. */
+ }
}
else
{
- /* Add the magic number to the exponent */
+ /* Add the magic number to the exponent. */
dest->exp += (3 * (1 << 13));
}
EXCEPTION(EX_Underflow);
+ if ( control_word & CW_Underflow )
+ {
+ /* The underflow exception is masked. */
+ EXCEPTION(EX_Precision);
+ return !(control_word & CW_Precision);
+ }
+
+ return !(control_word & CW_Underflow);
- return;
}
diff --git a/kernel/FPU-emu/exception.h b/kernel/FPU-emu/exception.h
index 3692065..7f90ede 100644
--- a/kernel/FPU-emu/exception.h
+++ b/kernel/FPU-emu/exception.h
@@ -35,6 +35,10 @@
#define EX_Invalid Const_(0x0001) /* invalid operation */
+#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
+#define PRECISION_LOST_DOWN Const_(EX_Precision)
+
+
#ifndef __ASSEMBLER__
#ifdef DEBUG
diff --git a/kernel/FPU-emu/fpu_arith.c b/kernel/FPU-emu/fpu_arith.c
index 96ba79a..b81e658 100644
--- a/kernel/FPU-emu/fpu_arith.c
+++ b/kernel/FPU-emu/fpu_arith.c
@@ -13,11 +13,16 @@
#include "fpu_system.h"
#include "fpu_emu.h"
#include "control_w.h"
+#include "status_w.h"
void fadd__()
{
/* fadd st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
@@ -25,6 +30,10 @@ void fadd__()
void fmul__()
{
/* fmul st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
@@ -33,6 +42,10 @@ void fmul__()
void fsub__()
{
/* fsub st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
@@ -40,6 +53,10 @@ void fsub__()
void fsubr_()
{
/* fsubr st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
@@ -47,6 +64,10 @@ void fsubr_()
void fdiv__()
{
/* fdiv st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word);
}
@@ -54,6 +75,10 @@ void fdiv__()
void fdivr_()
{
/* fdivr st,st(i) */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word);
}
@@ -62,6 +87,10 @@ void fdivr_()
void fadd_i()
{
/* fadd st(i),st */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
@@ -69,7 +98,11 @@ void fadd_i()
void fmul_i()
{
/* fmul st(i),st */
- reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
@@ -78,6 +111,10 @@ void fsubri()
/* fsubr st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
@@ -87,6 +124,10 @@ void fsub_i()
/* fsub st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
@@ -94,6 +135,10 @@ void fsub_i()
void fdivri()
{
/* fdivr st(i),st */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
}
@@ -101,6 +146,10 @@ void fdivri()
void fdiv_i()
{
/* fdiv st(i),st */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
}
@@ -109,16 +158,24 @@ void fdiv_i()
void faddp_()
{
/* faddp st(i),st */
- reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
}
void fmulp_()
{
/* fmulp st(i),st */
- reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
}
@@ -128,8 +185,12 @@ void fsubrp()
/* fsubrp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */
- reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
}
@@ -138,23 +199,35 @@ void fsubp_()
/* fsubp st(i),st */
/* This is the sense of the 80486 manual
reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */
- reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
+ pop();
}
void fdivrp()
{
/* fdivrp st(i),st */
- reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
}
void fdivp_()
{
/* fdivp st(i),st */
- reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word);
- pop();
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
+ if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) )
+ pop();
}
diff --git a/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c
index 0b9e2d1..e9f2bd2 100644
--- a/kernel/FPU-emu/fpu_aux.c
+++ b/kernel/FPU-emu/fpu_aux.c
@@ -14,14 +14,16 @@
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
+#include "control_w.h"
void fclex(void)
{
- status_word &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
+ partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
SW_Invalid);
+ NO_NET_DATA_EFFECT;
FPU_entry_eip = ip_offset; /* We want no net effect */
}
@@ -30,12 +32,18 @@ void finit()
{
int r;
control_word = 0x037f;
- status_word = 0;
+ partial_status = 0;
top = 0; /* We don't keep top in the status word internally. */
for (r = 0; r < 8; r++)
{
regs[r].tag = TW_Empty;
}
+ /* The behaviour is different to that detailed in
+ Section 15.1.6 of the Intel manual */
+ data_operand_offset = 0;
+ operand_selector = 0;
+ NO_NET_DATA_EFFECT;
+ FPU_entry_op_cs = 0;
FPU_entry_eip = ip_offset = 0;
}
@@ -51,12 +59,8 @@ void finit_()
static void fstsw_ax(void)
{
-
- status_word &= ~SW_Top;
- status_word |= (top&7) << SW_Top_Shift;
-
- *(short *) &FPU_EAX = status_word;
-
+ *(short *) &FPU_EAX = status_word();
+ NO_NET_INSTR_EFFECT;
}
static FUNC fstsw_table[] = {
@@ -123,16 +127,22 @@ void fxch_i()
stack_underflow_i(FPU_rm);
return;
}
- reg_move(sti_ptr, FPU_st0_ptr);
+ if ( control_word & CW_Invalid )
+ reg_move(sti_ptr, FPU_st0_ptr); /* Masked response */
stack_underflow_i(FPU_rm);
return;
}
if ( sti_ptr->tag == TW_Empty )
{
- reg_move(FPU_st0_ptr, sti_ptr);
+ if ( control_word & CW_Invalid )
+ reg_move(FPU_st0_ptr, sti_ptr); /* Masked response */
stack_underflow();
return;
}
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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 ed9cc06..a0c906a 100644
--- a/kernel/FPU-emu/fpu_emu.h
+++ b/kernel/FPU-emu/fpu_emu.h
@@ -38,6 +38,8 @@
#define EXP_BIAS Const(0)
#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
+#define EXP_Infinity EXP_OVER
+#define EXP_NaN EXP_OVER
#define SIGN_POS Const(0)
#define SIGN_NEG Const(1)
@@ -46,14 +48,12 @@
#define TW_Valid Const(0) /* valid */
#define TW_Zero Const(1) /* zero */
/* The following fold to 2 (Special) in the Tag Word */
-#define TW_Denormal Const(4) /* De-normal */
+/* #define TW_Denormal Const(4) */ /* De-normal */
#define TW_Infinity Const(5) /* + or - infinity */
#define TW_NaN Const(6) /* Not a Number */
#define TW_Empty Const(7) /* empty */
-/* #define TW_FPU_Interrupt Const(0x80) */ /* Signals an interrupt */
-
#ifndef __ASSEMBLER__
@@ -68,6 +68,16 @@ extern char emulating;
# define RE_ENTRANT_CHECK_ON
#endif PARANOID
+/* These are to defeat the default action, giving the instruction
+ no net effect: */
+#define NO_NET_DATA_EFFECT \
+ { FPU_data_address = (void *)data_operand_offset; \
+ FPU_data_selector = operand_selector; }
+#define NO_NET_INSTR_EFFECT \
+ { FPU_entry_eip = ip_offset; \
+ FPU_entry_op_cs = cs_selector; }
+
+
typedef void (*FUNC)(void);
typedef struct fpu_reg FPU_REG;
@@ -82,7 +92,10 @@ extern unsigned char FPU_rm;
extern char FPU_st0_tag;
extern FPU_REG *FPU_st0_ptr;
-extern void *FPU_data_address;
+/* ###### These need to be shifted to somewhere safe. */
+/* extern void *FPU_data_address; has been shifted */
+extern unsigned short FPU_data_selector;
+extern unsigned long FPU_entry_op_cs;
extern FPU_REG FPU_loaded_data;
@@ -99,7 +112,7 @@ extern FPU_REG FPU_loaded_data;
/*----- Prototypes for functions written in assembler -----*/
-/* extern "C" void reg_move(FPU_REG *a, FPU_REG *b); */
+/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
extern "C" void mul64(long long *a, long long *b, long long *result);
extern "C" void poly_div2(long long *x);
@@ -109,17 +122,17 @@ extern "C" void polynomial(unsigned accum[], unsigned x[],
unsigned short terms[][4], int n);
extern "C" void normalize(FPU_REG *x);
extern "C" void normalize_nuo(FPU_REG *x);
-extern "C" void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
-extern "C" void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
-extern "C" void reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
-extern "C" void reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
-extern "C" void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
+extern "C" int reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,
unsigned int control_w);
-extern "C" void wm_sqrt(FPU_REG *n, unsigned int control_w);
+extern "C" int wm_sqrt(FPU_REG *n, unsigned int control_w);
extern "C" unsigned shrx(void *l, unsigned x);
extern "C" unsigned shrxs(void *v, unsigned x);
extern "C" unsigned long div_small(unsigned long long *x, unsigned long y);
diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c
index f67c440..b3e188e 100644
--- a/kernel/FPU-emu/fpu_entry.c
+++ b/kernel/FPU-emu/fpu_entry.c
@@ -38,6 +38,15 @@
#include <asm/segment.h>
+#define FWAIT_OPCODE 0x9b
+#define OP_SIZE_PREFIX 0x66
+#define ADDR_SIZE_PREFIX 0x67
+#define PREFIX_CS 0x2e
+#define PREFIX_DS 0x3e
+#define PREFIX_ES 0x26
+#define PREFIX_SS 0x36
+#define PREFIX_FS 0x64
+#define PREFIX_GS 0x65
#define __BAD__ Un_impl /* Not implemented */
@@ -133,28 +142,43 @@ unsigned char FPU_rm;
char FPU_st0_tag;
FPU_REG *FPU_st0_ptr;
+/* ######## To be shifted */
+unsigned long FPU_entry_op_cs;
+unsigned short FPU_data_selector;
+
+
#ifdef PARANOID
char emulating=0;
#endif PARANOID
#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+static int valid_prefix(unsigned char byte);
extern "C" void math_emulate(long arg)
{
unsigned char FPU_modrm;
unsigned short code;
+ int unmasked;
#ifdef PARANOID
if ( emulating )
{
printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
}
- RE_ENTRANT_CHECK_ON
+ RE_ENTRANT_CHECK_ON;
#endif PARANOID
if (!current->used_math)
{
+ int i;
+ for ( i = 0; i < 8; i++ )
+ {
+ /* Make sure that the registers are compatible
+ with the assumptions of the emulator. */
+ regs[i].exp = 0;
+ regs[i].sigh = 0x80000000;
+ }
finit();
current->used_math = 1;
}
@@ -181,22 +205,44 @@ extern "C" void math_emulate(long arg)
do_another_FPU_instruction:
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
code = get_fs_word((unsigned short *) FPU_EIP);
- RE_ENTRANT_CHECK_ON
+ RE_ENTRANT_CHECK_ON;
+
+#ifdef PECULIAR_486
+ /* It would be more logical to do this only in get_address(),
+ but although it is supposed to be undefined for many fpu
+ instructions, an 80486 behaves as if this were done here: */
+ FPU_data_selector = FPU_DS;
+#endif PECULIAR_486
- if ( (code & 0xff) == 0x9b ) /* fwait */
+ if ( (code & 0xf8) != 0xd8 )
{
- if (status_word & SW_Summary)
- goto do_the_FPU_interrupt;
- else
+ if ( (code & 0xff) == FWAIT_OPCODE )
{
- FPU_EIP++;
- goto FPU_instruction_done;
+ if (partial_status & SW_Summary)
+ goto do_the_FPU_interrupt;
+ else
+ {
+ FPU_EIP++;
+ goto FPU_fwait_done;
+ }
+ }
+ else if ( valid_prefix(code & 0xff) )
+ {
+ goto do_another_FPU_instruction;
}
+#ifdef PARANOID
+ RE_ENTRANT_CHECK_OFF;
+ printk("FPU emulator: Unknown prefix byte 0x%02x\n", code & 0xff);
+ RE_ENTRANT_CHECK_ON;
+ EXCEPTION(EX_INTERNAL|0x126);
+ FPU_EIP++;
+ goto do_the_FPU_interrupt;
+#endif PARANOID
}
- if (status_word & SW_Summary)
+ if (partial_status & SW_Summary)
{
/* Ignore the error for now if the current instruction is a no-wait
control instruction */
@@ -210,9 +256,6 @@ do_another_FPU_instruction:
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.
@@ -223,9 +266,9 @@ do_another_FPU_instruction:
*/
do_the_FPU_interrupt:
cs_selector &= 0xffff0000;
- cs_selector |= (status_word & ~SW_Top) | ((top&7) << SW_Top_Shift);
+ cs_selector |= status_word();
operand_selector = tag_word();
- status_word = 0;
+ partial_status = 0;
top = 0;
{
int r;
@@ -235,7 +278,7 @@ do_another_FPU_instruction:
}
}
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
send_sig(SIGFPE, current, 1);
return;
}
@@ -243,12 +286,18 @@ do_another_FPU_instruction:
FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
- if ( (code & 0xff) == 0x66 ) /* size prefix */
+ {
+ unsigned short swapped_code = code;
+ bswapw(swapped_code);
+ FPU_entry_op_cs = (swapped_code << 16) | (FPU_CS & 0xffff) ;
+ }
+
+ if ( (code & 0xff) == OP_SIZE_PREFIX )
{
FPU_EIP++;
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
code = get_fs_word((unsigned short *) FPU_EIP);
- RE_ENTRANT_CHECK_ON
+ RE_ENTRANT_CHECK_ON;
}
FPU_EIP += 2;
@@ -261,23 +310,24 @@ do_another_FPU_instruction:
get_address(FPU_modrm);
if ( !(code & 1) )
{
- unsigned short status1 = status_word;
+ unsigned short status1 = partial_status;
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
/* Stack underflow has priority */
if ( NOT_EMPTY_0 )
{
+ unmasked = 0; /* Do this here to stop compiler warnings. */
switch ( (code >> 1) & 3 )
{
case 0:
- reg_load_single();
+ unmasked = reg_load_single();
break;
case 1:
reg_load_int32();
break;
case 2:
- reg_load_double();
+ unmasked = reg_load_double();
break;
case 3:
reg_load_int16();
@@ -297,27 +347,74 @@ do_another_FPU_instruction:
{
/* Restore the status word; we might have loaded a
denormal. */
- status_word = status1;
+ partial_status = 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. */
+ if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+ pop(); /* fcomp, masked, so we pop. */
}
else
- real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
+ {
+#ifdef PECULIAR_486
+ /* This is not really needed, but gives behaviour
+ identical to an 80486 */
+ if ( (FPU_modrm & 0x28) == 0x20 )
+ /* fdiv or fsub */
+ real_2op_NaN(&FPU_loaded_data, FPU_st0_ptr,
+ FPU_st0_ptr);
+ else
+#endif PECULIAR_486
+ /* fadd, fdivr, fmul, or fsubr */
+ real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data,
+ FPU_st0_ptr);
+ }
+ goto reg_mem_instr_done;
+ }
+
+ if ( unmasked && !((FPU_modrm & 0x30) == 0x10) )
+ {
+ /* Is not a comparison instruction. */
+ if ( (FPU_modrm & 0x38) == 0x38 )
+ {
+ /* fdivr */
+ if ( (FPU_st0_tag == TW_Zero) &&
+ (FPU_loaded_data.tag == TW_Valid) )
+ {
+ if ( divide_by_zero(FPU_loaded_data.sign,
+ FPU_st0_ptr) )
+ {
+ /* We use the fact here that the unmasked
+ exception in the loaded data was for a
+ denormal operand */
+ /* Restore the state of the denormal op bit */
+ partial_status &= ~SW_Denorm_Op;
+ partial_status |= status1 & SW_Denorm_Op;
+ }
+ }
+ }
goto reg_mem_instr_done;
}
switch ( (FPU_modrm >> 3) & 7 )
{
case 0: /* fadd */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 1: /* fmul */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
@@ -325,24 +422,44 @@ do_another_FPU_instruction:
compare_st_data();
break;
case 3: /* fcomp */
- compare_st_data();
- pop();
+ if ( !compare_st_data() && !unmasked )
+ pop();
break;
case 4: /* fsub */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 5: /* fsubr */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
break;
case 6: /* fdiv */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr,
control_word);
break;
case 7: /* fdivr */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information,
+ but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( FPU_st0_tag == TW_Zero )
- status_word = status1; /* Undo any denorm tag,
+ partial_status = status1; /* Undo any denorm tag,
zero-divide has priority. */
reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr,
control_word);
@@ -356,8 +473,8 @@ do_another_FPU_instruction:
/* 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. */
+ if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+ pop(); /* fcomp */
}
else
stack_underflow();
@@ -370,13 +487,22 @@ do_another_FPU_instruction:
reg_mem_instr_done:
- data_operand_offset = (unsigned long)FPU_data_address;
+#ifndef PECULIAR_486
+ *(unsigned short *)&operand_selector = FPU_data_selector;
+#endif PECULIAR_486
+ ;
}
else
{
/* None of these instructions access user memory */
unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
+#ifdef PECULIAR_486
+ /* This is supposed to be undefined, but a real 80486 seems
+ to do this: */
+ FPU_data_address = 0;
+#endif PECULIAR_486
+
FPU_st0_ptr = &st(0);
FPU_st0_tag = FPU_st0_ptr->tag;
switch ( type_table[(int) instr_index] )
@@ -400,8 +526,7 @@ do_another_FPU_instruction:
case _REGIp:
if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
{
- stack_underflow_i(FPU_rm);
- pop();
+ stack_underflow_pop(FPU_rm);
goto FPU_instruction_done;
}
break;
@@ -427,45 +552,72 @@ do_another_FPU_instruction:
FPU_instruction_done:
ip_offset = FPU_entry_eip;
- bswapw(code);
- *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
+ cs_selector = FPU_entry_op_cs;
+ data_operand_offset = (unsigned long)FPU_data_address;
+#ifdef PECULIAR_486
+ *(unsigned short *)&operand_selector = FPU_data_selector;
+#endif PECULIAR_486
+
+FPU_fwait_done:
#ifdef DEBUG
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
emu_printall();
- RE_ENTRANT_CHECK_ON
+ RE_ENTRANT_CHECK_ON;
#endif DEBUG
if (FPU_lookahead && !need_resched)
{
unsigned char next;
- /* (This test should generate no machine code) */
- while ( 1 )
+ RE_ENTRANT_CHECK_OFF;
+ next = get_fs_byte((unsigned char *) FPU_EIP);
+ RE_ENTRANT_CHECK_ON;
+ if ( valid_prefix(next) )
+ goto do_another_FPU_instruction;
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+}
+
+
+/* This function is not yet complete. To properly handle all prefix
+ bytes, it will be necessary to change all emulator code which
+ accesses user address space. Access to separate segments is
+ important for msdos emulation. */
+static int valid_prefix(unsigned char byte)
+{
+ unsigned long ip = FPU_EIP;
+
+ while ( 1 )
+ {
+ switch ( byte )
{
- RE_ENTRANT_CHECK_OFF
- next = get_fs_byte((unsigned char *) FPU_EIP);
- RE_ENTRANT_CHECK_ON
- if ( ((next & 0xf8) == 0xd8) || (next == 0x9b) ) /* fwait */
- {
- goto do_another_FPU_instruction;
- }
- else if ( next == 0x66 ) /* size prefix */
+ case ADDR_SIZE_PREFIX:
+ case PREFIX_DS: /* Redundant */
+ case PREFIX_CS:
+ case PREFIX_ES:
+ case PREFIX_SS:
+ case PREFIX_FS:
+ case PREFIX_GS:
+
+ case OP_SIZE_PREFIX: /* Used often by gcc, but has no effect. */
+ RE_ENTRANT_CHECK_OFF;
+ byte = get_fs_byte((unsigned char *) (++FPU_EIP));
+ RE_ENTRANT_CHECK_ON;
+ break;
+ case FWAIT_OPCODE:
+ return 1;
+ default:
+ if ( (byte & 0xf8) == 0xd8 )
+ return 1;
+ else
{
- 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;
- }
+ FPU_EIP = ip;
+ return 0;
}
- break;
}
}
-
- RE_ENTRANT_CHECK_OFF
}
@@ -473,7 +625,7 @@ void __math_abort(struct info * info, unsigned int signal)
{
FPU_EIP = FPU_ORIG_EIP;
send_sig(signal,current,1);
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
__asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
#ifdef PARANOID
printk("ERROR: wm-FPU-emu math_abort failed!\n");
@@ -487,7 +639,7 @@ void __math_abort(struct info * info, unsigned int signal)
extern "C" void math_emulate(long arg)
{
- printk("math-meulation not enabled and no coprocessor found.\n");
+ printk("math-emulation not enabled and no coprocessor found.\n");
printk("killing %s.\n",current->comm);
send_sig(SIGFPE,current,1);
schedule();
diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c
index 4afe891..f8aee63 100644
--- a/kernel/FPU-emu/fpu_etc.c
+++ b/kernel/FPU-emu/fpu_etc.c
@@ -22,7 +22,10 @@ static void fchs(void)
if ( NOT_EMPTY_0 )
{
FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
- status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
}
else
stack_underflow();
@@ -33,7 +36,10 @@ static void fabs(void)
if ( FPU_st0_tag ^ TW_Empty )
{
FPU_st0_ptr->sign = SIGN_POS;
- status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
}
else
stack_underflow();
@@ -48,16 +54,23 @@ static void ftst_(void)
setcc(SW_C3);
break;
case TW_Valid:
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
#ifdef DENORM_OPERAND
if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
+ {
+#ifdef PECULIAR_486
+ /* This is wierd! */
+ if (FPU_st0_ptr->sign == SIGN_POS)
+ setcc(SW_C3);
+#endif PECULIAR_486
+ return;
+ }
#endif DENORM_OPERAND
- if (FPU_st0_ptr->sign == SIGN_POS)
- setcc(0);
- else
- setcc(SW_C0);
break;
case TW_NaN:
setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
@@ -68,7 +81,6 @@ static void ftst_(void)
setcc(0);
else
setcc(SW_C0);
- EXCEPTION(EX_Invalid);
break;
case TW_Empty:
setcc(SW_C0|SW_C2|SW_C3);
@@ -97,7 +109,7 @@ static void fxam(void)
if ( FPU_st0_ptr->exp <= EXP_UNDER )
c = SW_C2|SW_C3; /* Denormal */
else
- c = SW_C3;
+ c = SW_C2;
break;
case TW_NaN:
c = SW_C0;
diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h
index 3bc692c..ac14a1e 100644
--- a/kernel/FPU-emu/fpu_proto.h
+++ b/kernel/FPU-emu/fpu_proto.h
@@ -5,15 +5,16 @@ extern void stack_overflow(void);
extern void stack_underflow(void);
extern void stack_underflow_i(int i);
extern void stack_underflow_pop(int i);
+extern int set_precision_flag(int flags);
extern "C" void exception(int n);
-extern "C" void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
-extern "C" void arith_invalid(FPU_REG *dest);
-extern "C" void divide_by_zero(int sign, FPU_REG *dest);
+extern "C" int real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest);
+extern "C" int arith_invalid(FPU_REG *dest);
+extern "C" int divide_by_zero(int sign, FPU_REG *dest);
extern "C" void set_precision_flag_up(void);
extern "C" void set_precision_flag_down(void);
extern "C" int denormal_operand(void);
-extern "C" void arith_overflow(FPU_REG *dest);
-extern "C" void arith_underflow(FPU_REG *dest);
+extern "C" int arith_overflow(FPU_REG *dest);
+extern "C" int arith_underflow(FPU_REG *dest);
/* fpu_arith.c */
extern void fadd__(void);
@@ -81,11 +82,11 @@ 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);
+extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert);
/* 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);
+extern int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
+extern int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w);
/* reg_compare.c */
extern int compare(FPU_REG *b);
@@ -101,9 +102,9 @@ extern void fucompp(void);
extern void fconst(void);
/* reg_ld_str.c */
-extern void reg_load_extended(void);
-extern void reg_load_double(void);
-extern void reg_load_single(void);
+extern int reg_load_extended(void);
+extern int reg_load_double(void);
+extern int reg_load_single(void);
extern void reg_load_int64(void);
extern void reg_load_int32(void);
extern void reg_load_int16(void);
@@ -123,4 +124,4 @@ extern char *fstenv(void);
extern void fsave(void);
/* reg_mul.c */
-extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w);
+extern int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w);
diff --git a/kernel/FPU-emu/fpu_system.h b/kernel/FPU-emu/fpu_system.h
index 3f0def7..080ed06 100644
--- a/kernel/FPU-emu/fpu_system.h
+++ b/kernel/FPU-emu/fpu_system.h
@@ -27,7 +27,7 @@
#define FPU_lookahead (I387.soft.lookahead)
#define FPU_entry_eip (I387.soft.entry_eip)
-#define status_word (I387.soft.swd)
+#define partial_status (I387.soft.swd)
#define control_word (I387.soft.cwd)
#define regs (I387.soft.regs)
#define top (I387.soft.top)
@@ -37,4 +37,7 @@
#define data_operand_offset (I387.soft.foo)
#define operand_selector (I387.soft.fos)
+/* ######## temporary and ugly ;-) */
+#define FPU_data_address ((void *)(I387.soft.twd))
+
#endif
diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c
index ddac32d..cd49903 100644
--- a/kernel/FPU-emu/fpu_trig.c
+++ b/kernel/FPU-emu/fpu_trig.c
@@ -18,33 +18,105 @@
#include "reg_constant.h"
-
-static int trig_arg(FPU_REG *X)
+static void rem_kernel(unsigned long long st0, unsigned long long *y,
+ unsigned long long st1,
+ long long q, int n);
+
+#define BETTER_THAN_486
+
+#define FCOS 4
+#define FPTAN 1
+
+/* Used only by fptan, fsin, fcos, and fsincos. */
+/* This routine produces very accurate results, similar to
+ using a value of pi with more than 128 bits precision. */
+/* Limited measurements show no results worse than 64 bit precision
+ except for the results for arguments close to 2^63, where the
+ precision of the result sometimes degrades to about 63.9 bits */
+static int trig_arg(FPU_REG *X, int even)
{
- FPU_REG tmp, quot;
- int rv;
+ FPU_REG tmp;
long long q;
- int old_cw = control_word;
+ int old_cw = control_word, saved_status = partial_status;
+
+ if ( X->exp >= EXP_BIAS + 63 )
+ {
+ partial_status |= SW_C2; /* Reduction incomplete. */
+ return -1;
+ }
control_word &= ~CW_RC;
control_word |= RC_CHOP;
-
- reg_move(X, &quot);
- reg_div(&quot, &CONST_PI2, &quot, FULL_PRECISION);
-
- reg_move(&quot, &tmp);
- round_to_int(&tmp);
- if ( tmp.sigh & 0x80000000 )
- return -1; /* |Arg| is >= 2^63 */
- tmp.exp = EXP_BIAS + 63;
- q = *(long long *)&(tmp.sigl);
- normalize(&tmp);
-
- reg_sub(&quot, &tmp, X, FULL_PRECISION);
- rv = q & 7;
-
+
+ reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+ round_to_int(&tmp); /* Fortunately, this can't overflow
+ to 2^64 */
+ q = ((long long *)&(tmp.sigl))[0];
+ if ( q )
+ {
+ rem_kernel(((unsigned long long *)&(X->sigl))[0],
+ (unsigned long long *)&(tmp.sigl),
+ ((unsigned long long *)&(CONST_PI2.sigl))[0],
+ q, X->exp - CONST_PI2.exp);
+ tmp.exp = CONST_PI2.exp;
+ normalize(&tmp);
+ reg_move(&tmp, X);
+ }
+
+ if ( even == FPTAN )
+ {
+ if ( ((X->exp >= EXP_BIAS) ||
+ ((X->exp == EXP_BIAS-1)
+ && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
+ even = FCOS;
+ else
+ even = 0;
+ }
+
+ if ( (even && !(q & 1)) || (!even && (q & 1)) )
+ {
+ reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
+#ifdef BETTER_THAN_486
+ /* So far, the results are exact but based upon a 64 bit
+ precision approximation to pi/2. The technique used
+ now is equivalent to using an approximation to pi/2 which
+ is accurate to about 128 bits. */
+ if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
+ {
+ /* This code gives the effect of having p/2 to better than
+ 128 bits precision. */
+ ((long long *)&(tmp.sigl))[0] = q + 1;
+ tmp.exp = EXP_BIAS + 63;
+ normalize(&tmp);
+ reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+ reg_add(X, &tmp, X, FULL_PRECISION);
+ }
+#endif BETTER_THAN_486
+ }
+#ifdef BETTER_THAN_486
+ else
+ {
+ /* So far, the results are exact but based upon a 64 bit
+ precision approximation to pi/2. The technique used
+ now is equivalent to using an approximation to pi/2 which
+ is accurate to about 128 bits. */
+ if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
+ {
+ /* This code gives the effect of having p/2 to better than
+ 128 bits precision. */
+ ((long long *)&(tmp.sigl))[0] = q;
+ tmp.exp = EXP_BIAS + 63;
+ normalize(&tmp);
+ reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(X, &tmp, X, FULL_PRECISION);
+ }
+ }
+#endif BETTER_THAN_486
+
control_word = old_cw;
- return rv;;
+ partial_status = saved_status & ~SW_C2; /* Reduction complete. */
+
+ return (q & 3) | even;
}
@@ -52,7 +124,7 @@ static int trig_arg(FPU_REG *X)
void convert_l2reg(long *arg, FPU_REG *dest)
{
long num = *arg;
-
+
if (num == 0)
{ reg_move(&CONST_Z, dest); return; }
@@ -60,7 +132,7 @@ void convert_l2reg(long *arg, FPU_REG *dest)
dest->sign = SIGN_POS;
else
{ num = -num; dest->sign = SIGN_NEG; }
-
+
dest->sigh = num;
dest->sigl = 0;
dest->exp = EXP_BIAS + 31;
@@ -77,8 +149,8 @@ static void single_arg_error(void)
if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
{
EXCEPTION(EX_Invalid);
- /* Convert to a QNaN */
- FPU_st0_ptr->sigh |= 0x40000000;
+ if ( control_word & CW_Invalid )
+ FPU_st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */
}
break; /* return with a NaN in st(0) */
case TW_Empty:
@@ -92,41 +164,91 @@ static void single_arg_error(void)
}
+static void single_arg_2_error(void)
+{
+ FPU_REG *st_new_ptr;
+
+ switch ( FPU_st0_tag )
+ {
+ case TW_NaN:
+ if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
+ {
+ EXCEPTION(EX_Invalid);
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ /* Convert to a QNaN */
+ FPU_st0_ptr->sigh |= 0x40000000;
+ st_new_ptr = &st(-1);
+ push();
+ reg_move(&st(1), FPU_st0_ptr);
+ }
+ }
+ else
+ {
+ /* A QNaN */
+ st_new_ptr = &st(-1);
+ push();
+ reg_move(&st(1), FPU_st0_ptr);
+ }
+ break; /* return with a NaN in st(0) */
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x0112);
+#endif PARANOID
+ }
+}
+
+
/*---------------------------------------------------------------------------*/
static void f2xm1(void)
{
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
switch ( FPU_st0_tag )
{
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 )
+ if ( FPU_st0_ptr->exp >= 0 )
+ {
+ /* For an 80486 FPU, the result is undefined. */
+ }
+ else if ( FPU_st0_ptr->exp >= -64 )
{
- /* poly_2xm1(x) requires 0 < x < 1. */
- if ( poly_2xm1(FPU_st0_ptr, &rv) )
- return; /* error */
- reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+ if ( FPU_st0_ptr->sign == SIGN_POS )
+ {
+ /* poly_2xm1(x) requires 0 < x < 1. */
+ poly_2xm1(FPU_st0_ptr, &rv);
+ reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
+ }
+ else
+ {
+ /* poly_2xm1(x) doesn't handle negative numbers yet. */
+ /* So we compute z=poly_2xm1(-x), and the answer is
+ then -z/(1+z) */
+ FPU_st0_ptr->sign = SIGN_POS;
+ poly_2xm1(FPU_st0_ptr, &rv);
+ reg_mul(&rv, FPU_st0_ptr, &rv, FULL_PRECISION);
+ reg_add(&rv, &CONST_1, &tmp, FULL_PRECISION);
+ reg_div(&rv, &tmp, FPU_st0_ptr, FULL_PRECISION);
+ FPU_st0_ptr->sign = SIGN_NEG;
+ }
}
else
{
-/* **** Should change poly_2xm1() to at least handle numbers near 0 */
- /* poly_2xm1(x) doesn't handle negative numbers. */
- /* So we compute (poly_2xm1(x+1)-1)/2, for -1 < x < 0 */
- reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION);
- poly_2xm1(&tmp, &rv);
- reg_mul(&rv, &tmp, &tmp, FULL_PRECISION);
- reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION);
- FPU_st0_ptr->exp--;
- if ( FPU_st0_ptr->exp <= EXP_UNDER )
- arith_underflow(FPU_st0_ptr);
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ /* For very small arguments, this is accurate enough. */
+ reg_mul(&CONST_LN2, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
}
+ set_precision_flag_up();
return;
}
case TW_Zero:
@@ -144,12 +266,26 @@ static void f2xm1(void)
}
}
+
static void fptan(void)
{
FPU_REG *st_new_ptr;
int q;
char arg_sign = FPU_st0_ptr->sign;
+ /* Stack underflow has higher priority */
+ if ( FPU_st0_tag == TW_Empty )
+ {
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ stack_underflow(); /* Puts a QNaN in the new st(0) */
+ }
+ return;
+ }
+
if ( STACK_OVERFLOW )
{ stack_overflow(); return; }
@@ -157,40 +293,56 @@ static void fptan(void)
{
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 )
+ if ( FPU_st0_ptr->exp > EXP_BIAS - 40 )
{
- if (q & 1)
- reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
-
- poly_tan(FPU_st0_ptr, FPU_st0_ptr);
-
- FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
-
- if ( FPU_st0_ptr->exp <= EXP_UNDER )
- arith_underflow(FPU_st0_ptr);
-
- push();
- reg_move(&CONST_1, FPU_st0_ptr);
- setcc(0);
+ FPU_st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(FPU_st0_ptr, FPTAN)) != -1 )
+ {
+ reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr,
+ FULL_PRECISION);
+ poly_tan(FPU_st0_ptr, FPU_st0_ptr, q & FCOS);
+ FPU_st0_ptr->sign = (q & 1) ^ arg_sign;
+ }
+ else
+ {
+ /* Operand is out of range */
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
}
else
{
- /* Operand is out of range */
- setcc(SW_C2);
- FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
- return;
+ /* For a small arg, the result == the argument */
+ /* Underflow may happen */
+
+ if ( FPU_st0_ptr->exp <= EXP_UNDER )
+ {
+#ifdef DENORM_OPERAND
+ if ( denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ if ( arith_underflow(FPU_st0_ptr) )
+ return;
+ }
+ else
+ set_precision_flag_up(); /* Must be up. */
}
+ push();
+ reg_move(&CONST_1, FPU_st0_ptr);
+ return;
break;
case TW_Infinity:
- /* Operand is out of range */
- setcc(SW_C2);
- FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(FPU_st0_ptr);
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ arith_invalid(FPU_st0_ptr);
+ }
return;
case TW_Zero:
push();
@@ -198,7 +350,7 @@ static void fptan(void)
setcc(0);
break;
default:
- single_arg_error();
+ single_arg_2_error();
break;
}
}
@@ -212,6 +364,10 @@ static void fxtract(void)
if ( STACK_OVERFLOW )
{ stack_overflow(); return; }
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( !(FPU_st0_tag ^ TW_Valid) )
{
long e;
@@ -231,7 +387,8 @@ static void fxtract(void)
else if ( FPU_st0_tag == TW_Zero )
{
char sign = FPU_st0_ptr->sign;
- divide_by_zero(SIGN_NEG, FPU_st0_ptr);
+ if ( divide_by_zero(SIGN_NEG, FPU_st0_ptr) )
+ return;
push();
reg_move(&CONST_Z, FPU_st0_ptr);
FPU_st0_ptr->sign = sign;
@@ -248,12 +405,8 @@ static void fxtract(void)
}
else if ( FPU_st0_tag == TW_NaN )
{
- if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
- {
- EXCEPTION(EX_Invalid);
- /* Convert to a QNaN */
- FPU_st0_ptr->sigh |= 0x40000000;
- }
+ if ( real_2op_NaN(FPU_st0_ptr, FPU_st0_ptr, FPU_st0_ptr) )
+ return;
push();
reg_move(st1_ptr, FPU_st0_ptr);
return;
@@ -279,17 +432,29 @@ static void fxtract(void)
static void fdecstp(void)
{
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
top--; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
}
static void fincstp(void)
{
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
top++; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
}
static void fsqrt_(void)
{
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( !(FPU_st0_tag ^ TW_Valid) )
{
int expon;
@@ -329,6 +494,8 @@ static void fsqrt_(void)
static void frndint_(void)
{
+ int flags;
+
if ( !(FPU_st0_tag ^ TW_Valid) )
{
if (FPU_st0_ptr->exp > EXP_BIAS+63)
@@ -339,7 +506,10 @@ static void frndint_(void)
return;
#endif DENORM_OPERAND
- round_to_int(FPU_st0_ptr); /* Fortunately, this can't overflow to 2^64 */
+ /* Fortunately, this can't overflow to 2^64 */
+ if ( (flags = round_to_int(FPU_st0_ptr)) )
+ set_precision_flag(flags);
+
FPU_st0_ptr->exp = EXP_BIAS + 63;
normalize(FPU_st0_ptr);
return;
@@ -357,42 +527,53 @@ static void fsin(void)
if ( FPU_st0_tag == TW_Valid )
{
+ FPU_REG rv;
int q;
- 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 )
+ if ( FPU_st0_ptr->exp > EXP_BIAS - 40 )
{
- FPU_REG rv;
-
- if (q & 1)
- reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION);
-
- poly_sine(FPU_st0_ptr, &rv);
-
- setcc(0);
- if (q & 2)
- rv.sign ^= SIGN_POS ^ SIGN_NEG;
- rv.sign ^= arg_sign;
- reg_move(&rv, FPU_st0_ptr);
+ FPU_st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(FPU_st0_ptr, 0)) != -1 )
+ {
+ reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, FULL_PRECISION);
- if ( FPU_st0_ptr->exp <= EXP_UNDER )
- arith_underflow(FPU_st0_ptr);
+ poly_sine(FPU_st0_ptr, &rv);
- set_precision_flag_up(); /* We do not really know if up or down */
+ if (q & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ rv.sign ^= arg_sign;
+ reg_move(&rv, FPU_st0_ptr);
- return;
+ /* We do not really know if up or down */
+ set_precision_flag_up();
+ return;
+ }
+ else
+ {
+ /* Operand is out of range */
+ FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
}
else
{
- /* Operand is out of range */
- setcc(SW_C2);
- FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
- return;
+ /* For a small arg, the result == the argument */
+ /* Underflow may happen */
+
+ if ( FPU_st0_ptr->exp <= EXP_UNDER )
+ {
+#ifdef DENORM_OPERAND
+ if ( denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ arith_underflow(FPU_st0_ptr);
+ return;
+ }
+
+ set_precision_flag_up(); /* Must be up. */
}
}
else if ( FPU_st0_tag == TW_Zero )
@@ -402,9 +583,8 @@ static void fsin(void)
}
else if ( FPU_st0_tag == TW_Infinity )
{
- /* Operand is out of range */
- setcc(SW_C2);
- FPU_st0_ptr->sign = arg_sign; /* restore st(0) */
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(FPU_st0_ptr);
return;
}
else
@@ -418,38 +598,49 @@ static int f_cos(FPU_REG *arg)
if ( arg->tag == TW_Valid )
{
+ FPU_REG rv;
int q;
-#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 )
+ if ( arg->exp > EXP_BIAS - 40 )
{
- FPU_REG rv;
-
- if ( !(q & 1) )
- reg_sub(&CONST_1, arg, arg, FULL_PRECISION);
-
- poly_sine(arg, &rv);
-
- setcc(0);
- if ((q+1) & 2)
- rv.sign ^= SIGN_POS ^ SIGN_NEG;
- reg_move(&rv, arg);
+ arg->sign = SIGN_POS;
+ if ( (q = trig_arg(arg, FCOS)) != -1 )
+ {
+ reg_div(arg, &CONST_PI2, arg, FULL_PRECISION);
+
+ poly_sine(arg, &rv);
- set_precision_flag_up(); /* We do not really know if up or down */
+ if ((q+1) & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ reg_move(&rv, arg);
+
+ /* We do not really know if up or down */
+ set_precision_flag_down();
- return 0;
+ return 0;
+ }
+ else
+ {
+ /* Operand is out of range */
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ }
}
else
{
- /* Operand is out of range */
- setcc(SW_C2);
- arg->sign = arg_sign; /* restore st(0) */
- return 1;
+#ifdef DENORM_OPERAND
+ if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
+ return 1;
+#endif DENORM_OPERAND
+
+ setcc(0);
+ reg_move(&CONST_1, arg);
+#ifdef PECULIAR_486
+ set_precision_flag_down(); /* 80486 appears to do this. */
+#else
+ set_precision_flag_up(); /* Must be up. */
+#endif PECULIAR_486
+ return 0;
}
}
else if ( arg->tag == TW_Zero )
@@ -460,9 +651,8 @@ static int f_cos(FPU_REG *arg)
}
else if ( FPU_st0_tag == TW_Infinity )
{
- /* Operand is out of range */
- setcc(SW_C2);
- arg->sign = arg_sign; /* restore st(0) */
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(FPU_st0_ptr);
return 1;
}
else
@@ -483,10 +673,40 @@ static void fsincos(void)
{
FPU_REG *st_new_ptr;
FPU_REG arg;
-
+
+ /* Stack underflow has higher priority */
+ if ( FPU_st0_tag == TW_Empty )
+ {
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ stack_underflow(); /* Puts a QNaN in the new st(0) */
+ }
+ return;
+ }
+
if ( STACK_OVERFLOW )
{ stack_overflow(); return; }
+ if ( FPU_st0_tag == TW_NaN )
+ {
+ single_arg_2_error();
+ return;
+ }
+ else if ( FPU_st0_tag == TW_Infinity )
+ {
+ /* The 80486 treats infinity as an invalid operand */
+ if ( !arith_invalid(FPU_st0_ptr) )
+ {
+ /* unmasked response */
+ push();
+ arith_invalid(FPU_st0_ptr);
+ }
+ return;
+ }
+
reg_move(FPU_st0_ptr,&arg);
if ( !f_cos(&arg) )
{
@@ -501,84 +721,186 @@ static void fsincos(void)
/*---------------------------------------------------------------------------*/
/* The following all require two arguments: st(0) and st(1) */
-/* remainder of st(0) / st(1) */
-/* Assumes that st(0) and st(1) are both TW_Valid */
-static void fprem_kernel(int round)
+/* A lean, mean kernel for the fprem instructions. This relies upon
+ the division and rounding to an integer in do_fprem giving an
+ exact result. Because of this, rem_kernel() needs to deal only with
+ the least significant 64 bits, the more significant bits of the
+ result must be zero.
+ */
+static void rem_kernel(unsigned long long st0, unsigned long long *y,
+ unsigned long long st1,
+ long long q, int n)
+{
+ unsigned long long x;
+
+ x = st0 << n;
+
+ /* Do the required multiplication and subtraction in the one operation */
+ asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
+ movl %3,%%eax; mull %4; subl %%eax,%1;
+ movl %2,%%eax; mull %5; subl %%eax,%1;"
+ :"=m" (x), "=m" (((unsigned *)&x)[1])
+ :"m" (st1),"m" (((unsigned *)&st1)[1]),
+ "m" (q),"m" (((unsigned *)&q)[1])
+ :"%ax","%dx");
+
+ *y = x;
+}
+
+
+/* Remainder of st(0) / st(1) */
+/* This routine produces exact results, i.e. there is never any
+ rounding or truncation, etc of the result. */
+static void do_fprem(int round)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
+ char sign = FPU_st0_ptr->sign;
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
FPU_REG tmp;
int old_cw = control_word;
- int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp;
+ int expdif = FPU_st0_ptr->exp - st1_ptr->exp;
+ long long q;
+ unsigned short saved_status;
+ int cc = 0;
#ifdef DENORM_OPERAND
- if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
- return;
+ if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
#endif DENORM_OPERAND
+ /* We want the status following the denorm tests, but don't want
+ the status changed by the arithmetic operations. */
+ saved_status = partial_status;
control_word &= ~CW_RC;
- control_word |= round;
-
+ control_word |= RC_CHOP;
+
if (expdif < 64)
{
/* 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 */
- tmp.exp = EXP_BIAS + 63;
- q = *(long long *)&(tmp.sigl);
- normalize(&tmp);
-
- reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
- reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
-
- if (q&4) c |= SW_C3;
- if (q&2) c |= SW_C1;
- if (q&1) c |= SW_C0;
-
- setcc(c);
+ if ( expdif > -2 )
+ {
+ reg_div(FPU_st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+
+ if ( tmp.exp >= EXP_BIAS )
+ {
+ round_to_int(&tmp); /* Fortunately, this can't overflow
+ to 2^64 */
+ q = ((long long *)&(tmp.sigl))[0];
+
+ rem_kernel(((unsigned long long *)&(FPU_st0_ptr->sigl))[0],
+ (unsigned long long *)&(tmp.sigl),
+ ((unsigned long long *)&(st1_ptr->sigl))[0],
+ q, expdif);
+
+ tmp.exp = st1_ptr->exp;
+ }
+ else
+ {
+ reg_move(FPU_st0_ptr, &tmp);
+ q = 0;
+ }
+ tmp.sign = sign;
+
+ if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
+ {
+ /* We may need to subtract st(1) once more. */
+ unsigned long long x;
+ x = ((unsigned long long *)&(st1_ptr->sigl))[0] -
+ ((unsigned long long *)&(tmp.sigl))[0];
+ if ( x < ((unsigned long long *)&(tmp.sigl))[0] )
+ {
+ tmp.sign ^= (SIGN_POS^SIGN_NEG);
+ ((unsigned long long *)&(tmp.sigl))[0] = x;
+ }
+ }
+
+ if (q & 4) cc |= SW_C0;
+ if (q & 2) cc |= SW_C3;
+ if (q & 1) cc |= SW_C1;
+ }
+ else
+ {
+ control_word = old_cw;
+ setcc(0);
+ return;
+ }
}
else
{
/* There is a large exponent difference ( >= 64 ) */
- int N_exp;
-
- reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION);
+ /* To make much sense, the code in this section should
+ be done at high precision. */
+ int exp_1;
+
+ /* prevent overflow here */
/* N is 'a number between 32 and 63' (p26-113) */
- N_exp = (tmp.exp & 31) + 32;
- tmp.exp = EXP_BIAS + N_exp;
-
+ reg_move(FPU_st0_ptr, &tmp);
+ tmp.exp = EXP_BIAS + 56;
+ exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS;
+ expdif -= 56;
+
+ reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+ st1_ptr->exp = exp_1;
+
round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
- tmp.exp = EXP_BIAS + 63;
- normalize(&tmp);
-
- tmp.exp = EXP_BIAS + expdif - N_exp;
-
- reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION);
- reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION);
-
- setcc(SW_C2);
+
+ rem_kernel(((unsigned long long *)&(FPU_st0_ptr->sigl))[0],
+ ((unsigned long long *)&(tmp.sigl)),
+ ((unsigned long long *)&(st1_ptr->sigl))[0],
+ ((long long *)&(tmp.sigl))[0],
+ tmp.exp - EXP_BIAS
+ );
+ tmp.exp = exp_1 + expdif;
+ tmp.sign = sign;
+
+ /* It is possible for the operation to be complete here.
+ What does the IEEE standard say? The Intel 80486 manual
+ implies that the operation will never be completed at this
+ point, and the behaviour of a real 80486 confirms this.
+ */
+ if ( !(tmp.sigh | tmp.sigl) )
+ {
+ /* The result is zero */
+ control_word = old_cw;
+ partial_status = saved_status;
+ reg_move(&CONST_Z, FPU_st0_ptr);
+ FPU_st0_ptr->sign = sign;
+#ifdef PECULIAR_486
+ setcc(SW_C2);
+#else
+ setcc(0);
+#endif PECULIAR_486
+ return;
+ }
+ cc = SW_C2;
}
+
control_word = old_cw;
+ partial_status = saved_status;
+ normalize(&tmp);
+ reg_move(&tmp, FPU_st0_ptr);
+ setcc(cc);
+
+ if ( FPU_st0_ptr->tag != TW_Zero )
+ /* Use round_reg() to properly detect under/overflow etc */
+ round_reg(FPU_st0_ptr, 0, control_word);
- 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();
+ return;
+ }
else if ( FPU_st0_tag == TW_Zero )
{
if ( st1_tag == TW_Valid )
{
-
#ifdef DENORM_OPERAND
if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
return;
@@ -628,7 +950,7 @@ static void fprem_kernel(int round)
EXCEPTION(EX_INTERNAL | 0x118);
#endif PARANOID
- real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr);
+ real_2op_NaN(st1_ptr, FPU_st0_ptr, FPU_st0_ptr);
}
@@ -639,6 +961,10 @@ static void fyl2x(void)
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
if ( FPU_st0_ptr->sign == SIGN_POS )
@@ -653,7 +979,7 @@ static void fyl2x(void)
/* We use the general purpose arithmetic,
so we need to save these. */
- saved_status = status_word;
+ saved_status = partial_status;
saved_control = control_word;
control_word = FULL_PRECISION;
@@ -661,7 +987,7 @@ static void fyl2x(void)
/* Enough of the basic arithmetic is done now */
control_word = saved_control;
- status_word = saved_status;
+ partial_status = saved_status;
/* Let the multiply set the flags */
reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
@@ -670,9 +996,9 @@ static void fyl2x(void)
}
else
{
- /* negative */
- pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr); /* st(0) cannot be negative */
+ /* negative */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
return;
}
}
@@ -683,8 +1009,8 @@ static void fyl2x(void)
}
else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
else if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
@@ -692,21 +1018,27 @@ static void fyl2x(void)
/* one of the args is zero, the other valid, or both zero */
if ( FPU_st0_tag == TW_Zero )
{
- pop(); FPU_st0_ptr = &st(0);
- if ( FPU_st0_ptr->tag == TW_Zero )
- arith_invalid(FPU_st0_ptr); /* Both args zero is invalid */
+ if ( st1_tag == TW_Zero )
+ {
+ /* Both args zero is invalid */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ }
#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;
+ reg_move(&CONST_INF, st1_ptr);
+ pop();
}
#endif PECULIAR_486
else
- divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, FPU_st0_ptr);
+ {
+ if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
+ pop();
+ }
return;
}
else
@@ -715,17 +1047,19 @@ static void fyl2x(void)
/* Zero is the valid answer */
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) */
+ /* log(negative) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
return;
}
+#ifdef DENORM_OPERAND
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
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);
@@ -737,9 +1071,12 @@ static void fyl2x(void)
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); /* log(-infinity) or 0*log(infinity) */
- return; }
+ {
+ /* log(-infinity) or 0*log(infinity) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
else
{
char sign = st1_ptr->sign;
@@ -765,8 +1102,9 @@ static void fyl2x(void)
(FPU_st0_ptr->sigl == 0) )
{
/* st(0) holds 1.0 */
- pop(); FPU_st0_ptr = &st(0);
- arith_invalid(FPU_st0_ptr); /* infinity*log(1) */
+ /* infinity*log(1) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
return;
}
/* st(0) is positive and > 1.0 */
@@ -791,17 +1129,20 @@ static void fyl2x(void)
/* st(0) must be zero or negative */
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);
+ if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
#endif PECULIAR_486
+ {
+ st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
+ pop();
+ }
}
else
{
- pop(); FPU_st0_ptr = st1_ptr;
- arith_invalid(FPU_st0_ptr); /* log(negative) */
+ /* log(negative) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
}
return;
}
@@ -812,12 +1153,17 @@ static void fpatan(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
+ char st1_sign = st1_ptr->sign, st0_sign = FPU_st0_ptr->sign;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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);
+ char inverted;
#ifdef DENORM_OPERAND
if ( ((FPU_st0_ptr->exp <= EXP_UNDER) ||
@@ -826,35 +1172,49 @@ static void fpatan(void)
#endif DENORM_OPERAND
/* We use the general purpose arithmetic so we need to save these. */
- saved_status = status_word;
+ saved_status = partial_status;
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_Denormal) == COMP_A_lt_B )
{
- quadrant |= 4;
+ inverted = 1;
reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION);
}
else
- reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+ {
+ inverted = 0;
+ if ( (st0_sign == 0) &&
+ (st1_ptr->exp - FPU_st0_ptr->exp < -64) )
+ {
+ control_word = saved_control;
+ partial_status = saved_status;
+ reg_div(st1_ptr, FPU_st0_ptr, st1_ptr,
+ control_word | PR_64_BITS);
+ st1_ptr->sign = st1_sign;
+ pop();
+ set_precision_flag_down();
+ return;
+ }
+ reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION);
+ }
poly_atan(&sum);
-
- if (quadrant & 4)
+
+ if ( inverted )
{
reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION);
}
- if (quadrant & 2)
+ if ( st0_sign )
{
reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION);
}
- if (quadrant & 1)
- sum.sign ^= SIGN_POS^SIGN_NEG;
+ sum.sign = st1_sign;
/* All of the basic arithmetic is done now */
control_word = saved_control;
- status_word = saved_status;
+ partial_status = saved_status;
reg_move(&sum, st1_ptr);
}
@@ -865,8 +1225,8 @@ static void fpatan(void)
}
else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
else if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
@@ -883,14 +1243,21 @@ static void fpatan(void)
}
else
{
-
#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
+ if ( st1_tag != TW_Zero )
+ {
+ 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); pop(); return; }
+ {
+ reg_move(&CONST_Z, st1_ptr);
+ st1_ptr->sign = sign; /* An 80486 preserves the sign */
+ pop();
+ return;
+ }
else
reg_move(&CONST_PI, st1_ptr);
}
@@ -899,8 +1266,11 @@ static void fpatan(void)
{
/* st(1) is infinity, st(0) not infinity */
#ifdef DENORM_OPERAND
- if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
+ if ( FPU_st0_tag != TW_Zero )
+ {
+ if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
#endif DENORM_OPERAND
reg_move(&CONST_PI2, st1_ptr);
@@ -913,12 +1283,15 @@ static void fpatan(void)
char sign = st1_ptr->sign;
#ifdef DENORM_OPERAND
- if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
+ if ( FPU_st0_tag != TW_Zero )
+ {
+ 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); pop(); return; }
+ { /* An 80486 preserves the sign */ pop(); return; }
else
reg_move(&CONST_PI, st1_ptr);
st1_ptr->sign = sign;
@@ -938,7 +1311,7 @@ static void fpatan(void)
}
#ifdef PARANOID
else
- EXCEPTION(EX_INTERNAL | 0x220);
+ EXCEPTION(EX_INTERNAL | 0x125);
#endif PARANOID
pop();
@@ -948,13 +1321,13 @@ static void fpatan(void)
static void fprem(void)
{
- fprem_kernel(RC_CHOP);
+ do_fprem(RC_CHOP);
}
static void fprem1(void)
{
- fprem_kernel(RC_RND);
+ do_fprem(RC_RND);
}
@@ -963,30 +1336,42 @@ static void fyl2xp1(void)
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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()) )
+ (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_status = partial_status;
saved_control = control_word;
control_word = FULL_PRECISION;
if ( poly_l2p1(FPU_st0_ptr, FPU_st0_ptr) )
{
- arith_invalid(st1_ptr); /* poly_l2p1() returned invalid */
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ control_word = saved_control;
+ partial_status = saved_status;
+ set_precision_flag_down();
+#else
+ if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */
+ return;
+#endif PECULIAR_486
pop(); return;
}
/* Enough of the basic arithmetic is done now */
control_word = saved_control;
- status_word = saved_status;
+ partial_status = saved_status;
/* Let the multiply set the flags */
reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
@@ -1002,26 +1387,26 @@ static void fyl2xp1(void)
{
if ( st1_tag <= TW_Zero )
{
-
#ifdef DENORM_OPERAND
- if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
- (denormal_operand()) )
- return;
+ if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+ (denormal_operand()) )
+ return;
#endif DENORM_OPERAND
-
- st1_ptr->sign ^= FPU_st0_ptr->sign;
+
+ FPU_st0_ptr->sign ^= st1_ptr->sign;
reg_move(FPU_st0_ptr, st1_ptr);
}
else if ( st1_tag == TW_Infinity )
{
- arith_invalid(st1_ptr); /* Infinity*log(1) */
- pop();
+ /* Infinity*log(1) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
return;
}
else if ( st1_tag == TW_NaN )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
#ifdef PARANOID
@@ -1042,7 +1427,11 @@ static void fyl2xp1(void)
if ( FPU_st0_ptr->exp >= EXP_BIAS )
{
/* st(0) holds <= -1.0 */
- arith_invalid(st1_ptr); /* infinity*log(1) */
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+#else
+ if ( arith_invalid(st1_ptr) ) return;
+#endif PECULIAR_486
pop(); return;
}
#ifdef DENORM_OPERAND
@@ -1067,7 +1456,11 @@ static void fyl2xp1(void)
(FPU_st0_ptr->sigl == 0)) )
{
/* st(0) holds < -1.0 */
- arith_invalid(st1_ptr);
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+#else
+ if ( arith_invalid(st1_ptr) ) return;
+#endif PECULIAR_486
pop(); return;
}
#ifdef DENORM_OPERAND
@@ -1085,38 +1478,61 @@ static void fyl2xp1(void)
}
if ( st1_tag == TW_NaN )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
}
else if ( FPU_st0_tag == TW_NaN )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
else if ( FPU_st0_tag == TW_Infinity )
{
if ( st1_tag == TW_NaN )
{
- real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr);
- pop();
+ if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) )
+ pop();
return;
}
- else if ( (FPU_st0_ptr->sign == SIGN_NEG) ||
- (st1_tag == TW_Zero) )
+ else if ( FPU_st0_ptr->sign == SIGN_NEG )
{
- arith_invalid(st1_ptr); /* log(infinity) */
+ int exponent = st1_ptr->exp;
+#ifndef PECULIAR_486
+ /* This should have higher priority than denormals, but... */
+ if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
+ return;
+#endif PECULIAR_486
+#ifdef DENORM_OPERAND
+ if ( st1_tag != TW_Zero )
+ {
+ if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
+#endif DENORM_OPERAND
+#ifdef PECULIAR_486
+ /* Denormal operands actually get higher priority */
+ if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
+ return;
+#endif PECULIAR_486
pop();
return;
}
+ else if ( st1_tag == TW_Zero )
+ {
+ /* log(infinity) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
/* st(1) must be valid here. */
#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
#endif DENORM_OPERAND
/* The Manual says that log(Infinity) is invalid, but a real
@@ -1142,7 +1558,12 @@ static void fscale(void)
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
int old_cw = control_word;
+ char sign = FPU_st0_ptr->sign;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
{
long scale;
@@ -1204,14 +1625,12 @@ static void fscale(void)
}
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 )
+ if ( st1_ptr->sign == SIGN_POS )
{ reg_move(&CONST_INF, FPU_st0_ptr); }
else
reg_move(&CONST_Z, FPU_st0_ptr);
diff --git a/kernel/FPU-emu/get_address.c b/kernel/FPU-emu/get_address.c
index b3d77b6..86ae753 100644
--- a/kernel/FPU-emu/get_address.c
+++ b/kernel/FPU-emu/get_address.c
@@ -40,9 +40,6 @@ static int reg_offset[] = {
#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
-void *FPU_data_address;
-
-
/* Decode the SIB byte. This function assumes mod != 0 */
static void *sib(int mod)
{
@@ -117,7 +114,12 @@ void get_address(unsigned char FPU_modrm)
unsigned char mod;
long *cpu_reg_ptr;
int offset = 0; /* Initialized just to stop compiler warnings. */
-
+
+#ifndef PECULIAR_486
+ /* This is a reasonable place to do this */
+ FPU_data_selector = FPU_DS;
+#endif PECULIAR_486
+
mod = (FPU_modrm >> 6) & 3;
if (FPU_rm == 4 && mod != 3)
diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c
index febe359..33affc0 100644
--- a/kernel/FPU-emu/load_store.c
+++ b/kernel/FPU-emu/load_store.c
@@ -24,6 +24,7 @@
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
+#include "control_w.h"
#define _NONE_ 0 /* FPU_st0_ptr etc not needed */
@@ -47,7 +48,8 @@ static unsigned char type_table[32] = {
void load_store_instr(char type)
{
- FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't change. */
+ FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't
+ change if the emulator is re-entered. */
pop_ptr = NULL; /* Initialized just to stop compiler warnings. */
switch ( type_table[(int) (unsigned) type] )
@@ -82,53 +84,109 @@ void load_store_instr(char type)
switch ( type )
{
case 000: /* fld m32real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_load_single();
- setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ if ( (FPU_loaded_data.tag == TW_NaN) &&
+ real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
+ {
+ top++;
+ break;
+ }
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 001: /* fild m32int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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 */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_load_double();
- setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
+ if ( (FPU_loaded_data.tag == TW_NaN) &&
+ real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) )
+ {
+ top++;
+ break;
+ }
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 003: /* fild m16int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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 */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_store_single();
break;
case 011: /* fist m32int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_store_int32();
break;
case 012: /* fst m64real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_store_double();
break;
case 013: /* fist m16int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_store_int16();
break;
case 014: /* fstp m32real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_single() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 015: /* fistp m32int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_int32() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 016: /* fstp m64real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_double() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 017: /* fistp m16int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_int16() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
@@ -140,72 +198,90 @@ switch ( type )
frstor();
break;
case 023: /* fbld m80dec */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
reg_load_bcd();
- setcc(0); /* Clear the SW_C1 bit, "other bits undefined" */
reg_move(&FPU_loaded_data, pop_ptr);
break;
case 024: /* fldcw */
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
control_word = get_fs_word((unsigned short *) FPU_data_address);
- RE_ENTRANT_CHECK_ON
-#ifdef NO_UNDERFLOW_TRAP
- if ( !(control_word & EX_Underflow) )
- {
- control_word |= EX_Underflow;
- }
-#endif
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ RE_ENTRANT_CHECK_ON;
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
+ else
+ partial_status &= ~(SW_Summary | SW_Backward);
+#ifdef PECULIAR_486
+ control_word |= 0x40; /* An 80486 appears to always set this bit */
+#endif PECULIAR_486
+ NO_NET_DATA_EFFECT;
+ NO_NET_INSTR_EFFECT;
break;
case 025: /* fld m80real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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 */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
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 */
fstenv();
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ NO_NET_DATA_EFFECT;
break;
case 032: /* fsave */
fsave();
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ NO_NET_DATA_EFFECT;
break;
case 033: /* fbstp m80dec */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_bcd() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 034: /* fstcw m16int */
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(control_word, (short *) FPU_data_address);
- RE_ENTRANT_CHECK_ON
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ RE_ENTRANT_CHECK_ON;
+ NO_NET_DATA_EFFECT;
+ NO_NET_INSTR_EFFECT;
break;
case 035: /* fstp m80real */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_extended() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
break;
case 036: /* fstsw m2byte */
- status_word &= ~SW_Top;
- status_word |= (top&7) << SW_Top_Shift;
- RE_ENTRANT_CHECK_OFF
+ RE_ENTRANT_CHECK_OFF;
verify_area(VERIFY_WRITE,FPU_data_address,2);
- put_fs_word(status_word,(short *) FPU_data_address);
- RE_ENTRANT_CHECK_ON
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ put_fs_word(status_word(),(short *) FPU_data_address);
+ RE_ENTRANT_CHECK_ON;
+ NO_NET_DATA_EFFECT;
+ NO_NET_INSTR_EFFECT;
break;
case 037: /* fistp m64int */
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
if ( reg_store_int64() )
pop_0(); /* pop only if the number was actually stored
(see the 80486 manual p16-28) */
diff --git a/kernel/FPU-emu/poly_2xm1.c b/kernel/FPU-emu/poly_2xm1.c
index 5dcccf2..7e8aec7 100644
--- a/kernel/FPU-emu/poly_2xm1.c
+++ b/kernel/FPU-emu/poly_2xm1.c
@@ -3,7 +3,8 @@
| |
| Function to compute 2^x-1 by a polynomial approximation. |
| |
- | 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 |
| |
| |
@@ -35,52 +36,38 @@ static unsigned short lterms[HIPOWER][4] =
/*--- poly_2xm1() -----------------------------------------------------------+
- | |
+ | Requires a positive argument which is TW_Valid and < 1. |
+---------------------------------------------------------------------------*/
int poly_2xm1(FPU_REG *arg, FPU_REG *result)
{
short exponent;
long long Xll;
- FPU_REG accum;
+ FPU_REG accum;
exponent = arg->exp - EXP_BIAS;
- if ( arg->tag == TW_Zero )
- {
- /* Return 0.0 */
- reg_move(&CONST_Z, result);
- return 0;
- }
-
- if ( exponent >= 0 ) /* Can't hack a number >= 1.0 */
- {
- arith_invalid(result); /* Number too large */
- return 1;
- }
-
- if ( arg->sign != SIGN_POS ) /* Can't hack a number < 0.0 */
+#ifdef PARANOID
+ if ( (arg->sign != SIGN_POS) /* Can't hack a number < 0.0 */
+ || (exponent >= 0) /* or a |number| >= 1.0 */
+ || (arg->tag != TW_Valid) )
{
- arith_invalid(result); /* Number negative */
+ /* Number negative, too large, or not Valid. */
+ EXCEPTION(EX_INTERNAL|0x127);
return 1;
}
-
- if ( exponent < -64 )
- {
- reg_move(&CONST_LN2, result);
- return 0;
- }
+#endif PARANOID
*(unsigned *)&Xll = arg->sigl;
*(((unsigned *)&Xll)+1) = arg->sigh;
if ( exponent < -1 )
{
- /* shift the argument right by the required places */
+ /* Shift the argument right by the required places. */
if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
Xll++; /* round up */
}
- *(short *)&(accum.sign) = 0; /* will be a valid positive nr with expon = 0 */
+ *(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
diff --git a/kernel/FPU-emu/poly_atan.c b/kernel/FPU-emu/poly_atan.c
index a549d2b..e9bceee 100644
--- a/kernel/FPU-emu/poly_atan.c
+++ b/kernel/FPU-emu/poly_atan.c
@@ -51,7 +51,7 @@ void poly_atan(FPU_REG *arg)
{
char recursions = 0;
short exponent;
- FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
+ FPU_REG odd_poly, even_poly, pos_poly, neg_poly, ratio;
FPU_REG argSq;
long long arg_signif, argSqSq;
@@ -85,6 +85,7 @@ void poly_atan(FPU_REG *arg)
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
+ return;
#endif PARANOID
}
@@ -162,9 +163,6 @@ void poly_atan(FPU_REG *arg)
reg_move(&pos_poly, &odd_poly);
poly_add_1(&odd_poly);
- /* The complete odd polynomial */
- reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
-
/* will be a valid positive nr with expon = 0 */
*(short *)&(even_poly.sign) = 0;
@@ -173,10 +171,13 @@ void poly_atan(FPU_REG *arg)
poly_add_1(&even_poly);
- reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION);
+ reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION);
+
+ reg_u_mul(&ratio, arg, arg, FULL_PRECISION);
if ( recursions )
reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
+
}
diff --git a/kernel/FPU-emu/poly_sin.c b/kernel/FPU-emu/poly_sin.c
index 98bed99..24c58fe 100644
--- a/kernel/FPU-emu/poly_sin.c
+++ b/kernel/FPU-emu/poly_sin.c
@@ -43,7 +43,7 @@ static unsigned short negterms[HIPOWER][4] =
void poly_sine(FPU_REG *arg, FPU_REG *result)
{
short exponent;
- FPU_REG Xx, Xx2, Xx4, accum, negaccum;
+ FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
exponent = arg->exp - EXP_BIAS;
@@ -76,34 +76,34 @@ void poly_sine(FPU_REG *arg, FPU_REG *result)
}
#endif PARANOID
- Xx.sigl = arg->sigl;
- Xx.sigh = arg->sigh;
+ fixed_arg.sigl = arg->sigl;
+ fixed_arg.sigh = arg->sigh;
if ( exponent < -1 )
{
/* shift the argument right by the required places */
- if ( shrx(&(Xx.sigl), -1-exponent) >= 0x80000000U )
- (*((long long *)(&(Xx.sigl))))++; /* round up */
+ if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U )
+ (*((long long *)(&(fixed_arg.sigl))))++; /* round up */
}
- mul64((long long *)&(Xx.sigl), (long long *)&(Xx.sigl),
- (long long *)&(Xx2.sigl));
- mul64((long long *)&(Xx2.sigl), (long long *)&(Xx2.sigl),
- (long long *)&(Xx4.sigl));
+ mul64((long long *)&(fixed_arg.sigl), (long long *)&(fixed_arg.sigl),
+ (long long *)&(arg_sqrd.sigl));
+ mul64((long long *)&(arg_sqrd.sigl), (long long *)&(arg_sqrd.sigl),
+ (long long *)&(arg_to_4.sigl));
/* will be a valid positive nr with expon = 0 */
*(short *)&(accum.sign) = 0;
accum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
- polynomial(&(accum.sigl), &(Xx4.sigl), lterms, HIPOWER-1);
+ polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1);
/* will be a valid positive nr with expon = 0 */
*(short *)&(negaccum.sign) = 0;
negaccum.exp = 0;
/* Do the basic fixed point polynomial evaluation */
- polynomial(&(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER-1);
- mul64((long long *)&(Xx2.sigl), (long long *)&(negaccum.sigl),
+ polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1);
+ mul64((long long *)&(arg_sqrd.sigl), (long long *)&(negaccum.sigl),
(long long *)&(negaccum.sigl));
/* Subtract the mantissas */
@@ -111,20 +111,17 @@ void poly_sine(FPU_REG *arg, FPU_REG *result)
/* Convert to 64 bit signed-compatible */
accum.exp = EXP_BIAS - 1 + accum.exp;
-
- *(short *)&(result->sign) = *(short *)&(accum.sign);
- result->exp = accum.exp;
- result->sigl = accum.sigl;
- result->sigh = accum.sigh;
-
+
+ reg_move(&accum, result);
+
normalize(result);
reg_mul(result, arg, result, FULL_PRECISION);
reg_u_add(result, arg, result, FULL_PRECISION);
- /* A small overflow may be possible... but an illegal result. */
if ( result->exp >= EXP_BIAS )
{
+ /* A small overflow may be possible... but an illegal result. */
if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
|| (result->sigl > 1) /* Larger than 1.0+msb */
|| (result->sigh != 0x80000000) /* Much > 1.0 */
@@ -149,4 +146,5 @@ void poly_sine(FPU_REG *arg, FPU_REG *result)
result->sigl = 0; /* Truncate the result to 1.00 */
}
+
}
diff --git a/kernel/FPU-emu/poly_tan.c b/kernel/FPU-emu/poly_tan.c
index b56d0e9..8cb202a 100644
--- a/kernel/FPU-emu/poly_tan.c
+++ b/kernel/FPU-emu/poly_tan.c
@@ -49,9 +49,8 @@ static unsigned short evennegterms[HIPOWERen][4] =
/*--- poly_tan() ------------------------------------------------------------+
| |
+---------------------------------------------------------------------------*/
-void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
+void poly_tan(FPU_REG *arg, FPU_REG *y_reg, int invert)
{
- char invert = 0;
short exponent;
FPU_REG odd_poly, even_poly, pos_poly, neg_poly;
FPU_REG argSq;
@@ -59,39 +58,6 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
exponent = arg->exp - EXP_BIAS;
-
- if ( arg->tag == TW_Zero )
- {
- /* Return 0.0 */
- reg_move(&CONST_Z, y_reg);
- return;
- }
-
- if ( exponent >= -1 )
- {
- /* argument is in the range [0.5 .. 1.0] */
- if ( exponent >= 0 )
- {
-#ifdef PARANOID
- if ( (exponent == 0) &&
- (arg->sigl == 0) && (arg->sigh == 0x80000000) )
-#endif PARANOID
- {
- arith_overflow(y_reg);
- return;
- }
-#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) */
- /* Convert the argument to a number in the range (0.0 .. 0.5] */
- *((long long *)(&arg->sigl)) = - *((long long *)(&arg->sigl));
- normalize(arg); /* Needed later */
- exponent = arg->exp - EXP_BIAS;
- invert = 1;
- }
#ifdef PARANOID
if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
@@ -135,8 +101,8 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
normalize(&odd_poly);
reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
- reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd polynomial */
-
+ /* Complete the odd polynomial. */
+ reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);
/* will be a valid positive nr with expon = 0 */
*(short *)&(pos_poly.sign) = 0;
@@ -171,7 +137,8 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg)
reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
- reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even polynomial */
+ /* Complete the even polynomial */
+ reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);
/* Now ready to copy the results */
if ( invert )
diff --git a/kernel/FPU-emu/polynomial.S b/kernel/FPU-emu/polynomial.S
index 3f12fbb..894eded 100644
--- a/kernel/FPU-emu/polynomial.S
+++ b/kernel/FPU-emu/polynomial.S
@@ -22,10 +22,17 @@
#include "fpu_asm.h"
-// #define EXTRA_PRECISE
+// #define EXTRA_PRECISE // Do not use: not complete
#define TERM_SIZE $8
-
+#define SUM_MS -20(%ebp) /* sum ms long */
+#define SUM_MIDDLE -24(%ebp) /* sum middle long */
+#define SUM_LS -28(%ebp) /* sum ls long */
+#define SUM_LS_HI -25(%ebp) /* high byte of sum ls */
+#define ACCUM_MS -4(%ebp) /* accum ms long */
+#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */
+#define ACCUM_LS -12(%ebp) /* accum ls long */
+#define ACCUM_LS_HI -9(%ebp) /* high byte of accum ls */
.text
.align 2,144
@@ -38,101 +45,92 @@ _polynomial:
pushl %edi
pushl %ebx
- movl PARAM1,%esi // accum
- movl PARAM2,%edi // x
- movl PARAM3,%ebx // terms
- movl PARAM4,%ecx // n
+ movl PARAM2,%esi // x
+ movl PARAM3,%edi // terms
movl TERM_SIZE,%eax
- mull %ecx
- movl %eax,%ecx
+ mull PARAM4 // n
+ addl %eax,%edi
- movl 4(%ebx,%ecx,1),%edx // terms[n]
- movl %edx,-20(%ebp)
- movl (%ebx,%ecx,1),%edx // terms[n]
- movl %edx,-24(%ebp)
+ movl 4(%edi),%edx // terms[n]
+ movl %edx,SUM_MS
+ movl (%edi),%edx // terms[n]
+ movl %edx,SUM_MIDDLE
xor %eax,%eax
- movl %eax,-28(%ebp)
+ movl %eax,SUM_LS
- subl TERM_SIZE,%ecx
+ subl TERM_SIZE,%edi
+ decl PARAM4
js L_accum_done
L_accum_loop:
xor %eax,%eax
- movl %eax,-4(%ebp)
- movl %eax,-8(%ebp)
-
-#ifdef EXTRA_PRECISE
- movl -28(%ebp),%eax
- mull 4(%edi) // x ms long
- movl %edx,-12(%ebp)
-#endif EXTRA_PRECISE
+ movl %eax,ACCUM_MS
+ movl %eax,ACCUM_MIDDLE
- movl -24(%ebp),%eax
- mull (%edi) // x ls long
+ movl SUM_MIDDLE,%eax
+ mull (%esi) // x ls long
// movl %eax,-16(%ebp) // Not needed
- addl %edx,-12(%ebp)
- adcl $0,-8(%ebp)
-
- movl -24(%ebp),%eax
- mull 4(%edi) // x ms long
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl -20(%ebp),%eax
- mull (%edi)
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl -20(%ebp),%eax
- mull 4(%edi)
- addl %eax,-8(%ebp)
- adcl %edx,-4(%ebp)
-
-/* Now add the next term */
- movl (%ebx,%ecx,1),%eax
- addl %eax,-8(%ebp)
- movl 4(%ebx,%ecx,1),%eax
- adcl %eax,-4(%ebp)
-
-/* And put into the second register */
- movl -4(%ebp),%eax
- movl %eax,-20(%ebp)
- movl -8(%ebp),%eax
- movl %eax,-24(%ebp)
+ movl %edx,ACCUM_LS
+
+ movl SUM_MIDDLE,%eax
+ mull 4(%esi) // x ms long
+ addl %eax,ACCUM_LS
+ adcl %edx,ACCUM_MIDDLE
+ adcl $0,ACCUM_MS
+
+ movl SUM_MS,%eax
+ mull (%esi) // x ls long
+ addl %eax,ACCUM_LS
+ adcl %edx,ACCUM_MIDDLE
+ adcl $0,ACCUM_MS
+
+ movl SUM_MS,%eax
+ mull 4(%esi) // x ms long
+ addl %eax,ACCUM_MIDDLE
+ adcl %edx,ACCUM_MS
+
+// Now put the sum of next term and the accumulator
+// into the sum register
+ movl ACCUM_MIDDLE,%eax
+ addl (%edi),%eax // term ls long
+ movl %eax,SUM_MIDDLE
+ movl ACCUM_MS,%eax
+ adcl 4(%edi),%eax // term ms long
+ movl %eax,SUM_MS
#ifdef EXTRA_PRECISE
- movl -12(%ebp),%eax
- movl %eax,-28(%ebp)
+ movl ACCUM_LS,%eax
+ movl %eax,SUM_LS
#else
- testb $128,-25(%ebp)
+ testb $0x80,ACCUM_LS_HI // ms bit of ACCUM_LS
je L_no_poly_round
- addl $1,-24(%ebp)
- adcl $0,-20(%ebp)
+ addl $1,SUM_MIDDLE
+ adcl $0,SUM_MS
L_no_poly_round:
#endif EXTRA_PRECISE
- subl TERM_SIZE,%ecx
+ subl TERM_SIZE,%edi
+ decl PARAM4
jns L_accum_loop
L_accum_done:
#ifdef EXTRA_PRECISE
/* And round the result */
- testb $128,-25(%ebp)
+ testb $128,SUM_LS_HI
je L_poly_done
- addl $1,-24(%ebp)
- adcl $0,-20(%ebp)
+ addl $1,SUM_MIDDLE
+ adcl $0,SUM_MS
#endif EXTRA_PRECISE
L_poly_done:
- movl -24(%ebp),%eax
- movl %eax,(%esi)
- movl -20(%ebp),%eax
- movl %eax,4(%esi)
+ movl PARAM1,%edi // accum
+ movl SUM_MIDDLE,%eax
+ movl %eax,(%edi)
+ movl SUM_MS,%eax
+ movl %eax,4(%edi)
popl %ebx
popl %edi
diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c
index 380b481..890484e 100644
--- a/kernel/FPU-emu/reg_add_sub.c
+++ b/kernel/FPU-emu/reg_add_sub.c
@@ -22,8 +22,9 @@
#include "fpu_system.h"
-void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
{
+ char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
@@ -32,9 +33,13 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
if (!(a->sign ^ b->sign))
{
/* signs are the same */
- reg_u_add(a, b, dest, control_w);
dest->sign = a->sign;
- return;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
}
/* The signs are different, so do a subtraction */
@@ -52,11 +57,20 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
if (diff > 0)
{
- reg_u_sub(a, b, dest, control_w);
dest->sign = a->sign;
+ if ( reg_u_sub(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
}
else if ( diff == 0 )
{
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
dest->sign = ((control_w & CW_RC) != RC_DOWN)
@@ -64,15 +78,19 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
}
else
{
- reg_u_sub(b, a, dest, control_w);
dest->sign = b->sign;
+ if ( reg_u_sub(b, a, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
}
- return;
+ return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { real_2op_NaN(a, b, dest); return; }
+ { return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Zero)
{
if (b->tag == TW_Zero)
@@ -93,20 +111,20 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
}
- return;
+ return 0;
}
else if (b->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
- reg_move(a, dest); return;
+ reg_move(a, dest); return 0;
}
else if (a->tag == TW_Infinity)
{
@@ -115,37 +133,38 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
- reg_move(a, dest); return;
+ reg_move(a, dest); return 0;
}
if (a->sign == b->sign)
{
/* They are both + or - infinity */
- reg_move(a, dest); return;
+ reg_move(a, dest); return 0;
}
- arith_invalid(dest); /* Infinity-Infinity is undefined. */
- return;
+ return arith_invalid(dest); /* Infinity-Infinity is undefined. */
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
- reg_move(b, dest); return;
+ reg_move(b, dest); return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x101);
#endif
+ return 1;
}
/* Subtract b from a. (a-b) -> dest */
-void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
+int reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
{
+ char saved_sign = dest->sign;
int diff;
if ( !(a->tag | b->tag) )
@@ -162,22 +181,28 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
diff = -(a->sigl < b->sigl);
}
}
-
+
switch (a->sign*2 + b->sign)
{
case 0: /* P - P */
case 3: /* N - N */
if (diff > 0)
{
- reg_u_sub(a, b, dest, control_w);
+ /* |a| > |b| */
dest->sign = a->sign;
+ if ( reg_u_sub(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
}
else if ( diff == 0 )
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(&CONST_Z, dest);
/* sign depends upon rounding mode */
@@ -186,24 +211,37 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
}
else
{
- reg_u_sub(b, a, dest, control_w);
dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
+ if ( reg_u_sub(b, a, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
}
- return;
+ break;
case 1: /* P - N */
- reg_u_add(a, b, dest, control_w);
dest->sign = SIGN_POS;
- return;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ break;
case 2: /* N - P */
- reg_u_add(a, b, dest, control_w);
dest->sign = SIGN_NEG;
- return;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ break;
}
+ return 0;
}
else
{
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { real_2op_NaN(a, b, dest); return; }
+ { return real_2op_NaN(b, a, dest); }
else if (b->tag == TW_Zero)
{
if (a->tag == TW_Zero)
@@ -223,22 +261,22 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
}
- return;
+ return 0;
}
else if (a->tag == TW_Zero)
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
- return;
+ return 0;
}
else if (a->tag == TW_Infinity)
{
@@ -247,33 +285,34 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
- reg_move(a, dest); return;
+ reg_move(a, dest); return 0;
}
/* Both args are Infinity */
if (a->sign == b->sign)
{
- arith_invalid(dest); /* Infinity-Infinity is undefined. */
- return;
+ /* Infinity-Infinity is undefined. */
+ return arith_invalid(dest);
}
reg_move(a, dest);
- return;
+ return 0;
}
else if (b->tag == TW_Infinity)
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign ^= SIGN_POS^SIGN_NEG;
- return;
+ return 0;
}
}
#ifdef PARANOID
EXCEPTION(EX_INTERNAL|0x110);
#endif
+ return 1;
}
diff --git a/kernel/FPU-emu/reg_compare.c b/kernel/FPU-emu/reg_compare.c
index defbcd4..d1ec646 100644
--- a/kernel/FPU-emu/reg_compare.c
+++ b/kernel/FPU-emu/reg_compare.c
@@ -32,22 +32,25 @@ int compare(FPU_REG *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;
+ | ((b->exp <= EXP_UNDER) ?
+ COMP_Denormal : 0)
#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;
+ | ((FPU_st0_ptr->exp <= EXP_UNDER )
+ ? COMP_Denormal : 0 )
#endif DENORM_OPERAND
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B ;
+ ;
}
}
@@ -55,12 +58,13 @@ 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;
+ | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0 )
#endif DENORM_OPERAND
- return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
+;
}
else if ( b->tag == TW_Infinity )
{
@@ -74,13 +78,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;
+ | (((FPU_st0_ptr->tag == TW_Valid)
+ && (FPU_st0_ptr->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
#endif DENORM_OPERAND
- return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
+ ;
}
/* Fall through to the NaN code */
}
@@ -106,15 +110,18 @@ int compare(FPU_REG *b)
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)
+#ifdef DENORM_OPERAND
+ |
+ ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+
diff = FPU_st0_ptr->exp - b->exp;
if ( diff == 0 )
{
@@ -129,10 +136,33 @@ 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)
+#ifdef DENORM_OPERAND
+ |
+ ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
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)
+#ifdef DENORM_OPERAND
+ |
+ ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+
+ return COMP_A_eq_B
+#ifdef DENORM_OPERAND
+ |
+ ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
}
@@ -144,21 +174,13 @@ int compare_st_data(void)
c = compare(&FPU_loaded_data);
- if (c & (COMP_NaN|COMP_Denormal))
+ if (c & COMP_NaN)
{
- 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;
- }
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
}
else
- switch (c)
+ switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
@@ -180,7 +202,11 @@ int compare_st_data(void)
#endif PARANOID
}
setcc(f);
- return 1;
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
}
@@ -193,26 +219,18 @@ static int compare_st_st(int nr)
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
- return control_word & CW_Invalid;
+ return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
- if (c & (COMP_NaN|COMP_Denormal))
+ if (c & COMP_NaN)
{
- 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;
- }
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ EXCEPTION(EX_Invalid);
+ return !(control_word & CW_Invalid);
}
else
- switch (c)
+ switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
@@ -234,7 +252,11 @@ static int compare_st_st(int nr)
#endif PARANOID
}
setcc(f);
- return 1;
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
}
@@ -247,31 +269,23 @@ static int compare_u_st_st(int nr)
setcc(SW_C3 | SW_C2 | SW_C0);
/* Stack fault */
EXCEPTION(EX_StackUnder);
- return control_word & CW_Invalid;
+ return !(control_word & CW_Invalid);
}
c = compare(&st(nr));
- if (c & (COMP_NaN|COMP_Denormal))
+ if (c & COMP_NaN)
{
- 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
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (c & COMP_SNaN) /* This is the only difference between
+ un-ordered and ordinary comparisons */
{
- /* One of the operands is a de-normal */
- return control_word & CW_Denormal;
+ EXCEPTION(EX_Invalid);
+ return !(control_word & CW_Invalid);
}
+ return 0;
}
else
- switch (c)
+ switch (c & 7)
{
case COMP_A_lt_B:
f = SW_C0;
@@ -293,34 +307,39 @@ static int compare_u_st_st(int nr)
#endif PARANOID
}
setcc(f);
- return 1;
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
}
/*---------------------------------------------------------------------------*/
-void fcom_st(void)
+void fcom_st()
{
/* fcom st(i) */
compare_st_st(FPU_rm);
}
-void fcompst(void)
+void fcompst()
{
/* fcomp st(i) */
- if ( compare_st_st(FPU_rm) )
+ if ( !compare_st_st(FPU_rm) )
pop();
}
-void fcompp(void)
+void fcompp()
{
/* fcompp */
- if (FPU_rm != 1) {
- Un_impl();
- return;
- }
- if ( compare_st_st(1) )
+ if (FPU_rm != 1)
+ {
+ Un_impl();
+ return;
+ }
+ if ( !compare_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
@@ -328,7 +347,7 @@ void fcompp(void)
}
-void fucom_(void)
+void fucom_()
{
/* fucom st(i) */
compare_u_st_st(FPU_rm);
@@ -336,20 +355,20 @@ void fucom_(void)
}
-void fucomp(void)
+void fucomp()
{
/* fucomp st(i) */
- if ( compare_u_st_st(FPU_rm) )
+ if ( !compare_u_st_st(FPU_rm) )
pop();
}
-void fucompp(void)
+void fucompp()
{
/* fucompp */
if (FPU_rm == 1)
{
- if ( compare_u_st_st(1) )
+ if ( !compare_u_st_st(1) )
{
pop(); FPU_st0_ptr = &st(0);
pop();
diff --git a/kernel/FPU-emu/reg_constant.c b/kernel/FPU-emu/reg_constant.c
index 88c03b6..6cda7f0 100644
--- a/kernel/FPU-emu/reg_constant.c
+++ b/kernel/FPU-emu/reg_constant.c
@@ -37,8 +37,12 @@ FPU_REG CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
FPU_REG CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
0xd1cf79ac, 0xb17217f7 };
+/* Extra bits to take pi/2 to more than 128 bits precision. */
+FPU_REG CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
+ 0xfc8f8cbb, 0xece675d1 };
+
/* Only the sign (and tag) is used in internal zeroes */
-FPU_REG CONST_Z = { SIGN_POS, TW_Zero, 0, 0x0, 0x0 };
+FPU_REG CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
/* Only the sign and significand (and tag) are used in internal NaNs */
/* The 80486 never generates one of these
@@ -63,7 +67,10 @@ static void fld_const(FPU_REG *c)
}
push();
reg_move(c, FPU_st0_ptr);
- status_word &= ~SW_C1;
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ clear_C1();
+#endif PECULIAR_486
}
diff --git a/kernel/FPU-emu/reg_constant.h b/kernel/FPU-emu/reg_constant.h
index a039bc6..23ba06d 100644
--- a/kernel/FPU-emu/reg_constant.h
+++ b/kernel/FPU-emu/reg_constant.h
@@ -18,6 +18,7 @@ extern FPU_REG CONST_L2T;
extern FPU_REG CONST_L2E;
extern FPU_REG CONST_PI;
extern FPU_REG CONST_PI2;
+extern FPU_REG CONST_PI2extra;
extern FPU_REG CONST_PI4;
extern FPU_REG CONST_LG2;
extern FPU_REG CONST_LN2;
diff --git a/kernel/FPU-emu/reg_div.S b/kernel/FPU-emu/reg_div.S
index 854e617..9d65f60 100644
--- a/kernel/FPU-emu/reg_div.S
+++ b/kernel/FPU-emu/reg_div.S
@@ -86,8 +86,9 @@ L_div_special:
L_arg1_NaN:
L_arg2_NaN:
pushl %edi // Destination
- pushl %ebx
+// pushl %ebx
pushl %esi
+ pushl %ebx // Ordering is important here
call _real_2op_NaN
jmp LDiv_exit
@@ -196,23 +197,18 @@ L_copy_arg1:
movl SIGH(%esi),%eax
movl %eax,SIGH(%edi)
+LDiv_set_result_sign:
movb SIGN(%esi),%cl
cmpb %cl,SIGN(%ebx)
jne LDiv_negative_result
movb SIGN_POS,SIGN(%edi)
- jmp LDiv_exit
-
-LDiv_set_result_sign:
- movb SIGN(%esi),%cl
- cmpb %cl,SIGN(%edi)
- jne LDiv_negative_result
-
- movb SIGN_POS,SIGN(%ebx)
+ xorl %eax,%eax // Valid result
jmp LDiv_exit
LDiv_negative_result:
movb SIGN_NEG,SIGN(%edi)
+ xorl %eax,%eax // Valid result
LDiv_exit:
leal -12(%ebp),%esp
@@ -225,6 +221,10 @@ LDiv_exit:
L_return_zero:
+ xorl %eax,%eax
+ movl %eax,SIGH(%edi)
+ movl %eax,SIGL(%edi)
+ movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp LDiv_set_result_sign
@@ -240,5 +240,5 @@ L_unknown_tags:
movl %eax,SIGL(%edi)
movl _CONST_QNaN+8,%eax
movl %eax,SIGH(%edi)
- jmp LDiv_exit
+ jmp LDiv_exit // %eax is nz
#endif PARANOID
diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c
index 8cbef5a..1ddc553 100644
--- a/kernel/FPU-emu/reg_ld_str.c
+++ b/kernel/FPU-emu/reg_ld_str.c
@@ -27,7 +27,6 @@
#include "status_w.h"
-#define EXTENDED_Emax 0x3fff /* largest valid exponent */
#define EXTENDED_Ebias 0x3fff
#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
@@ -39,14 +38,13 @@
#define SINGLE_Ebias 127
#define SINGLE_Emin (-126) /* smallest valid exponent */
-#define LOST_UP (EX_Precision | SW_C1)
-#define LOST_DOWN EX_Precision
+static void write_to_extended(FPU_REG *rp, char *d);
FPU_REG FPU_loaded_data;
/* Get a long double from user memory */
-void reg_load_extended(void)
+int reg_load_extended(void)
{
long double *s = (long double *)FPU_data_address;
unsigned long sigl, sigh, exp;
@@ -59,63 +57,94 @@ void reg_load_extended(void)
exp = get_fs_word(4 + (unsigned short *) s);
RE_ENTRANT_CHECK_ON
+ FPU_loaded_data.tag = TW_Valid; /* Default */
FPU_loaded_data.sigl = sigl;
FPU_loaded_data.sigh = sigh;
- FPU_loaded_data.exp = exp;
-
- if (FPU_loaded_data.exp & 0x8000)
+ if (exp & 0x8000)
FPU_loaded_data.sign = SIGN_NEG;
else
FPU_loaded_data.sign = SIGN_POS;
- if ( (FPU_loaded_data.exp &= 0x7fff) == 0 )
+ exp &= 0x7fff;
+ FPU_loaded_data.exp = exp - EXTENDED_Ebias + EXP_BIAS;
+
+ /* Assume that optimisation can keep sigl, sigh, and exp in
+ registers, otherwise it would be more efficient to work
+ with FPU_loaded_data (which is static) here. */
+ if ( exp == 0 )
{
- if ( !(FPU_loaded_data.sigl | FPU_loaded_data.sigh) )
+ if ( !(sigh | sigl) )
{
FPU_loaded_data.tag = TW_Zero;
- return;
+ return 0;
}
/* 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. */
+ if (sigh & 0x80000000)
+ {
+ /* Is a pseudodenormal. */
+ /* Convert it for internal use. */
+ /* This is non-80486 behaviour because the number
+ loses its 'denormal' identity. */
+ FPU_loaded_data.exp++;
+ return 1;
+ }
+ else
+ {
+ /* Is a denormal. */
+ /* Convert it for internal use. */
+ FPU_loaded_data.exp++;
+ normalize_nuo(&FPU_loaded_data);
+ return 0;
+ }
}
- else if ( FPU_loaded_data.exp == 0x7fff )
+ else if ( exp == 0x7fff )
{
- FPU_loaded_data.exp = EXTENDED_Emax;
- if ( (FPU_loaded_data.sigh == 0x80000000)
- && (FPU_loaded_data.sigl == 0) )
+ if ( !((sigh ^ 0x80000000) | sigl) )
{
+ /* Matches the bit pattern for Infinity. */
+ FPU_loaded_data.exp = EXP_Infinity;
FPU_loaded_data.tag = TW_Infinity;
- return;
+ return 0;
}
- else if ( !(FPU_loaded_data.sigh & 0x80000000) )
+
+ FPU_loaded_data.exp = EXP_NaN;
+ FPU_loaded_data.tag = TW_NaN;
+ if ( !(sigh & 0x80000000) )
{
- /* Unsupported NaN data type */
- EXCEPTION(EX_Invalid);
- FPU_loaded_data.tag = TW_NaN;
- return;
+ /* NaNs have the ms bit set to 1. */
+ /* This is therefore an Unsupported NaN data type. */
+ /* This is non 80486 behaviour */
+ /* This should generate an Invalid Operand exception
+ later, so we convert it to a SNaN */
+ FPU_loaded_data.sigh = 0x80000000;
+ FPU_loaded_data.sigl = 0x00000001;
+ FPU_loaded_data.sign = SIGN_NEG;
+ return 1;
}
- FPU_loaded_data.tag = TW_NaN;
- return;
+ return 0;
}
- FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
- + EXP_BIAS;
- FPU_loaded_data.tag = TW_Valid;
if ( !(sigh & 0x80000000) )
{
- /* Unsupported data type */
- EXCEPTION(EX_Invalid);
- normalize_nuo(&FPU_loaded_data);
+ /* Unsupported data type. */
+ /* Valid numbers have the ms bit set to 1. */
+ /* Unnormal. */
+ /* Convert it for internal use. */
+ /* This is non-80486 behaviour */
+ /* This should generate an Invalid Operand exception
+ later, so we convert it to a SNaN */
+ FPU_loaded_data.sigh = 0x80000000;
+ FPU_loaded_data.sigl = 0x00000001;
+ FPU_loaded_data.sign = SIGN_NEG;
+ FPU_loaded_data.exp = EXP_NaN;
+ FPU_loaded_data.tag = TW_NaN;
+ return 1;
}
+ return 0;
}
/* Get a double from user memory */
-void reg_load_double(void)
+int reg_load_double(void)
{
double *dfloat = (double *)FPU_data_address;
int exp;
@@ -138,19 +167,21 @@ void reg_load_double(void)
if ((m64 == 0) && (l64 == 0))
{
/* +- infinity */
- FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.sigh = 0x80000000;
+ FPU_loaded_data.sigl = 0x00000000;
+ FPU_loaded_data.exp = EXP_Infinity;
FPU_loaded_data.tag = TW_Infinity;
- return;
+ return 0;
}
else
{
/* Must be a signaling or quiet NaN */
- FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.exp = EXP_NaN;
FPU_loaded_data.tag = TW_NaN;
FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
FPU_loaded_data.sigh |= l64 >> 21;
FPU_loaded_data.sigl = l64 << 11;
- return;
+ return 0; /* The calling function must look for NaNs */
}
}
else if ( exp < DOUBLE_Emin )
@@ -162,19 +193,18 @@ void reg_load_double(void)
int c = FPU_loaded_data.sign;
reg_move(&CONST_Z, &FPU_loaded_data);
FPU_loaded_data.sign = c;
- return;
+ return 0;
}
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_nuo(&FPU_loaded_data);
- return;
+ return denormal_operand();
}
}
else
@@ -185,13 +215,13 @@ void reg_load_double(void)
FPU_loaded_data.sigh |= l64 >> 21;
FPU_loaded_data.sigl = l64 << 11;
- return;
+ return 0;
}
}
/* Get a float from user memory */
-void reg_load_single(void)
+int reg_load_single(void)
{
float *single = (float *)FPU_data_address;
unsigned m32;
@@ -211,20 +241,19 @@ void reg_load_single(void)
int c = FPU_loaded_data.sign;
reg_move(&CONST_Z, &FPU_loaded_data);
FPU_loaded_data.sign = c;
- return;
+ return 0;
}
exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
m32 = (m32 & 0x7fffff) << 8;
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_nuo(&FPU_loaded_data);
- return;
+ return denormal_operand();
}
else if ( exp > SINGLE_Emax )
{
@@ -232,18 +261,20 @@ void reg_load_single(void)
if ( m32 == 0 )
{
/* +- infinity */
- FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.sigh = 0x80000000;
+ FPU_loaded_data.sigl = 0x00000000;
+ FPU_loaded_data.exp = EXP_Infinity;
FPU_loaded_data.tag = TW_Infinity;
- return;
+ return 0;
}
else
{
/* Must be a signaling or quiet NaN */
- FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.exp = EXP_NaN;
FPU_loaded_data.tag = TW_NaN;
FPU_loaded_data.sigh = m32 | 0x80000000;
FPU_loaded_data.sigl = 0;
- return;
+ return 0; /* The calling function must look for NaNs */
}
}
else
@@ -252,6 +283,7 @@ void reg_load_single(void)
FPU_loaded_data.sigh = m32 | 0x80000000;
FPU_loaded_data.sigl = 0;
FPU_loaded_data.tag = TW_Valid;
+ return 0;
}
}
@@ -397,122 +429,36 @@ void reg_load_bcd(void)
/* Put a long double into user memory */
int reg_store_extended(void)
{
+ /*
+ The only exception raised by an attempt to store to an
+ extended format is the Invalid Stack exception, i.e.
+ attempting to store from an empty register.
+ */
long double *d = (long double *)FPU_data_address;
- long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
- unsigned short sign = FPU_st0_ptr->sign*0x8000;
- unsigned long ls, ms;
-
- if ( FPU_st0_tag == TW_Valid )
+ if ( FPU_st0_tag != TW_Empty )
{
- if ( e >= 0x7fff )
- {
- EXCEPTION(EX_Overflow); /* Overflow */
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- if ( control_word & EX_Overflow )
- {
- /* Overflow to infinity */
- ls = 0;
- ms = 0x80000000;
- e = 0x7fff;
- }
- else
- return 0;
- }
- else if ( e <= 0 )
- {
- if ( e > -63 )
- {
- /* Correctly format the de-normal */
- int precision_loss;
- FPU_REG tmp;
-
- EXCEPTION(EX_Denormal);
- reg_move(FPU_st0_ptr, &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 )
- {
- /* Underflow to zero */
- ls = 0;
- ms = 0;
- e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
- }
- else
- return 0;
- }
- }
- else
- {
- ls = FPU_st0_ptr->sigl;
- ms = FPU_st0_ptr->sigh;
- }
- }
- else if ( FPU_st0_tag == TW_Zero )
- {
- ls = ms = 0;
- e = 0;
+ verify_area(VERIFY_WRITE, d, 10);
+ write_to_extended(FPU_st0_ptr, (char *) FPU_data_address);
+ return 1;
}
- else if ( FPU_st0_tag == TW_Infinity )
- {
- ls = 0;
- ms = 0x80000000;
- e = 0x7fff;
- }
- else if ( FPU_st0_tag == TW_NaN )
+
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & CW_Invalid )
{
- ls = FPU_st0_ptr->sigl;
- ms = FPU_st0_ptr->sigh;
- e = 0x7fff;
- }
- else if ( FPU_st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- ls = 0;
- ms = 0xc0000000;
- e = 0xffff;
- }
- else
- return 0;
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ RE_ENTRANT_CHECK_OFF;
+ verify_area(VERIFY_WRITE,d,10);
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0xc0000000, 1 + (unsigned long *) d);
+ put_fs_word(0xffff, 4 + (short *) d);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
}
else
- {
- /* We don't use TW_Denormal yet ... perhaps never! */
- EXCEPTION(EX_Invalid);
- /* Store a NaN */
- e = 0x7fff;
- ls = 1;
- ms = 0x80000000;
- }
- RE_ENTRANT_CHECK_OFF
- verify_area(VERIFY_WRITE,d,10);
- put_fs_long(ls, (unsigned long *) d);
- put_fs_long(ms, 1 + (unsigned long *) d);
- put_fs_word((unsigned short)e | sign, 4 + (short *) d);
- RE_ENTRANT_CHECK_ON
-
- return 1;
+ return 0;
}
@@ -522,6 +468,7 @@ int reg_store_double(void)
{
double *dfloat = (double *)FPU_data_address;
unsigned long l[2];
+ unsigned long increment = 0; /* avoid gcc warnings */
if (FPU_st0_tag == TW_Valid)
{
@@ -533,11 +480,19 @@ int reg_store_double(void)
if ( exp < DOUBLE_Emin ) /* It may be a denormal */
{
- /* Make a de-normal */
int precision_loss;
- if ( exp <= -EXTENDED_Ebias )
- EXCEPTION(EX_Denormal);
+ /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+ /* An 80486 is supposed to be able to generate
+ a denormal exception here, but... */
+ if ( FPU_st0_ptr->exp <= EXP_UNDER )
+ {
+ /* Underflow has priority. */
+ if ( control_word & CW_Underflow )
+ denormal_operand();
+ }
+#endif PECULIAR_486
tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
@@ -548,18 +503,19 @@ int reg_store_double(void)
/* 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
+ if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+ (FPU_st0_ptr->sigl & 0x000007ff)) )
#endif PECULIAR_486
{
- EXCEPTION(EX_Underflow | precision_loss);
+ EXCEPTION(EX_Underflow);
/* This is a special case: see sec 16.2.5.1 of
the 80486 book */
- if ( !(control_word & EX_Underflow) )
+ if ( !(control_word & CW_Underflow) )
return 0;
}
+ EXCEPTION(precision_loss);
+ if ( !(control_word & CW_Precision) )
+ return 0;
}
l[0] = tmp.sigl;
l[1] = tmp.sigh;
@@ -568,8 +524,6 @@ int reg_store_double(void)
{
if ( tmp.sigl & 0x000007ff )
{
- unsigned long increment = 0; /* avoid gcc warnings */
-
switch (control_word & CW_RC)
{
case RC_RND:
@@ -629,15 +583,16 @@ int reg_store_double(void)
{
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
+ if ( !(control_word & CW_Overflow) )
+ return 0;
+ set_precision_flag_up();
+ if ( !(control_word & CW_Precision) )
return 0;
+
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
}
else
{
@@ -662,12 +617,13 @@ int reg_store_double(void)
/* See if we can get a valid NaN from the FPU_REG */
l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
- if ( !(l[0] | l[1]) )
+ if ( !(FPU_st0_ptr->sigh & 0x40000000) )
{
- /* This case does not seem to be handled by the 80486 specs */
+ /* It is a signalling NaN */
EXCEPTION(EX_Invalid);
- /* Make the quiet NaN "real indefinite" */
- goto put_indefinite;
+ if ( !(control_word & CW_Invalid) )
+ return 0;
+ l[1] |= (0x40000000 >> 11);
}
l[1] |= 0x7ff00000;
}
@@ -675,11 +631,10 @@ int reg_store_double(void)
{
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
+ if ( control_word & CW_Invalid )
{
/* The masked response */
/* Put out the QNaN indefinite */
-put_indefinite:
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,(void *)dfloat,8);
put_fs_long(0, (unsigned long *) dfloat);
@@ -690,14 +645,6 @@ 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;
@@ -716,6 +663,7 @@ int reg_store_single(void)
{
float *single = (float *)FPU_data_address;
long templ;
+ unsigned long increment = 0; /* avoid gcc warnings */
if (FPU_st0_tag == TW_Valid)
{
@@ -727,11 +675,19 @@ int reg_store_single(void)
if ( exp < SINGLE_Emin )
{
- /* Make a de-normal */
int precision_loss;
- if ( exp <= -EXTENDED_Ebias )
- EXCEPTION(EX_Denormal);
+ /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+ /* An 80486 is supposed to be able to generate
+ a denormal exception here, but... */
+ if ( FPU_st0_ptr->exp <= EXP_UNDER )
+ {
+ /* Underflow has priority. */
+ if ( control_word & CW_Underflow )
+ denormal_operand();
+ }
+#endif PECULIAR_486
tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
@@ -742,18 +698,19 @@ int reg_store_single(void)
/* 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
+ if ( !((tmp.sigl == 0x00800000) &&
+ ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl)) )
#endif PECULIAR_486
{
- EXCEPTION(EX_Underflow | precision_loss);
+ EXCEPTION(EX_Underflow);
/* This is a special case: see sec 16.2.5.1 of
the 80486 book */
if ( !(control_word & EX_Underflow) )
return 0;
}
+ EXCEPTION(precision_loss);
+ if ( !(control_word & EX_Precision) )
+ return 0;
}
templ = tmp.sigl;
}
@@ -761,7 +718,6 @@ int reg_store_single(void)
{
if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
{
- unsigned long increment = 0; /* avoid gcc warnings */
unsigned long sigh = tmp.sigh;
unsigned long sigl = tmp.sigl;
@@ -819,14 +775,15 @@ int reg_store_single(void)
{
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 */
- templ = 0x7f800000;
- }
- else
+ if ( !(control_word & CW_Overflow) )
return 0;
+ set_precision_flag_up();
+ if ( !(control_word & CW_Precision) )
+ return 0;
+
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
+ /* Masked respose is overflow to infinity. */
+ templ = 0x7f800000;
}
else
templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
@@ -844,12 +801,13 @@ int reg_store_single(void)
{
/* See if we can get a valid NaN from the FPU_REG */
templ = FPU_st0_ptr->sigh >> 8;
- if ( !(templ & 0x3fffff) )
+ if ( !(FPU_st0_ptr->sigh & 0x40000000) )
{
- /* This case does not seem to be handled by the 80486 specs */
+ /* It is a signalling NaN */
EXCEPTION(EX_Invalid);
- /* Make the quiet NaN "real indefinite" */
- goto put_indefinite;
+ if ( !(control_word & CW_Invalid) )
+ return 0;
+ templ |= (0x40000000 >> 8);
}
templ |= 0x7f800000;
}
@@ -861,7 +819,6 @@ int reg_store_single(void)
{
/* The masked response */
/* Put out the QNaN indefinite */
-put_indefinite:
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,(void *)single,4);
put_fs_long(0xffc00000, (unsigned long *) single);
@@ -871,14 +828,6 @@ 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
{
@@ -904,42 +853,48 @@ int reg_store_int64(void)
long long *d = (long long *)FPU_data_address;
FPU_REG t;
long long tll;
+ int precision_loss;
if ( FPU_st0_tag == TW_Empty )
{
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- goto put_indefinite;
- }
- else
- return 0;
+ goto invalid_operand;
+ }
+ else if ( (FPU_st0_tag == TW_Infinity) ||
+ (FPU_st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
}
reg_move(FPU_st0_ptr, &t);
- round_to_int(&t);
+ precision_loss = round_to_int(&t);
((long *)&tll)[0] = t.sigl;
((long *)&tll)[1] = t.sigh;
- if ( (t.sigh & 0x80000000) &&
- !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG)) )
+ if ( (precision_loss == 1) ||
+ ((t.sigh & 0x80000000) &&
+ !((t.sigh == 0x80000000) && (t.sigl == 0) &&
+ (t.sign == SIGN_NEG))) )
{
EXCEPTION(EX_Invalid);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
if ( control_word & EX_Invalid )
{
- /* Produce "indefinite" */
-put_indefinite:
- ((long *)&tll)[1] = 0x80000000;
- ((long *)&tll)[0] = 0;
+ /* Produce something like QNaN "indefinite" */
+ tll = 0x8000000000000000LL;
}
else
return 0;
}
- else if ( t.sign )
- tll = - tll;
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ tll = - tll;
+ }
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,(void *)d,8);
@@ -956,43 +911,45 @@ int reg_store_int32(void)
{
long *d = (long *)FPU_data_address;
FPU_REG t;
+ int precision_loss;
if ( FPU_st0_tag == TW_Empty )
{
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- RE_ENTRANT_CHECK_OFF
- verify_area(VERIFY_WRITE,d,4);
- put_fs_long(0x80000000, (unsigned long *) d);
- RE_ENTRANT_CHECK_ON
- return 1;
- }
- else
- return 0;
+ goto invalid_operand;
+ }
+ else if ( (FPU_st0_tag == TW_Infinity) ||
+ (FPU_st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
}
reg_move(FPU_st0_ptr, &t);
- round_to_int(&t);
+ precision_loss = round_to_int(&t);
if (t.sigh ||
((t.sigl & 0x80000000) &&
!((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
{
EXCEPTION(EX_Invalid);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
if ( control_word & EX_Invalid )
{
- /* Produce "indefinite" */
+ /* Produce something like QNaN "indefinite" */
t.sigl = 0x80000000;
}
else
return 0;
}
- else if ( t.sign )
- t.sigl = -(long)t.sigl;
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ t.sigl = -(long)t.sigl;
+ }
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,d,4);
@@ -1008,44 +965,45 @@ int reg_store_int16(void)
{
short *d = (short *)FPU_data_address;
FPU_REG t;
- short ts;
+ int precision_loss;
if ( FPU_st0_tag == TW_Empty )
{
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- RE_ENTRANT_CHECK_OFF
- verify_area(VERIFY_WRITE,d,2);
- put_fs_word(0x8000, (unsigned short *) d);
- RE_ENTRANT_CHECK_ON
- return 1;
- }
- else
- return 0;
+ goto invalid_operand;
+ }
+ else if ( (FPU_st0_tag == TW_Infinity) ||
+ (FPU_st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
}
reg_move(FPU_st0_ptr, &t);
- round_to_int(&t);
+ precision_loss = round_to_int(&t);
if (t.sigh ||
((t.sigl & 0xffff8000) &&
!((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
{
EXCEPTION(EX_Invalid);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
if ( control_word & EX_Invalid )
{
- /* Produce "indefinite" */
- ts = 0x8000;
+ /* Produce something like QNaN "indefinite" */
+ t.sigl = 0x8000;
}
else
return 0;
}
- else if ( t.sign )
- t.sigl = -t.sigl;
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ t.sigl = -t.sigl;
+ }
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,d,2);
@@ -1063,25 +1021,18 @@ int reg_store_bcd(void)
FPU_REG t;
unsigned long long ll;
unsigned char b;
- int i;
+ int i, precision_loss;
unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
if ( FPU_st0_tag == TW_Empty )
{
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- goto put_indefinite;
- }
- else
- return 0;
+ goto invalid_operand;
}
reg_move(FPU_st0_ptr, &t);
- round_to_int(&t);
+ precision_loss = round_to_int(&t);
ll = *(unsigned long long *)(&t.sigl);
/* Check for overflow, by comparing with 999999999999999999 decimal. */
@@ -1090,13 +1041,13 @@ int reg_store_bcd(void)
{
EXCEPTION(EX_Invalid);
/* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
if ( control_word & EX_Invalid )
{
-put_indefinite:
- /* Produce "indefinite" */
+ /* Produce the QNaN "indefinite" */
RE_ENTRANT_CHECK_OFF
verify_area(VERIFY_WRITE,d,10);
- put_fs_byte(0xff,(unsigned char *) d+7);
+ put_fs_byte(0xff,(unsigned char *) d+7); /* This byte undefined */
put_fs_byte(0xff,(unsigned char *) d+8);
put_fs_byte(0xff,(unsigned char *) d+9);
RE_ENTRANT_CHECK_ON
@@ -1105,6 +1056,11 @@ put_indefinite:
else
return 0;
}
+ else if ( precision_loss )
+ {
+ if ( set_precision_flag(precision_loss) )
+ return 0;
+ }
verify_area(VERIFY_WRITE,d,10);
for ( i = 0; i < 9; i++)
@@ -1132,7 +1088,6 @@ put_indefinite:
/* 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 */
-/* The value returned in eax is never actually needed :-) */
int round_to_int(FPU_REG *r)
{
char very_big;
@@ -1164,7 +1119,7 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
- return LOST_UP;
+ return PRECISION_LOST_UP;
}
break;
case RC_DOWN:
@@ -1172,7 +1127,7 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
- return LOST_UP;
+ return PRECISION_LOST_UP;
}
break;
case RC_UP:
@@ -1180,14 +1135,14 @@ int round_to_int(FPU_REG *r)
{
if ( very_big ) return 1; /* overflow */
(*(long long *)(&r->sigl)) ++;
- return LOST_UP;
+ return PRECISION_LOST_UP;
}
break;
case RC_CHOP:
break;
}
- return eax ? LOST_DOWN : 0;
+ return eax ? PRECISION_LOST_DOWN : 0;
}
@@ -1202,7 +1157,7 @@ char *fldenv(void)
RE_ENTRANT_CHECK_OFF
control_word = get_fs_word((unsigned short *) s);
- status_word = get_fs_word((unsigned short *) (s+4));
+ partial_status = get_fs_word((unsigned short *) (s+4));
tag_word = get_fs_word((unsigned short *) (s+8));
ip_offset = get_fs_long((unsigned long *) (s+0x0c));
cs_selector = get_fs_long((unsigned long *) (s+0x10));
@@ -1210,32 +1165,50 @@ char *fldenv(void)
operand_selector = get_fs_long((unsigned long *) (s+0x18));
RE_ENTRANT_CHECK_ON
- top = (status_word >> SW_Top_Shift) & 7;
+ top = (partial_status >> SW_Top_Shift) & 7;
+
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
+ else
+ partial_status &= ~(SW_Summary | SW_Backward);
for ( i = 0; i < 8; i++ )
{
tag = tag_word & 3;
tag_word >>= 2;
- switch ( tag )
+ if ( tag == 3 )
+ /* New tag is empty. Accept it */
+ regs[i].tag = TW_Empty;
+ else if ( regs[i].tag == TW_Empty )
{
- case 0:
- regs[i].tag = TW_Valid;
- break;
- case 1:
- regs[i].tag = TW_Zero;
- break;
- case 2:
- regs[i].tag = TW_NaN;
- break;
- case 3:
- regs[i].tag = TW_Empty;
- break;
- }
+ /* Old tag is empty and new tag is not empty. New tag is determined
+ by old reg contents */
+ if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
+ {
+ if ( !(regs[i].sigl | regs[i].sigh) )
+ regs[i].tag = TW_Zero;
+ else
+ regs[i].tag = TW_Valid;
+ }
+ else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
+ {
+ if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
+ regs[i].tag = TW_Infinity;
+ else
+ regs[i].tag = TW_NaN;
+ }
+ else
+ regs[i].tag = TW_Valid;
+ }
+ /* Else old tag is not empty and new tag is not empty. Old tag
+ remains correct */
}
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
- FPU_entry_eip = ip_offset; /* We want no net effect */
+ /* Ensure that the values just loaded are not changed by
+ fix-up operations. */
+ NO_NET_DATA_EFFECT;
+ NO_NET_INSTR_EFFECT;
return s + 0x1c;
}
@@ -1245,35 +1218,24 @@ void frstor(void)
{
int i, stnr;
unsigned char tag;
- unsigned short saved_status, saved_control;
- char *s = (char *)fldenv();
+ char *s = 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 */
+ /* Load each register. */
FPU_data_address = (void *)(s+i*10);
reg_load_extended();
stnr = (i+top) & 7;
- tag = regs[stnr].tag; /* derived from the loaded tag word */
+ tag = regs[stnr].tag; /* Derived from the loaded tag word. */
reg_move(&FPU_loaded_data, &regs[stnr]);
- if ( 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
+ if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */
regs[stnr].tag = tag;
}
- control_word = saved_control;
- status_word = saved_status;
- FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ /* Reverse the effect which loading the registers had on the
+ data pointer */
+ NO_NET_DATA_EFFECT;
+
}
@@ -1287,9 +1249,6 @@ unsigned short tag_word(void)
{
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;
@@ -1301,7 +1260,7 @@ unsigned short tag_word(void)
case TW_Empty:
tag = 3;
break;
- /* TW_Valid and TW_Zero already have the correct value */
+ /* TW_Zero already has the correct value */
}
word <<= 2;
word |= tag;
@@ -1316,21 +1275,30 @@ char *fstenv(void)
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
+#ifdef PECULIAR_486
+ /* An 80486 sets all the reserved bits to 1. */
+ put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
+ put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
+ put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
+#else
put_fs_word(control_word, (unsigned short *) d);
- put_fs_word((status_word & ~SW_Top) | ((top&7) << SW_Top_Shift),
- (unsigned short *) (d+4));
+ put_fs_word(status_word(), (unsigned short *) (d+4));
put_fs_word(tag_word(), (unsigned short *) (d+8));
+#endif PECULIAR_486
put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
- put_fs_long(cs_selector, (unsigned long *) (d+0x10));
+ put_fs_long(cs_selector & ~0xf8000000, (unsigned long *) (d+0x10));
put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
+#ifdef PECULIAR_486
+ /* An 80486 sets all the reserved bits to 1. */
+ put_fs_long(0xffff0000 | operand_selector, (unsigned long *) (d+0x18));
+#else
put_fs_long(operand_selector, (unsigned long *) (d+0x18));
+#endif PECULIAR_486
RE_ENTRANT_CHECK_ON
+
+ control_word |= CW_Exceptions;
+ partial_status &= ~(SW_Summary | SW_Backward);
return d + 0x1c;
}
@@ -1339,101 +1307,79 @@ char *fstenv(void)
void fsave(void)
{
char *d;
- FPU_REG tmp, *rp;
int i;
- short e;
d = fstenv();
verify_area(VERIFY_WRITE,d,80);
for ( i = 0; i < 8; i++ )
- {
- /* Store each register in the order: st(0), st(1), ... */
- rp = &regs[(top+i) & 7];
-
- e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
-
- if ( rp->tag == TW_Valid )
- {
- if ( e >= 0x7fff )
- {
- /* Overflow to infinity */
- RE_ENTRANT_CHECK_OFF
- 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 > -63 )
- {
- /* Make a de-normal */
- reg_move(rp, &tmp);
- tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
- round_to_int(&tmp);
- RE_ENTRANT_CHECK_OFF
- 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));
- put_fs_long(0, (unsigned long *) (d+i*10+4));
- RE_ENTRANT_CHECK_ON
- }
- e = 0;
- }
- else
- {
- RE_ENTRANT_CHECK_OFF
- 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));
- 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));
- 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));
- put_fs_long(rp->sigh, (unsigned long *) (d+i*10+4));
- RE_ENTRANT_CHECK_ON
- e = 0x7fff;
- }
- else if ( rp->tag == TW_Empty )
- {
- /* just copy the reg */
- RE_ENTRANT_CHECK_OFF
- 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+8));
- RE_ENTRANT_CHECK_ON
- }
+ write_to_extended(&regs[(top + i) & 7], d + 10 * i);
finit();
}
/*===========================================================================*/
+
+/*
+ A call to this function must be preceeded by a call to
+ verify_area() to verify access to the 10 bytes at d
+ */
+static void write_to_extended(FPU_REG *rp, char *d)
+{
+ long e;
+ FPU_REG tmp;
+
+ e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
+
+#ifdef PARANOID
+ switch ( rp->tag )
+ {
+ case TW_Zero:
+ if ( rp->sigh | rp->sigl | e )
+ EXCEPTION(EX_INTERNAL | 0x114);
+ break;
+ case TW_Infinity:
+ case TW_NaN:
+ if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
+ EXCEPTION(EX_INTERNAL | 0x114);
+ break;
+ default:
+ if (e > 0x7fff || e < -63)
+ EXCEPTION(EX_INTERNAL | 0x114);
+ }
+#endif PARANOID
+
+ /*
+ All numbers except denormals are stored internally in a
+ format which is compatible with the extended real number
+ format.
+ */
+ if ( e > 0 )
+ {
+ /* just copy the reg */
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_long(rp->sigl, (unsigned long *) d);
+ put_fs_long(rp->sigh, (unsigned long *) (d + 4));
+ RE_ENTRANT_CHECK_ON;
+ }
+ else
+ {
+ /*
+ The number is a de-normal stored as a normal using our
+ extra exponent range, or is Zero.
+ Convert it back to a de-normal, or leave it as Zero.
+ */
+ reg_move(rp, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */
+ round_to_int(&tmp);
+ e = 0;
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_long(tmp.sigl, (unsigned long *) d);
+ put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
+ RE_ENTRANT_CHECK_ON;
+ }
+ e |= rp->sign == SIGN_POS ? 0 : 0x8000;
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_word(e, (unsigned short *) (d + 8));
+ RE_ENTRANT_CHECK_ON;
+}
diff --git a/kernel/FPU-emu/reg_mul.c b/kernel/FPU-emu/reg_mul.c
index 67a08c1..a7d48ad 100644
--- a/kernel/FPU-emu/reg_mul.c
+++ b/kernel/FPU-emu/reg_mul.c
@@ -21,16 +21,21 @@
/* 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)
+int reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
{
+ char saved_sign = dest->sign;
char sign = (a->sign ^ b->sign);
if (!(a->tag | b->tag))
{
- /* This should be the most common case */
- reg_u_mul(a, b, dest, control_w);
+ /* Both regs Valid, this should be the most common case. */
dest->sign = sign;
- return;
+ if ( reg_u_mul(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
}
else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
{
@@ -38,7 +43,7 @@ void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
{
- if ( denormal_operand() ) return;
+ if ( denormal_operand() ) return 1;
}
#endif DENORM_OPERAND
/* Must have either both arguments == zero, or
@@ -50,58 +55,50 @@ void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w)
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 */
- /* Internal de-normalized numbers are not supported yet */
- EXCEPTION(EX_INTERNAL|0x105);
- reg_move(&CONST_Z, dest);
+ return 0;
}
-#endif
else
{
/* Must have infinities, NaNs, etc */
if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { real_2op_NaN(a, b, dest); return; }
+ { return real_2op_NaN(a, b, dest); }
else if (a->tag == TW_Infinity)
{
if (b->tag == TW_Zero)
- { arith_invalid(dest); return; } /* Zero*Infinity is invalid */
+ { return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(a, dest);
dest->sign = sign;
}
- return;
+ return 0;
}
else if (b->tag == TW_Infinity)
{
if (a->tag == TW_Zero)
- { arith_invalid(dest); return; } /* Zero*Infinity is invalid */
+ { return arith_invalid(dest); } /* Zero*Infinity is invalid */
else
{
#ifdef DENORM_OPERAND
if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
denormal_operand() )
- return;
+ return 1;
#endif DENORM_OPERAND
reg_move(b, dest);
dest->sign = sign;
}
- return;
+ return 0;
}
#ifdef PARANOID
else
{
EXCEPTION(EX_INTERNAL|0x102);
+ return 1;
}
#endif PARANOID
}
diff --git a/kernel/FPU-emu/reg_round.S b/kernel/FPU-emu/reg_round.S
index 4097606..7c338c2 100644
--- a/kernel/FPU-emu/reg_round.S
+++ b/kernel/FPU-emu/reg_round.S
@@ -16,6 +16,9 @@
| From C, call as: |
| void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
| |
+ | For correct "up" and "down" rounding, the argument must have the correct |
+ | sign. |
+ | |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
@@ -70,8 +73,11 @@
#include "exception.h"
#include "control_w.h"
+/* Flags for FPU_bits_lost */
#define LOST_DOWN $1
#define LOST_UP $2
+
+/* Flags for FPU_denormal */
#define DENORMAL $1
#define UNMASKED_UNDERFLOW $2
@@ -345,9 +351,9 @@ LDo_64_round_up:
adcl $0,%eax
LCheck_Round_Overflow:
- jnc LRe_normalise /* Rounding done, no overflow */
+ jnc LRe_normalise
- /* Overflow, adjust the result (to 1.0) */
+ /* Overflow, adjust the result (significand to 1.0) */
rcrl $1,%eax
rcrl $1,%ebx
incl EXP(%edi)
@@ -372,9 +378,6 @@ xL_Normalised:
je xL_precision_lost_down
xL_no_precision_loss:
- cmpl EXP_OVER,EXP(%edi)
- jge L_overflow
-
/* store the result */
movb TW_Valid,TAG(%edi)
@@ -382,6 +385,11 @@ xL_Store_significand:
movl %eax,SIGH(%edi)
movl %ebx,SIGL(%edi)
+ xorl %eax,%eax // No errors detected.
+
+ cmpl EXP_OVER,EXP(%edi)
+ jge L_overflow
+
FPU_Arith_exit:
popl %ebx
popl %edi
@@ -419,9 +427,8 @@ xMake_denorm:
movb DENORMAL,FPU_denormal
pushl %ecx // Save
- movl EXP(%edi),%ecx
- subl EXP_UNDER+1,%ecx
- negl %ecx
+ movl EXP_UNDER+1,%ecx
+ subl EXP(%edi),%ecx
cmpl $64,%ecx /* shrd only works for 0..31 bits */
jnc xDenorm_shift_more_than_63
@@ -434,7 +441,7 @@ xMake_denorm:
// Shift by [1..31] bits
addl %ecx,EXP(%edi)
orl %edx,%edx // extension
- setne %ch
+ setne %ch // Save whether %edx is non-zero
xorl %edx,%edx
shrd %cl,%ebx,%edx
shrd %cl,%eax,%ebx
@@ -495,8 +502,6 @@ xDenorm_shift_more_than_64:
xUnmasked_underflow:
- // Increase the exponent by the magic number
- addl $(3*(1<<13)),EXP(%edi)
movb UNMASKED_UNDERFLOW,FPU_denormal
jmp xDenorm_done
@@ -513,7 +518,20 @@ xNormalise_result:
jne L_norm_bugged
#endif PARANOID
- orl %eax,%eax // ms bits
+#ifdef PECULIAR_486
+ // This implements a special feature of 80486 behaviour.
+ // Underflow will be signalled even if the number is
+ // not a denormal after rounding.
+ // This difference occurs only for masked underflow, and not
+ // in the unmasked case.
+ // Actual 80486 behaviour differs from this in some circumstances.
+ orl %eax,%eax // ms bits
+ js LNormalise_shift_done // Will be masked underflow
+#endif PECULIAR_486
+
+ orl %eax,%eax // ms bits
+ js xL_Normalised // No longer a denormal
+
jnz LNormalise_shift_up_to_31 // Shift left 0 - 31 bits
orl %ebx,%ebx
@@ -558,6 +576,8 @@ L_underflow_to_zero:
popl %eax
popl %eax
+// Reduce the exponent to EXP_UNDER
+ movl EXP_UNDER,EXP(%edi)
movb TW_Zero,TAG(%edi)
jmp xL_Store_significand
@@ -571,6 +591,16 @@ L_overflow:
xSignal_underflow:
+ // The number may have been changed to a non-denormal
+ // by the rounding operations.
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xDo_unmasked_underflow
+
+ jmp xL_Normalised
+
+xDo_unmasked_underflow:
+ // Increase the exponent by the magic number
+ addl $(3*(1<<13)),EXP(%edi)
push %eax
pushl EX_Underflow
call EXCEPTION
@@ -585,17 +615,19 @@ L_bugged:
pushl EX_INTERNAL|0x201
call EXCEPTION
popl %ebx
- jmp FPU_Arith_exit
+ jmp L_exception_exit
L_norm_bugged:
pushl EX_INTERNAL|0x216
call EXCEPTION
popl %ebx
- jmp FPU_Arith_exit
+ jmp L_exception_exit
L_entry_bugged:
pushl EX_INTERNAL|0x217
call EXCEPTION
popl %ebx
+L_exception_exit:
+ mov $1,%eax
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 3aca1bc..fbcffa3 100644
--- a/kernel/FPU-emu/reg_u_add.S
+++ b/kernel/FPU-emu/reg_u_add.S
@@ -82,8 +82,8 @@ L_arg1_larger:
L_accum_loaded:
movl PARAM3,%edi /* destination */
- movb SIGN(%esi),%dl
- movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+// movb SIGN(%esi),%dl
+// movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
movl EXP(%esi),%edx
diff --git a/kernel/FPU-emu/reg_u_sub.S b/kernel/FPU-emu/reg_u_sub.S
index 88f7f5c..191f48a 100644
--- a/kernel/FPU-emu/reg_u_sub.S
+++ b/kernel/FPU-emu/reg_u_sub.S
@@ -61,13 +61,11 @@ xOp1_not_denorm:
xOp2_not_denorm:
#endif DENORM_OPERAND
-// xorl %ecx,%ecx
movl EXP(%esi),%ecx
subl EXP(%edi),%ecx /* exp1 - exp2 */
#ifdef PARANOID
/* source 2 is always smaller than source 1 */
-// jc L_bugged
js L_bugged_1
testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
@@ -87,8 +85,8 @@ xOp2_not_denorm:
movl PARAM3,%edi /* destination */
movl EXP(%esi),%edx
movl %edx,EXP(%edi) /* Copy exponent to destination */
- movb SIGN(%esi),%dl
- movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
+// movb SIGN(%esi),%dl
+// movb %dl,SIGN(%edi) /* Copy the sign from the first arg */
xorl %edx,%edx // register extension
@@ -213,8 +211,8 @@ L_subtr:
/* Shift left 64 bits */
subl $64,EXP(%edi)
- movl %edx,%eax
- jmp L_store
+ xchg %edx,%eax
+ jmp FPU_round
L_must_be_zero:
#ifdef PARANOID
@@ -227,7 +225,7 @@ L_must_be_zero:
movl $0,EXP(%edi) /* exponent */
movl $0,SIGL(%edi)
movl $0,SIGH(%edi)
- jmp L_exit // Does not underflow
+ jmp L_exit // %eax contains zero
L_shift_32:
movl %ebx,%eax
@@ -254,57 +252,39 @@ L_bugged_1:
pushl EX_INTERNAL|0x206
call EXCEPTION
pop %ebx
- jmp L_exit
+ jmp L_error_exit
L_bugged_2:
pushl EX_INTERNAL|0x209
call EXCEPTION
pop %ebx
- jmp L_exit
+ jmp L_error_exit
L_bugged_3:
pushl EX_INTERNAL|0x210
call EXCEPTION
pop %ebx
- jmp L_exit
+ jmp L_error_exit
L_bugged_4:
pushl EX_INTERNAL|0x211
call EXCEPTION
pop %ebx
- jmp L_exit
+ jmp L_error_exit
L_bugged:
pushl EX_INTERNAL|0x212
call EXCEPTION
pop %ebx
- jmp L_exit
+ jmp L_error_exit
#endif PARANOID
-L_store:
-/*------------------------------+
- | Store the result |
- +------------------------------*/
- movl %eax,SIGH(%edi)
- movl %ebx,SIGL(%edi)
-
- movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
-
- cmpl EXP_UNDER,EXP(%edi)
- jle L_underflow
-
+L_error_exit:
+ movl $1,%eax
L_exit:
popl %ebx
popl %edi
popl %esi
leave
ret
-
-
-L_underflow:
- push %edi
- call _arith_underflow
- pop %ebx
- jmp L_exit
-
diff --git a/kernel/FPU-emu/status_w.h b/kernel/FPU-emu/status_w.h
index 00e291f..0e23d6c 100644
--- a/kernel/FPU-emu/status_w.h
+++ b/kernel/FPU-emu/status_w.h
@@ -45,9 +45,14 @@
#define COMP_NaN 0x40
#define COMP_SNaN 0x80
+#define status_word() \
+ ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
#define setcc(cc) ({ \
- status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
- status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+ partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+ partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+/* Clear the SW_C1 bit, "other bits undefined" */
+#define clear_C1() { partial_status &= ~SW_C1; }
#endif __ASSEMBLER__
diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h
index b0b1fe6..fd562ce 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.4"
+#define FPU_VERSION "wm-FPU-emu version BETA 1.5"
diff --git a/kernel/Makefile b/kernel/Makefile
index 1009703..7c9637a 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -21,7 +21,7 @@ SUBDIRS = chr_drv blk_drv FPU-emu
OBJS = sched.o sys_call.o traps.o irq.o dma.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o ptrace.o ioport.o itimer.o \
- info.o
+ info.o ldt.o
all: kernel.o kernelsubdirs
diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile
index 621e6f7..3cf68b3 100644
--- a/kernel/blk_drv/Makefile
+++ b/kernel/blk_drv/Makefile
@@ -18,7 +18,7 @@
SUBDIRS = scsi
-OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o
+OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o cdu31a.o mcd.o
all: blk_drv.a scsisubdirs
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index 9167318..2e4f158 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -79,6 +79,8 @@ extern int * blk_size[MAX_BLKDEV];
extern int * blksize_size[MAX_BLKDEV];
extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
extern int is_read_only(int dev);
extern void set_device_ro(int dev,int flag);
@@ -168,6 +170,23 @@ static void floppy_off(unsigned int nr);
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
+#elif (MAJOR_NR == 15)
+/* CDU31A CD-ROM */
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == 23)
+/* MITSUMI CD-ROM */
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
#else
/* unknown blk device */
#error "unknown blk device"
diff --git a/kernel/blk_drv/cdu31a.c b/kernel/blk_drv/cdu31a.c
new file mode 100644
index 0000000..0267039
--- /dev/null
+++ b/kernel/blk_drv/cdu31a.c
@@ -0,0 +1,1705 @@
+/*
+ * Sony CDU-31A CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h). With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunatly) a polled interface. This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables. Some (like mine) do not even have the capability to
+ * handle interrupts or DMA. For this reason you will see a lot of
+ * the following:
+ *
+ * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ * while ((retry_count > jiffies) && (! <some condition to wait for))
+ * {
+ * while (handle_sony_cd_attention())
+ * ;
+ *
+ * sony_sleep();
+ * }
+ * if (the condition not met)
+ * {
+ * return an error;
+ * }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try. it also handles attentions, which are
+ * asyncronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk. The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal. A lot of conversion goes on.
+ *
+ * Copyright (C) 1993 Corey Minyard
+ *
+ * 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.
+ *
+ * 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>
+#ifdef CONFIG_CDU31A
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+#include <linux/cdu31a.h>
+
+#define MAJOR_NR 15
+#include "blk.h"
+
+
+static unsigned short cdu31a_addresses[] =
+{
+ 0x340, /* Standard configuration Sony Interface */
+ 0x1f88, /* Fusion CD-16 */
+ 0x360, /* Secondary standard Sony Interface */
+ 0x320, /* Secondary standard Sony Interface */
+ 0x330, /* Secondary standard Sony Interface */
+ 0
+};
+
+
+static int handle_sony_cd_attention(void);
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int scd_open(struct inode *inode, struct file *filp);
+
+
+/* The base I/O address of the Sony Interface. This is a variable (not a
+ #define) so it can be easily changed via some future ioctl() */
+static unsigned short sony_cd_base_io = 0;
+
+/*
+ * The following are I/O addresses of the various registers for the drive. The
+ * comment for the base address also applies here.
+ */
+static volatile unsigned short sony_cd_cmd_reg;
+static volatile unsigned short sony_cd_param_reg;
+static volatile unsigned short sony_cd_write_reg;
+static volatile unsigned short sony_cd_control_reg;
+static volatile unsigned short sony_cd_status_reg;
+static volatile unsigned short sony_cd_result_reg;
+static volatile unsigned short sony_cd_read_reg;
+static volatile unsigned short sony_cd_fifost_reg;
+
+
+static int initialized = 0; /* Has the drive been initialized? */
+static int sony_disc_changed = 1; /* Has the disk been changed
+ since the last check? */
+static int sony_toc_read = 0; /* Has the table of contents been
+ read? */
+static int sony_spun_up = 0; /* Has the drive been spun up? */
+static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead
+ buffer. */
+static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of
+ the read-ahead buffer. */
+static unsigned int sony_usage = 0; /* How many processes have the
+ drive open. */
+
+static volatile int sony_first_block = -1; /* First OS block (512 byte) in
+ the read-ahead buffer */
+static volatile int sony_last_block = -1; /* Last OS block (512 byte) in
+ the read-ahead buffer */
+
+static struct s_sony_toc *sony_toc; /* Points to the table of
+ contents. */
+static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
+ subcode address read */
+static unsigned char * volatile sony_buffer; /* Points to the read-ahead
+ buffer */
+
+static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation at a time
+ allowed */
+
+static struct wait_queue * sony_wait = NULL;
+
+static struct task_struct *has_cd_task = NULL; /* The task that is currently using the
+ CDROM drive, or NULL if none. */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play. The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over. This holds the
+ * position during a pause so a resume can restart it. It uses the
+ * audio status variable above to tell if it is paused.
+ */
+unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
+unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't. Setting flag to 0 resets the changed flag.
+ */
+int
+check_cdu31a_media_change(int full_dev, int flag)
+{
+ int retval, target;
+
+
+ target = MINOR(full_dev);
+
+ if (target > 0) {
+ printk("Sony CD-ROM request error: invalid device.\n");
+ return 0;
+ }
+
+ retval = sony_disc_changed;
+ if (!flag)
+ {
+ sony_disc_changed = 0;
+ }
+
+ return retval;
+}
+
+
+/*
+ * Wait a little while (used for polling the drive). If in initialization,
+ * setting a timeout doesn't work, so just loop for a while.
+ */
+static inline void
+sony_sleep(void)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies;
+ schedule();
+}
+
+
+/*
+ * The following are convenience routine to read various status and set
+ * various conditions in the drive.
+ */
+static inline int
+is_attention(void)
+{
+ return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
+}
+
+static inline int
+is_busy(void)
+{
+ return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
+}
+
+static inline int
+is_data_ready(void)
+{
+ return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
+}
+
+static inline int
+is_data_requested(void)
+{
+ return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
+}
+
+static inline int
+is_result_ready(void)
+{
+ return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
+}
+
+static inline int
+is_param_write_rdy(void)
+{
+ return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
+}
+
+static inline void
+reset_drive(void)
+{
+ outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_attention(void)
+{
+ outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_result_ready(void)
+{
+ outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_data_ready(void)
+{
+ outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline void
+clear_param_reg(void)
+{
+ outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg);
+}
+
+static inline unsigned char
+read_status_register(void)
+{
+ return(inb(sony_cd_status_reg));
+}
+
+static inline unsigned char
+read_result_register(void)
+{
+ return(inb(sony_cd_result_reg));
+}
+
+static inline unsigned char
+read_data_register(void)
+{
+ return(inb(sony_cd_read_reg));
+}
+
+static inline void
+write_param(unsigned char param)
+{
+ outb(param, sony_cd_param_reg);
+}
+
+static inline void
+write_cmd(unsigned char cmd)
+{
+ outb(cmd, sony_cd_cmd_reg);
+ outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
+}
+
+
+/*
+ * This routine writes data to the parameter register. Since this should
+ * happen fairly fast, it is polled with no OS waits between.
+ */
+static int
+write_params(unsigned char *params,
+ int num_params)
+{
+ unsigned int retry_count;
+
+
+ retry_count = SONY_READY_RETRIES;
+ while ((retry_count > 0) && (!is_param_write_rdy()))
+ {
+ retry_count--;
+ }
+ if (!is_param_write_rdy())
+ {
+ return -EIO;
+ }
+
+ while (num_params > 0)
+ {
+ write_param(*params);
+ params++;
+ num_params--;
+ }
+
+ return 0;
+}
+
+
+/*
+ * The following reads data from the command result register. It is a
+ * fairly complex routine, all status info flows back through this
+ * interface. The algorithm is stolen directly from the flowcharts in
+ * the drive manual.
+ */
+static void
+get_result(unsigned char *result_buffer,
+ unsigned int *result_size)
+{
+ unsigned char a, b;
+ int i;
+ unsigned int retry_count;
+
+
+ while (handle_sony_cd_attention())
+ ;
+ /* Wait for the result data to be ready */
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
+ {
+ sony_sleep();
+
+ while (handle_sony_cd_attention())
+ ;
+ }
+ if (is_busy() || (!(is_result_ready())))
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ return;
+ }
+
+ /*
+ * Get the first two bytes. This determines what else needs
+ * to be done.
+ */
+ clear_result_ready();
+ a = read_result_register();
+ *result_buffer = a;
+ result_buffer++;
+ b = read_result_register();
+ *result_buffer = b;
+ result_buffer++;
+ *result_size = 2;
+
+ /*
+ * 0x20 means an error occured. Byte 2 will have the error code.
+ * Otherwise, the command succeded, byte 2 will have the count of
+ * how many more status bytes are coming.
+ *
+ * The result register can be read 10 bytes at a time, a wait for
+ * result ready to be asserted must be done between every 10 bytes.
+ */
+ if ((a & 0xf0) != 0x20)
+ {
+ if (b > 8)
+ {
+ for (i=0; i<8; i++)
+ {
+ *result_buffer = read_result_register();
+ result_buffer++;
+ (*result_size)++;
+ }
+ b = b - 8;
+
+ while (b > 10)
+ {
+ retry_count = SONY_READY_RETRIES;
+ while ((retry_count > 0) && (!is_result_ready()))
+ {
+ retry_count--;
+ }
+ if (!is_result_ready())
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ return;
+ }
+
+ clear_result_ready();
+
+ for (i=0; i<10; i++)
+ {
+ *result_buffer = read_result_register();
+ result_buffer++;
+ (*result_size)++;
+ }
+ b = b - 10;
+ }
+
+ if (b > 0)
+ {
+ retry_count = SONY_READY_RETRIES;
+ while ((retry_count > 0) && (!is_result_ready()))
+ {
+ retry_count--;
+ }
+ if (!is_result_ready())
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ return;
+ }
+ }
+ }
+
+ while (b > 0)
+ {
+ *result_buffer = read_result_register();
+ result_buffer++;
+ (*result_size)++;
+ b--;
+ }
+ }
+}
+
+
+/*
+ * This routine issues a read data command and gets the data. I don't
+ * really like the way this is done (I would prefer for do_sony_cmd() to
+ * handle it automatically) but I found that the drive returns status
+ * when it finishes reading (not when the host has read all the data)
+ * or after it gets an error. This means that the status can be
+ * received at any time and should be handled immediately (at least
+ * between every 2048 byte block) to check for errors, we can't wait
+ * until all the data is read.
+ */
+static void
+get_data(unsigned char *data,
+ unsigned char *params, /* 6 bytes with the MSF start address
+ and number of sectors to read. */
+ unsigned int data_size,
+ unsigned char *result_buffer,
+ unsigned int *result_size)
+{
+ int i;
+ unsigned int cur_offset;
+ unsigned int retry_count;
+ int result_read;
+ int num_retries;
+
+
+ cli();
+ if (current != has_cd_task) /* Allow recursive calls to this routine */
+ {
+ while (sony_inuse)
+ {
+ interruptible_sleep_on(&sony_wait);
+ if (current->signal & ~current->blocked)
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_SIGNAL_OP_ERR;
+ *result_size = 2;
+ return;
+ }
+ }
+ }
+ sony_inuse = 1;
+ has_cd_task = current;
+ sti();
+
+ num_retries = 0;
+retry_data_operation:
+
+ /*
+ * Clear any outstanding attentions and wait for the drive to
+ * complete any pending operations.
+ */
+ while (handle_sony_cd_attention())
+ ;
+
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while ((retry_count > jiffies) && (is_busy()))
+ {
+ sony_sleep();
+
+ while (handle_sony_cd_attention())
+ ;
+ }
+ if (is_busy())
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ goto get_data_end;
+ }
+
+ /* Issue the command */
+ clear_result_ready();
+ clear_param_reg();
+
+ write_params(params, 6);
+ write_cmd(SONY_READ_CMD);
+
+ /*
+ * Read the data from the drive one 2048 byte sector at a time. Handle
+ * any results received between sectors, if an error result is returned
+ * terminate the operation immediately.
+ */
+ cur_offset = 0;
+ result_read = 0;
+ while (data_size > 0)
+ {
+ /* Wait for the drive to tell us we have something */
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
+ {
+ while (handle_sony_cd_attention())
+ ;
+
+ sony_sleep();
+ }
+ if (!(is_result_ready() || is_data_ready()))
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ goto get_data_end;
+ }
+
+ /* Handle results first */
+ if (is_result_ready())
+ {
+ result_read = 1;
+ get_result(result_buffer, result_size);
+ if ((*result_size < 2) || (result_buffer[0] != 0))
+ {
+ goto get_data_end;
+ }
+ }
+ else /* Handle data next */
+ {
+ /*
+ * The drive has to be polled for status on a byte-by-byte basis
+ * to know if the data is ready. Yuck. I really wish I could use DMA.
+ */
+ clear_data_ready();
+ for (i=0; i<2048; i++)
+ {
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while ((retry_count > jiffies) && (!is_data_requested()))
+ {
+ while (handle_sony_cd_attention())
+ ;
+
+ sony_sleep();
+ }
+ if (!is_data_requested())
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ goto get_data_end;
+ }
+
+ *data = read_data_register();
+ data++;
+ data_size--;
+ }
+ cur_offset = cur_offset + 2048;
+ }
+ }
+
+ /* Make sure the result has been read */
+ if (!result_read)
+ {
+ get_result(result_buffer, result_size);
+ }
+
+get_data_end:
+ if ( ((result_buffer[0] & 0x20) == 0x20)
+ && (num_retries < MAX_CDU31A_RETRIES))
+ {
+ num_retries++;
+ goto retry_data_operation;
+ }
+
+ has_cd_task = NULL;
+ sony_inuse = 0;
+ wake_up_interruptible(&sony_wait);
+}
+
+
+/*
+ * Do a command that does not involve data transfer.
+ */
+static void
+do_sony_cd_cmd(unsigned char cmd,
+ unsigned char *params,
+ unsigned int num_params,
+ unsigned char *result_buffer,
+ unsigned int *result_size)
+{
+ unsigned int retry_count;
+ int num_retries;
+
+
+ cli();
+ if (current != has_cd_task) /* Allow recursive calls to this routine */
+ {
+ while (sony_inuse)
+ {
+ interruptible_sleep_on(&sony_wait);
+ if (current->signal & ~current->blocked)
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_SIGNAL_OP_ERR;
+ *result_size = 2;
+ return;
+ }
+ }
+ }
+ sony_inuse = 1;
+ has_cd_task = current;
+ sti();
+
+ num_retries = 0;
+retry_cd_operation:
+
+ while (handle_sony_cd_attention())
+ ;
+
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while ((retry_count > jiffies) && (is_busy()))
+ {
+ sony_sleep();
+
+ while (handle_sony_cd_attention())
+ ;
+ }
+ if (is_busy())
+ {
+ result_buffer[0] = 0x20;
+ result_buffer[1] = SONY_TIMEOUT_OP_ERR;
+ *result_size = 2;
+ goto do_cmd_end;
+ }
+
+ clear_result_ready();
+ clear_param_reg();
+
+ write_params(params, num_params);
+ write_cmd(cmd);
+
+ get_result(result_buffer, result_size);
+
+do_cmd_end:
+ if ( ((result_buffer[0] & 0x20) == 0x20)
+ && (num_retries < MAX_CDU31A_RETRIES))
+ {
+ num_retries++;
+ goto retry_cd_operation;
+ }
+
+ has_cd_task = NULL;
+ sony_inuse = 0;
+ wake_up_interruptible(&sony_wait);
+}
+
+
+/*
+ * Handle an attention from the drive. This will return 1 if it found one
+ * or 0 if not (if one is found, the caller might want to call again).
+ */
+static int
+handle_sony_cd_attention(void)
+{
+ unsigned char atten_code;
+ unsigned char res_reg[2];
+ unsigned int res_size;
+
+
+ if (is_attention())
+ {
+ clear_attention();
+ atten_code = read_result_register();
+
+ switch (atten_code)
+ {
+ /* Someone changed the CD. Mark it as changed */
+ case SONY_MECH_LOADED_ATTN:
+ sony_disc_changed = 1;
+ sony_toc_read = 0;
+ sony_audio_status = CDROM_AUDIO_NO_STATUS;
+ sony_first_block = -1;
+ sony_last_block = -1;
+ if (initialized)
+ {
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+ sony_get_toc();
+ }
+ break;
+
+ case SONY_AUDIO_PLAY_DONE_ATTN:
+ sony_audio_status = CDROM_AUDIO_COMPLETED;
+ read_subcode();
+ break;
+
+ case SONY_EJECT_PUSHED_ATTN:
+ sony_audio_status = CDROM_AUDIO_INVALID;
+ break;
+
+ case SONY_LEAD_IN_ERR_ATTN:
+ case SONY_LEAD_OUT_ERR_ATTN:
+ case SONY_DATA_TRACK_ERR_ATTN:
+ case SONY_AUDIO_PLAYBACK_ERR_ATTN:
+ sony_audio_status = CDROM_AUDIO_ERROR;
+ break;
+ }
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+ int retval;
+
+
+ retval = (val / 10) << 4;
+ retval = retval | val % 10;
+ return(retval);
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+ return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, unsigned char *msf)
+{
+ log = log + LOG_START_OFFSET;
+ msf[0] = int_to_bcd(log / 4500);
+ log = log % 4500;
+ msf[1] = int_to_bcd(log / 75);
+ msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(unsigned char *msf)
+{
+ unsigned int log;
+
+
+ log = bcd_to_int(msf[2]);
+ log += bcd_to_int(msf[1]) * 75;
+ log += bcd_to_int(msf[0]) * 4500;
+ log = log - LOG_START_OFFSET;
+
+ return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size,
+ unsigned char *buf)
+{
+ buf[0] = size / 65536;
+ size = size % 65536;
+ buf[1] = size / 256;
+ buf[2] = size % 256;
+}
+
+
+/*
+ * The OS calls this to perform a read or write operation to the drive.
+ * Write obviously fail. Reads to a read ahead of sony_buffer_size
+ * bytes to help speed operations. This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu31a_request(void)
+{
+ int block;
+ unsigned int dev;
+ int nsect;
+ unsigned char params[10];
+ unsigned char res_reg[2];
+ unsigned int res_size;
+ int copyoff;
+ int spin_up_retry;
+ unsigned int read_size;
+
+
+ if (!sony_spun_up)
+ {
+ scd_open (NULL,NULL);
+ }
+
+ while (1)
+ {
+ /*
+ * The beginning here is stolen from the hard disk driver. I hope
+ * its right.
+ */
+ if (!(CURRENT) || CURRENT->dev < 0)
+ {
+ return;
+ }
+
+ INIT_REQUEST;
+ dev = MINOR(CURRENT->dev);
+ block = CURRENT->sector;
+ nsect = CURRENT->nr_sectors;
+ if (dev != 0)
+ {
+ end_request(0);
+ continue;
+ }
+
+ switch(CURRENT->cmd)
+ {
+ case READ:
+ /*
+ * If the block address is invalid or the request goes beyond the end of
+ * the media, return an error.
+ */
+ if ((block / 4) >= sony_toc->lead_out_start_lba)
+ {
+ end_request(0);
+ return;
+ }
+ if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
+ {
+ end_request(0);
+ return;
+ }
+
+ while (nsect > 0)
+ {
+ /*
+ * If the requested sector is not currently in the read-ahead buffer,
+ * it must be read in.
+ */
+ if ((block < sony_first_block) || (block > sony_last_block))
+ {
+ sony_first_block = (block / 4) * 4;
+ log_to_msf(block/4, params);
+
+ /*
+ * If the full read-ahead would go beyond the end of the media, trim
+ * it back to read just till the end of the media.
+ */
+ if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
+ {
+ sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+ read_size = sony_toc->lead_out_start_lba - (block / 4);
+ }
+ else
+ {
+ sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+ read_size = sony_buffer_sectors;
+ }
+ size_to_buf(read_size, &params[3]);
+
+ /*
+ * Read the data. If the drive was not spinning, spin it up and try
+ * once more. I know, the goto is ugly, but I am too lazy to fix it.
+ */
+ spin_up_retry = 0;
+try_read_again:
+ get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size);
+ if ((res_size < 2) || (res_reg[0] != 0))
+ {
+ if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
+ {
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+ spin_up_retry = 1;
+ goto try_read_again;
+ }
+
+ printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]);
+ sony_first_block = -1;
+ sony_last_block = -1;
+ end_request(0);
+ return;
+ }
+ }
+
+ /*
+ * The data is in memory now, copy it to the buffer and advance to the
+ * next block to read.
+ */
+ copyoff = (block - sony_first_block) * 512;
+ memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
+
+ block += 1;
+ nsect -= 1;
+ CURRENT->buffer += 512;
+ }
+
+ end_request(1);
+ break;
+
+ case WRITE:
+ end_request(0);
+ break;
+
+ default:
+ panic("Unkown SONY CD cmd");
+ }
+ }
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+ unsigned int res_size;
+
+
+ if (!sony_toc_read)
+ {
+ do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD,
+ NULL,
+ 0,
+ (unsigned char *) sony_toc,
+ &res_size);
+ if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20))
+ {
+ return;
+ }
+ sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+ sony_toc_read = 1;
+ }
+}
+
+
+/*
+ * Search for a specific track in the table of contents.
+ */
+static int
+find_track(int track)
+{
+ int i;
+ int num_tracks;
+
+
+ num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1;
+ for (i = 0; i < num_tracks; i++)
+ {
+ if (sony_toc->tracks[i].track == track)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+ unsigned int res_size;
+
+
+ do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
+ NULL,
+ 0,
+ (unsigned char *) last_sony_subcode,
+ &res_size);
+ if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
+ last_sony_subcode->exec_status[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing). If the drive is paused or completed, the subcode information has
+ * already been stored, just use that. The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+ struct cdrom_subchnl schi;
+
+
+ /* Get attention stuff */
+ while (handle_sony_cd_attention())
+ ;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ {
+ return -EIO;
+ }
+
+ verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
+ verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+
+ memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
+
+ switch (sony_audio_status)
+ {
+ case CDROM_AUDIO_PLAY:
+ if (read_subcode() < 0)
+ {
+ return -EIO;
+ }
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ case CDROM_AUDIO_COMPLETED:
+ break;
+
+ case CDROM_AUDIO_NO_STATUS:
+ schi.cdsc_audiostatus = sony_audio_status;
+ memcpy_tofs((char *) arg, &schi, sizeof(schi));
+ return 0;
+ break;
+
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_ERROR:
+ default:
+ return -EIO;
+ }
+
+ schi.cdsc_audiostatus = sony_audio_status;
+ schi.cdsc_adr = last_sony_subcode->address;
+ schi.cdsc_ctrl = last_sony_subcode->control;
+ schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
+ schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
+ if (schi.cdsc_format == CDROM_MSF)
+ {
+ schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
+ schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
+ schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
+
+ schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
+ schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
+ schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
+ }
+ else if (schi.cdsc_format == CDROM_LBA)
+ {
+ schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
+ schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
+ }
+
+ memcpy_tofs((char *) arg, &schi, sizeof(schi));
+ return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+scd_ioctl(struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned int arg)
+{
+ unsigned int dev;
+ unsigned char res_reg[2];
+ unsigned int res_size;
+ unsigned char params[7];
+ int i;
+
+
+ if (!inode)
+ {
+ return -EINVAL;
+ }
+ dev = MINOR(inode->i_rdev) >> 6;
+ if (dev != 0)
+ {
+ return -EINVAL;
+ }
+
+ switch (cmd)
+ {
+ case CDROMSTART: /* Spin up the drive */
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
+ return -EIO;
+ }
+ return 0;
+ break;
+
+ case CDROMSTOP: /* Spin down the drive */
+ do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+
+ /*
+ * Spin the drive down, ignoring the error if the disk was
+ * already not spinning.
+ */
+ sony_audio_status = CDROM_AUDIO_NO_STATUS;
+ do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+ if ( ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ && (res_reg[1] != SONY_NOT_SPIN_ERR))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
+ return -EIO;
+ }
+
+ return 0;
+ break;
+
+ case CDROMPAUSE: /* Pause the drive */
+ do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
+ return -EIO;
+ }
+
+ /* Get the current position and save it for resuming */
+ if (read_subcode() < 0)
+ {
+ return -EIO;
+ }
+ cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+ cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+ cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+ sony_audio_status = CDROM_AUDIO_PAUSED;
+ return 0;
+ break;
+
+ case CDROMRESUME: /* Start the drive after being paused */
+ if (sony_audio_status != CDROM_AUDIO_PAUSED)
+ {
+ return -EINVAL;
+ }
+
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+
+ /* Start the drive at the saved position. */
+ params[1] = cur_pos_msf[0];
+ params[2] = cur_pos_msf[1];
+ params[3] = cur_pos_msf[2];
+ params[4] = final_pos_msf[0];
+ params[5] = final_pos_msf[1];
+ params[6] = final_pos_msf[2];
+ params[0] = 0x03;
+ do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
+ return -EIO;
+ }
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ break;
+
+ case CDROMPLAYMSF: /* Play starting at the given MSF address. */
+ verify_area(VERIFY_READ, (char *) arg, 6);
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+ memcpy_fromfs(&(params[1]), (void *) arg, 6);
+
+ /* The parameters are given in int, must be converted */
+ for (i=1; i<7; i++)
+ {
+ params[i] = int_to_bcd(params[i]);
+ }
+ params[0] = 0x03;
+ do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
+ return -EIO;
+ }
+
+ /* Save the final position for pauses and resumes */
+ final_pos_msf[0] = params[4];
+ final_pos_msf[1] = params[5];
+ final_pos_msf[2] = params[6];
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ break;
+
+ case CDROMREADTOCHDR: /* Read the table of contents header */
+ {
+ struct cdrom_tochdr *hdr;
+ struct cdrom_tochdr loc_hdr;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ {
+ return -EIO;
+ }
+
+ hdr = (struct cdrom_tochdr *) arg;
+ verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
+ loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+ loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+ memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
+ }
+ return 0;
+ break;
+
+ case CDROMREADTOCENTRY: /* Read a given table of contents entry */
+ {
+ struct cdrom_tocentry *entry;
+ struct cdrom_tocentry loc_entry;
+ int track_idx;
+ unsigned char *msf_val = NULL;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ {
+ return -EIO;
+ }
+
+ entry = (struct cdrom_tocentry *) arg;
+ verify_area(VERIFY_READ, entry, sizeof(*entry));
+ verify_area(VERIFY_WRITE, entry, sizeof(*entry));
+
+ memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
+
+ /* Lead out is handled separately since it is special. */
+ if (loc_entry.cdte_track == CDROM_LEADOUT)
+ {
+ loc_entry.cdte_adr = sony_toc->address2;
+ loc_entry.cdte_ctrl = sony_toc->control2;
+ msf_val = sony_toc->lead_out_start_msf;
+ }
+ else
+ {
+ track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+ if (track_idx < 0)
+ {
+ return -EINVAL;
+ }
+
+ loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
+ loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+ msf_val = sony_toc->tracks[track_idx].track_start_msf;
+ }
+
+ /* Logical buffer address or MSF format requested? */
+ if (loc_entry.cdte_format == CDROM_LBA)
+ {
+ loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+ }
+ else if (loc_entry.cdte_format == CDROM_MSF)
+ {
+ loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
+ loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
+ loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
+ }
+ memcpy_tofs(entry, &loc_entry, sizeof(*entry));
+ }
+ return 0;
+ break;
+
+ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
+ {
+ struct cdrom_ti ti;
+ int track_idx;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ {
+ return -EIO;
+ }
+
+ verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
+
+ memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
+ if ( (ti.cdti_trk0 < sony_toc->first_track_num)
+ || (ti.cdti_trk0 > sony_toc->last_track_num)
+ || (ti.cdti_trk1 < ti.cdti_trk0))
+ {
+ return -EINVAL;
+ }
+
+ track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+ if (track_idx < 0)
+ {
+ return -EINVAL;
+ }
+ params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+ params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+ params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
+
+ /*
+ * If we want to stop after the last track, use the lead-out
+ * MSF to do that.
+ */
+ if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
+ {
+ log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
+ &(params[4]));
+ }
+ else
+ {
+ track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
+ if (track_idx < 0)
+ {
+ return -EINVAL;
+ }
+ log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
+ &(params[4]));
+ }
+ params[0] = 0x03;
+
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+
+ do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
+ params[2], params[3], params[4], params[5], params[6]);
+ printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
+ return -EIO;
+ }
+
+ /* Save the final position for pauses and resumes */
+ final_pos_msf[0] = params[4];
+ final_pos_msf[1] = params[5];
+ final_pos_msf[2] = params[6];
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ }
+
+ case CDROMSUBCHNL: /* Get subchannel info */
+ return sony_get_subchnl_info(arg);
+
+ case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
+ {
+ struct cdrom_volctrl volctrl;
+
+ verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
+
+ memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+ params[0] = SONY_SD_AUDIO_VOLUME;
+ params[1] = volctrl.channel0;
+ params[2] = volctrl.channel1;
+ do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]);
+ return -EIO;
+ }
+ }
+ return 0;
+
+ case CDROMEJECT: /* Eject the drive */
+ do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
+ do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+ sony_audio_status = CDROM_AUDIO_INVALID;
+ do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]);
+ return -EIO;
+ }
+ return 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * Open the drive for operations. Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+scd_open(struct inode *inode,
+ struct file *filp)
+{
+ unsigned char res_reg[2];
+ unsigned int res_size;
+
+
+ if (!sony_spun_up)
+ {
+ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
+
+ /* The drive sometimes returns error 0. I don't know why, but ignore
+ it. It seems to mean the drive has already done the operation. */
+ if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+ {
+ printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
+ return -EIO;
+ }
+
+ do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
+
+ /* The drive sometimes returns error 0. I don't know why, but ignore
+ it. It seems to mean the drive has already done the operation. */
+ if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
+ {
+ /* If the drive is already playing, its ok. */
+ if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
+ {
+ goto drive_spinning;
+ }
+
+ printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
+ do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+ return -EIO;
+ }
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ {
+ do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+ return -EIO;
+ }
+
+ sony_spun_up = 1;
+ }
+
+drive_spinning:
+
+ if (inode)
+ {
+ check_disk_change(inode->i_rdev);
+ }
+
+ sony_usage++;
+
+ return 0;
+}
+
+
+/*
+ * Close the drive. Spin it down if no task is using it. The spin
+ * down will fail if playing audio, so audio play is OK.
+ */
+static void
+scd_release(struct inode *inode,
+ struct file *filp)
+{
+ unsigned char res_reg[2];
+ unsigned int res_size;
+
+
+ if (sony_usage > 0)
+ {
+ sony_usage--;
+ }
+ if (sony_usage == 0)
+ {
+ sync_dev(inode->i_rdev);
+ do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
+
+ sony_spun_up = 0;
+ }
+}
+
+
+static struct file_operations scd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ scd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ scd_open, /* open */
+ scd_release /* release */
+};
+
+
+/* The different types of disc loading mechanisms supported */
+static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
+
+/* Read-ahead buffer sizes for different drives. These are just arbitrary
+ values, I don't know what is really optimum. */
+static unsigned int mem_size[] = { 4096, 8192, 16384, 2048 };
+
+void
+get_drive_configuration(unsigned short base_io,
+ unsigned char res_reg[],
+ unsigned int *res_size)
+{
+ int retry_count;
+
+
+ /* Set the base address */
+ sony_cd_base_io = base_io;
+
+ /* Set up all the register locations */
+ sony_cd_cmd_reg = sony_cd_base_io + SONY_CMD_REG_OFFSET;
+ sony_cd_param_reg = sony_cd_base_io + SONY_PARAM_REG_OFFSET;
+ sony_cd_write_reg = sony_cd_base_io + SONY_WRITE_REG_OFFSET;
+ sony_cd_control_reg = sony_cd_base_io + SONY_CONTROL_REG_OFFSET;
+ sony_cd_status_reg = sony_cd_base_io + SONY_STATUS_REG_OFFSET;
+ sony_cd_result_reg = sony_cd_base_io + SONY_RESULT_REG_OFFSET;
+ sony_cd_read_reg = sony_cd_base_io + SONY_READ_REG_OFFSET;
+ sony_cd_fifost_reg = sony_cd_base_io + SONY_FIFOST_REG_OFFSET;
+
+ /*
+ * Check to see if anything exists at the status register location.
+ * I don't know if this is a good way to check, but it seems to work
+ * ok for me.
+ */
+ if (read_status_register() != 0xff)
+ {
+ /*
+ * Reset the drive and wait for attention from it (to say its reset).
+ * If you don't wait, the next operation will probably fail.
+ */
+ reset_drive();
+ retry_count = jiffies + SONY_RESET_TIMEOUT;
+ while ((retry_count > jiffies) && (!is_attention()))
+ {
+ sony_sleep();
+ }
+
+ /* If attention is never seen probably not a CDU31a present */
+ if (!is_attention())
+ {
+ res_reg[0] = 0x20;
+ return;
+ }
+
+ /*
+ * Get the drive configuration.
+ */
+ do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
+ NULL,
+ 0,
+ (unsigned char *) res_reg,
+ res_size);
+ return;
+ }
+
+ /* Return an error */
+ res_reg[0] = 0x20;
+}
+
+
+/*
+ * Initialize the driver.
+ */
+unsigned long
+cdu31a_init(unsigned long mem_start, unsigned long mem_end)
+{
+ struct s_sony_drive_config drive_config;
+ unsigned char params[3];
+ unsigned char res_reg[2];
+ unsigned int res_size;
+ int i;
+ int drive_found;
+
+
+ /*
+ * According to Alex Freed (freed@europa.orion.adobe.com), this is
+ * required for the Fusion CD-16 package. If the sound driver is
+ * loaded, it should work fine, but just in case...
+ *
+ * The following turn on the CD-ROM interface for a Fusion CD-16.
+ */
+ outb(0xbc, 0x9a01);
+ outb(0xe2, 0x9a01);
+
+ i = 0;
+ drive_found = 0;
+ while ( (cdu31a_addresses[i] != 0)
+ && (!drive_found))
+ {
+ get_drive_configuration(cdu31a_addresses[i],
+ drive_config.exec_status,
+ &res_size);
+ if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00))
+ {
+ drive_found = 1;
+
+ if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
+ {
+ printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
+ return mem_start;
+ }
+
+ sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)];
+ sony_buffer_sectors = sony_buffer_size / 2048;
+
+ printk("Sony I/F CDROM : %8.8s %16.16s %8.8s with %s load mechanism\n",
+ drive_config.vendor_id,
+ drive_config.product_id,
+ drive_config.product_rev_level,
+ load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+ printk(" using %d byte buffer", sony_buffer_size);
+ if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
+ {
+ printk(", capable of audio playback");
+ }
+ printk("\n");
+
+ params[0] = SONY_SD_MECH_CONTROL;
+ params[1] = 0x03;
+ do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
+ params,
+ 2,
+ res_reg,
+ &res_size);
+ if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
+ {
+ printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
+ }
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
+
+ sony_toc = (struct s_sony_toc *) mem_start;
+ mem_start += sizeof(*sony_toc);
+ last_sony_subcode = (struct s_sony_subcode *) mem_start;
+ mem_start += sizeof(*last_sony_subcode);
+ sony_buffer = (unsigned char *) mem_start;
+ mem_start += sony_buffer_size;
+
+ initialized = 1;
+ }
+
+ i++;
+ }
+
+ return mem_start;
+}
+
+#endif
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 8890c36..493571e 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -121,8 +121,12 @@ static unsigned char running = 0;
/*
* The DMA channel used by the floppy controller cannot access data at
* addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
*/
-#define LAST_DMA_ADDR (0x1000000 - BLOCK_SIZE)
+#define LAST_DMA_ADDR (0x100000 - BLOCK_SIZE)
/*
* globals used by 'result()'
@@ -1137,6 +1141,8 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
case FDFMTTRK:
if (!suser())
return -EPERM;
+ if (fd_ref[drive & 3] != 1)
+ return -EBUSY;
cli();
while (format_status != FORMAT_NONE)
sleep_on(&format_done);
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index b1f547b..d1d2258 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -556,6 +556,13 @@ static int hd_ioctl(struct inode * inode, struct file * file,
put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!inode->i_rdev) return -EINVAL;
+ sync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
RO_IOCTLS(inode->i_rdev,arg);
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index f6b3cae..4920317 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -185,6 +185,22 @@ repeat:
sti();
return;
}
+ else if ( req->dev == bh->b_dev &&
+ !req->waiting &&
+ req->cmd == rw &&
+ req->sector - count == sector &&
+ req->nr_sectors < 254)
+ {
+ req->nr_sectors += count;
+ bh->b_reqnext = req->bh;
+ req->buffer = bh->b_data;
+ req->current_nr_sectors = count;
+ req->sector = sector;
+ bh->b_dirt = 0;
+ req->bh = bh;
+ sti();
+ return;
+ }
req = req->next;
}
}
@@ -415,6 +431,12 @@ long blk_dev_init(long mem_start, long mem_end)
#ifdef CONFIG_BLK_DEV_XD
mem_start = xd_init(mem_start,mem_end);
#endif
+#ifdef CONFIG_CDU31A
+ mem_start = cdu31a_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_MCD
+ mem_start = mcd_init(mem_start,mem_end);
+#endif
if (ramdisk_size)
mem_start += rd_init(mem_start, ramdisk_size*1024);
return mem_start;
diff --git a/kernel/blk_drv/mcd.c b/kernel/blk_drv/mcd.c
new file mode 100644
index 0000000..d05c872
--- /dev/null
+++ b/kernel/blk_drv/mcd.c
@@ -0,0 +1,1196 @@
+/*
+ linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
+
+ Copyright (C) 1992 Martin Harriss
+
+ martin@bdsi.com
+
+ 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.
+
+ HISTORY
+
+ 0.1 First attempt - internal use only
+ 0.2 Cleaned up delays and use of timer - alpha release
+ 0.3 Audio support added
+ 0.3.1 Changes for mitsumi CRMC LU005S march version
+ (stud11@cc4.kuleuven.ac.be)
+ 0.3.2 bug fixes to the ioclts and merged with ALPHA0.99-pl12
+ (Jon Tombs <jon@robots.ox.ac.uk>)
+*/
+
+#include <linux/config.h>
+#ifdef CONFIG_MCD
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+
+/* #define REALLY_SLOW_IO */
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR 23
+#include "blk.h"
+#include <linux/mcd.h>
+
+#if 0
+static int mcd_sizes[] = { 0 };
+#endif
+
+static int mcdPresent = 0;
+
+static char mcd_buf[2048]; /* buffer for block size conversion */
+static int mcd_bn = -1;
+
+static int McdTimeout, McdTries;
+static struct wait_queue *mcd_waitq = NULL;
+
+static struct mcd_DiskInfo DiskInfo;
+static struct mcd_Toc Toc[MAX_TRACKS];
+static struct mcd_Play_msf mcd_Play;
+
+static int audioStatus;
+static char mcdDiskChanged;
+static char tocUpToDate;
+static char mcdVersion;
+
+static void mcd_transfer(void);
+static void mcd_start(void);
+static void mcd_status(void);
+static void mcd_read_cmd(void);
+static void mcd_data(void);
+static void do_mcd_request(void);
+static void hsg2msf(long hsg, struct msf *msf);
+static void bin2bcd(unsigned char *p);
+static int bcd2bin(unsigned char bcd);
+static int mcdStatus(void);
+static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
+static int getMcdStatus(int timeout);
+static int GetQChannelInfo(struct mcd_Toc *qp);
+static int updateToc(void);
+static int GetDiskInfo(void);
+static int GetToc(void);
+static int getValue(unsigned char *result);
+
+
+int
+check_mcd_media_change(int full_dev, int flag)
+{
+ int retval, target;
+
+
+ target = MINOR(full_dev);
+
+ if (target > 0) {
+ printk("Mitsumi CD-ROM request error: invalid device.\n");
+ return 0;
+ }
+
+ retval = mcdDiskChanged;
+ if (!flag)
+ {
+ mcdDiskChanged = 0;
+ }
+
+ return retval;
+}
+
+
+/*
+ * Do a 'get status' command and get the result. Only use from the top half
+ * because it calls 'getMcdStatus' which sleeps.
+ */
+
+static int
+statusCmd(void)
+{
+ int st, retry;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */
+ st = getMcdStatus(100);
+ if (st != -1)
+ break;
+ }
+
+ return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status. Use only from the top half.
+ */
+
+static int
+mcdPlay(struct mcd_Play_msf *arg)
+{
+ int retry, st;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ sendMcdCmd(MCMD_PLAY_READ, arg);
+ st = getMcdStatus(200);
+ if (st != -1)
+ break;
+ }
+
+ return st;
+}
+
+
+long
+msf2hsg(struct msf *mp)
+{
+ return bcd2bin(mp -> frame)
+ + bcd2bin(mp -> sec) * 75
+ + bcd2bin(mp -> min) * 4500
+ - 150;
+}
+
+
+static int
+mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ int i, st;
+ struct mcd_Toc qInfo;
+ struct cdrom_ti ti;
+ struct cdrom_tochdr tocHdr;
+ struct cdrom_msf msf;
+ struct cdrom_tocentry entry;
+ struct mcd_Toc *tocPtr;
+ struct cdrom_subchnl subchnl;
+#if 0
+ struct cdrom_volctrl volctrl;
+#endif
+
+ if (!ip)
+ return -EINVAL;
+
+ st = statusCmd();
+ if (st < 0)
+ return -EIO;
+
+ if (!tocUpToDate)
+ {
+ i = updateToc();
+ if (i < 0)
+ return i; /* error reading TOC */
+ }
+
+ switch (cmd)
+ {
+ case CDROMSTART: /* Spin up the drive */
+ /* Don't think we can do this. Even if we could,
+ * I think the drive times out and stops after a while
+ * anyway. For now, ignore it.
+ */
+
+ return 0;
+
+ case CDROMSTOP: /* Spin down the drive */
+ outb(MCMD_STOP, MCDPORT(0));
+ i = getMcdStatus(100);
+
+ /* should we do anything if it fails? */
+
+ audioStatus = CDROM_AUDIO_NO_STATUS;
+ return 0;
+
+ case CDROMPAUSE: /* Pause the drive */
+ if (audioStatus != CDROM_AUDIO_PLAY)
+ return -EINVAL;
+
+ outb(MCMD_STOP, MCDPORT(0));
+ i = getMcdStatus(100);
+
+ if (GetQChannelInfo(&qInfo) < 0)
+ {
+ /* didn't get q channel info */
+
+ audioStatus = CDROM_AUDIO_NO_STATUS;
+ return 0;
+ }
+
+ mcd_Play.start = qInfo.diskTime; /* remember restart point */
+
+ audioStatus = CDROM_AUDIO_PAUSED;
+ return 0;
+
+ case CDROMRESUME: /* Play it again, Sam */
+ if (audioStatus != CDROM_AUDIO_PAUSED)
+ return -EINVAL;
+
+ /* restart the drive at the saved position. */
+
+ i = mcdPlay(&mcd_Play);
+ if (i < 0)
+ {
+ audioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+
+ audioStatus = CDROM_AUDIO_PLAY;
+ return 0;
+
+ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
+
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+ if (st)
+ return st;
+
+ memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+
+ if (ti.cdti_trk0 < DiskInfo.first
+ || ti.cdti_trk0 > DiskInfo.last
+ || ti.cdti_trk1 < ti.cdti_trk0)
+ {
+ return -EINVAL;
+ }
+
+ if (ti.cdti_trk1 > DiskInfo.last)
+ ti. cdti_trk1 = DiskInfo.last;
+
+ mcd_Play.start = Toc[ti.cdti_trk0].diskTime;
+ mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+
+#ifdef MCD_DEBUG
+printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+ mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+ mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+ i = mcdPlay(&mcd_Play);
+ if (i < 0)
+ {
+ audioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+
+ audioStatus = CDROM_AUDIO_PLAY;
+ return 0;
+
+ case CDROMPLAYMSF: /* Play starting at the given MSF address. */
+
+ if (audioStatus == CDROM_AUDIO_PLAY) {
+ outb(MCMD_STOP, MCDPORT(0));
+ i = getMcdStatus(100);
+ audioStatus = CDROM_AUDIO_NO_STATUS;
+ }
+
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+ if (st)
+ return st;
+
+ memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+
+ /* convert to bcd */
+
+ bin2bcd(&msf.cdmsf_min0);
+ bin2bcd(&msf.cdmsf_sec0);
+ bin2bcd(&msf.cdmsf_frame0);
+ bin2bcd(&msf.cdmsf_min1);
+ bin2bcd(&msf.cdmsf_sec1);
+ bin2bcd(&msf.cdmsf_frame1);
+
+ mcd_Play.start.min = msf.cdmsf_min0;
+ mcd_Play.start.sec = msf.cdmsf_sec0;
+ mcd_Play.start.frame = msf.cdmsf_frame0;
+ mcd_Play.end.min = msf.cdmsf_min1;
+ mcd_Play.end.sec = msf.cdmsf_sec1;
+ mcd_Play.end.frame = msf.cdmsf_frame1;
+
+#ifdef MCD_DEBUG
+printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
+mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
+#endif
+
+ i = mcdPlay(&mcd_Play);
+ if (i < 0)
+ {
+ audioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+
+ audioStatus = CDROM_AUDIO_PLAY;
+ return 0;
+
+ case CDROMREADTOCHDR: /* Read the table of contents header */
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
+ if (st)
+ return st;
+
+ tocHdr.cdth_trk0 = DiskInfo.first;
+ tocHdr.cdth_trk1 = DiskInfo.last;
+ memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+ return 0;
+
+ case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
+
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+ if (st)
+ return st;
+
+ memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+ if (entry.cdte_track == CDROM_LEADOUT)
+ /* XXX */
+ tocPtr = &Toc[DiskInfo.last + 1];
+
+ else if (entry.cdte_track > DiskInfo.last
+ || entry.cdte_track < DiskInfo.first)
+ return -EINVAL;
+
+ else
+ tocPtr = &Toc[entry.cdte_track];
+
+ entry.cdte_adr = tocPtr -> ctrl_addr;
+ entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+
+ if (entry.cdte_format == CDROM_LBA)
+ entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
+
+ else if (entry.cdte_format == CDROM_MSF)
+ {
+ entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);
+ entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);
+ entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);
+ }
+
+ else
+ return -EINVAL;
+
+ memcpy_tofs((void *) arg, &entry, sizeof entry);
+ return 0;
+
+ case CDROMSUBCHNL: /* Get subchannel info */
+
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
+ if (st)
+ return st;
+
+ memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+
+ if (GetQChannelInfo(&qInfo) < 0)
+ return -EIO;
+
+ subchnl.cdsc_audiostatus = audioStatus;
+ subchnl.cdsc_adr = qInfo.ctrl_addr;
+ subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+ subchnl.cdsc_trk = bcd2bin(qInfo.track);
+ subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
+
+ if (subchnl.cdsc_format == CDROM_LBA)
+ {
+ subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
+ subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
+ }
+
+ else if (subchnl.cdsc_format == CDROM_MSF)
+ {
+ subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
+ subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
+ subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);
+
+ subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
+ subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
+ subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);
+ }
+
+ else
+ return -EINVAL;
+
+ memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+ return 0;
+
+ case CDROMVOLCTRL: /* Volume control */
+ /*
+ * This is not working yet. Setting the volume by itself does
+ * nothing. Following the 'set' by a 'play' results in zero
+ * volume. Something to work on for the next release.
+ */
+#if 0
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
+ if (st)
+ return st;
+
+ memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);
+ outb(MCMD_SET_VOLUME, MCDPORT(0));
+ outb(volctrl.channel0, MCDPORT(0));
+ outb(0, MCDPORT(0));
+ outb(volctrl.channel1, MCDPORT(0));
+ outb(1, MCDPORT(0));
+
+ i = getMcdStatus(100);
+ if (i < 0)
+ return -EIO;
+
+ {
+ int a, b, c, d;
+
+ getValue(&a);
+ getValue(&b);
+ getValue(&c);
+ getValue(&d);
+ printk("%02X %02X %02X %02X\n", a, b, c, d);
+ }
+
+ outb(0xF8, MCDPORT(0));
+ i = getMcdStatus(100);
+ printk("F8 -> %02X\n", i & 0xFF);
+#endif
+ return 0;
+
+ case CDROMEJECT: /* Eject the drive - N/A */
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+
+static void
+mcd_transfer(void)
+{
+ long offs;
+
+ while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4)
+ {
+ offs = (CURRENT -> sector & 3) * 512;
+ memcpy(CURRENT -> buffer, mcd_buf + offs, 512);
+ CURRENT -> nr_sectors--;
+ CURRENT -> sector++;
+ CURRENT -> buffer += 512;
+ }
+}
+
+
+/*
+ * We only seem to get interrupts after an error.
+ * Just take the interrupt and clear out the status reg.
+ */
+
+static void
+mcd_interrupt(int unused)
+{
+ int st;
+
+ st = inb(MCDPORT(1)) & 0xFF;
+ if (st != 0xFF)
+ {
+ st = inb(MCDPORT(0)) & 0xFF;
+#if 0
+ printk("<int-%02X>", st);
+#endif
+ }
+}
+
+
+/*
+ * I/O request routine called from Linux kernel.
+ */
+
+static void
+do_mcd_request(void)
+{
+ unsigned int block,dev;
+ unsigned int nsect;
+
+repeat:
+ INIT_REQUEST;
+ dev = MINOR(CURRENT->dev);
+ block = CURRENT->sector;
+ nsect = CURRENT->nr_sectors;
+
+ if (CURRENT == NULL || CURRENT -> sector == -1)
+ return;
+
+ if (CURRENT -> cmd != READ)
+ {
+ printk("mcd: bad cmd %d\n", CURRENT -> cmd);
+ end_request(0);
+ goto repeat;
+ }
+
+ mcd_transfer();
+
+ /* if we satisfied the request from the buffer, we're done. */
+
+ if (CURRENT -> nr_sectors == 0)
+ {
+ end_request(1);
+ goto repeat;
+ }
+
+ McdTries = 3;
+ mcd_start();
+}
+
+
+/*
+ * Start the I/O for the cdrom. Handle retry count.
+ */
+
+static void
+mcd_start()
+{
+ if (McdTries == 0)
+ {
+ printk("mcd: read failed after 3 tries\n");
+ end_request(0);
+ SET_TIMER(do_mcd_request, 1); /* wait a bit, try again */
+ return;
+ }
+
+ McdTries--;
+ outb(0x40, MCDPORT(0)); /* get status */
+ McdTimeout = 100;
+ SET_TIMER(mcd_status, 1);
+}
+
+
+/*
+ * Called from the timer to check the results of the get-status cmd.
+ * On success, send the set-mode command.
+ */
+
+static void
+mcd_status()
+{
+ int st;
+
+ McdTimeout--;
+ st = mcdStatus();
+ if (st == -1)
+ {
+ if (McdTimeout == 0)
+ {
+ printk("mcd: status timed out\n");
+ SET_TIMER(mcd_start, 1); /* wait a bit, try again */
+ return;
+ }
+
+ SET_TIMER(mcd_status, 1);
+ return;
+ }
+
+ if (st & MST_DSK_CHG)
+ {
+ mcdDiskChanged = 1;
+ }
+
+ if ((st & MST_READY) == 0)
+ {
+ printk("mcd: disk removed\n");
+ mcdDiskChanged = 1;
+ end_request(0);
+ do_mcd_request();
+ return;
+ }
+
+ outb(0x50, MCDPORT(0)); /* set mode */
+ outb(0x01, MCDPORT(0)); /* mode = cooked data */
+ McdTimeout = 100;
+ SET_TIMER(mcd_read_cmd, 1);
+}
+
+
+/*
+ * Check the result of the set-mode command. On success, send the
+ * read-data command.
+ */
+
+static void
+mcd_read_cmd()
+{
+ int st;
+ long block;
+ struct mcd_Play_msf mcdcmd;
+
+ McdTimeout--;
+ st = mcdStatus();
+
+ if (st & MST_DSK_CHG)
+ {
+ mcdDiskChanged = 1;
+ }
+
+ if (st == -1)
+ {
+ if (McdTimeout == 0)
+ {
+ printk("mcd: set mode timed out\n");
+ SET_TIMER(mcd_start, 1); /* wait a bit, try again */
+ return;
+ }
+
+ SET_TIMER(mcd_read_cmd, 1);
+ return;
+ }
+
+ mcd_bn = -1; /* purge our buffer */
+ block = CURRENT -> sector / 4;
+ hsg2msf(block, &mcdcmd.start); /* cvt to msf format */
+
+ mcdcmd.end.min = 0;
+ mcdcmd.end.sec = 0;
+ mcdcmd.end.frame = 1;
+
+ sendMcdCmd(MCMD_PLAY_READ, &mcdcmd); /* read command */
+ McdTimeout = 200;
+ SET_TIMER(mcd_data, 1);
+}
+
+
+/*
+ * Check the completion of the read-data command. On success, read
+ * the 2048 bytes of data from the disk into our buffer.
+ */
+
+static void
+mcd_data()
+{
+ int i;
+
+ McdTimeout--;
+ cli();
+ i =inb(MCDPORT(1)) & (MFL_STATUS | MFL_DATA);
+ if (i == MFL_DATA)
+ {
+ printk("mcd: read failed\n");
+#ifdef MCD_DEBUG
+ printk("got 0xB %02X\n", inb(MCDPORT(0)) & 0xFF);
+#endif
+ SET_TIMER(mcd_start, 1);
+ sti();
+ return;
+ }
+
+ if (i == (MFL_STATUS | MFL_DATA))
+ {
+ if (McdTimeout == 0)
+ {
+ printk("mcd: data timeout, retrying\n");
+ SET_TIMER(mcd_start, 1);
+ }
+
+ else
+ SET_TIMER(mcd_data, 1);
+
+ sti();
+ return;
+ }
+
+ CLEAR_TIMER;
+ READ_DATA(MCDPORT(0), &mcd_buf[0], 2048);
+ sti();
+
+ mcd_bn = CURRENT -> sector / 4;
+ mcd_transfer();
+ end_request(1);
+ SET_TIMER(do_mcd_request, 1);
+}
+
+
+/*
+ * Open the device special file. Check that a disk is in.
+ */
+
+int
+mcd_open(struct inode *ip, struct file *fp)
+{
+ int st;
+
+ if (mcdPresent == 0)
+ return -ENXIO; /* no hardware */
+
+ st = statusCmd(); /* check drive status */
+ if (st == -1)
+ return -EIO; /* drive doesn't respond */
+
+ if ((st & MST_READY) == 0) /* no disk in drive */
+ {
+ printk("mcd: no disk in drive\n");
+ return -EIO;
+ }
+
+ if (updateToc() < 0)
+ return -EIO;
+
+ return 0;
+}
+
+
+/*
+ * On close, we flush all mcd blocks from the buffer cache.
+ */
+
+static void
+mcd_release(struct inode * inode, struct file * file)
+{
+ mcd_bn = -1;
+ sync_dev(inode->i_rdev);
+ invalidate_buffers(inode -> i_rdev);
+}
+
+
+static struct file_operations mcd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ mcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ mcd_open, /* open */
+ mcd_release /* release */
+};
+
+
+/*
+ * MCD interrupt descriptor
+ */
+
+static struct sigaction mcd_sigaction = {
+ mcd_interrupt,
+ 0,
+ SA_INTERRUPT,
+ NULL
+};
+
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ */
+
+unsigned long
+mcd_init(unsigned long mem_start, unsigned long mem_end)
+{
+ int count;
+ unsigned char result[3];
+
+ if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
+ {
+ printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
+ return mem_start;
+ }
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 4;
+
+ /* check for card */
+
+ outb(0, MCDPORT(1)); /* send reset */
+ for (count = 0; count < 1000000; count++)
+ (void) inb(MCDPORT(1)); /* delay a bit */
+
+ outb(0x40, MCDPORT(0)); /* send get-stat cmd */
+ for (count = 0; count < 1000000; count++)
+ if (!(inb(MCDPORT(1)) & MFL_STATUS))
+ break;
+
+ if (count >= 1000000) {
+ printk("mitsumi init failed...\n");
+ return mem_start;
+ }
+ count = inb(MCDPORT(0)); /* pick up the status */
+
+ outb(MCMD_GET_VERSION,MCDPORT(0));
+ for(count=0;count<3;count++)
+ if(getValue(result+count)) {
+ printk("mitsumi get version failed...\n");
+ return mem_start;
+ }
+
+ printk("Mitsumi version : %02X %c %x\n",result[0],result[1],result[2]);
+
+ mcdVersion=result[2];
+
+ if (mcdVersion >=4)
+ outb(4,MCDPORT(2)); /* magic happens */
+
+ /* don't get the IRQ until we know for sure the drive is there */
+
+ if (irqaction(MCD_INTR_NR, &mcd_sigaction))
+ {
+ printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", MCD_INTR_NR);
+ return mem_start;
+ }
+
+ mcdPresent = 1;
+ printk("Mitsumi CD-ROM Drive present\n");
+ return mem_start;
+}
+
+
+static void
+hsg2msf(long hsg, struct msf *msf)
+{
+ hsg += 150;
+ msf -> min = hsg / 4500;
+ hsg %= 4500;
+ msf -> sec = hsg / 75;
+ msf -> frame = hsg % 75;
+
+ bin2bcd(&msf -> min); /* convert to BCD */
+ bin2bcd(&msf -> sec);
+ bin2bcd(&msf -> frame);
+}
+
+
+static void
+bin2bcd(unsigned char *p)
+{
+ int u, t;
+
+ u = *p % 10;
+ t = *p / 10;
+ *p = u | (t << 4);
+}
+
+static int
+bcd2bin(unsigned char bcd)
+{
+ return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+/*
+ * See if a status is ready from the drive and return it
+ * if it is ready.
+ */
+
+static int
+mcdStatus(void)
+{
+ int i;
+ int st;
+
+ st = inb(MCDPORT(1)) & MFL_STATUS;
+ if (!st)
+ {
+ i = inb(MCDPORT(0)) & 0xFF;
+ return i;
+ }
+ else
+ return -1;
+}
+
+
+/*
+ * Send a play or read command to the drive
+ */
+
+static void
+sendMcdCmd(int cmd, struct mcd_Play_msf *params)
+{
+ outb(cmd, MCDPORT(0));
+ outb(params -> start.min, MCDPORT(0));
+ outb(params -> start.sec, MCDPORT(0));
+ outb(params -> start.frame, MCDPORT(0));
+ outb(params -> end.min, MCDPORT(0));
+ outb(params -> end.sec, MCDPORT(0));
+ outb(params -> end.frame, MCDPORT(0));
+}
+
+
+/*
+ * Timer interrupt routine to test for status ready from the drive.
+ * (see the next routine)
+ */
+
+static void
+mcdStatTimer(void)
+{
+ if (!(inb(MCDPORT(1)) & MFL_STATUS))
+ {
+ wake_up(&mcd_waitq);
+ return;
+ }
+
+ McdTimeout--;
+ if (McdTimeout <= 0)
+ {
+ wake_up(&mcd_waitq);
+ return;
+ }
+
+ SET_TIMER(mcdStatTimer, 1);
+}
+
+
+/*
+ * Wait for a status to be returned from the drive. The actual test
+ * (see routine above) is done by the timer interrupt to avoid
+ * excessive rescheduling.
+ */
+
+static int
+getMcdStatus(int timeout)
+{
+ int st;
+
+ McdTimeout = timeout;
+ SET_TIMER(mcdStatTimer, 1);
+ sleep_on(&mcd_waitq);
+ if (McdTimeout <= 0)
+ return -1;
+
+ st = inb(MCDPORT(0)) & 0xFF;
+ if (st == 0xFF)
+ return -1;
+
+ if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
+ /* XXX might be an error? look at q-channel? */
+ audioStatus = CDROM_AUDIO_COMPLETED;
+
+ if (st & MST_DSK_CHG)
+ {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ audioStatus = CDROM_AUDIO_NO_STATUS;
+ }
+
+ return st;
+}
+
+
+/*
+ * Read a value from the drive. Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling.
+ */
+
+static int
+getValue(unsigned char *result)
+{
+ int count;
+ int s;
+
+ for (count = 0; count < 2000; count++)
+ if (!(inb(MCDPORT(1)) & MFL_STATUS))
+ break;
+
+ if (count >= 2000)
+ {
+ printk("mcd: getValue timeout\n");
+ return -1;
+ }
+
+ s = inb(MCDPORT(0)) & 0xFF;
+ *result = (unsigned char) s;
+ return 0;
+}
+
+
+/*
+ * Read the current Q-channel info. Also used for reading the
+ * table of contents.
+ */
+
+int
+GetQChannelInfo(struct mcd_Toc *qp)
+{
+ unsigned char notUsed;
+ int retry;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
+ if (getMcdStatus(100) != -1)
+ break;
+ }
+
+ if (retry >= 3)
+ return -1;
+
+ if (getValue(&qp -> ctrl_addr) < 0) return -1;
+ if (getValue(&qp -> track) < 0) return -1;
+ if (getValue(&qp -> pointIndex) < 0) return -1;
+ if (getValue(&qp -> trackTime.min) < 0) return -1;
+ if (getValue(&qp -> trackTime.sec) < 0) return -1;
+ if (getValue(&qp -> trackTime.frame) < 0) return -1;
+ if (getValue(&notUsed) < 0) return -1;
+ if (getValue(&qp -> diskTime.min) < 0) return -1;
+ if (getValue(&qp -> diskTime.sec) < 0) return -1;
+ if (getValue(&qp -> diskTime.frame) < 0) return -1;
+
+ return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC) and TOC header if neccessary
+ */
+
+static int
+updateToc()
+{
+ if (tocUpToDate)
+ return 0;
+
+ if (GetDiskInfo() < 0)
+ return -EIO;
+
+ if (GetToc() < 0)
+ return -EIO;
+
+ tocUpToDate = 1;
+ return 0;
+}
+
+
+/*
+ * Read the table of contents header
+ */
+
+static int
+GetDiskInfo()
+{
+ int retry;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_GET_DISK_INFO, MCDPORT(0));
+ if (getMcdStatus(100) != -1)
+ break;
+ }
+
+ if (retry >= 3)
+ return -1;
+
+ if (getValue(&DiskInfo.first) < 0) return -1;
+ if (getValue(&DiskInfo.last) < 0) return -1;
+
+ DiskInfo.first = bcd2bin(DiskInfo.first);
+ DiskInfo.last = bcd2bin(DiskInfo.last);
+
+ if (getValue(&DiskInfo.diskLength.min) < 0) return -1;
+ if (getValue(&DiskInfo.diskLength.sec) < 0) return -1;
+ if (getValue(&DiskInfo.diskLength.frame) < 0) return -1;
+ if (getValue(&DiskInfo.firstTrack.min) < 0) return -1;
+ if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1;
+ if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1;
+
+#ifdef MCD_DEBUG
+printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
+ DiskInfo.first,
+ DiskInfo.last,
+ DiskInfo.diskLength.min,
+ DiskInfo.diskLength.sec,
+ DiskInfo.diskLength.frame,
+ DiskInfo.firstTrack.min,
+ DiskInfo.firstTrack.sec,
+ DiskInfo.firstTrack.frame);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+
+static int
+GetToc()
+{
+ int i, px;
+ int limit;
+ int retry;
+ struct mcd_Toc qInfo;
+
+ for (i = 0; i < MAX_TRACKS; i++)
+ Toc[i].pointIndex = 0;
+
+ i = DiskInfo.last + 3;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_STOP, MCDPORT(0));
+ if (getMcdStatus(100) != -1)
+ break;
+ }
+
+ if (retry >= 3)
+ return -1;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_SET_MODE, MCDPORT(0));
+ outb(0x05, MCDPORT(0)); /* mode: toc */
+ if (getMcdStatus(100) != -1)
+ break;
+ }
+
+ if (retry >= 3)
+ return -1;
+
+ for (limit = 300; limit > 0; limit--)
+ {
+ if (GetQChannelInfo(&qInfo) < 0)
+ break;
+
+ px = bcd2bin(qInfo.pointIndex);
+ if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+ if (Toc[px].pointIndex == 0)
+ {
+ Toc[px] = qInfo;
+ i--;
+ }
+
+ if (i <= 0)
+ break;
+ }
+
+ Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+ for (retry = 0; retry < 3; retry++)
+ {
+ outb(MCMD_SET_MODE, MCDPORT(0));
+ outb(0x01, MCDPORT(0));
+ if (getMcdStatus(100) != -1)
+ break;
+ }
+
+#ifdef MCD_DEBUG
+for (i = 1; i <= DiskInfo.last; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+for (i = 100; i < 103; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+ return limit > 0 ? 0 : -1;
+}
+
+#endif
diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c
index 0bd237e..3f50ea0 100644
--- a/kernel/blk_drv/ramdisk.c
+++ b/kernel/blk_drv/ramdisk.c
@@ -104,7 +104,7 @@ void rd_load(void)
{
struct buffer_head *bh;
struct minix_super_block s;
- int block, try;
+ int block, tries;
int i = 1;
int nblocks;
char *cp;
@@ -125,8 +125,8 @@ void rd_load(void)
* 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;
+ for (tries = 0; tries < 1000; tries += 512) {
+ block = tries;
bh = breada(ROOT_DEV,block+1,block,block+2,-1);
if (!bh) {
printk("RAMDISK: I/O error while looking for super block!\n");
diff --git a/kernel/blk_drv/scsi/Makefile b/kernel/blk_drv/scsi/Makefile
index 1b0ea8a..aecb001 100644
--- a/kernel/blk_drv/scsi/Makefile
+++ b/kernel/blk_drv/scsi/Makefile
@@ -6,12 +6,11 @@
# unless its something special (ie not a .c file).
#
-SCSI_HOSTS := 0
SCSI_OBJS :=
ifdef CONFIG_SCSI
-SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o
+SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o
ifdef CONFIG_BLK_DEV_ST
SCSI_OBJS := $(SCSI_OBJS) st.o
@@ -27,42 +26,34 @@ endif
ifdef CONFIG_SCSI_AHA1542
SCSI_OBJS := $(SCSI_OBJS) aha1542.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
ifdef CONFIG_SCSI_AHA1740
SCSI_OBJS := $(SCSI_OBJS) aha1740.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
ifdef CONFIG_SCSI_FUTURE_DOMAIN
SCSI_OBJS := $(SCSI_OBJS) fdomain.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
ifdef CONFIG_SCSI_ULTRASTOR
SCSI_OBJS := $(SCSI_OBJS) ultrastor.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
ifdef CONFIG_SCSI_7000FASST
SCSI_OBJS := $(SCSI_OBJS) wd7000.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
ifdef CONFIG_SCSI_SEAGATE
SCSI_OBJS := $(SCSI_OBJS) seagate.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
else
ifdef CONFIG_SCSI_FD_88x
SCSI_OBJS := $(SCSI_OBJS) seagate.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
endif
ifdef CONFIG_SCSI_DEBUG
SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
-SCSI_HOSTS := 1+$(SCSI_HOSTS)
endif
scsi.a: $(SCSI_OBJS)
@@ -79,10 +70,11 @@ scsi.a:
endif
-CFLAGS := $(CFLAGS) "-DMAX_SCSI_HOSTS=($(SCSI_HOSTS))"
-
seagate.o: seagate.c
- $(CC) -Wall -I$(KERNELHDRS) -x c++ -c seagate.c
+ $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c
+
+constants.o: constants.c
+ $(CC) $(CFLAGS) -c constants.c
clean:
rm -f core *.o *.a *.s
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
index 4141c81..dc9ffe5 100644
--- a/kernel/blk_drv/scsi/aha1542.c
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -376,7 +376,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
if(*cmd == REQUEST_SENSE){
#ifndef DEBUG
- if (bufflen != 16) {
+ if (bufflen != sizeof(SCpnt->sense_buffer)) {
printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
panic("aha1542.c");
};
@@ -472,9 +472,11 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
panic("Foooooooood fight!");
};
any2scsi(cptr[i].dataptr, sgpnt[i].address);
+ if(((unsigned int) sgpnt[i].address) & 0xff000000) goto baddma;
any2scsi(cptr[i].datalen, sgpnt[i].length);
};
any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain));
+ if(((unsigned int) buff & 0xff000000)) goto baddma;
any2scsi(ccb[mbo].dataptr, cptr);
#ifdef DEBUG
printk("cptr %x: ",cptr);
@@ -511,6 +513,8 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
printk("aha1542_queuecommand: done can't be NULL\n");
return 0;
+ baddma:
+ panic("Buffer at address > 16Mb used for 1542B");
}
static volatile int internal_done_flag = 0;
diff --git a/kernel/blk_drv/scsi/aha1740.c b/kernel/blk_drv/scsi/aha1740.c
index b5b11ce..ef86500 100644
--- a/kernel/blk_drv/scsi/aha1740.c
+++ b/kernel/blk_drv/scsi/aha1740.c
@@ -247,7 +247,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
if(*cmd == REQUEST_SENSE)
{
- if (bufflen != 16)
+ if (bufflen != sizeof(SCpnt->sense_buffer))
{
printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
panic("aha1740.c");
diff --git a/kernel/blk_drv/scsi/constants.c b/kernel/blk_drv/scsi/constants.c
new file mode 100644
index 0000000..031a06c
--- /dev/null
+++ b/kernel/blk_drv/scsi/constants.c
@@ -0,0 +1,460 @@
+/*
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_SCSI
+#include "../blk.h"
+#include <linux/kernel.h>
+#include "scsi.h"
+static const char reserved[] = "RESERVED";
+static const char unknown[] = "UNKNOWN";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+#define CONST_COMMAND 0x01
+#define CONST_STATUS 0x02
+#define CONST_SENSE 0x04
+#define CONST_XSENSE 0x08
+
+#ifdef CONFIG_SCSI_CONSTANTS
+#ifdef CONSTANTS
+#undef CONSTANTS
+#endif
+#define CONSTANTS (CONST_CMD | CONST_STATUS | CONST_SENSE | CONST_XSENSE)
+#endif
+
+#if (CONSTANTS & CONST_COMMAND)
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */ unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Deffect Data",
+/* 38-3c */ unknown, "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-39 */ unknown, "Read Long", unknown,
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", unknown,
+/* 42-48 */ unknown, unknown, unknown, unknown, unknown, unknown, unknown,
+/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP 0
+#define VENDOR_GROUP 1
+#define NOTEXT_GROUP 2
+
+static const char **commands[] = {
+group_0_commands, group_1_commands, group_2_commands,
+(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
+(const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
+(const char **) VENDOR_GROUP};
+
+
+static void print_opcode(int opcode) {
+ char **table = commands[ group(opcode) ];
+ switch ((int) table) {
+ case RESERVED_GROUP:
+ printk("%s(0x%02x) ", reserved, opcode);
+ break;
+ case NOTEXT_GROUP:
+ printk("%s(0x%02x) ", unknown, opcode);
+ break;
+ case VENDOR_GROUP:
+ printk("%s(0x%02x) ", vendor, opcode);
+ break;
+ default:
+ printk("%s ",table[opcode & 0x31]);
+ }
+}
+#else /* CONST & CONST_COMMAND */
+static void print_opcode(int opcode) {
+ printk("0x%02x ", opcode);
+}
+#endif
+
+void print_command (unsigned char *command) {
+ int i,s;
+ print_opcode(command[0]);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ printk("%02x ", command[i]);
+ printk("\n");
+}
+
+#if (CONSTANTS & CONST_STATUS)
+static const char * statuses[] = {
+/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy",
+/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown,
+/* a-d */ "Interemediate Good", unknown, "Reservation Conflict", unknown,
+/* e-f */ unknown, unknown,
+};
+#endif
+
+void print_status (int status) {
+ status = (status >> 1) & 0xf;
+#if (CONSTANTS & CONST_STATUS)
+ printk("%s ",statuses[status]);
+#else
+ printk("0x%0x ", status);
+#endif
+}
+
+#if (CONSTANTS & CONST_MSG)
+static const char * msgs[] = {
+};
+void print_msg (int msg) {
+ printk("%s ", msgs[msg]);
+}
+#else
+void print_msg (int msg) {
+ printk("0x%02x ", msg);
+}
+#endif
+
+#if (CONSTANTS & CONST_XSENSE)
+#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x004 /* PRINTER DEVICE */
+#define P 0x008 /* PROCESSOR DEVICE */
+#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x040 /* SCANNER DEVICE */
+#define O 0x080 /* OPTICAL MEMORY DEVICE */
+#define M 0x100 /* MEDIA CHANGER DEVICE */
+#define C 0x200 /* COMMUNICATION DEVICE */
+
+struct error_info{
+ unsigned char code1, code2;
+ unsigned short int devices;
+ char * text;
+};
+
+struct error_info2{
+ unsigned char code1, code2_min, code2_max;
+ unsigned short int devices;
+ char * text;
+};
+
+static struct error_info2 additional2[] =
+{
+ {0x40,0x00,0x7f,D,"Ram failure (%x)"},
+ {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
+ {0x41,0x00,0xff,D,"Data path failure (%x)"},
+ {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
+ {0, 0, 0, 0, NULL}
+};
+
+static struct error_info additional[] =
+{
+ {0x00,0x01,T,"Filemark detected"},
+ {0x00,0x02,T|S,"End-of-partition/medium detected"},
+ {0x00,0x03,T,"Setmark detected"},
+ {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
+ {0x00,0x05,T|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x11,R,"Audio play operation in progress"},
+ {0x00,0x12,R,"Audio play operation paused"},
+ {0x00,0x13,R,"Audio play operation successfully completed"},
+ {0x00,0x14,R,"Audio play operation stopped due to error"},
+ {0x00,0x15,R,"No current audio status to return"},
+ {0x01,0x00,D|W|O,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x03,0x01,T,"No write current"},
+ {0x03,0x02,T,"Excessive write errors"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, initializing command required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, manual intervention required"},
+ {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
+ {0x09,0x00,D|T|W|R|O,"Track following error"},
+ {0x09,0x01,W|R|O,"Tracking servo failure"},
+ {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x09,0x03,W|R|O,"Spindle servo failure"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
+ {0x0C,0x00,T|S,"Write error"},
+ {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
+ {0x10,0x00,D|W|O,"Id crc or ecc error"},
+ {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
+ {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
+ {0x11,0x06,W|R|O,"Circ unrecovered error"},
+ {0x11,0x07,W|O,"Data resychronization error"},
+ {0x11,0x08,T,"Incomplete block read"},
+ {0x11,0x09,T,"No gap found"},
+ {0x11,0x0A,D|T|O,"Miscorrected error"},
+ {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
+ {0x12,0x00,D|W|O,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x14,0x02,T,"Filemark or setmark not found"},
+ {0x14,0x03,T,"End-of-data not found"},
+ {0x14,0x04,T,"Block sequence error"},
+ {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O,"Data synchronization mark error"},
+ {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
+ {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
+ {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with circ"},
+ {0x18,0x04,R,"Recovered data with lec"},
+ {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
+ {0x19,0x00,D|O,"Defect list error"},
+ {0x19,0x01,D|O,"Defect list not available"},
+ {0x19,0x02,D|O,"Defect list error in primary list"},
+ {0x19,0x03,D|O,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O,"Defect list not found"},
+ {0x1C,0x01,D|O,"Primary defect list not found"},
+ {0x1C,0x02,D|O,"Grown defect list not found"},
+ {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
+ {0x21,0x01,M,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
+ {0x27,0x00,D|T|W|O,"Write protected"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
+ {0x28,0x01,M,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x2C,0x01,S,"Too many windows specified"},
+ {0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2D,0x00,T,"Overwrite error on update in place"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T,"Cleaning cartridge installed"},
+ {0x31,0x00,D|T|W|O,"Medium format corrupted"},
+ {0x31,0x01,D|L|O,"Format command failed"},
+ {0x32,0x00,D|W|O,"No defect spare location available"},
+ {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x33,0x00,T,"Tape length error"},
+ {0x36,0x00,L,"Ribbon, ink, or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x3B,0x00,T|L,"Sequential positioning error"},
+ {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
+ {0x3B,0x02,T,"Tape position error at end-of-medium"},
+ {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
+ {0x3B,0x04,L,"Slew failure"},
+ {0x3B,0x05,L,"Paper jam"},
+ {0x3B,0x06,L,"Failed to sense top-of-form"},
+ {0x3B,0x07,L,"Failed to sense bottom-of-form"},
+ {0x3B,0x08,T,"Reposition error"},
+ {0x3B,0x09,S,"Read past end of medium"},
+ {0x3B,0x0A,S,"Read past beginning of medium"},
+ {0x3B,0x0B,S,"Position past end of medium"},
+ {0x3B,0x0C,S,"Position past beginning of medium"},
+ {0x3B,0x0D,M,"Medium destination element full"},
+ {0x3B,0x0E,M,"Medium source element empty"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x50,0x00,T,"Write append error"},
+ {0x50,0x01,T,"Write append position error"},
+ {0x50,0x02,T,"Position error related to timing"},
+ {0x51,0x00,T|O,"Erase failure"},
+ {0x52,0x00,T,"Cartridge fault"},
+ {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x01,T,"Unload tape failure"},
+ {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x54,0x00,P,"Scsi to host system interface failure"},
+ {0x55,0x00,P,"System resource failure"},
+ {0x57,0x00,R,"Unable to recover table-of-contents"},
+ {0x58,0x00,O,"Generation does not exist"},
+ {0x59,0x00,O,"Updated block read"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
+ {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5C,0x00,D|O,"Rpl status change"},
+ {0x5C,0x01,D|O,"Spindles synchronized"},
+ {0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x60,0x00,S,"Lamp failure"},
+ {0x61,0x00,S,"Video acquisition error"},
+ {0x61,0x01,S,"Unable to acquire video"},
+ {0x61,0x02,S,"Out of focus"},
+ {0x62,0x00,S,"Scan head positioning error"},
+ {0x63,0x00,R,"End of user area encountered on this track"},
+ {0x64,0x00,R,"Illegal mode for this track"},
+ {0, 0, 0, NULL}
+};
+#endif
+
+#if (CONSTANTS & CONST_SENSE)
+static char *snstext[] = {
+ "None","Recovered Error","Not Ready","Medium Error","Hardware Error",
+ "Illegal Request","Unit Attention","Data Protect","Blank Check",
+ "Key=E","Key=F","Filemark","End-Of-Medium","Incorrect Block Length",
+ "14","15"};
+#endif
+
+
+/* Print sense information */
+void print_sense(char * devclass, Scsi_Cmnd * SCpnt)
+{
+ int i, s;
+ int sense_class, valid, code;
+ unsigned char * sense_buffer = SCpnt->sense_buffer;
+ char * error = NULL;
+ int dev = SCpnt->request.dev;
+
+ sense_class = (sense_buffer[0] >> 4) & 0x07;
+ code = sense_buffer[0] & 0xf;
+ valid = sense_buffer[0] & 0x80;
+
+ if (sense_class == 7) {
+ s = sense_buffer[7] + 8;
+ if(s > sizeof(SCpnt->sense_buffer)) s = sizeof(SCpnt->sense_buffer);
+
+ if (!valid)
+ printk("extra data not valid ");
+
+ if (sense_buffer[2] & 0x80) printk( "FMK ");
+ if (sense_buffer[2] & 0x40) printk( "EOM ");
+ if (sense_buffer[2] & 0x20) printk( "ILI ");
+
+ switch (code) {
+ case 0x0:
+ error = "Current";
+ break;
+ case 0x1:
+ error = "Deferred";
+ break;
+ default:
+ error = "Invalid";
+ }
+
+ printk("%s error ", error);
+
+#if (CONSTANTS & CONST_SENSE)
+ if (sense_buffer[2] & 0x80) printk( "FMK ");
+ if (sense_buffer[2] & 0x40) printk( "EOM ");
+ if (sense_buffer[2] & 0x20) printk( "ILI ");
+ printk( "%s%x: sense key %s\n", devclass, dev, snstext[sense_buffer[2] & 0x0f]);
+#else
+ printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]);
+#endif
+
+ /* Check to see if additional sense information is available */
+ if(sense_buffer[7] + 7 < 13 ||
+ (sense_buffer[12] == 0 && sense_buffer[13] == 0)) goto done;
+
+#if (CONSTANTS & CONST_XSENSE)
+ for(i=0; additional[i].text; i++)
+ if(additional[i].code1 == sense_buffer[12] &&
+ additional[i].code2 == sense_buffer[13])
+ printk("Additional sense indicates %s\n", additional[i].text);
+
+ for(i=0; additional2[i].text; i++)
+ if(additional2[i].code1 == sense_buffer[12] &&
+ additional2[i].code2_min >= sense_buffer[13] &&
+ additional2[i].code2_max <= sense_buffer[13]) {
+ printk("Additional sense indicates ");
+ printk(additional2[i].text, sense_buffer[13]);
+ printk("\n");
+ };
+#else
+ printk("ASC=%2x ASCQ=%2x\n", sense_buffer[12], sense_buffer[13]);
+#endif
+ } else {
+
+#if (CONSTANTS & CONST_SENSE)
+ if (sense_buffer[0] < 15)
+ printk("%s%x: old sense key %s\n", devclass, dev, snstext[sense_buffer[0] & 0x0f]);
+ else
+#endif
+ printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]);
+
+ printk("Non-extended sense class %d code 0x%0x ", sense_class, code);
+ s = 4;
+ }
+
+ done:
+ for (i = 0; i < s; ++i)
+ printk("0x%02x ", sense_buffer[i]);
+
+ return;
+}
+
+
+#endif CONFIG_SCSI
diff --git a/kernel/blk_drv/scsi/constants.h b/kernel/blk_drv/scsi/constants.h
new file mode 100644
index 0000000..7a37602
--- /dev/null
+++ b/kernel/blk_drv/scsi/constants.h
@@ -0,0 +1,6 @@
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+extern void print_command(unsigned char *);
+extern void print_sense(char *, Scsi_Cmnd *);
+extern void print_status(int);;
+#endif /* def _CONSTANTS_H */
diff --git a/kernel/blk_drv/scsi/fdomain.c b/kernel/blk_drv/scsi/fdomain.c
index 958ac9e..7bb9e19 100644
--- a/kernel/blk_drv/scsi/fdomain.c
+++ b/kernel/blk_drv/scsi/fdomain.c
@@ -1,10 +1,10 @@
/* fdomain.c -- Future Domain TMC-16x0 driver
* Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Sat Jul 3 13:48:18 1993 by faith@cs.unc.edu
+ * Revised: Fri Aug 13 22:44:05 1993 by faith@cs.unc.edu
* Author: Rickard E. Faith, faith@cs.unc.edu
* Copyright 1992, 1993 Rickard E. Faith
*
- * $Id: fdomain.c,v 3.17 1993/07/03 14:06:31 root Exp $
+ * $Id: fdomain.c,v 3.18 1993/08/13 22:44:12 root 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
@@ -17,8 +17,6 @@
* General Public License for more details.
**************************************************************************
-
-
DESCRIPTION:
@@ -141,7 +139,7 @@
#include <linux/errno.h>
#include <linux/string.h>
-#define VERSION "$Revision: 3.17 $"
+#define VERSION "$Revision: 3.18 $"
/* START OF USER DEFINABLE OPTIONS */
@@ -240,7 +238,6 @@ static int adapter_mask = 0x40;
static volatile int in_interrupt_flag = 0;
#endif
-
static int SCSI_Mode_Cntl_port;
static int FIFO_Data_Count_port;
static int Interrupt_Cntl_port;
@@ -277,11 +274,12 @@ static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
READ EVERY WORD, ESPECIALLY THE WORD *NOT*
- This driver works *ONLY* for Future Domain cards using the TMC-1800 chip.
- This includes models TMC-1660, 1670, and 1680 *ONLY*.
+ This driver works *ONLY* for Future Domain cards using the TMC-1800 or
+ the TMC-18C50 chip. This includes models TMC-1650, 1660, 1670, and 1680.
The following BIOS signature signatures are for boards which do *NOT*
- work with this driver (but they should work with the Seagate driver):
+ work with this driver (these TMC-8xx and TMC-9xx boards may work with the
+ Seagate driver):
FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
@@ -302,14 +300,22 @@ struct signature {
} signatures[] = {
/* 1 2 3 4 5 6 */
/* 123456789012345678901234567890123456789012345678901234567890 */
- { "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 },
+ { "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 },
+ { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1 },
/* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE
Also, fix the disk geometry code for your signature and send your
changes for faith@cs.unc.edu. Above all, do *NOT* change any old
- signatures! */
+ signatures!
+
+ Note that the last line will match a "generic" 18XX bios. Because
+ Future Domain has changed the host SCSI ID and/or the location of the
+ geometry information in the on-board RAM area for each of the first
+ three BIOS's, it is still important to enter a fully qualified
+ signature in the table for any new BIOS's (after the host SCSI ID and
+ geometry location are verified. */
};
#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
@@ -561,6 +567,46 @@ int fdomain_16x0_detect( int hostnum )
return 0;
}
+ this_host = hostnum;
+
+ if (!interrupt_level) {
+ panic( "Future Domain: *NO* interrupt level selected!\n" );
+ } else {
+ /* Register the IRQ with the kernel */
+
+ sa.sa_handler = fdomain_16x0_intr;
+ sa.sa_flags = SA_INTERRUPT;
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+
+ retcode = irqaction( interrupt_level, &sa );
+
+ if (retcode < 0) {
+ if (retcode == -EINVAL) {
+ printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
+ 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!\n" );
+ } else {
+ printk( "Future Domain: Error getting IRQ %d\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
+ }
+ panic( "Future Domain: Driver requires interruptions\n" );
+ } else {
+ printk( "Future Domain: IRQ %d requested from kernel\n",
+ interrupt_level );
+ }
+ }
+
+ if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
+ adapter_mask = 0x80;
+ scsi_hosts[this_host].this_id = 7;
+ }
+
#if DO_DETECT
/* These routines are here because of the way the SCSI bus behaves after
@@ -614,46 +660,6 @@ int fdomain_16x0_detect( int hostnum )
}
#endif
- this_host = hostnum;
-
- if (!interrupt_level) {
- panic( "Future Domain: *NO* interrupt level selected!\n" );
- } else {
- /* Register the IRQ with the kernel */
-
- sa.sa_handler = fdomain_16x0_intr;
- sa.sa_flags = SA_INTERRUPT;
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
-
- retcode = irqaction( interrupt_level, &sa );
-
- if (retcode < 0) {
- if (retcode == -EINVAL) {
- printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
- 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!\n" );
- } else {
- printk( "Future Domain: Error getting IRQ %d\n", interrupt_level );
- printk( " This shouldn't happen!\n" );
- printk( " Send mail to faith@cs.unc.edu\n" );
- }
- panic( "Future Domain: Driver requires interruptions\n" );
- } else {
- printk( "Future Domain: IRQ %d requested from kernel\n",
- interrupt_level );
- }
- }
-
- if (bios_major == 3 && bios_minor >= 2) {
- adapter_mask = 0x80;
- scsi_hosts[this_host].this_id = 7;
- }
-
return 1;
}
@@ -1306,12 +1312,31 @@ int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
return 0;
}
+/* The following code, which simulates the old-style command function, was
+ taken from Tommy Thorn's aha1542.c file. This code is Copyright (C)
+ 1992 Tommy Thorn. */
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done( Scsi_Cmnd *SCpnt )
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
{
- panic( "Future Domain: must use interrupt-driven driver\n" );
- return -1;
+ fdomain_16x0_queue( SCpnt, internal_done );
+
+ while (!internal_done_flag)
+ ;
+ internal_done_flag = 0;
+ return internal_done_errcode;
}
+/* End of code derived from Tommy Thorn's work. */
+
void print_info( Scsi_Cmnd *SCpnt )
{
unsigned int imr;
@@ -1490,12 +1515,24 @@ int fdomain_16x0_biosparam( int size, int dev, int *info_array )
info_array[2] = i->cylinders;
} else {
/* How the data is stored in the RAM area is very BIOS-dependent.
- Therefore, return nothing if we are not *SURE* of the information. */
+ Therefore, assume a version 3 layout, and check for validity. */
- info_array[0]
- = info_array[1]
- = info_array[2]
- = 0;
+ i = (struct drive_info *)( (char *)bios_base + 0x1f71 + drive * 10 );
+ info_array[0] = i->heads + 1;
+ info_array[1] = i->sectors;
+ info_array[2] = i->cylinders;
+
+ if (!info_array[0]
+ || !info_array[1]
+ || !info_array[2]
+ || info_array[2] > 1024 /* DOS uses only 10 bits */
+ ) {
+
+ info_array[0]
+ = info_array[1]
+ = info_array[2]
+ = 0;
+ }
}
return 0;
diff --git a/kernel/blk_drv/scsi/hosts.c b/kernel/blk_drv/scsi/hosts.c
index 50c9c3f..e1a7680 100644
--- a/kernel/blk_drv/scsi/hosts.c
+++ b/kernel/blk_drv/scsi/hosts.c
@@ -106,6 +106,8 @@ Scsi_Host scsi_hosts[] =
#endif
};
+#define MAX_SCSI_HOSTS (sizeof(scsi_hosts) / sizeof(Scsi_Host))
+
/*
* Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here.
*/
diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c
index 0fcc4ac..66b2222 100644
--- a/kernel/blk_drv/scsi/scsi.c
+++ b/kernel/blk_drv/scsi/scsi.c
@@ -23,11 +23,15 @@
#include "../blk.h"
#include "scsi.h"
#include "hosts.h"
+#include "constants.h"
/*
static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.1 1992/07/24 06:27:38 root Exp root $";
*/
+/* Command groups 3 and 4 are reserved and should never be used. */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
+
#define INTERNAL_ERROR (printk ("Internal error in file %s, line %d.\n", __FILE__, __LINE__), panic(""))
static void scsi_done (Scsi_Cmnd *SCpnt);
@@ -91,7 +95,7 @@ extern int last_reset[];
#define SENSE_TIMEOUT 50
#define RESET_TIMEOUT 50
#define ABORT_TIMEOUT 50
- #define MIN_RESET_DELAY 25
+ #define MIN_RESET_DELAY 100
#endif
/* The following devices are known not to tolerate a lun != 0 scan for
@@ -109,6 +113,9 @@ static struct blist blacklist[] =
{"SEAGATE","ST296","921"}, /* Responds to all lun */
{"SONY","CD-ROM CDU-541","4.3d"},
{"DENON","DRD-25X","V"}, /* A cdrom that locks up when probed at lun != 0 */
+ {"TEXEL","CD-ROM","1.06"}, /* causes failed REQUEST SENSE on lun 1 for seagate
+ * controller, which causes SCSI code to reset bus.*/
+ {"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */
{NULL, NULL, NULL}};
static int blacklisted(unsigned char * response_data){
@@ -174,13 +181,26 @@ static void scan_scsis (void)
time out */
for (dev = 0; dev < 8; ++dev)
if (scsi_hosts[host_nr].this_id != dev)
+/*
+ * We need the for so our continue, etc. work fine.
+ */
+
+#ifdef NO_MULTI_LUN
+ for (lun = 0; lun < 1; ++lun)
+#else
for (lun = 0; lun < 8; ++lun)
+#endif
{
scsi_devices[NR_SCSI_DEVICES].host_no = host_nr;
scsi_devices[NR_SCSI_DEVICES].id = dev;
scsi_devices[NR_SCSI_DEVICES].lun = lun;
scsi_devices[NR_SCSI_DEVICES].index = NR_SCSI_DEVICES;
scsi_devices[NR_SCSI_DEVICES].device_wait = NULL;
+/*
+ * Assume that the device will have handshaking problems, and then
+ * fix this field later if it turns out it doesn't.
+ */
+ scsi_devices[NR_SCSI_DEVICES].borken = 1;
scsi_cmd[0] = TEST_UNIT_READY;
scsi_cmd[1] = lun << 5;
@@ -196,13 +216,19 @@ static void scan_scsis (void)
SCmd.old_use_sg = 0;
SCmd.transfersize = 0;
SCmd.underflow = 0;
+ SCmd.index = NR_SCSI_DEVICES;
scsi_do_cmd (&SCmd,
(void *) scsi_cmd, (void *)
scsi_result, 256, scan_scsis_done,
- SCSI_TIMEOUT + 400, 3);
+ SCSI_TIMEOUT + 400, 5);
while (SCmd.request.dev != 0xfffe);
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
+ printk("scsi: return code %08x\n", SCmd.result);
+#endif
+
if(SCmd.result) {
if ((driver_byte(SCmd.result) & DRIVER_SENSE) &&
@@ -217,6 +243,10 @@ static void scan_scsis (void)
break;
};
+#if defined (DEBUG) || defined(DEBUG_INIT)
+ printk("scsi: performing INQUIRY\n");
+#endif
+
/*
* Build an INQUIRY command block.
*/
@@ -239,7 +269,15 @@ static void scan_scsis (void)
the_result = SCmd.result;
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ if (!the_result)
+ printk("scsi: INQUIRY successful\n");
+ else
+ printk("scsi: INQUIRY failed with code %08x\n");
+#endif
+
if(the_result) break;
+
/* skip other luns on this device */
if (!the_result)
@@ -273,6 +311,12 @@ static void scan_scsis (void)
scsi_devices[NR_SCSI_DEVICES].writeable = 0;
break;
default :
+#if 0
+#ifdef DEBUG
+ printk("scsi: unknown type %d\n", type);
+ print_inquiry(scsi_result);
+#endif
+#endif
type = -1;
}
@@ -311,6 +355,29 @@ static void scan_scsis (void)
(scsi_result[3] & 0x0f) == 1))
scsi_devices[NR_SCSI_DEVICES].scsi_level++;
+/*
+ * Some revisions of the Texel CD ROM drives have handshaking
+ * problems when used with the Seagate controllers. Before we
+ * know what type of device we're talking to, we assume it's
+ * borken and then change it here if it turns out that it isn't
+ * a TEXEL drive.
+ */
+
+ if(memcmp("TEXEL", &scsi_result[8], 5) != 0 ||
+ memcmp("CD-ROM", &scsi_result[16], 6) != 0
+/*
+ * XXX 1.06 has problems, some one should figure out the others too so
+ * ALL TEXEL drives don't suffer in performance, especially when I finish
+ * integrating my seagate patches which do multiple I_T_L nexuses.
+ */
+
+#ifdef notyet
+ || (strncmp("1.06", &scsi_result[[, 4) != 0)
+#endif
+ )
+ scsi_devices[NR_SCSI_DEVICES].borken = 0;
+
+
/* These devices need this "key" to unlock the device
so we can use it */
if(memcmp("INSITE", &scsi_result[8], 6) == 0 &&
@@ -693,6 +760,9 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
SCpnt->timeout_per_command = timeout;
memcpy ((void *) SCpnt->cmnd , (void *) cmnd, 10);
+ /* Zero the sense buffer. Some host adapters automatically request
+ sense on error. 0 is not a valid sense code. */
+ memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
SCpnt->request_buffer = buffer;
SCpnt->request_bufflen = bufflen;
SCpnt->old_use_sg = SCpnt->use_sg;
@@ -718,7 +788,7 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
static void reset (Scsi_Cmnd * SCpnt)
{
#ifdef DEBUG
- printk("reset(%d)\n", SCpnt->host);
+ printk("scsi: reset(%d)\n", SCpnt->host);
#endif
SCpnt->flags |= (WAS_RESET | IS_RESETTING);
@@ -735,9 +805,18 @@ static void reset (Scsi_Cmnd * SCpnt)
static int check_sense (Scsi_Cmnd * SCpnt)
{
- if (((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ /* If there is no sense information, request it. */
+ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7)
+ return SUGGEST_SENSE;
+
+#ifdef DEBUG_INIT
+ printk("scsi%d : ", SCpnt->host);
+ print_sense("", SCpnt);
+ printk("\n");
+#endif
if (SCpnt->sense_buffer[2] &0xe0)
return SUGGEST_ABORT;
+
switch (SCpnt->sense_buffer[2] & 0xf)
{
case NO_SENSE:
@@ -765,9 +844,6 @@ static int check_sense (Scsi_Cmnd * SCpnt)
return SUGGEST_ABORT;
}
}
- else
- return SUGGEST_RETRY;
- }
/* This function is the mid-level interrupt routine, which decides how
* to handle error conditions. Each invocation of this function must
@@ -824,14 +900,16 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
break;
}
- if (status_byte(result) && (SCpnt->flags &
- WAS_SENSE)) /* Failed to obtain sense information */
+ if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
+ /* Failed to obtain sense information */
{
SCpnt->flags &= ~WAS_SENSE;
SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
if (!(SCpnt->flags & WAS_RESET))
{
+ printk("scsi%d : target %d lun %d request sense failed, performing reset.\n",
+ SCpnt->host, SCpnt->target, SCpnt->lun);
reset(SCpnt);
return;
}
@@ -858,6 +936,7 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
switch (checked = check_sense(SCpnt))
{
+ case SUGGEST_SENSE:
case 0:
#ifdef DEBUG
printk("NO SENSE. status = REDO\n");
@@ -900,32 +979,41 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
break;
case CHECK_CONDITION:
-
-#ifdef DEBUG
- printk("CHECK CONDITION message returned, performing request sense.\n");
-#endif
-
+ switch (check_sense(SCpnt))
+ {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
scsi_request_sense (SCpnt);
status = PENDING;
break;
+ }
+ break;
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
-#ifdef DEBUG
- printk("CONDITION GOOD, INTERMEDIATE GOOD, or INTERMEDIATE CONDITION GOOD recieved and ignored. \n");
-#endif
break;
case BUSY:
-#ifdef DEBUG
- printk("BUSY message returned, performing REDO");
-#endif
update_timeout(SCpnt, oldto);
status = REDO;
break;
case RESERVATION_CONFLICT:
+ printk("scsi%d : RESERVATION CONFLICT performing reset.\n",
+ SCpnt->host);
reset(SCpnt);
return;
#if 0
@@ -987,12 +1075,29 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
case DID_RESET:
if(msg_byte(result) == GOOD &&
status_byte(result) == CHECK_CONDITION) {
+ switch (check_sense(SCpnt)) {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
scsi_request_sense (SCpnt);
status = PENDING;
break;
- };
+ }
+ } else {
status=REDO;
exit = SUGGEST_RETRY;
+ }
break;
default :
exit = (DRIVER_ERROR | SUGGEST_DIE);
@@ -1015,6 +1120,8 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
if ((SCpnt->retries >= (SCpnt->allowed >> 1))
&& !(SCpnt->flags & WAS_RESET))
{
+ printk("scsi%d : reseting for second half of retries.\n",
+ SCpnt->host);
reset(SCpnt);
break;
}
diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h
index c19c3e7..0b0e094 100644
--- a/kernel/blk_drv/scsi/scsi.h
+++ b/kernel/blk_drv/scsi/scsi.h
@@ -76,28 +76,8 @@
#define MODE_SELECT_10 0x55
#define MODE_SENSE_10 0x5a
-static __inline__ int COMMAND_SIZE (int opcode) {
- int group = (opcode >> 5) & 7;
- switch (group) {
- case 0:
- return 6;
- case 1:
- case 2:
- return 10;
- case 3:
- case 4:
- printk("COMMAND_SIZE : reserved command group %d\n", group);
- panic ("");
- case 5:
- return 12;
- default:
-#ifdef DEBUG
- printk("COMMAND_SIZE : vendor specific command group %d - assuming"
- " 10 bytes\n", group);
-#endif
- return 10;
- }
-}
+extern const unsigned char scsi_command_size[8];
+#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
/*
MESSAGE CODES
@@ -206,6 +186,7 @@ static __inline__ int COMMAND_SIZE (int opcode) {
#define SUGGEST_ABORT 0x20
#define SUGGEST_REMAP 0x30
#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
#define DRIVER_SENSE 0x08
@@ -280,6 +261,8 @@ typedef struct scsi_device {
unsigned changed:1; /* Data invalid due to media change */
unsigned busy:1; /* Used to prevent races */
unsigned lockable:1; /* Able to prevent media removal */
+ unsigned borken:1; /* Tell the Seagate driver to be
+ painfully slow on this device */
} Scsi_Device;
/*
Use these to separate status msg and our bytes
diff --git a/kernel/blk_drv/scsi/scsi_debug.c b/kernel/blk_drv/scsi/scsi_debug.c
index d17ab23..e5bc737 100644
--- a/kernel/blk_drv/scsi/scsi_debug.c
+++ b/kernel/blk_drv/scsi/scsi_debug.c
@@ -402,7 +402,7 @@ static void scsi_debug_intr_handle(void)
};
if(i == SCSI_DEBUG_MAILBOXES){
- pending = 0x7fffffff;
+ pending = INT_MAX;
for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
if (SCint[i] == 0) continue;
if (timeout[i] == 0) continue;
@@ -412,7 +412,7 @@ static void scsi_debug_intr_handle(void)
continue;
};
};
- if (pending && pending != 0x7fffffff) {
+ if (pending && pending != INT_MAX) {
timer_table[SCSI_DEBUG_TIMER].expires =
(pending <= jiffies ? jiffies+1 : pending);
timer_active |= 1 << SCSI_DEBUG_TIMER;
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c
index 1ba190e..f25fb77 100644
--- a/kernel/blk_drv/scsi/scsi_ioctl.c
+++ b/kernel/blk_drv/scsi/scsi_ioctl.c
@@ -217,6 +217,7 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
}
+
/*
the scsi_ioctl() function differs from most ioctls in that it does
not take a major/minor number as the dev filed. Rather, it takes
@@ -267,3 +268,19 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
return -EINVAL;
}
}
+
+/*
+ * Just like scsi_ioctl, only callable from kernel space with no
+ * fs segment fiddling.
+ */
+
+int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
+ unsigned long oldfs;
+ int tmp;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ tmp = scsi_ioctl (dev, cmd, arg);
+ set_fs(oldfs);
+ return tmp;
+}
+
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.h b/kernel/blk_drv/scsi/scsi_ioctl.h
index c88e7c4..dac9548 100644
--- a/kernel/blk_drv/scsi/scsi_ioctl.h
+++ b/kernel/blk_drv/scsi/scsi_ioctl.h
@@ -8,12 +8,12 @@
the cdrom */
#define SCSI_IOCTL_DOORLOCK 0x5380 /* lock the eject mechanism */
#define SCSI_IOCTL_DOORUNLOCK 0x5381 /* unlock the mechanism */
-#define SCSI_IOCTL_GET_IDLUN 0x5382
#define SCSI_REMOVAL_PREVENT 1
#define SCSI_REMOVAL_ALLOW 0
extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
#endif
diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c
index 1ffd3c4..9675767 100644
--- a/kernel/blk_drv/scsi/sd.c
+++ b/kernel/blk_drv/scsi/sd.c
@@ -25,6 +25,7 @@
#include "hosts.h"
#include "sd.h"
#include "scsi_ioctl.h"
+#include "constants.h"
#include <linux/genhd.h>
@@ -38,7 +39,7 @@ static const char RCSid[] = "$Header:";
* Time out in seconds
*/
-#define SD_TIMEOUT 200
+#define SD_TIMEOUT 300
struct hd_struct * sd;
@@ -245,11 +246,6 @@ static void rw_intr (Scsi_Cmnd *SCpnt)
*/
if (driver_byte(result) != 0) {
- {
- int i;
- printk("Dumping sense buffer: ");
- for(i=0;i<10;i++) printk(" %d",SCpnt->sense_buffer[i]);
- };
if (sugestion(result) == SUGGEST_REMAP) {
#ifdef REMAP
/*
@@ -301,11 +297,7 @@ of the disk.
rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->lun, result);
if (driver_byte(result) & DRIVER_SENSE)
- printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(SCpnt->sense_buffer[0]),
- sense_error(SCpnt->sense_buffer[0]),
- SCpnt->sense_buffer[2] & 0xf);
-
+ print_sense("sd", SCpnt);
end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
requeue_sd_request(SCpnt);
return;
@@ -675,7 +667,7 @@ static int sd_init_onedisk(int i)
{
int j = 0;
unsigned char cmd[10];
- unsigned char buffer[513];
+ unsigned char *buffer;
int the_result, retries;
Scsi_Cmnd * SCpnt;
@@ -684,19 +676,21 @@ static int sd_init_onedisk(int i)
bus reset. */
SCpnt = allocate_device(NULL, rscsi_disks[i].device->index, 1);
+ buffer = (unsigned char *) scsi_malloc(512);
retries = 3;
do {
cmd[0] = READ_CAPACITY;
cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
memset ((void *) &cmd[2], 0, 8);
+ memset ((void *) buffer, 0, 8);
SCpnt->request.dev = 0xffff; /* Mark as really busy again */
SCpnt->sense_buffer[0] = 0;
SCpnt->sense_buffer[2] = 0;
scsi_do_cmd (SCpnt,
(void *) cmd, (void *) buffer,
- 512, sd_init_done, SD_TIMEOUT,
+ 8, sd_init_done, SD_TIMEOUT,
MAX_RETRIES);
if (current == task[0])
@@ -784,6 +778,7 @@ static int sd_init_onedisk(int i)
rscsi_disks[j] = rscsi_disks[++j];
--i;
--NR_SD;
+ scsi_free(buffer, 512);
return i;
};
}
@@ -795,6 +790,7 @@ static int sd_init_onedisk(int i)
rscsi_disks[i].ten = 1;
rscsi_disks[i].remap = 1;
+ scsi_free(buffer, 512);
return i;
}
diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c
index 668ca79..9365cb6 100644
--- a/kernel/blk_drv/scsi/sd_ioctl.c
+++ b/kernel/blk_drv/scsi/sd_ioctl.c
@@ -52,6 +52,13 @@ int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne
put_fs_long(sd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!inode->i_rdev) return -EINVAL;
+ sync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
case BLKRRPART: /* Re-read partition tables */
return revalidate_scsidisk(dev, 1);
default:
diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c
index 3728360..1631d62 100644
--- a/kernel/blk_drv/scsi/seagate.c
+++ b/kernel/blk_drv/scsi/seagate.c
@@ -1,9 +1,15 @@
/*
- * seagate.c Copyright (C) 1992 Drew Eckhardt
- * low level scsi driver for ST01/ST02 by
+ * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ * low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ * TMC-950 by
+ *
* Drew Eckhardt
*
* <drew@colorado.edu>
+ *
+ * Note : TMC-880 boards don't work because they have two bits in
+ * the status register flipped, I'll fix this "RSN"
+ *
*/
/*
@@ -13,6 +19,18 @@
*
* -DFAST or -DFAST32 will use blind transfers where possible
*
+ * -DARBITRATE will cause the host adapter to arbitrate for the
+ * bus for better SCSI-II compatability, rather than just
+ * waiting for BUS FREE and then doing it's thing. Should
+ * let us do one command per Lun when I integrate my
+ * reorganization changes into the distribution sources.
+ *
+ * -DSLOW_HANDSHAKE will allow compatability with broken devices that don't
+ * handshake fast enough (ie, some CD ROM's) for the Seagate
+ * code.
+ *
+ * -DSLOW_RATE=x, x some number will let you specify a default
+ * transfer rate if handshaking isn't working correctly.
*/
#include <linux/config.h>
@@ -26,6 +44,8 @@
#include "scsi.h"
#include "hosts.h"
#include "seagate.h"
+#include "constants.h"
+
#ifndef IRQ
#define IRQ 5
@@ -35,6 +55,15 @@
#define FAST
#endif
+#if defined(SLOW_RATE) && !defined(SLOW_HANDSHAKE)
+#define SLOW_HANDSHAKE
+#endif
+
+#if defined(SLOW_HANDSHAKE) && !defined(SLOW_RATE)
+#define SLOW_RATE 50
+#endif
+
+
#if defined(LINKED)
#undef LINKED /* Linked commands are currently broken ! */
#endif
@@ -88,6 +117,7 @@ static unsigned char controller_type; /* set to SEAGATE for ST0x boards or FD fo
#define CONTROL STATUS
#define DATA (*(volatile unsigned char *) st0x_dr)
+
#ifndef OVERRIDE
static const char * seagate_bases[] = {
(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
@@ -152,15 +182,83 @@ SEAGATE SCSI BIOS REVISION 3.2
static int hostno = -1;
static void seagate_reconnect_intr(int);
+#ifdef FAST
+static int fast = 1;
+#endif
+
+#ifdef SLOW_HANDSHAKE
/*
- * We try to autodetect the 0ws jumper. If we get an over / under run,
- * then we assume that the the handshaking done by 0ws to synchronize the
- * SCSI and ISA busses failed, and disable fast transfers.
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem. Namely, a lack
+ * thereof for slow devices. You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer. In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems. In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
+ * transition low->high, and everything will work.
+ *
+ * However, there's nothing in the state machine that says
+ * you *HAVE* to see a high-low-high set of transitions before
+ * sending the next byte, and slow things like the Trantor CD ROMS
+ * will break because of this.
+ *
+ * So, we need to slow things down, which isn't as simple as it
+ * seems. We can't slow things down period, because then people
+ * who don't recompile their kernels will shoot me for ruining
+ * their performance. We need to do it on a case per case basis.
+ *
+ * The best for performance will be to, only for borken devices
+ * (this is stored on a per-target basis in the scsi_devices array)
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer. If we timeout, continue anyways. We don't need
+ * a long timeout, because REQ should only be asserted until the
+ * corresponding ACK is recieved and processed.
+ *
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
+ * gettimeofday() and the beeper routines use that. So,
+ * the best thing for us to do will be to calibrate a timing
+ * loop in the initialization code using the timer chip before
+ * gettimeofday() can screw with it.
*/
-#ifdef FAST
-static int fast = 1;
+static int borken_calibration = 0;
+static void borken_init (void) {
+ register int count = 0, start = jiffies + 1, stop = start + 25;
+
+ while (jiffies < start);
+ for (;jiffies < stop; ++count);
+
+/*
+ * Ok, we now have a count for .25 seconds. Convert to a
+ * count per second and divide by transer rate in K.
+ */
+
+ borken_calibration = (count * 4) / (SLOW_RATE*1024);
+
+ if (borken_calibration < 1)
+ borken_calibration = 1;
+#if (DEBUG & DEBUG_BORKEN)
+ printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n",
+ hostno, BORKEN_RATE, borken_calibration);
+#endif
+}
+
+static inline void borken_wait(void) {
+ register int count;
+ for (count = borken_calibration; count && (STATUS & STAT_REQ);
+ --count);
+ if (count)
+#if (DEBUG & DEBUG_BORKEN)
+ printk("scsi%d : borken timeout\n", hostno);
+#else
+ ;
#endif
+}
+
+#endif /* def SLOW_HANDSHAKE */
int seagate_st0x_detect (int hostnum)
{
@@ -207,6 +305,9 @@ static struct sigaction seagate_sigaction = {
* from the BIOS version notice in all the possible locations
* of the ROM's. This has a nice sideeffect of not trashing
* any register locations that might be used by something else.
+ *
+ * XXX - note that we probably should be probing the address
+ * space for the on-board RAM instead.
*/
for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i)
@@ -238,6 +339,10 @@ static struct sigaction seagate_sigaction = {
hostno, IRQ);
return 0;
}
+#ifdef SLOW_HANDSHAKE
+ borken_init();
+#endif
+
return -1;
}
else
@@ -252,6 +357,12 @@ static struct sigaction seagate_sigaction = {
const char *seagate_st0x_info(void) {
static char buffer[256];
sprintf(buffer, "scsi%d : %s at irq %d address %p options :"
+#ifdef ARBITRATE
+" ARBITRATE"
+#endif
+#ifdef SLOW_HANDSHAKE
+" SLOW_HANDSHAKE"
+#endif
#ifdef FAST
#ifdef FAST32
" FAST32"
@@ -459,13 +570,21 @@ static int internal_command(unsigned char target, unsigned char lun, const void
int nobuffs;
int clock;
int temp;
+#ifdef SLOW_HANDSHAKE
+ int borken; /* Does the current target require Very Slow I/O ? */
+#endif
+
+#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT)
+ int transfered = 0;
+#endif
-#if ((DEBUG & PHASE_ETC) || (DEBUG & PRINT_COMMAND) || (DEBUG & PHASE_EXIT))
+#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \
+ (DEBUG & PHASE_EXIT))
int i;
#endif
-#if (DEBUG & PHASE_ETC)
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
int phase=0, newphase;
#endif
@@ -479,10 +598,13 @@ static int internal_command(unsigned char target, unsigned char lun, const void
incommand = 0;
st0x_aborted = 0;
+#ifdef SLOW_HANDSHAKE
+ borken = (int) scsi_devices[SCint->index].borken;
+#endif
+
#if (DEBUG & PRINT_COMMAND)
printk ("scsi%d : target = %d, command = ", hostno, target);
- for (i = 0; i < COMMAND_SIZE(((unsigned char *)cmnd)[0]); ++i)
- printk("%02x ", ((unsigned char *) cmnd)[i]);
+ print_command((unsigned char *) cmnd);
printk("\n");
#endif
@@ -577,7 +699,11 @@ static int internal_command(unsigned char target, unsigned char lun, const void
* we must respond to the reselection by asserting BSY ourselves
*/
+#if 1
CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+#else
+ CONTROL = (BASE_CMD | CMD_BSY);
+#endif
/*
* The target will drop SEL, and raise BSY, at which time we must drop
@@ -632,6 +758,7 @@ connect_loop :
clock = jiffies + ST0X_BUS_FREE_DELAY;
+#if !defined (ARBITRATE)
while (((STATUS | STATUS | STATUS) &
(STAT_BSY | STAT_SEL)) &&
(!st0x_aborted) && (jiffies < clock));
@@ -640,19 +767,7 @@ connect_loop :
return retcode(DID_BUS_BUSY);
else if (st0x_aborted)
return retcode(st0x_aborted);
-
-/*
- * Bus free has been detected, within BUS settle. I used to
- * support an arbitration phase - however, on the Seagate, this
- * degraded performance by a factor > 10 - so it is no more.
- */
-
-/*
- * SELECTION PHASE
- *
- * Now, we select the disk, giving it the SCSI ID at data
- * and a command of PARITY if necessary, and we raise SEL.
- */
+#endif
#if (DEBUG & PHASE_SELECTION)
printk("scsi%d : phase = SELECTION\n", hostno);
@@ -661,35 +776,63 @@ connect_loop :
clock = jiffies + ST0X_SELECTION_DELAY;
/*
- * If we wish to disconnect, we should request a MESSAGE OUT
- * at this point. Technically, ATTN should be raised before
- * SEL = true and BSY = false (from arbitration), but I think this
- * should do.
+ * Arbitration/selection procedure :
+ * 1. Disable drivers
+ * 2. Write HOST adapter address bit
+ * 3. Set start arbitration.
+ * 4. We get either ARBITRATION COMPLETE or SELECT at this
+ * point.
+ * 5. OR our ID and targets on bus.
+ * 6. Enable SCSI drivers and asserted SEL and ATTN
*/
- if (reselect)
- CONTROL = BASE_CMD | CMD_DRVR_ENABLE |
- CMD_ATTN;
-/*
- * We must assert both our ID and our target's ID on the bus.
- */
- DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+#if defined(ARBITRATE)
+ cli();
+ CONTROL = 0;
+ DATA = (controller_type == SEAGATE) ? 0x80 : 0x40;
+ CONTROL = CMD_START_ARB;
+ sti();
+ while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
+ (jiffies < clock) && !st0x_aborted);
+
+ if (!(status_read & STAT_ARB_CMPL)) {
+#if (DEBUG & PHASE_SELECTION)
+ if (status_read & STAT_SEL)
+ printk("scsi%d : arbitration lost\n", hostno);
+ else
+ printk("scsi%d : arbitration timeout.\n", hostno);
+#endif
+ CONTROL = BASE_CMD;
+ return retcode(DID_NO_CONNECT);
+ };
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : arbitration complete\n", hostno);
+#endif
+#endif
-/*
- * If we are allowing ourselves to reconnect, then I will keep
- * ATTN raised so we get MSG OUT.
- */
- CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
- (reselect ? CMD_ATTN : 0);
/*
* When the SCSI device decides that we're gawking at it, it will
* respond by asserting BUSY on the bus.
+ *
+ * Note : the Seagate ST-01/02 product manual says that we should
+ * twiddle the DATA register before the control register. However,
+ * this does not work reliably so we do it the other way arround.
+ *
+ * Probably could be a problem with arbitration too, we really should
+ * try this with a SCSI protocol or logic analyzer to see what is
+ * going on.
*/
+ cli();
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0);
+ DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+ sti();
while (!((status_read = STATUS) & STAT_BSY) &&
(jiffies < clock) && !st0x_aborted)
-#if (DEBUG & PHASE_SELECTION)
+#if 0 && (DEBUG & PHASE_SELECTION)
{
temp = clock - jiffies;
@@ -705,10 +848,9 @@ connect_loop :
#endif
- if ((jiffies > clock) || (!st0x_aborted &&
- !(status_read & STAT_BSY)))
+ if ((jiffies >= clock) && !(status_read & STAT_BSY))
{
-#if (DEBUG & PHASE_SELECT)
+#if (DEBUG & PHASE_SELECTION)
printk ("scsi%d : NO CONNECT with target %d, status = %x \n",
hostno, target, STATUS);
#endif
@@ -724,6 +866,8 @@ connect_loop :
if (st0x_aborted) {
CONTROL = BASE_CMD;
if (STATUS & STAT_BSY) {
+ printk("scsi%d : BST asserted after we've been aborted.\n",
+ hostno);
seagate_st0x_reset();
return retcode(DID_RESET);
}
@@ -756,6 +900,10 @@ connect_loop :
data = (unsigned char *) SCint->request_buffer;
}
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT))
+ printk("scsi%d : len = %d\n", hostno, len);
+#endif
+
break;
#ifdef LINKED
case LINKED_RIGHT:
@@ -791,7 +939,7 @@ connect_loop :
* really adds up.
*/
-#if (DEBUG & PHASE_ETC)
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
#endif
@@ -813,14 +961,14 @@ connect_loop :
#ifdef PARITY
if (status_read & STAT_PARITY)
{
- done = 1;
+ printk("scsi%d : got parity error\n", hostno);
st0x_aborted = DID_PARITY;
}
#endif
if (status_read & STAT_REQ)
{
-#if (DEBUG & PHASE_ETC)
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
if ((newphase = (status_read & REQ_MASK)) != phase)
{
phase = newphase;
@@ -853,8 +1001,7 @@ connect_loop :
default :
printk("scsi%d : phase = UNKNOWN\n",
hostno);
- st0x_aborted = 1;
- done = 1;
+ st0x_aborted = DID_ERROR;
}
}
#endif
@@ -890,7 +1037,6 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
__asm__("
cld;
- movl %0, %%edi;
"
#ifdef FAST32
" shr $2, %%ecx;
@@ -904,9 +1050,9 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
#endif
" loop 1b;" : :
/* input */
- "r" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
+ "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
/* clobbered */
- "eax", "esi", "ecx", "edi");
+ "eax", "ecx", "esi" );
len -= transfersize;
data += transfersize;
@@ -936,8 +1082,6 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
Test for any data here at all.
*/
- "movl %0, %%esi\n" /* local value of data */
- "\tmovl %1, %%ecx\n" /* local value of len */
"\torl %%ecx, %%ecx
jz 2f
@@ -969,15 +1113,13 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
loop 1b
2:
- movl %%esi, %2
- movl %%ecx, %3
":
/* output */
-"=r" (data), "=r" (len) :
+"=S" (data), "=c" (len) :
/* input */
"0" (data), "1" (len) :
/* clobbered */
-"ebx", "ecx", "edi", "esi");
+"eax", "ebx", "edi");
}
if (!len && nobuffs) {
@@ -993,17 +1135,22 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
break;
case REQ_DATAIN :
-#ifdef FAST
-if (!len) {
-#if 0
- printk("scsi%d: overflow from target %d lun %d \n",
- hostno, target, lun);
- st0x_aborted = DID_ERROR;
- fast = 0;
-#endif
- break;
+#ifdef SLOW_HANDSHAKE
+ if (borken) {
+#if (DEBUG & (PHASE_DATAIN))
+ transfered += len;
+#endif
+ for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
+ STAT_REQ); --len) {
+ *data++ = DATA;
+ borken_wait();
}
-
+#if (DEBUG & (PHASE_DATAIN))
+ transfered -= len;
+#endif
+ } else
+#endif
+#ifdef FAST
if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
#ifdef FAST32
&& !(transfersize % 4)
@@ -1016,7 +1163,6 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
#endif
__asm__("
cld;
- movl %0, %%esi;
"
#ifdef FAST32
" shr $2, %%ecx;
@@ -1031,13 +1177,18 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
" loop 1b;" : :
/* input */
- "r" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
+ "S" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
/* clobbered */
- "eax", "ecx", "edi", "esi");
+ "eax", "ecx", "edi");
len -= transfersize;
data += transfersize;
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, transfersize);
+ transfered += transfersize;
+#endif
+
#if (DEBUG & DEBUG_FAST)
printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
hostno, len, data);
@@ -1046,6 +1197,13 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
} else
#endif
{
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, len);
+ transfered += len; /* Assume we'll transfer it all, then
+ subtract what we *didn't* transfer */
+#endif
+
/*
* We loop as long as we are in a data in phase, there is room to read,
* and BSY is still active
@@ -1061,9 +1219,6 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
Test for room to read
*/
-
- "movl %0, %%edi\n" /* data */
- "\tmovl %1, %%ecx\n" /* len */
"\torl %%ecx, %%ecx
jz 2f
@@ -1095,18 +1250,24 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
movb (%%ebx), %%al
stosb
- loop 1b
+ loop 1b\n"
-2: movl %%edi, %2\n" /* data */
- "\tmovl %%ecx, %3\n" /* len */
+"2:\n"
:
/* output */
-"=r" (data), "=r" (len) :
+"=D" (data), "=c" (len) :
/* input */
"0" (data), "1" (len) :
/* clobbered */
-"ebx", "ecx", "edi", "esi");
+"eax","ebx", "esi");
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered -= %d\n", hostno, len);
+ transfered -= len; /* Since we assumed all of Len got
+ * transfered, correct our mistake */
+#endif
}
+
if (!len && nobuffs) {
--nobuffs;
++buffer;
@@ -1126,6 +1287,10 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
if (status_read & STAT_REQ) {
DATA = *(unsigned char *) cmnd;
cmnd = 1+(unsigned char *) cmnd;
+#ifdef SLOW_HANDSHAKE
+ if (borken)
+ borken_wait();
+#endif
}
break;
@@ -1147,6 +1312,7 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
switch (reselect) {
case CAN_RECONNECT:
DATA = IDENTIFY(1, lun);
+
#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT))
printk("scsi%d : sent IDENTIFY message.\n", hostno);
#endif
@@ -1166,9 +1332,7 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
#endif
default:
DATA = NOP;
-#if (DEBUG & PHASE_MSGOUT)
- printk("scsi%d : sent NOP message.\n", hostno);
-#endif
+ printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
}
break;
@@ -1263,21 +1427,39 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
printk("scsi%d : unknown phase.\n", hostno);
st0x_aborted = DID_ERROR;
}
- } /* while ends */
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * I really don't care to deal with borken devices in each single
+ * byte transfer case (ie, message in, message out, status), so
+ * I'll do the wait here if necessary.
+ */
+ if (borken)
+ borken_wait();
+#endif
+
} /* if ends */
+ } /* while ends */
#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
- printk("Transfered %d bytes, allowed %d additional bytes\n", (bufflen - len), len);
+ printk("scsi%d : Transfered %d bytes\n", hostno, transfered);
#endif
#if (DEBUG & PHASE_EXIT)
+#if 0 /* Doesn't work for scatter / gather */
printk("Buffer : \n");
for (i = 0; i < 20; ++i)
printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */
printk("\n");
- printk("Status = %02x, message = %02x\n", status, message);
+#endif
+ printk("scsi%d : status = ", hostno);
+ print_status(status);
+ printk("message = %02x\n", message);
#endif
+
+/* We shouldn't reach this until *after* BSY has been deasserted */
+#ifdef notyet
if (st0x_aborted) {
if (STATUS & STAT_BSY) {
seagate_st0x_reset();
@@ -1285,6 +1467,8 @@ if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
}
abort_confirm = 1;
}
+#endif
+
#ifdef LINKED
else {
/*
@@ -1404,7 +1588,6 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
int *sizes, result, formatted_sectors, total_sectors;
int cylinders, heads, sectors;
- unsigned long oldfs;
Scsi_Device *disk;
disk = rscsi_disks[MINOR(dev) >> 4].device;
@@ -1420,14 +1603,6 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
sizes = (int *) buf;
data = (unsigned char *) (sizes + 2);
-/*
- * Point fs, which normally points to user space, at kernel space so that
- * we can do a syscall with the data coming from kernel space.
- */
-
- oldfs = get_fs();
- set_fs(get_ds());
-
cmd[0] = MODE_SENSE;
cmd[1] = (disk->lun << 5) & 0xe5;
cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
@@ -1445,7 +1620,7 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
memcpy (data, cmd, 6);
- if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
/*
* The mode page lies beyond the MODE SENSE header, with length 4, and
* the BLOCK DESCRIPTOR, with length header[3].
@@ -1458,7 +1633,7 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
cmd[2] = 0x03; /* Read page 3, format page current values */
memcpy (data, cmd, 6);
- if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
page = data + 4 + data[3];
sectors = (page[10] << 8) | page[11];
@@ -1484,7 +1659,10 @@ int seagate_st0x_biosparam(int size, int dev, int* ip) {
printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
hostno, heads, cylinders, sectors, total_sectors, formatted_sectors);
- cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
+ if (!heads || !sectors)
+ cylinders = 1025;
+ else
+ cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
/*
* Now, we need to do a sanity check on the geometry to see if it is
@@ -1508,7 +1686,6 @@ printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %
}
}
- set_fs(oldfs);
return result;
}
#endif /* CONFIG_BLK_DEV_SD */
diff --git a/kernel/blk_drv/scsi/seagate.h b/kernel/blk_drv/scsi/seagate.h
index 71287c4..0be87ab 100644
--- a/kernel/blk_drv/scsi/seagate.h
+++ b/kernel/blk_drv/scsi/seagate.h
@@ -12,7 +12,6 @@
$Header
*/
#ifndef ASM
-int seagate_st0x_biosparam(int, int, int*);
int seagate_st0x_detect(int);
int seagate_st0x_command(Scsi_Cmnd *);
int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
@@ -26,18 +25,16 @@ int seagate_st0x_reset(void);
#endif
#ifdef CONFIG_BLK_DEV_SD
-#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \
- seagate_st0x_info, seagate_st0x_command, \
- seagate_st0x_queue_command, seagate_st0x_abort, \
- seagate_st0x_reset, NULL, seagate_st0x_biosparam, \
- 1, 7, SG_ALL, 1, 0, 0}
+int seagate_st0x_biosparam(int, int, int*);
#else
+#define seagate_st0x_biosparam NULL
+#endif
+
#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \
seagate_st0x_info, seagate_st0x_command, \
seagate_st0x_queue_command, seagate_st0x_abort, \
- seagate_st0x_reset, NULL, NULL, \
+ seagate_st0x_reset, NULL, seagate_st0x_biosparam, \
1, 7, SG_ALL, 1, 0, 0}
-#endif /* CONFIG_BLK_DEV_SD */
#endif
@@ -47,13 +44,6 @@ int seagate_st0x_reset(void);
#define PARITY
-/*
- defining ARBITRATE causes the arbitration sequence to be used. And speed to drop by a
- factor of ten.
-*/
-
-#undef ARBITRATE
-
/*
Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
@@ -127,13 +117,15 @@ extern volatile int seagate_st0x_timeout;
#define DEBUG_FAST 0x1000
#define DEBUG_SG 0x2000
#define DEBUG_LINKED 0x4000
+#define DEBUG_BORKEN 0x8000
/*
* Control options - these are timeouts specified in .01 seconds.
*/
+/* 30, 20 work */
#define ST0X_BUS_FREE_DELAY 25
-#define ST0X_SELECTION_DELAY 15
+#define ST0X_SELECTION_DELAY 25
#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::)
diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c
index c745d83..32da76e 100644
--- a/kernel/blk_drv/scsi/sr.c
+++ b/kernel/blk_drv/scsi/sr.c
@@ -27,6 +27,7 @@
#include "hosts.h"
#include "sr.h"
#include "scsi_ioctl.h" /* For the door lock/unlock commands */
+#include "constants.h"
#define MAX_RETRIES 1
#define SR_TIMEOUT 500
@@ -247,15 +248,12 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->lun,
result);
- if (status_byte(result) == CHECK_CONDITION)
- printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(SCpnt->sense_buffer[0]),
- sense_error(SCpnt->sense_buffer[0]),
- SCpnt->sense_buffer[2] & 0xf);
-
- end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
- requeue_sr_request(SCpnt);
- }
+ if (status_byte(result) == CHECK_CONDITION)
+ print_sense("sr", SCpnt);
+
+ end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sr_request(SCpnt);
+ }
}
static int sr_open(struct inode * inode, struct file * filp)
diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c
index c8e226f..2c38c19 100644
--- a/kernel/blk_drv/scsi/st.c
+++ b/kernel/blk_drv/scsi/st.c
@@ -8,6 +8,8 @@
Features:
- support for different block sizes and internal buffering
+ - support for fixed and variable block size (within buffer limit;
+ blocksize set to zero)
- *nix-style ioctl with codes from mtio.h from the QIC-02 driver by
Hennus Bergman (command MTSETBLK added)
- character device
@@ -28,9 +30,10 @@
you should disable write behind and in addition to that check that the
tapes are readable.
- Kai Makisara, Nov 9, 1992 email makisara@vtinsx.ins.vtt.fi or
- Kai.Makisara@vtt.fi
- Last changes Jan 3, 1993.
+ Copyright 1992, 1993 Kai Makisara
+ email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
+
+ Last modified: Sat Jul 10 17:40:05 1993 by root
*/
#include <linux/fs.h>
@@ -50,7 +53,8 @@
#include "scsi_ioctl.h"
#include "st.h"
-#define MAX_RETRIES 5
+#define MAX_RETRIES 0
+#define MAX_READY_RETRIES 5
#define NO_TAPE NOT_READY
/* Uncomment the following if you want the rewind, etc. commands return
@@ -64,7 +68,7 @@
/* #define DEBUG */
-#define ST_TIMEOUT 6000
+#define ST_TIMEOUT 9000
#define ST_LONG_TIMEOUT 200000
/* Number of ST_BLOCK_SIZE blocks in the buffers */
@@ -76,6 +80,12 @@
#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)
#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)
+/* The buffer size should fit into the 24 bits reserved for length in the
+ 6-byte SCSI read and write commands. */
+#if ST_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
static int st_nbr_buffers;
static ST_buffer *st_buffers[2];
@@ -98,7 +108,7 @@ static void st_sleep_done (Scsi_Cmnd * SCpnt)
if (scsi_tapes[st_nbr].buffer->writing &&
(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40))
- scsi_tapes[st_nbr].buffer->last_result = 0x7fffffff;
+ scsi_tapes[st_nbr].buffer->last_result = INT_MAX;
else
scsi_tapes[st_nbr].buffer->last_result = SCpnt->result;
if (scsi_tapes[st_nbr].buffer->writing)
@@ -114,44 +124,18 @@ static void st_sleep_done (Scsi_Cmnd * SCpnt)
#endif
}
-
-
-#ifdef DEBUG
-/* Print sense information */
-static void decode_sns(int dev, char *sense_buffer)
-{
- static char *snstext[] = {
- "None","Recovered Error","Not Ready","Medium Error","Hardware Error",
- "Illegal Request","Unit Attention","Data Protect","Blank Check",
- "Key=E","Key=F","Filemark","End-Of-Medium","Incorrect Block Length",
- "14","15"};
-
- if (sense_buffer[0]!=0) {
- if ((sense_buffer[0] & 0x70) == 0x70) {
- if (sense_buffer[2] & 0x80) printk( "FMK ");
- if (sense_buffer[2] & 0x40) printk( "EOM ");
- if (sense_buffer[2] & 0x20) printk( "ILI ");
- printk( "st%d: sense key %s\n", dev, snstext[sense_buffer[2] & 0x0f]);
- } else {
- if (sense_buffer[0] < 15)
- printk("st%d: old sense key %s\n", dev, snstext[sense_buffer[0] & 0x0f]);
- else
- printk("st%d: sns = %2x %2x\n", dev, sense_buffer[0], sense_buffer[2]);
- }
- }
- return;
-}
-#endif
-
-
/* Convert the result to success code */
-static int st_chk_result(int dev, int result, unsigned char *sense)
+static int st_chk_result(Scsi_Cmnd * SCpnt)
{
+ int dev = SCpnt->request.dev;
+ int result = SCpnt->result;
+ char * sense = SCpnt->sense_buffer;
+
if (!result)
return 0;
#ifdef DEBUG
printk("st%d: Error: %x\n", dev, result);
- decode_sns(dev, sense);
+ print_sense("st", SCpnt);
#endif
if ((sense[0] & 0x70) == 0x70 &&
((sense[2] & 0x80) /* || ((sense[2] & 0x0f) == 8) */ ))
@@ -185,7 +169,7 @@ static void write_behind_check(int dev)
#endif
-/* Flush the write buffer */
+/* Flush the write buffer (never need to write if variable blocksize). */
static int flush_write_buffer(int dev)
{
int offset, transfer, blks;
@@ -236,7 +220,7 @@ static int flush_write_buffer(int dev)
if (SCpnt->result != 0) {
printk("st%d: Error on flush:\n", dev);
#ifdef DEBUG
- st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ st_chk_result(SCpnt);
#endif
result = (-EIO);
}
@@ -258,11 +242,14 @@ static int flush_buffer(struct inode * inode, struct file * filp,
int dev;
int backspace, result;
- dev = inode->i_rdev & 127;
+ dev = MINOR(inode->i_rdev) & 127;
if (scsi_tapes[dev].rw == 2) /* Writing */
return flush_write_buffer(dev);
+ if (scsi_tapes[dev].block_size == 0)
+ return 0;
+
backspace = (scsi_tapes[dev].buffer->buffer_bytes +
scsi_tapes[dev].buffer->read_pointer) / scsi_tapes[dev].block_size -
(scsi_tapes[dev].buffer->read_pointer + scsi_tapes[dev].block_size - 1) /
@@ -291,7 +278,7 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
- dev = inode->i_rdev & 127;
+ dev = MINOR(inode->i_rdev) & 127;
if (dev >= NR_ST)
return (-ENODEV);
if (scsi_tapes[dev].in_use) {
@@ -332,14 +319,15 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt,
(void *) cmd, (void *) scsi_tapes[dev].buffer->b_data,
- ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES);
+ ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT,
+ MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
#ifdef DEBUG
- decode_sns(dev, SCpnt->sense_buffer);
+ print_sense("st", SCpnt);
#endif
SCpnt->sense_buffer[0]=0;
memset ((void *) &cmd[0], 0, 10);
@@ -347,14 +335,15 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt,
(void *) cmd, (void *) scsi_tapes[dev].buffer->b_data,
- ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, MAX_RETRIES);
+ ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT,
+ MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
}
if (SCpnt->result != 0) {
#ifdef DEBUG
- decode_sns(dev, SCpnt->sense_buffer);
+ print_sense("st", SCpnt);
#endif
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE)
@@ -373,7 +362,7 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt,
(void *) cmd, (void *) scsi_tapes[dev].buffer->b_data,
- ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
@@ -401,11 +390,11 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt,
(void *) cmd, (void *) scsi_tapes[dev].buffer->b_data,
- ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
- i = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ i = st_chk_result(SCpnt);
if (i) {
#ifdef DEBUG
printk("st%d: No Mode Sense.\n", dev);
@@ -422,15 +411,18 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
#endif
if (scsi_tapes[dev].buffer->b_data[3] >= 8) {
+ scsi_tapes[dev].drv_buffer = (scsi_tapes[dev].buffer->b_data[2] >> 4) & 7;
+ scsi_tapes[dev].density = scsi_tapes[dev].buffer->b_data[4];
scsi_tapes[dev].block_size = scsi_tapes[dev].buffer->b_data[9] * 65536 +
scsi_tapes[dev].buffer->b_data[10] * 256 + scsi_tapes[dev].buffer->b_data[11];
#ifdef DEBUG
- printk("st%d: Density %x, tape length: %x, blocksize: %d\n", dev,
+ printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", dev,
scsi_tapes[dev].buffer->b_data[4], scsi_tapes[dev].buffer->b_data[5] *
65536 + scsi_tapes[dev].buffer->b_data[6] * 256 +
scsi_tapes[dev].buffer->b_data[7], scsi_tapes[dev].buffer->b_data[9] *
65536 + scsi_tapes[dev].buffer->b_data[10] * 256 +
- scsi_tapes[dev].buffer->b_data[11]);
+ scsi_tapes[dev].buffer->b_data[11],
+ scsi_tapes[dev].drv_buffer);
#endif
if (scsi_tapes[dev].block_size > ST_BUFFER_SIZE) {
printk("st%d: Blocksize %d too large for buffer.\n", dev,
@@ -440,24 +432,20 @@ static int scsi_tape_open(struct inode * inode, struct file * filp)
return (-EIO);
}
- if (scsi_tapes[dev].block_size == 0) {
- printk("st%d: Fixing block size to 512 bytes.\n", dev);
- if (st_int_ioctl(inode, filp, MTSETBLK, ST_BLOCK_SIZE)) {
- printk("st%d: Can't set fixed block size.\n", dev);
- scsi_tapes[dev].buffer->in_use = 0;
- scsi_tapes[dev].in_use = 0;
- return (-EIO);
- }
- scsi_tapes[dev].block_size = ST_BLOCK_SIZE;
- }
}
else
scsi_tapes[dev].block_size = ST_BLOCK_SIZE;
- scsi_tapes[dev].buffer->buffer_blocks =
- ST_BUFFER_SIZE / scsi_tapes[dev].block_size;
- scsi_tapes[dev].buffer->buffer_size =
- scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size;
+ if (scsi_tapes[dev].block_size > 0) {
+ scsi_tapes[dev].buffer->buffer_blocks =
+ ST_BUFFER_SIZE / scsi_tapes[dev].block_size;
+ scsi_tapes[dev].buffer->buffer_size =
+ scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size;
+ }
+ else {
+ scsi_tapes[dev].buffer->buffer_blocks = 1;
+ scsi_tapes[dev].buffer->buffer_size = ST_BUFFER_SIZE;
+ }
scsi_tapes[dev].buffer->buffer_bytes = scsi_tapes[dev].buffer->read_pointer = 0;
#ifdef DEBUG
@@ -486,7 +474,7 @@ static void scsi_tape_close(struct inode * inode, struct file * filp)
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
- dev = inode->i_rdev;
+ dev = MINOR(inode->i_rdev);
rewind = (dev & 0x80) == 0;
dev = dev & 127;
@@ -513,9 +501,9 @@ static void scsi_tape_close(struct inode * inode, struct file * filp)
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
if (SCpnt->result) {
- printk("st%d: Error on write filemark:\n", dev);
+ printk("st%d: Error on write filemark.\n", dev);
#ifdef DEBUG
- st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ st_chk_result(SCpnt);
#endif
}
SCpnt->request.dev = -1; /* Mark as not busy */
@@ -552,7 +540,7 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
char *b_point;
Scsi_Cmnd * SCpnt;
- dev = inode->i_rdev & 127;
+ dev = MINOR(inode->i_rdev) & 127;
#ifdef DEBUG
if (!scsi_tapes[dev].in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -563,6 +551,9 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
if (scsi_tapes[dev].write_prot)
return (-EACCES);
+ if (scsi_tapes[dev].block_size == 0 && count > ST_BUFFER_SIZE)
+ return (-EOVERFLOW);
+
if (scsi_tapes[dev].rw == 1) {
retval = flush_buffer(inode, filp, 0);
if (retval)
@@ -578,7 +569,7 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
printk("st%d: Async write error %x.\n", dev,
scsi_tapes[dev].buffer->last_result);
#endif
- /*if (scsi_tapes[dev].buffer->last_result = 0x7fffffff)
+ /*if (scsi_tapes[dev].buffer->last_result = INT_MAX)
retval = (-ENOSPC);
else */
retval = (-EIO);
@@ -593,19 +584,34 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
memset(cmd, 0, 10);
cmd[0] = WRITE_6;
- cmd[1] = 1;
+ cmd[1] = (scsi_tapes[dev].block_size != 0);
scsi_tapes[dev].rw = 2;
b_point = buf;
- while((scsi_tapes[dev].buffer->buffer_bytes + count) >=
- scsi_tapes[dev].buffer->buffer_size) {
- do_count = scsi_tapes[dev].buffer->buffer_size -
- scsi_tapes[dev].buffer->buffer_bytes;
+ while(
+#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
+ scsi_tapes[dev].block_size != 0 &&
+ (scsi_tapes[dev].buffer->buffer_bytes + count) >
+ scsi_tapes[dev].buffer->buffer_size)
+#else
+ (scsi_tapes[dev].block_size == 0 && count > 0) ||
+ (scsi_tapes[dev].buffer->buffer_bytes + count) >=
+ scsi_tapes[dev].buffer->buffer_size)
+#endif
+ {
+ if (scsi_tapes[dev].block_size == 0)
+ do_count = count;
+ else
+ do_count = scsi_tapes[dev].buffer->buffer_size -
+ scsi_tapes[dev].buffer->buffer_bytes;
memcpy_fromfs(scsi_tapes[dev].buffer->b_data +
- scsi_tapes[dev].buffer->buffer_bytes,b_point,do_count);
+ scsi_tapes[dev].buffer->buffer_bytes, b_point, do_count);
- blks = scsi_tapes[dev].buffer->buffer_blocks;
+ if (scsi_tapes[dev].block_size == 0)
+ blks = do_count;
+ else
+ blks = scsi_tapes[dev].buffer->buffer_blocks;
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
@@ -621,7 +627,7 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
if (SCpnt->result || SCpnt->sense_buffer[0] != 0) {
#ifdef DEBUG
printk("st%d: Error on write:\n", dev);
- st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ st_chk_result(SCpnt);
#endif
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40))
@@ -649,20 +655,27 @@ int st_write(struct inode * inode, struct file * filp, char * buf, int count)
count = 0;
}
- do_count = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ do_count = st_chk_result(SCpnt);
if (do_count) {
SCpnt->request.dev = -1;
return do_count;
}
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
- if (scsi_tapes[dev].buffer->buffer_bytes >= ST_WRITE_THRESHOLD) {
+ if (scsi_tapes[dev].buffer->buffer_bytes >= ST_WRITE_THRESHOLD ||
+ scsi_tapes[dev].block_size == 0) {
/* Schedule an asynchronous write */
- scsi_tapes[dev].buffer->writing = (scsi_tapes[dev].buffer->buffer_bytes /
- scsi_tapes[dev].block_size) * scsi_tapes[dev].block_size;
+ if (scsi_tapes[dev].block_size == 0)
+ scsi_tapes[dev].buffer->writing = scsi_tapes[dev].buffer->buffer_bytes;
+ else
+ scsi_tapes[dev].buffer->writing = (scsi_tapes[dev].buffer->buffer_bytes /
+ scsi_tapes[dev].block_size) * scsi_tapes[dev].block_size;
scsi_tapes[dev].dirty = 0;
- blks = scsi_tapes[dev].buffer->writing / scsi_tapes[dev].block_size;
+ if (scsi_tapes[dev].block_size == 0)
+ blks = scsi_tapes[dev].buffer->writing;
+ else
+ blks = scsi_tapes[dev].buffer->writing / scsi_tapes[dev].block_size;
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
@@ -687,11 +700,11 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
{
int dev;
int total;
- int transfer, blks;
+ int transfer, blks, bytes;
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
- dev = inode->i_rdev & 127;
+ dev = MINOR(inode->i_rdev) & 127;
#ifdef DEBUG
if (!scsi_tapes[dev].in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -699,6 +712,9 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
}
#endif
+ if (scsi_tapes[dev].block_size == 0 && count > ST_BUFFER_SIZE)
+ return (-EOVERFLOW);
+
if (scsi_tapes[dev].rw == 2) {
transfer = flush_buffer(inode, filp, 0);
if (transfer)
@@ -726,8 +742,13 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
memset(cmd, 0, 10);
cmd[0] = READ_6;
- cmd[1] = 1;
- blks = scsi_tapes[dev].buffer->buffer_blocks;
+ cmd[1] = (scsi_tapes[dev].block_size != 0);
+ if (scsi_tapes[dev].block_size == 0)
+ blks = bytes = count;
+ else {
+ blks = scsi_tapes[dev].buffer->buffer_blocks;
+ bytes = blks * scsi_tapes[dev].block_size;
+ }
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
@@ -760,9 +781,16 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
(SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
if (SCpnt->sense_buffer[2] & 0x20) {
- printk("st%d: Incorrect block size.\n", dev);
- SCpnt->request.dev = -1; /* Mark as not busy */
- return (-EIO);
+ if (scsi_tapes[dev].block_size == 0) {
+ if (transfer <= 0)
+ transfer = 0;
+ scsi_tapes[dev].buffer->buffer_bytes = count - transfer;
+ }
+ else {
+ printk("st%d: Incorrect block size.\n", dev);
+ SCpnt->request.dev = -1; /* Mark as not busy */
+ return (-EIO);
+ }
}
else if (SCpnt->sense_buffer[2] & 0x40) {
scsi_tapes[dev].eof = 2; /* What should be done at EOM ? */
@@ -776,9 +804,13 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
}
else if (SCpnt->sense_buffer[2] & 0x80) {
scsi_tapes[dev].eof = 1;
- scsi_tapes[dev].buffer->buffer_bytes =
- (scsi_tapes[dev].buffer->buffer_blocks - transfer) *
- scsi_tapes[dev].block_size;
+ if (scsi_tapes[dev].block_size == 0)
+ scsi_tapes[dev].buffer->buffer_bytes =
+ count - transfer;
+ else
+ scsi_tapes[dev].buffer->buffer_bytes =
+ (scsi_tapes[dev].buffer->buffer_blocks - transfer) *
+ scsi_tapes[dev].block_size;
#ifdef DEBUG
printk("st%d: EOF detected (%d blocks read, transferred %d bytes).\n",
dev, scsi_tapes[dev].buffer->buffer_blocks - transfer, total);
@@ -789,7 +821,7 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
#ifdef DEBUG
printk("st%d: Tape error. Sense key %x\n", dev,
SCpnt->sense_buffer[2] & 0x0f);
- decode_sns(dev, SCpnt->sense_buffer);
+ print_sense("st", SCpnt);
#endif
SCpnt->request.dev = -1;
if (total)
@@ -799,14 +831,14 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
}
}
else {
- transfer = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ transfer = st_chk_result(SCpnt);
SCpnt->request.dev = -1; /* Mark as not busy */
return transfer;
}
}
- else
- scsi_tapes[dev].buffer->buffer_bytes =
- scsi_tapes[dev].buffer->buffer_size;
+ else /* Read successfu| */
+ scsi_tapes[dev].buffer->buffer_bytes = bytes;
+
} /* if (scsi_tapes[dev].buffer->buffer_bytes == 0 &&
scsi_tapes[dev].eof == 0) */
@@ -836,6 +868,9 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
return total;
}
+ if (scsi_tapes[dev].block_size == 0)
+ count = total; /* Read only one variable length block */
+
} /* for (total = 0; total < count; ) */
SCpnt->request.dev = -1; /* Mark as not busy */
@@ -848,7 +883,7 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count)
static int st_int_ioctl(struct inode * inode,struct file * file,
unsigned int cmd_in, unsigned long arg)
{
- int dev = inode->i_rdev;
+ int dev = MINOR(inode->i_rdev);
int timeout = ST_LONG_TIMEOUT;
long ltmp;
int ioctl_result;
@@ -1003,9 +1038,11 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
break;
case MTSETBLK: /* Set block length */
case MTSETDENSITY: /* Set tape density */
+ case MTSETDRVBUFFER: /* Set drive buffering */
if (scsi_tapes[dev].dirty || scsi_tapes[dev].buffer->buffer_bytes != 0)
return (-EIO); /* Not allowed if data in buffer */
if (cmd_in == MTSETBLK &&
+ arg != 0 &&
(arg < scsi_tapes[dev].min_block || arg > scsi_tapes[dev].max_block ||
arg > ST_BUFFER_SIZE)) {
printk("st%d: Illegal block size.\n", dev);
@@ -1015,14 +1052,20 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
cmd[4] = 12;
memset(scsi_tapes[dev].buffer->b_data, 0, 12);
- scsi_tapes[dev].buffer->b_data[2] = 0x10; /* buffered mode */
+ if (cmd_in == MTSETDRVBUFFER)
+ scsi_tapes[dev].buffer->b_data[2] = (arg & 7) << 4;
+ else
+ scsi_tapes[dev].buffer->b_data[2] =
+ scsi_tapes[dev].drv_buffer << 4;
scsi_tapes[dev].buffer->b_data[3] = 8; /* block descriptor length */
+ if (cmd_in == MTSETDENSITY)
+ scsi_tapes[dev].buffer->b_data[4] = arg;
+ else
+ scsi_tapes[dev].buffer->b_data[4] = scsi_tapes[dev].density;
if (cmd_in == MTSETBLK)
ltmp = arg;
- else {
- scsi_tapes[dev].buffer->b_data[4] = arg;
+ else
ltmp = scsi_tapes[dev].block_size;
- }
scsi_tapes[dev].buffer->b_data[9] = (ltmp >> 16);
scsi_tapes[dev].buffer->b_data[10] = (ltmp >> 8);
scsi_tapes[dev].buffer->b_data[11] = ltmp;
@@ -1033,9 +1076,12 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
scsi_tapes[dev].buffer->b_data[9] * 65536 +
scsi_tapes[dev].buffer->b_data[10] * 256 +
scsi_tapes[dev].buffer->b_data[11]);
- else
+ else if (cmd_in == MTSETDENSITY)
printk("st%d: Setting density code to %x.\n", dev,
scsi_tapes[dev].buffer->b_data[4]);
+ else
+ printk("st%d: Setting drive buffer code to %d.\n",
+ (scsi_tapes[dev].buffer->b_data[2] >> 4) & 7);
#endif
break;
default:
@@ -1052,9 +1098,9 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
- SCpnt->request.dev = -1; /* Mark as not busy */
+ ioctl_result = st_chk_result(SCpnt);
- ioctl_result = st_chk_result(dev, SCpnt->result, SCpnt->sense_buffer);
+ SCpnt->request.dev = -1; /* Mark as not busy */
if (!ioctl_result) {
if (cmd_in == MTBSFM)
@@ -1063,13 +1109,23 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
ioctl_result = st_int_ioctl(inode, file, MTBSF, 1);
else if (cmd_in == MTSETBLK) {
scsi_tapes[dev].block_size = arg;
- scsi_tapes[dev].buffer->buffer_blocks =
- ST_BUFFER_SIZE / scsi_tapes[dev].block_size;
- scsi_tapes[dev].buffer->buffer_size =
- scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size;
+ if (arg != 0) {
+ scsi_tapes[dev].buffer->buffer_blocks =
+ ST_BUFFER_SIZE / scsi_tapes[dev].block_size;
+ scsi_tapes[dev].buffer->buffer_size =
+ scsi_tapes[dev].buffer->buffer_blocks * scsi_tapes[dev].block_size;
+ }
+ else {
+ scsi_tapes[dev].buffer->buffer_blocks = 1;
+ scsi_tapes[dev].buffer->buffer_size = ST_BUFFER_SIZE;
+ }
scsi_tapes[dev].buffer->buffer_bytes =
scsi_tapes[dev].buffer->read_pointer = 0;
}
+ else if (cmd_in == MTSETDRVBUFFER)
+ scsi_tapes[dev].drv_buffer = arg;
+ else if (cmd_in == MTSETDENSITY)
+ scsi_tapes[dev].density = arg;
if (cmd_in == MTEOM || cmd_in == MTWEOF) {
scsi_tapes[dev].eof = 2;
scsi_tapes[dev].eof_hit = 0;
@@ -1089,7 +1145,7 @@ static int st_int_ioctl(struct inode * inode,struct file * file,
static int st_ioctl(struct inode * inode,struct file * file,
unsigned int cmd_in, unsigned long arg)
{
- int dev = inode->i_rdev;
+ int dev = MINOR(inode->i_rdev);
int i, cmd, result;
struct mtop mtc;
struct mtpos mt_pos;
@@ -1167,7 +1223,7 @@ static int st_ioctl(struct inode * inode,struct file * file,
SCpnt->sense_buffer[0] = 0;
scsi_do_cmd(SCpnt,
(void *) scmd, (void *) scsi_tapes[dev].buffer->b_data,
- ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &scsi_tapes[dev].waiting );
@@ -1249,6 +1305,8 @@ unsigned long st_init(unsigned long mem_start, unsigned long mem_end)
scsi_tapes[i].eof = 0;
scsi_tapes[i].waiting = NULL;
scsi_tapes[i].in_use = 0;
+ scsi_tapes[i].drv_buffer = 1; /* Try buffering if no mode sense */
+ scsi_tapes[i].density = 0;
}
diff --git a/kernel/blk_drv/scsi/st.h b/kernel/blk_drv/scsi/st.h
index 1df386c..0c50024 100644
--- a/kernel/blk_drv/scsi/st.h
+++ b/kernel/blk_drv/scsi/st.h
@@ -31,6 +31,8 @@ typedef struct {
unsigned write_prot:1;
unsigned in_use:1;
unsigned eof_hit:1;
+ unsigned drv_buffer:3;
+ unsigned char density;
ST_buffer * buffer;
int block_size;
int min_block;
diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c
index 35fb4e8..cc5bbca 100644
--- a/kernel/blk_drv/scsi/ultrastor.c
+++ b/kernel/blk_drv/scsi/ultrastor.c
@@ -384,7 +384,14 @@ int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
mscp.opcode = OP_SCSI;
mscp.xdir = DTD_SCSI;
mscp.dcn = FALSE;
- mscp.ca = TRUE;
+ /* Tape drives don't work properly if the cache is used. The SCSI
+ READ command for a tape doesn't have a block offset, and the adapter
+ incorrectly assumes that all reads from the tape read the same
+ blocks. Results will depend on read buffer size and other disk
+ activity.
+
+ ??? Which other device types should never use the cache? */
+ mscp.ca = scsi_devices[SCpnt->index].type != TYPE_TAPE;
mscp.target_id = SCpnt->target;
mscp.ch_no = 0;
mscp.lun = SCpnt->lun;
diff --git a/kernel/blk_drv/scsi/ultrastor.h b/kernel/blk_drv/scsi/ultrastor.h
index d306c74..f79ed5d 100644
--- a/kernel/blk_drv/scsi/ultrastor.h
+++ b/kernel/blk_drv/scsi/ultrastor.h
@@ -33,7 +33,7 @@ int ultrastor_biosparam(int, int, int *);
{ "UltraStor 14F", ultrastor_detect, ultrastor_info, 0, \
ultrastor_queuecommand, ultrastor_abort, ultrastor_reset, \
0, ultrastor_biosparam, ULTRASTOR_14F_MAX_CMDS, 0, \
- ULTRASTOR_14F_MAX_SG, ULTRASTOR_14F_MAX_CMDS_PER_LUN, 0, 0 }
+ ULTRASTOR_14F_MAX_SG, ULTRASTOR_14F_MAX_CMDS_PER_LUN, 0, 1 }
#ifdef ULTRASTOR_PRIVATE
diff --git a/kernel/blk_drv/xd.c b/kernel/blk_drv/xd.c
index be1105f..14a4c3c 100644
--- a/kernel/blk_drv/xd.c
+++ b/kernel/blk_drv/xd.c
@@ -219,6 +219,13 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
return (0);
}
break;
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!inode->i_rdev) return -EINVAL;
+ sync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
case BLKRRPART: return (xd_reread_partitions(inode->i_rdev));
RO_IOCTLS(inode->i_rdev,arg);
}
diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile
index 0525714..d864667 100644
--- a/kernel/chr_drv/Makefile
+++ b/kernel/chr_drv/Makefile
@@ -21,7 +21,7 @@ SUBDIRS= sound
OBJS = tty_io.o console.o keyboard.o serial.o \
tty_ioctl.o pty.o lp.o vt.o mem.o mouse.o \
busmouse.o psaux.o msbusmouse.o atixlmouse.o \
- tpqic02.o
+ tpqic02.o defkeymap.o
all: chr_drv.a subdirs
@@ -37,7 +37,7 @@ clean:
for i in $(SUBDIRS); do (cd $$i && $(MAKE) clean) || exit; done
dep:
- $(CPP) -M $(KEYBOARD) *.c > .depend
+ $(CPP) -M *.c > .depend
for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
dummy:
diff --git a/kernel/chr_drv/atixlmouse.c b/kernel/chr_drv/atixlmouse.c
index f33e497..350e557 100644
--- a/kernel/chr_drv/atixlmouse.c
+++ b/kernel/chr_drv/atixlmouse.c
@@ -52,8 +52,8 @@
/* Same general mouse structure */
static struct mouse_status {
- char buttons;
- char latch_buttons;
+ unsigned char buttons;
+ unsigned char latch_buttons;
int dx;
int dy;
int present;
@@ -70,7 +70,7 @@ void mouse_interrupt(int unused)
outb(ATIXL_MSE_READ_Y, ATIXL_MSE_CONTROL_PORT); /* Select IR2 - Y movement */
mouse.dy += inb( ATIXL_MSE_DATA_PORT);
outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */
- mouse.latch_buttons |= inb( ATIXL_MSE_DATA_PORT);
+ mouse.latch_buttons |= inb(ATIXL_MSE_DATA_PORT);
ATIXL_MSE_ENABLE_UPDATE();
mouse.ready = 1;
wake_up_interruptible(&mouse.wait);
@@ -118,9 +118,9 @@ static int read_mouse(struct inode * inode, struct file * file, char * buffer, i
return -EAGAIN;
ATIXL_MSE_DISABLE_UPDATE();
/* Allowed interrupts to occur during data gathering - shouldn't hurt */
- put_fs_byte((char)(~mouse.latch_buttons&7) | 0x80 , buffer);
- put_fs_byte((char)mouse.dx, buffer + 1);
- put_fs_byte((char)-mouse.dy, buffer + 2);
+ put_fs_byte((~mouse.latch_buttons & 7) | 0x80 , buffer);
+ put_fs_byte(mouse.dx, buffer + 1);
+ put_fs_byte(-mouse.dy, buffer + 2);
mouse.dx = 0;
mouse.dy = 0;
mouse.latch_buttons = mouse.buttons;
diff --git a/kernel/chr_drv/busmouse.c b/kernel/chr_drv/busmouse.c
index 8710e37..79703b4 100644
--- a/kernel/chr_drv/busmouse.c
+++ b/kernel/chr_drv/busmouse.c
@@ -42,7 +42,8 @@ void bmouse_setup(char *str, int *ints)
static void mouse_interrupt(int unused)
{
- char dx, dy, buttons;
+ char dx, dy;
+ unsigned char buttons;
MSE_INT_OFF();
outb(MSE_READ_X_LOW, MSE_CONTROL_PORT);
diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c
index 7295fe8..cd8523c 100644
--- a/kernel/chr_drv/console.c
+++ b/kernel/chr_drv/console.c
@@ -21,6 +21,8 @@
*
* Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
* Chars, and VT100 enhancements by Peter MacDonald.
+ *
+ * Copy and paste function by Andrew Haylett.
*/
/*
@@ -222,9 +224,9 @@ static unsigned char * translations[] = {
"\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
- "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
+ "\376\245\376\376\376\376\231\376\235\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",
+ "\376\244\225\242\223\376\224\366\233\227\243\226\201\376\376\230",
/* vt100 graphics */
(unsigned char *)
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -308,6 +310,9 @@ static unsigned short __origin;
static inline void __set_origin(unsigned short offset)
{
+#ifdef CONFIG_SELECTION
+ clear_selection();
+#endif /* CONFIG_SELECTION */
cli();
__origin = offset;
outb_p(12, video_port_reg);
@@ -1545,13 +1550,16 @@ int con_open(struct tty_struct *tty, struct file * filp)
}
#ifdef CONFIG_SELECTION
+/* correction factor for when screen is hardware-scrolled */
+#define hwscroll_offset ((__real_origin - __origin) << 1)
+
/* set reverse video on characters s-e of console with selection. */
static void highlight(const int currcons, const int s, const int e)
{
unsigned char *p, *p1, *p2;
- p1 = (unsigned char *)origin + s + 1;
- p2 = (unsigned char *)origin + e + 1;
+ p1 = (unsigned char *)origin - hwscroll_offset + s + 1;
+ p2 = (unsigned char *)origin - hwscroll_offset + e + 1;
if (p1 > p2)
{
p = p1;
@@ -1585,7 +1593,7 @@ int set_selection(const int arg)
int sel_mode, new_sel_start, new_sel_end, spc;
char *bp, *obp, *spos;
int i, ps, pe;
- char *off = (char *)origin;
+ char *off = (char *)origin - hwscroll_offset;
unblank_screen();
args = (unsigned short *)(arg + 1);
@@ -1689,7 +1697,7 @@ int set_selection(const int arg)
obp = bp = sel_buffer;
for (i = sel_start; i <= sel_end; i += 2)
{
- spos = (char *)origin + i;
+ spos = (char *)off + i;
*bp++ = *spos;
if (!isspace(*spos))
obp = bp;
@@ -1719,6 +1727,9 @@ int paste_selection(struct tty_struct *tty)
{
char *bp = sel_buffer;
+ if (! *bp)
+ return 0;
+ unblank_screen();
while (*bp)
{
put_tty_queue(*bp, &tty->read_q);
diff --git a/kernel/chr_drv/defkeymap.c b/kernel/chr_drv/defkeymap.c
new file mode 100644
index 0000000..3193e1f
--- /dev/null
+++ b/kernel/chr_drv/defkeymap.c
@@ -0,0 +1,351 @@
+/* Automatically generated by mktable */
+/* Do not edit this file! */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+
+u_short key_map[NR_KEYMAPS][NR_KEYS] = {
+ {
+ 0x0200, 0x001b, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036,
+ 0x0037, 0x0038, 0x0039, 0x0030, 0x002d, 0x003d, 0x007f, 0x0009,
+ 0x0071, 0x0077, 0x0065, 0x0072, 0x0074, 0x0079, 0x0075, 0x0069,
+ 0x006f, 0x0070, 0x005b, 0x005d, 0x0201, 0x0702, 0x0061, 0x0073,
+ 0x0064, 0x0066, 0x0067, 0x0068, 0x006a, 0x006b, 0x006c, 0x003b,
+ 0x0027, 0x0060, 0x0700, 0x005c, 0x007a, 0x0078, 0x0063, 0x0076,
+ 0x0062, 0x006e, 0x006d, 0x002c, 0x002e, 0x002f, 0x0700, 0x030c,
+ 0x0703, 0x0020, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
+ 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0209, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003c, 0x010a,
+ 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x001b, 0x0021, 0x0040, 0x0023, 0x0024, 0x0025, 0x005e,
+ 0x0026, 0x002a, 0x0028, 0x0029, 0x005f, 0x002b, 0x007f, 0x0009,
+ 0x0051, 0x0057, 0x0045, 0x0052, 0x0054, 0x0059, 0x0055, 0x0049,
+ 0x004f, 0x0050, 0x007b, 0x007d, 0x0201, 0x0702, 0x0041, 0x0053,
+ 0x0044, 0x0046, 0x0047, 0x0048, 0x004a, 0x004b, 0x004c, 0x003a,
+ 0x0022, 0x007e, 0x0700, 0x007c, 0x005a, 0x0058, 0x0043, 0x0056,
+ 0x0042, 0x004e, 0x004d, 0x003c, 0x003e, 0x003f, 0x0700, 0x030c,
+ 0x0703, 0x0020, 0x0207, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e,
+ 0x010f, 0x0110, 0x0111, 0x0112, 0x0113, 0x0208, 0x0203, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x003e, 0x010a,
+ 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x020b, 0x0601, 0x0602, 0x0117, 0x0600, 0x020a, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0040, 0x0200, 0x0024, 0x0200, 0x0200,
+ 0x007b, 0x005b, 0x005d, 0x007d, 0x005c, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x007e, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x050c, 0x050d, 0x050e, 0x050f, 0x0510,
+ 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0208, 0x0202, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x007c, 0x0516,
+ 0x0517, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0000, 0x001b, 0x001c, 0x001d, 0x001e,
+ 0x001f, 0x007f, 0x0200, 0x0200, 0x001f, 0x0200, 0x0200, 0x0200,
+ 0x0011, 0x0017, 0x0005, 0x0012, 0x0014, 0x0019, 0x0015, 0x0009,
+ 0x000f, 0x0010, 0x001b, 0x001d, 0x0201, 0x0702, 0x0001, 0x0013,
+ 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, 0x0200,
+ 0x0007, 0x0000, 0x0700, 0x001c, 0x001a, 0x0018, 0x0003, 0x0016,
+ 0x0002, 0x000e, 0x000d, 0x0200, 0x0200, 0x007f, 0x0700, 0x030c,
+ 0x0703, 0x0000, 0x0207, 0x0100, 0x0101, 0x0102, 0x0103, 0x0104,
+ 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x0208, 0x0204, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x010a,
+ 0x010b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x081b, 0x0831, 0x0832, 0x0833, 0x0834, 0x0835, 0x0836,
+ 0x0837, 0x0838, 0x0839, 0x0830, 0x082d, 0x083d, 0x087f, 0x0809,
+ 0x0871, 0x0877, 0x0865, 0x0872, 0x0874, 0x0879, 0x0875, 0x0869,
+ 0x086f, 0x0870, 0x085b, 0x085d, 0x080d, 0x0702, 0x0861, 0x0873,
+ 0x0864, 0x0866, 0x0867, 0x0868, 0x086a, 0x086b, 0x086c, 0x083b,
+ 0x0827, 0x0860, 0x0700, 0x085c, 0x087a, 0x0878, 0x0863, 0x0876,
+ 0x0862, 0x086e, 0x086d, 0x082c, 0x082e, 0x082f, 0x0700, 0x030c,
+ 0x0703, 0x0820, 0x0207, 0x0500, 0x0501, 0x0502, 0x0503, 0x0504,
+ 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x0208, 0x0209, 0x0907,
+ 0x0908, 0x0909, 0x030b, 0x0904, 0x0905, 0x0906, 0x030a, 0x0901,
+ 0x0902, 0x0903, 0x0900, 0x0310, 0x0206, 0x0200, 0x083c, 0x050a,
+ 0x050b, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x001c, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x020c, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x020c,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ }, {
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0201, 0x0702, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0700, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0700, 0x030c,
+ 0x0703, 0x0200, 0x0207, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0208, 0x0200, 0x0307,
+ 0x0308, 0x0309, 0x030b, 0x0304, 0x0305, 0x0306, 0x030a, 0x0301,
+ 0x0302, 0x0303, 0x0300, 0x0310, 0x0206, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x030e, 0x0702, 0x030d, 0x0200, 0x0701, 0x0205, 0x0114, 0x0603,
+ 0x0118, 0x0601, 0x0602, 0x0117, 0x0600, 0x0119, 0x0115, 0x0116,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+ },
+};
+
+char func_buf[FUNC_BUFSIZE] = {
+ '\033', '[', '[', 'A', 0,
+ '\033', '[', '[', 'B', 0,
+ '\033', '[', '[', 'C', 0,
+ '\033', '[', '[', 'D', 0,
+ '\033', '[', '[', 'E', 0,
+ '\033', '[', '1', '7', '~', 0,
+ '\033', '[', '1', '8', '~', 0,
+ '\033', '[', '1', '9', '~', 0,
+ '\033', '[', '2', '0', '~', 0,
+ '\033', '[', '2', '1', '~', 0,
+ '\033', '[', '2', '3', '~', 0,
+ '\033', '[', '2', '4', '~', 0,
+ '\033', '[', '2', '5', '~', 0,
+ '\033', '[', '2', '6', '~', 0,
+ '\033', '[', '2', '8', '~', 0,
+ '\033', '[', '2', '9', '~', 0,
+ '\033', '[', '3', '1', '~', 0,
+ '\033', '[', '3', '2', '~', 0,
+ '\033', '[', '3', '3', '~', 0,
+ '\033', '[', '3', '4', '~', 0,
+ '\033', '[', '1', '~', 0,
+ '\033', '[', '2', '~', 0,
+ '\033', '[', '3', '~', 0,
+ '\033', '[', '4', '~', 0,
+ '\033', '[', '5', '~', 0,
+ '\033', '[', '6', '~', 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+char *func_table[NR_FUNC] = {
+ func_buf + 0,
+ func_buf + 5,
+ func_buf + 10,
+ func_buf + 15,
+ func_buf + 20,
+ func_buf + 25,
+ func_buf + 31,
+ func_buf + 37,
+ func_buf + 43,
+ func_buf + 49,
+ func_buf + 55,
+ func_buf + 61,
+ func_buf + 67,
+ func_buf + 73,
+ func_buf + 79,
+ func_buf + 85,
+ func_buf + 91,
+ func_buf + 97,
+ func_buf + 103,
+ func_buf + 109,
+ func_buf + 115,
+ func_buf + 120,
+ func_buf + 125,
+ func_buf + 130,
+ func_buf + 135,
+ func_buf + 140,
+ func_buf + 145,
+ func_buf + 146,
+ func_buf + 147,
+ func_buf + 148,
+ func_buf + 149,
+ func_buf + 150,
+};
diff --git a/kernel/chr_drv/defkeymap.map b/kernel/chr_drv/defkeymap.map
new file mode 100644
index 0000000..cf43e69
--- /dev/null
+++ b/kernel/chr_drv/defkeymap.map
@@ -0,0 +1,246 @@
+keycode 0 =
+keycode 1 = Escape Escape
+ alt keycode 1 = Meta_Escape
+keycode 2 = one exclam
+ alt keycode 2 = Meta_one
+keycode 3 = two at at
+ control keycode 3 = nul
+ alt keycode 3 = Meta_two
+keycode 4 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+keycode 5 = four dollar dollar
+ control keycode 5 = Control_backslash
+ alt keycode 5 = Meta_four
+keycode 6 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+keycode 7 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+keycode 8 = seven ampersand braceleft
+ control keycode 8 = Control_underscore
+ alt keycode 8 = Meta_seven
+keycode 9 = eight asterisk bracketleft
+ control keycode 9 = Delete
+ alt keycode 9 = Meta_eight
+keycode 10 = nine parenleft bracketright
+ alt keycode 10 = Meta_nine
+keycode 11 = zero parenright braceright
+ alt keycode 11 = Meta_zero
+keycode 12 = minus underscore backslash
+ control keycode 12 = Control_underscore
+ alt keycode 12 = Meta_minus
+keycode 13 = equal plus
+ alt keycode 13 = Meta_equal
+keycode 14 = Delete Delete
+ alt keycode 14 = Meta_Delete
+keycode 15 = Tab Tab
+ alt keycode 15 = Meta_Tab
+keycode 16 = q
+keycode 17 = w
+keycode 18 = e
+keycode 19 = r
+keycode 20 = t
+keycode 21 = y
+keycode 22 = u
+keycode 23 = i
+keycode 24 = o
+keycode 25 = p
+keycode 26 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+keycode 27 = bracketright braceright asciitilde
+ control keycode 27 = Control_bracketright
+ alt keycode 27 = Meta_bracketright
+keycode 28 = Return
+ alt keycode 28 = 0x080d
+keycode 29 = Control
+keycode 30 = a
+keycode 31 = s
+keycode 32 = d
+keycode 33 = f
+keycode 34 = g
+keycode 35 = h
+keycode 36 = j
+keycode 37 = k
+keycode 38 = l
+keycode 39 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+keycode 40 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+keycode 41 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+keycode 42 = Shift
+keycode 43 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+keycode 44 = z
+keycode 45 = x
+keycode 46 = c
+keycode 47 = v
+keycode 48 = b
+keycode 49 = n
+keycode 50 = m
+keycode 51 = comma less
+ alt keycode 51 = Meta_comma
+keycode 52 = period greater
+ alt keycode 52 = Meta_period
+keycode 53 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+keycode 54 = Shift
+keycode 55 = KP_Multiply
+keycode 56 = Alt
+keycode 57 = space space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+keycode 58 = Caps_Lock
+keycode 59 = F1 F11 Console_13
+ control keycode 59 = F1
+ alt keycode 59 = Console_1
+keycode 60 = F2 F12 Console_14
+ control keycode 60 = F2
+ alt keycode 60 = Console_2
+keycode 61 = F3 F13 Console_15
+ control keycode 61 = F3
+ alt keycode 61 = Console_3
+keycode 62 = F4 F14 Console_16
+ control keycode 62 = F4
+ alt keycode 62 = Console_4
+keycode 63 = F5 F15 Console_17
+ control keycode 63 = F5
+ alt keycode 63 = Console_5
+keycode 64 = F6 F16 Console_18
+ control keycode 64 = F6
+ alt keycode 64 = Console_6
+keycode 65 = F7 F17 Console_19
+ control keycode 65 = F7
+ alt keycode 65 = Console_7
+keycode 66 = F8 F18 Console_20
+ control keycode 66 = F8
+ alt keycode 66 = Console_8
+keycode 67 = F9 F19 Console_21
+ control keycode 67 = F9
+ alt keycode 67 = Console_9
+keycode 68 = F10 F20 Console_22
+ control keycode 68 = F10
+ alt keycode 68 = Console_10
+keycode 69 = Num_Lock
+keycode 70 = Scroll_Lock Show_Memory Show_Registers
+ control keycode 70 = Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 71 = KP_7
+ alt keycode 71 = Ascii_7
+keycode 72 = KP_8
+ alt keycode 72 = Ascii_8
+keycode 73 = KP_9
+ alt keycode 73 = Ascii_9
+keycode 74 = KP_Subtract
+keycode 75 = KP_4
+ alt keycode 75 = Ascii_4
+keycode 76 = KP_5
+ alt keycode 76 = Ascii_5
+keycode 77 = KP_6
+ alt keycode 77 = Ascii_6
+keycode 78 = KP_Add
+keycode 79 = KP_1
+ alt keycode 79 = Ascii_1
+keycode 80 = KP_2
+ alt keycode 80 = Ascii_2
+keycode 81 = KP_3
+ alt keycode 81 = Ascii_3
+keycode 82 = KP_0
+ alt keycode 82 = Ascii_0
+keycode 83 = KP_Period
+ altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 84 = Last_Console
+keycode 85 =
+keycode 86 = less greater bar
+ alt keycode 86 = Meta_less
+keycode 87 = F11 F11 Console_23
+ control keycode 87 = F11
+ alt keycode 87 = Console_11
+keycode 88 = F12 F12 Console_24
+ control keycode 88 = F12
+ alt keycode 88 = Console_12
+keycode 89 =
+keycode 90 =
+keycode 91 =
+keycode 92 =
+keycode 93 =
+keycode 94 =
+keycode 95 =
+keycode 96 = KP_Enter
+keycode 97 = Control
+keycode 98 = KP_Divide
+keycode 99 = Control_backslash
+ control keycode 99 = Control_backslash
+ alt keycode 99 = Control_backslash
+keycode 100 = AltGr
+keycode 101 = Break
+keycode 102 = Find
+keycode 103 = Up
+keycode 104 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 105 = Left
+keycode 106 = Right
+keycode 107 = Select
+keycode 108 = Down
+keycode 109 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 110 = Insert
+keycode 111 = Remove
+ altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 112 =
+keycode 113 =
+keycode 114 =
+keycode 115 =
+keycode 116 =
+keycode 117 =
+keycode 118 =
+keycode 119 =
+keycode 120 =
+keycode 121 =
+keycode 122 =
+keycode 123 =
+keycode 124 =
+keycode 125 =
+keycode 126 =
+keycode 127 =
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string F21 = ""
+string F22 = ""
+string F23 = ""
+string F24 = ""
+string F25 = ""
+string F26 = ""
diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c
index 3dabcac..ca1dad5 100644
--- a/kernel/chr_drv/keyboard.c
+++ b/kernel/chr_drv/keyboard.c
@@ -21,19 +21,26 @@
#include <linux/interrupt.h>
#include <linux/config.h>
#include <linux/signal.h>
+#include <linux/string.h>
+
+#include <asm/bitops.h>
#ifndef KBD_DEFFLAGS
+
#ifdef CONFIG_KBD_META
-#define KBD_DEFFLAGS ((1 << VC_NUMLOCK) | (1 << VC_REPEAT) | (1 << VC_META))
+#define KBD_META (1 << VC_META)
#else
-#define KBD_DEFFLAGS ((1 << VC_NUMLOCK) | (1 << VC_REPEAT))
+#define KBD_META 0
#endif
+
+#ifdef CONFIG_KBD_NUML
+#define KBD_NUML (1 << VC_NUMLOCK)
+#else
+#define KBD_NUML 0
#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))
+#define KBD_DEFFLAGS (KBD_NUML | (1 << VC_REPEAT) | KBD_META)
+#endif
/*
* The default IO slowdown is doing 'inb()'s from 0x61, which should be
@@ -55,10 +62,10 @@ extern void scrollfront(int);
#define fake_keyboard_interrupt() \
__asm__ __volatile__("int $0x21")
-unsigned long kbd_flags = 0;
unsigned long kbd_dead_keys = 0;
unsigned long kbd_prev_dead_keys = 0;
+static unsigned char k_down[NR_SHIFT] = {0, };
static int want_console = -1;
static int last_console = 0; /* last used VC */
static char rep = 0; /* flag telling character repeat */
@@ -69,7 +76,6 @@ static struct tty_struct * tty = NULL;
static volatile unsigned char acknowledge = 0;
static volatile unsigned char resend = 0;
-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);
@@ -80,17 +86,21 @@ 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 void do_meta(unsigned char value, char up_flag);
+static void do_ascii(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
+ do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
+ do_meta, do_ascii
};
/* maximum values each key_handler can handle */
const int max_vals[] = {
- 255, 25, 13, 16, 4, 255, 3, 255
+ 255, NR_FUNC - 1, 13, 16, 4, 255, 3, NR_SHIFT,
+ 255, 9
};
-const int NR_TYPES = (sizeof(max_vals) / sizeof(u_char));
+const int NR_TYPES = (sizeof(max_vals) / sizeof(int));
#define E0_BASE 96
@@ -103,6 +113,8 @@ static unsigned int handle_diacr(unsigned int);
static struct pt_regs * pt_regs;
+static inline void translate(unsigned char scancode);
+
static inline void kb_wait(void)
{
int i;
@@ -115,7 +127,6 @@ 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;
@@ -137,16 +148,53 @@ static void keyboard_interrupt(int int_pt_regs)
tty = TTY_TABLE(0);
kbd = kbd_table + fg_console;
if (vc_kbd_flag(kbd,VC_RAW)) {
- kbd_flags = 0;
+ memset(k_down, 0, sizeof(k_down));
put_queue(scancode);
goto end_kbd_intr;
- }
+ } else
+ translate(scancode);
+end_kbd_intr:
+ return;
+}
+
+static inline void translate(unsigned char scancode)
+{
+ char break_flag;
+ static unsigned long key_down[8] = { 0, };
+ 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 */
+#ifdef LK450
+ 0x3d, /* f13 */
+ 0x3e, /* f14 */
+ 0x3f, /* help */
+ 0x40, /* do */
+ 0x41, /* f17 */
+ 0x4e /* keypad minus/plus */
+#endif
+ };
+
if (scancode == 0xe0) {
set_kbd_dead(KGD_E0);
- goto end_kbd_intr;
- } else if (scancode == 0xe1) {
+ return;
+ }
+ if (scancode == 0xe1) {
set_kbd_dead(KGD_E1);
- goto end_kbd_intr;
+ return;
}
/*
* The keyboard maintains its own internal caps lock and num lock
@@ -157,67 +205,54 @@ static void keyboard_interrupt(int int_pt_regs)
*/
if (kbd_dead(KGD_E0) && (scancode == 0x2a || scancode == 0xaa ||
scancode == 0x36 || scancode == 0xb6))
- goto end_kbd_intr;
+ return;
+
+ /* map two byte scancodes into one byte id's */
+
+ 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
+ return;
+ }
+
+ 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) {
+#if 0
+ printk("keyboard: unknown scancode e0 %02x\n", scancode);
+#endif
+ return;
+ }
+ }
+
+ rep = 0;
+ if (break_flag)
+ clear_bit(scancode, key_down);
+ else
+ rep = set_bit(scancode, key_down);
+
/*
* 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 under heavy loads.
*/
- 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))))) {
- 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
- goto end_kbd_intr;
- }
-
- 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) {
-#if 0
- printk("keyboard: unknown scancode e0 %02x\n", scancode);
-#endif
- goto end_kbd_intr;
- }
- }
+ u_short key_code;
key_code = key_map[shift_state][scancode];
(*key_handler[key_code >> 8])(key_code & 0xff, break_flag);
- }
-end_kbd_intr:
- return;
+ }
}
static void put_queue(int ch)
@@ -276,10 +311,16 @@ static void caps_toggle(void)
{
if (rep)
return;
- set_kbd_flag(KG_CAPSLOCK);
chg_vc_kbd_flag(kbd,VC_CAPSLOCK);
}
+static void caps_on(void)
+{
+ if (rep)
+ return;
+ set_vc_kbd_flag(kbd,VC_CAPSLOCK);
+}
+
static void show_ptregs(void)
{
if (!pt_regs)
@@ -300,12 +341,6 @@ static void show_ptregs(void)
static void hold(void)
{
- /* 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))
@@ -320,7 +355,7 @@ static void hold(void)
static void num(void)
{
#if 0
- if (kbd_flags & CTRL_KEYS) {
+ if (k_down[KG_CTRL]) {
/* pause key pressed, sends E1 1D 45, ChN */
chg_vc_kbd_flag(kbd,VC_PAUSE);
return;
@@ -346,13 +381,30 @@ static void send_intr(void)
put_queue(INTR_CHAR(tty));
}
+static void scrll_forw(void)
+{
+ scrollfront(0);
+}
+
+static void scrll_back(void)
+{
+ scrollback(0);
+}
+
+static void boot_it(void)
+{
+ ctrl_alt_del();
+}
+
+
static void do_spec(unsigned char value, char up_flag)
{
typedef void (*fnp)(void);
fnp fn_table[] = {
NULL, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
- num, hold
+ num, hold, scrll_forw, scrll_back,
+ boot_it, caps_on
};
if (value >= sizeof(fn_table)/sizeof(fnp))
@@ -371,42 +423,20 @@ static void do_self(unsigned char value, char up_flag)
value = handle_diacr(value);
- /* kludge... */
+ /* kludge... but works for ISO 8859-1 */
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(value);
- } else
- put_queue(value|0x80);
- else
- put_queue(value);
+ put_queue(value);
}
static unsigned char ret_diacr[] =
{'`', '\'', '^', '~', '"' }; /* Must not end with 0 */
-/* If a dead key pressed twice, output a character corresponding it, */
+/* If a dead key pressed twice, output a character corresponding to it, */
/* otherwise just remember the dead key. */
static void do_dead(unsigned char value, char up_flag)
@@ -469,37 +499,10 @@ static void do_cons(unsigned char value, char up_flag)
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_flags & ALT_KEYS) && value < 12) {
- want_console = value;
- return;
- }
puts_queue(func_table[value]);
}
@@ -511,19 +514,8 @@ static void do_pad(unsigned char value, char up_flag)
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 ((kbd_flags & ALT_KEYS) && value <= 9) { /* Alt-numpad */
- npadch = (npadch * 10 + value) % 1000;
- return;
- }
-
/* kludge... shift forces cursor/number keys */
- if (vc_kbd_flag(kbd,VC_APPLIC) && shift_state != 1) {
+ if (vc_kbd_flag(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
applkey(app_map[value], 1);
return;
}
@@ -562,12 +554,12 @@ static void do_pad(unsigned char value, char up_flag)
do_fn(KVAL(K_PGUP), 0);
return;
case KVAL(K_P5):
- applkey('G', vc_kbd_flag(kbd,VC_APPLIC));
+ applkey('G', vc_kbd_flag(kbd, VC_APPLIC));
return;
}
put_queue(pad_chars[value]);
- if (value == KVAL(K_PENTER) && vc_kbd_flag(kbd,VC_CRLF))
+ if (value == KVAL(K_PENTER) && vc_kbd_flag(kbd, VC_CRLF))
put_queue(10);
}
@@ -582,73 +574,54 @@ static void do_cur(unsigned char value, char up_flag)
static void do_shift(unsigned char value, char up_flag)
{
- shift_state = 0;
- if (up_flag)
- clr_kbd_flag(value);
+ int old_state = shift_state;
+
+ if (rep)
+ return;
+
+ /* kludge... */
+ if (value == KVAL(K_CAPSSHIFT)) {
+ value = KVAL(K_SHIFT);
+ clr_vc_kbd_flag(kbd, VC_CAPSLOCK);
+ }
+
+ if (up_flag) {
+ if (k_down[value])
+ k_down[value]--;
+ } else
+ k_down[value]++;
+
+ if (k_down[value])
+ shift_state |= (1 << value);
else
- 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) {
+ shift_state &= ~ (1 << value);
+
+ /* kludge */
+ if (up_flag && shift_state != old_state && npadch != 0) {
put_queue(npadch);
- npadch=0;
+ npadch = 0;
}
}
-#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 }};
+static void do_meta(unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ if (vc_kbd_flag(kbd, VC_META)) {
+ put_queue('\033');
+ put_queue(value);
+ } else
+ put_queue(value | 0x80);
+}
+
+static void do_ascii(unsigned char value, char up_flag)
+{
+ if (up_flag)
+ return;
+
+ npadch = (npadch * 10 + value) % 1000;
+}
/*
* send_data sends a character to the keyboard and waits
@@ -752,6 +725,7 @@ unsigned long kbd_init(unsigned long kmem_start)
kbd->flags = KBD_DEFFLAGS;
kbd->default_flags = KBD_DEFFLAGS;
}
+
bh_base[KEYBOARD_BH].routine = kbd_bh;
request_irq(KEYBOARD_IRQ,keyboard_interrupt);
mark_bh(KEYBOARD_BH);
diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c
index 59d8910..5f3beec 100644
--- a/kernel/chr_drv/mem.c
+++ b/kernel/chr_drv/mem.c
@@ -63,14 +63,14 @@ static int read_core(struct inode * inode, struct file * file,char * buf, int co
read += count1;
}
- while (p < (4096 + 4096) && count > 0) {
+ while (p < 2*PAGE_SIZE && count > 0) {
put_fs_byte(0,buf);
buf++;
p++;
count--;
read++;
}
- memcpy_tofs(buf,(void *) (p - 4096),count);
+ memcpy_tofs(buf,(void *) (p - PAGE_SIZE),count);
read += count;
file->f_pos += read;
return read;
@@ -88,7 +88,7 @@ static int read_mem(struct inode * inode, struct file * file,char * buf, int cou
if (count > high_memory - p)
count = high_memory - p;
read = 0;
- while (p < 4096 && count > 0) {
+ while (p < PAGE_SIZE && count > 0) {
put_fs_byte(0,buf);
buf++;
p++;
@@ -113,7 +113,7 @@ static int write_mem(struct inode * inode, struct file * file,char * buf, int co
if (count > high_memory - p)
count = high_memory - p;
written = 0;
- while (p < 4096 && count > 0) {
+ while (p < PAGE_SIZE && count > 0) {
/* Hmm. Do something? */
buf++;
p++;
diff --git a/kernel/chr_drv/msbusmouse.c b/kernel/chr_drv/msbusmouse.c
index 3d9bcee..0fd252d 100644
--- a/kernel/chr_drv/msbusmouse.c
+++ b/kernel/chr_drv/msbusmouse.c
@@ -44,7 +44,8 @@ static struct mouse_status mouse;
static void ms_mouse_interrupt(int unused)
{
- char dx, dy, buttons;
+ char dx, dy;
+ unsigned char buttons;
outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT);
outb((inb(MS_MSE_DATA_PORT) | 0x20), MS_MSE_DATA_PORT);
@@ -103,18 +104,22 @@ static int write_mouse(struct inode * inode, struct file * file, char * buffer,
static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count)
{
- int i;
+ int i, dx, dy;
if (count < 3)
return -EINVAL;
if (!mouse.ready)
return -EAGAIN;
put_fs_byte(mouse.buttons | 0x80, buffer);
- put_fs_byte((char)(mouse.dx<-127 ? -127 : mouse.dx>127 ? 127 : mouse.dx), buffer + 1);
- put_fs_byte((char)(mouse.dy<-127 ? 127 : mouse.dy>127 ? -127 : -mouse.dy), buffer + 2);
+ dx = mouse.dx < -127 ? -127 : mouse.dx > 127 ? 127 : mouse.dx;
+ dy = mouse.dy < -127 ? 127 : mouse.dy > 127 ? -127 : -mouse.dy;
+ put_fs_byte((char)dx, buffer + 1);
+ put_fs_byte((char)dy, buffer + 2);
for (i = 3; i < count; i++)
put_fs_byte(0x00, buffer + i);
- mouse.dx = mouse.dy = mouse.ready = 0;
+ mouse.dx -= dx;
+ mouse.dy += dy;
+ mouse.ready = 0;
return i;
}
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index b9742d1..6d3bb7a 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -49,9 +49,14 @@
* CONFIG_ACCENT_ASYNC
* Enables support for the Accent Async 4 port serial
* port.
- *
+ *
+ * CONFIG_HUB6
+ * Enables support for the venerable Bell Technologies
+ * HUB6 card.
*/
+#undef ISR_HACK
+
#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
/*
@@ -109,13 +114,35 @@ static void autoconfig(struct async_struct * info);
#else
#define BOCA_FLAGS AUTO_IRQ_FLAG
#endif
+
+#ifdef CONFIG_HUB6
+#define HUB6_FLAGS (ASYNC_BOOT_AUTOCONF)
+#else
+#define HUB6_FLAGS 0
+#endif
+/*
+ * The following define the access methods for the HUB6 card. All
+ * access is through two ports for all 24 possible chips. The card is
+ * selected through the high 2 bits, the port on that card with the
+ * "middle" 3 bits, and the register on that port with the bottom
+ * 3 bits.
+ *
+ * While the access port and interrupt is configurable, the default
+ * port locations are 0x302 for the port control register, and 0x303
+ * for the data read/write register. Normally, the interrupt is at irq3
+ * but can be anything from 3 to 7 inclusive. Note tht using 3 will
+ * require disabling com2.
+ */
+
+#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
+
struct async_struct rs_table[] = {
/* UART CLK PORT IRQ FLAGS */
- { 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, 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 */
@@ -127,10 +154,10 @@ struct async_struct rs_table[] = {
{ BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */
{ BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */
- { 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, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */
+ { BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */
+ { BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare; user configurable) */
+ { BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare; user configurable) */
{ BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */
{ BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */
@@ -148,6 +175,22 @@ struct async_struct rs_table[] = {
{ BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */
{ BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */
{ BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */
+
+/* You can have up to four HUB6's in the system, but I've only
+ * included two cards here for a total of twelve ports.
+ */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS32 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS33 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS34 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS35 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS36 */
+ { BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS37 */
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
@@ -163,26 +206,75 @@ static void rs_throttle(struct tty_struct * tty, int status);
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
+ if (info->hub6) {
+ outb(info->hub6 - 1 + offset, info->port);
+ return inb(info->port+1);
+ } else
return inb(info->port + offset);
}
static inline unsigned int serial_inp(struct async_struct *info, int offset)
{
+ if (info->hub6) {
+ outb(info->hub6 - 1 + offset, info->port);
+ return inb_p(info->port+1);
+ } else
return inb_p(info->port + offset);
}
static inline void serial_out(struct async_struct *info, int offset, int value)
{
+ if (info->hub6) {
+ outb(info->hub6 - 1 + offset, info->port);
+ outb(value, info->port+1);
+ } else
outb(value, info->port+offset);
}
static inline void serial_outp(struct async_struct *info, int offset,
int value)
{
+ if (info->hub6) {
+ outb(info->hub6 - 1 + offset, info->port);
+ outb_p(value, info->port+1);
+ } else
outb_p(value, info->port+offset);
}
/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+ struct async_struct *info;
+
+ info = rs_table + DEV_TO_SL(tty->line);
+
+ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+#ifdef ISR_HACK
+ serial_out(info, UART_IER, info->IER);
+#endif
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+ struct async_struct *info;
+
+ info = rs_table + DEV_TO_SL(tty->line);
+
+ info->IER = (UART_IER_MSI | UART_IER_RLSI |
+ UART_IER_THRI | UART_IER_RDI);
+#ifdef ISR_HACK
+ serial_out(info, UART_IER, info->IER);
+#endif
+}
+
+/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
@@ -268,6 +360,9 @@ static inline void receive_chars(struct async_struct *info,
&info->tty->flags))
rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
rs_sched_event(info, RS_EVENT_READ_PROCESS);
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR...");
+#endif
}
static inline void transmit_chars(struct async_struct *info, int *done_work)
@@ -278,8 +373,13 @@ static inline void transmit_chars(struct async_struct *info, int *done_work)
queue = &info->tty->write_q;
head = queue->head;
tail = queue->tail;
- if (head==tail && !info->x_char)
+ if (head==tail && !info->x_char) {
+ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+#ifdef ISR_HACK
+ serial_out(info, UART_IER, info->IER);
+#endif
return;
+ }
count = info->xmit_fifo_size;
if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
@@ -298,7 +398,7 @@ static inline void transmit_chars(struct async_struct *info, int *done_work)
mark_bh(TTY_BH);
}
}
-#ifdef SERIAL_INT_DEBUG
+#ifdef SERIAL_DEBUG_INTR
printk("THRE...");
#endif
(*done_work)++;
@@ -311,19 +411,34 @@ static inline int check_modem_status(struct async_struct *info)
status = serial_in(info, UART_MSR);
if ((status & UART_MSR_DDCD) && !C_LOCAL(info->tty)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("DDCD...");
+#endif
if (status & UART_MSR_DCD)
rs_sched_event(info, RS_EVENT_OPEN_WAKEUP);
- else
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP)))
rs_sched_event(info, RS_EVENT_HANGUP);
}
if (C_RTSCTS(info->tty)) {
if (info->tty->stopped) {
if (status & UART_MSR_CTS) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("CTS tx start...");
+#endif
info->tty->stopped = 0;
+ rs_start(info->tty);
return 1;
}
- } else
- info->tty->stopped = !(status & UART_MSR_CTS);
+ } else {
+ if (!(status & UART_MSR_CTS)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("CTS tx stop...");
+#endif
+ info->tty->stopped = 1;
+ rs_stop(info->tty);
+ }
+ }
}
return 0;
}
@@ -353,7 +468,7 @@ static void rs_interrupt(int irq)
{
int status;
struct async_struct * info;
- int done, done_work, pass_number;
+ int done, done_work, pass_number, recheck_count;
rs_irq_triggered = irq;
rs_triggered |= 1 << irq;
@@ -372,14 +487,24 @@ static void rs_interrupt(int irq)
receive_chars(info, &status);
done_work++;
}
+ recheck_count = 0;
recheck_write:
if ((status & UART_LSR_THRE) &&
!info->tty->stopped) {
transmit_chars(info, &done_work);
}
- if (check_modem_status(info))
+ if (check_modem_status(info) &&
+ (recheck_count++ <= 64))
goto recheck_write;
+#ifdef SERIAL_DEBUG_INTR
+ if (recheck_count > 16)
+ printk("recheck_count = %d\n", recheck_count);
+#endif
}
+#ifdef ISR_HACK
+ serial_outp(info, UART_IER, 0);
+ serial_out(info, UART_IER, info->IER);
+#endif
info = info->next_port;
if (!info && !done) {
@@ -389,7 +514,14 @@ static void rs_interrupt(int irq)
break; /* Prevent infinite loops */
}
}
- if (IRQ_ports[irq]) {
+ if (info = IRQ_ports[irq]) {
+#ifdef 0
+ do {
+ serial_outp(info, UART_IER, 0);
+ serial_out(info, UART_IER, info->IER);
+ info = info->next_port;
+ } while (info);
+#endif
if (irq && !done_work)
IRQ_timer[irq] = jiffies + 1500;
else
@@ -476,12 +608,9 @@ static void rs_timer(void)
for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
IRQ_active &= ~mask;
- if (i) {
- cli();
- rs_interrupt(i);
- sti();
- } else
- rs_interrupt(i);
+ cli();
+ rs_interrupt(i);
+ sti();
}
if (mask & IRQ_active) {
if (!timeout || (IRQ_timer[i] < timeout))
@@ -553,7 +682,7 @@ static void figure_IRQ_timeout(int irq)
info = IRQ_ports[irq];
if (!info) {
- IRQ_timeout[irq] = 0;
+ IRQ_timeout[irq] = 6000;
return;
}
while (info) {
@@ -563,11 +692,15 @@ static void figure_IRQ_timeout(int irq)
}
if (!irq)
timeout = timeout / 2;
- IRQ_timeout[irq] = timeout;
+ IRQ_timeout[irq] = timeout ? timeout : 1;
}
static inline void unlink_port(struct async_struct *info)
{
+#ifdef SERIAL_DEBUG_OPEN
+ printk("unlinking serial port %d from irq %d....", info->line,
+ info->irq);
+#endif
if (info->next_port)
info->next_port->prev_port = info->prev_port;
if (info->prev_port)
@@ -579,6 +712,10 @@ static inline void unlink_port(struct async_struct *info)
static inline void link_port(struct async_struct *info)
{
+#ifdef SERIAL_DEBUG_OPEN
+ printk("linking serial port %d into irq %d...", info->line,
+ info->irq);
+#endif
info->prev_port = 0;
info->next_port = IRQ_ports[info->irq];
if (info->next_port)
@@ -631,7 +768,14 @@ static void startup(struct async_struct * info)
/*
* Finally, enable interrupts
*/
- serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
+#ifdef ISR_HACK
+ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+ serial_outp(info, UART_IER, info->IER); /* enable interrupts */
+#else
+ info->IER = (UART_IER_MSI | UART_IER_RLSI |
+ UART_IER_THRI | UART_IER_RDI);
+ serial_outp(info, UART_IER, info->IER); /* enable all intrs */
+#endif
if (info->flags & ASYNC_FOURPORT) {
/* Enable interrupts on the AST Fourport board */
ICP = (info->port & 0xFE0) | 0x01F;
@@ -654,10 +798,11 @@ static void startup(struct async_struct * info)
* Set up parity check flag
*/
if (info->tty && info->tty->termios && I_INPCK(info->tty))
- info->read_status_mask = UART_LSR_BI | UART_LSR_FE |
- UART_LSR_PE;
+ info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
+ UART_LSR_FE | UART_LSR_PE);
else
- info->read_status_mask = UART_LSR_BI | UART_LSR_FE;
+ info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
+ UART_LSR_FE);
restore_flags(flags);
}
@@ -670,7 +815,12 @@ static void shutdown(struct async_struct * info)
unsigned long flags;
save_flags(flags); cli();
+ info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
+ if (info->flags & ASYNC_FOURPORT) {
+ /* reset interrupts on the AST Fourport board */
+ (void) inb((info->port & 0xFE0) | 0x01F);
+ }
if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
serial_outp(info, UART_MCR, UART_MCR_DTR);
else
@@ -726,13 +876,16 @@ static void change_speed(unsigned int line)
quot = 0;
info->timeout = 0;
}
+ cli();
mcr = serial_in(info, UART_MCR);
if (quot)
serial_out(info, UART_MCR, mcr | UART_MCR_DTR);
else {
serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR);
+ sti();
return;
}
+ sti();
/* byte size and parity */
cval = cflag & (CSIZE | CSTOPB);
cval >>= 4;
@@ -761,13 +914,14 @@ static void change_speed(unsigned int line)
*
* Note: this subroutine must be called with the interrupts *off*
*/
-static void restart_port(struct async_struct *info)
+static inline 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);
@@ -799,8 +953,15 @@ void rs_write(struct tty_struct * tty)
if (!tty || tty->stopped)
return;
info = rs_table + DEV_TO_SL(tty->line);
+ if (!info)
+ return;
cli();
restart_port(info);
+ info->IER = (UART_IER_MSI | UART_IER_RLSI |
+ UART_IER_THRI | UART_IER_RDI);
+#ifdef ISR_HACK
+ serial_out(info, UART_IER, info->IER);
+#endif
sti();
}
@@ -809,7 +970,7 @@ void rs_write(struct tty_struct * tty)
* rs_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled (and that the throttled
+ * incoming characters should be throttled (and that the throttle
* should be released).
* ------------------------------------------------------------
*/
@@ -817,8 +978,10 @@ static void rs_throttle(struct tty_struct * tty, int status)
{
struct async_struct *info;
unsigned char mcr;
+ unsigned long flags;
-#if 0
+ save_flags(flags); cli();
+#if SERIAL_DEBUG_THROTTLE
printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
status, LEFT(&tty->read_q), LEFT(&tty->secondary));
#endif
@@ -836,12 +999,10 @@ static void rs_throttle(struct tty_struct * tty, int status)
case TTY_THROTTLE_RQ_AVAIL:
info = rs_table + DEV_TO_SL(tty->line);
if (tty->termios->c_iflag & IXOFF) {
- cli();
if (info->x_char)
info->x_char = 0;
else
info->x_char = START_CHAR(tty);
- sti();
} else {
mcr = serial_in(info, UART_MCR);
mcr |= UART_MCR_RTS;
@@ -849,6 +1010,7 @@ static void rs_throttle(struct tty_struct * tty, int status)
}
break;
}
+ restore_flags(flags);
}
/*
@@ -873,6 +1035,7 @@ static int get_serial_info(struct async_struct * info,
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.custom_divisor = info->custom_divisor;
+ tmp.hub6 = info->hub6;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
@@ -892,18 +1055,18 @@ static int set_serial_info(struct async_struct * info,
old_info = *info;
change_irq = new_serial.irq != info->irq;
- change_port = new_serial.port != info->port;
+ change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
if (!suser()) {
if (change_irq || change_port ||
(new_serial.baud_base != info->baud_base) ||
(new_serial.type != info->type) ||
(new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ASYNC_FLAGS) !=
- (info->flags & ~ASYNC_FLAGS)))
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (info->flags & ~ASYNC_USR_MASK)))
return -EPERM;
- info->flags = ((info->flags & ~ASYNC_SPD_MASK) |
- (new_serial.flags & ASYNC_SPD_MASK));
+ info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
new_serial.port = 0; /* Prevent initialization below */
goto check_and_exit;
@@ -969,6 +1132,7 @@ static int set_serial_info(struct async_struct * info,
}
info->irq = new_serial.irq;
info->port = new_serial.port;
+ info->hub6 = new_serial.hub6;
}
check_and_exit:
@@ -993,8 +1157,10 @@ static int get_modem_info(struct async_struct * info, unsigned int *value)
unsigned char control, status;
unsigned int result;
+ cli();
control = serial_in(info, UART_MCR);
status = serial_in(info, UART_MSR);
+ sti();
result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
| ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
@@ -1011,7 +1177,9 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd,
unsigned char control;
unsigned int arg = get_fs_long((unsigned long *) value);
+ cli();
control = serial_in(info, UART_MCR);
+ sti();
switch (cmd) {
case TIOCMBIS:
@@ -1034,7 +1202,9 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd,
default:
return -EINVAL;
}
+ cli();
serial_out(info, UART_MCR, control);
+ sti();
return 0;
}
@@ -1052,7 +1222,7 @@ static int do_autoconfig(struct async_struct * info)
if (info->flags & ASYNC_INITIALIZED) {
shutdown(info);
unlink_port(info);
- if (info->irq)
+ if (info->irq && !IRQ_ports[info->irq])
free_irq(info->irq);
}
@@ -1087,9 +1257,11 @@ static void send_break( struct async_struct * info, int duration)
return;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + duration;
+ cli();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
schedule();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ sti();
}
/*
@@ -1236,10 +1408,11 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
wake_up_interruptible(&info->open_wait);
if (I_INPCK(tty))
- info->read_status_mask = UART_LSR_BI | UART_LSR_FE |
- UART_LSR_PE;
+ info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
+ UART_LSR_FE | UART_LSR_PE);
else
- info->read_status_mask = UART_LSR_BI | UART_LSR_FE;
+ info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
+ UART_LSR_FE);
}
/*
@@ -1267,6 +1440,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
if (--info->count > 0)
return;
tty->stopped = 0; /* Force flush to succeed */
+ rs_start(tty);
wait_until_sent(tty);
clear_bit(line, rs_event);
info->event = 0;
@@ -1305,7 +1479,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct async_struct *info)
{
struct wait_queue wait = { current, NULL };
- int retval;
+ int retval;
+ int do_clocal = C_LOCAL(tty);
+ struct termios orig_termios;
+ int tty_line = tty->line;
/*
* If this is a callout device, then just make sure the normal
@@ -1314,6 +1491,14 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (MAJOR(filp->f_rdev) == 5) {
if (info->flags & ASYNC_NORMAL_ACTIVE)
return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session))
+ return -EBUSY;
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp))
+ return -EBUSY;
info->flags |= ASYNC_CALLOUT_ACTIVE;
return 0;
}
@@ -1344,22 +1529,27 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
#endif
info->count--;
info->blocked_open++;
+ memset(&orig_termios, 0, sizeof(orig_termios));
+ if (tty_termios[tty_line])
+ orig_termios = *tty_termios[tty_line];
while (1) {
+ cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
serial_out(info, UART_MCR,
serial_inp(info, UART_MCR) | UART_MCR_DTR);
+ sti();
current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp)) {
- if (info->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTNOINTR;
+ if (tty_hung_up_p(filp) && (info->flags & ASYNC_HUP_NOTIFY)) {
+ retval = -EAGAIN;
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
- (C_LOCAL(tty) ||
- (serial_in(info, UART_MSR) & UART_MSR_DCD)))
+ (do_clocal || (serial_in(info, UART_MSR) &
+ UART_MSR_DCD))) {
+ if (tty_hung_up_p(filp))
+ retval = -ERESTARTNOINTR;
break;
+ }
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
@@ -1381,6 +1571,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
+ if ((info->flags & ASYNC_TERMIOS_RESTORE) &&
+ tty_termios[tty_line])
+ *tty_termios[tty_line] = orig_termios;
return 0;
}
@@ -1411,6 +1604,8 @@ int rs_open(struct tty_struct *tty, struct file * filp)
tty->ioctl = rs_ioctl;
tty->throttle = rs_throttle;
tty->set_termios = rs_set_termios;
+ tty->stop = rs_stop;
+ tty->start = rs_start;
if (!(info->flags & ASYNC_INITIALIZED)) {
if (!info->port || !info->type) {
@@ -1443,6 +1638,9 @@ int rs_open(struct tty_struct *tty, struct file * filp)
retval = block_til_ready(tty, filp, info);
if (retval)
return retval;
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
return 0;
}
@@ -1462,7 +1660,7 @@ int rs_open(struct tty_struct *tty, struct file * filp)
*/
static void show_serial_version(void)
{
- printk("Serial driver version 3.95 with");
+ printk("Serial driver version 3.96 with");
#ifdef CONFIG_AST_FOURPORT
printk(" AST_FOURPORT");
#define SERIAL_OPT
@@ -1471,6 +1669,10 @@ static void show_serial_version(void)
printk(" ACCENT_ASYNC");
#define SERIAL_OPT
#endif
+#ifdef CONFIG_HUB6
+ printk(" HUB-6");
+#define SERIAL_OPT
+#endif
#ifdef CONFIG_AUTO_IRQ
printk (" AUTO_IRQ");
#define SERIAL_OPT
@@ -1498,6 +1700,7 @@ static int get_auto_irq(struct async_struct *info)
* Enable interrupts and see who answers
*/
rs_irq_triggered = 0;
+ cli();
save_IER = serial_inp(info, UART_IER);
save_MCR = serial_inp(info, UART_MCR);
if (info->flags & ASYNC_FOURPORT) {
@@ -1512,6 +1715,7 @@ static int get_auto_irq(struct async_struct *info)
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
}
+ sti();
/*
* Next, clear the interrupt registers.
*/
@@ -1528,10 +1732,12 @@ static int get_auto_irq(struct async_struct *info)
/*
* Now check to see if we got any business, and clean up.
*/
+ cli();
serial_outp(info, UART_IER, save_IER);
serial_outp(info, UART_MCR, save_MCR);
if (info->flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
+ sti();
return(rs_irq_triggered);
}
@@ -1582,11 +1788,14 @@ static void autoconfig(struct async_struct * info)
{
unsigned char status1, status2, scratch, scratch2;
unsigned port = info->port;
+ unsigned long flags;
info->type = PORT_UNKNOWN;
if (!port)
return;
+
+ save_flags(flags); cli();
/*
* Do a simple existence test first; if we fail this, there's
@@ -1596,8 +1805,10 @@ static void autoconfig(struct async_struct * info)
serial_outp(info, UART_IER, 0);
scratch2 = serial_inp(info, UART_IER);
serial_outp(info, UART_IER, scratch);
- if (scratch2)
+ if (scratch2) {
+ restore_flags(flags);
return; /* We failed; there's nothing here */
+ }
/*
* Check to see if a UART is really there. Certain broken
@@ -1616,8 +1827,10 @@ static void autoconfig(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)
+ if (status1 != 0x90) {
+ restore_flags(flags);
return;
+ }
}
/*
@@ -1627,8 +1840,8 @@ static void autoconfig(struct async_struct * info)
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;
+ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ scratch = serial_in(info, UART_IIR) >> 6;
info->xmit_fifo_size = 1;
switch (scratch) {
case 0:
@@ -1646,16 +1859,18 @@ static void autoconfig(struct async_struct * info)
break;
}
if (info->type == PORT_16450) {
- scratch = inb(UART_SCR + port);
- outb_p(0xa5, UART_SCR + port);
- status1 = inb(UART_SCR + port);
- outb_p(0x5a, UART_SCR + port);
- status2 = inb(UART_SCR + port);
- outb_p(scratch, UART_SCR + port);
+ scratch = serial_in(info, UART_SCR);
+ serial_outp(info, UART_SCR, 0xa5);
+ status1 = serial_in(info, UART_SCR);
+ serial_outp(info, UART_SCR, 0x5a);
+ status2 = serial_in(info, UART_SCR);
+ serial_outp(info, UART_SCR, scratch);
+
if ((status1 != 0xa5) || (status2 != 0x5a))
info->type = PORT_8250;
}
shutdown(info);
+ restore_flags(flags);
}
/*
diff --git a/kernel/chr_drv/tpqic02.c b/kernel/chr_drv/tpqic02.c
index c6aec05..5283f42 100644
--- a/kernel/chr_drv/tpqic02.c
+++ b/kernel/chr_drv/tpqic02.c
@@ -1,9 +1,9 @@
/* $Id: tpqic02.c,v 0.2.1.21 1993/06/18 19:04:33 root Exp root $
*
- * Driver for tape drive support for Linux-i386 0.99.8.
+ * Driver for tape drive support for Linux-i386 0.99.12.
*
- * Copyright (c) 1992 by H. H. Bergman. All rights reserved.
- * Current e-mail address: s0356514@let.rug.nl
+ * Copyright (c) 1993 by H. H. Bergman. All rights reserved.
+ * Current e-mail address: csg279@wing.rug.nl
* [If you are unable to reach me directly, try the TAPE mailing list
* channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as
* the first line in your message.]
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index a81db13..532ef8d 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -58,6 +58,7 @@
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 *termios_locked[MAX_TTYS]; /* Bitfield of locked termios flags*/
struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
int tty_check_write[MAX_TTYS/32]; /* bitfield for the bh handler */
@@ -125,9 +126,11 @@ int get_tty_queue(struct tty_queue * queue)
/*
* This routine copies out a maximum of buflen characters from the
- * read_q; it is a convenience for line disciplins so they can grab a
+ * read_q; it is a convenience for line disciplines so they can grab a
* large block of data without calling get_tty_char directly. It
- * returns the number of characters actually read.
+ * returns the number of characters actually read. Return terminates
+ * if an error character is read from the queue and the return value
+ * is negated.
*/
int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen)
{
@@ -135,19 +138,21 @@ int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen)
unsigned char *p = bufp;
unsigned long flags;
int head, tail;
+ int ok = 1;
save_flags(flags);
cli();
tail = tty->read_q.tail;
head = tty->read_q.head;
- while ((result < buflen) && (tail!=head)) {
+ while ((result < buflen) && (tail!=head) && ok) {
+ ok = !clear_bit (tail, &tty->readq_flags);
*p++ = tty->read_q.buf[tail++];
tail &= TTY_BUF_SIZE-1;
result++;
}
tty->read_q.tail = tail;
restore_flags(flags);
- return result;
+ return (ok) ? result : -result;
}
@@ -616,6 +621,8 @@ void copy_to_cooked(struct tty_struct * tty)
tty->status_changed = 1;
tty->ctrl_status |= TIOCPKT_STOP;
tty->stopped=1;
+ if (tty->stop)
+ (tty->stop)(tty);
if (IS_A_CONSOLE(tty->line)) {
set_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK);
set_leds();
@@ -627,6 +634,8 @@ void copy_to_cooked(struct tty_struct * tty)
tty->status_changed = 1;
tty->ctrl_status |= TIOCPKT_START;
tty->stopped=0;
+ if (tty->start)
+ (tty->start)(tty);
if (IS_A_CONSOLE(tty->line)) {
clr_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK);
set_leds();
@@ -787,20 +796,22 @@ static int read_chan(struct tty_struct * tty, struct file * file, char * buf, in
if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
&& clear_bit(TTY_SQ_THROTTLED, &tty->flags))
tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
- if (b-buf >= minimum || !current->timeout)
- break;
- if (current->signal & ~current->blocked)
- 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)
+ && tty->link->count <= 1) {
+ file->f_flags &= ~O_NONBLOCK;
break;
+ }
+ } else if (!tty->link->count) {
+ file->f_flags &= ~O_NONBLOCK;
+ break;
}
}
+ if (b-buf >= minimum || !current->timeout)
+ break;
+ if (current->signal & ~current->blocked)
+ break;
TTY_READ_FLUSH(tty);
if (tty->link)
TTY_WRITE_FLUSH(tty->link);
@@ -1042,47 +1053,67 @@ static int tty_write(struct inode * inode, struct file * file, char * buf, int c
static int init_dev(int dev)
{
struct tty_struct *tty, *o_tty;
- struct termios *tp, *o_tp;
+ struct termios *tp, *o_tp, *ltp, *o_ltp;
int retval;
int o_dev;
o_dev = PTY_OTHER(dev);
tty = o_tty = NULL;
tp = o_tp = NULL;
+ ltp = o_ltp = NULL;
repeat:
retval = -EAGAIN;
if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count)
goto end_init;
retval = -ENOMEM;
if (!tty_table[dev] && !tty) {
- tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
- if (!tty)
+ if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
goto end_init;
initialize_tty_struct(dev, tty);
goto repeat;
}
if (!tty_termios[dev] && !tp) {
- tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL);
+ tp = (struct termios *) kmalloc(sizeof(struct termios),
+ GFP_KERNEL);
if (!tp)
goto end_init;
initialize_termios(dev, tp);
goto repeat;
}
+ if (!termios_locked[dev] && !ltp) {
+ ltp = (struct termios *) kmalloc(sizeof(struct termios),
+ GFP_KERNEL);
+ if (!ltp)
+ goto end_init;
+ memset(ltp, 0, sizeof(struct termios));
+ goto repeat;
+ }
if (IS_A_PTY(dev)) {
if (!tty_table[o_dev] && !o_tty) {
- o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+ o_tty = (struct tty_struct *)
+ get_free_page(GFP_KERNEL);
if (!o_tty)
goto end_init;
initialize_tty_struct(o_dev, o_tty);
goto repeat;
}
if (!tty_termios[o_dev] && !o_tp) {
- o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL);
+ o_tp = (struct termios *)
+ kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_tp)
goto end_init;
initialize_termios(o_dev, o_tp);
goto repeat;
}
+ if (!termios_locked[o_dev] && !o_ltp) {
+ o_ltp = (struct termios *)
+ kmalloc(sizeof(struct termios), GFP_KERNEL);
+ if (!o_ltp)
+ goto end_init;
+ memset(o_ltp, 0, sizeof(struct termios));
+ goto repeat;
+ }
+
}
/* Now we have allocated all the structures: update all the pointers.. */
if (!tty_termios[dev]) {
@@ -1094,11 +1125,19 @@ repeat:
tty_table[dev] = tty;
tty = NULL;
}
+ if (!termios_locked[dev]) {
+ termios_locked[dev] = ltp;
+ ltp = NULL;
+ }
if (IS_A_PTY(dev)) {
if (!tty_termios[o_dev]) {
tty_termios[o_dev] = o_tp;
o_tp = NULL;
}
+ if (!termios_locked[o_dev]) {
+ termios_locked[o_dev] = o_ltp;
+ o_ltp = NULL;
+ }
if (!tty_table[o_dev]) {
o_tty->termios = tty_termios[o_dev];
tty_table[o_dev] = o_tty;
@@ -1120,6 +1159,10 @@ end_init:
kfree_s(tp, sizeof(struct termios));
if (o_tp)
kfree_s(o_tp, sizeof(struct termios));
+ if (ltp)
+ kfree_s(ltp, sizeof(struct termios));
+ if (o_ltp)
+ kfree_s(o_ltp, sizeof(struct termios));
return retval;
}
@@ -1405,7 +1448,7 @@ void do_SAK( struct tty_struct *tty)
((session > 0) && ((*p)->session == session)))
send_sig(SIGKILL, *p, 1);
else {
- for (i=0; i < NR_FILE; i++) {
+ for (i=0; i < NR_OPEN; i++) {
filp = (*p)->filp[i];
if (filp && (filp->f_op == &tty_fops) &&
(MINOR(filp->f_rdev) == line)) {
@@ -1581,8 +1624,8 @@ long tty_init(long kmem_start)
{
int i;
- if (sizeof(struct tty_struct) > 4096)
- panic("size of tty structure > 4096!");
+ if (sizeof(struct tty_struct) > PAGE_SIZE)
+ panic("size of tty structure > PAGE_SIZE!");
if (register_chrdev(4,"tty",&tty_fops))
panic("unable to get major 4 for tty device");
if (register_chrdev(5,"tty",&tty_fops))
diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c
index 5557a5c..9efe47f 100644
--- a/kernel/chr_drv/tty_ioctl.c
+++ b/kernel/chr_drv/tty_ioctl.c
@@ -37,6 +37,8 @@ extern int set_selection(const int arg);
extern int paste_selection(struct tty_struct *tty);
#endif /* CONFIG_SELECTION */
+static int tty_set_ldisc(struct tty_struct *tty, int ldisc);
+
static void flush(struct tty_queue * queue)
{
if (queue) {
@@ -127,6 +129,29 @@ static int do_get_ps_info(int arg)
return(0);
}
+static void unset_locked_termios(struct termios *termios,
+ struct termios *old,
+ struct termios *locked)
+{
+ int i;
+
+#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
+
+ if (!locked) {
+ printk("Warning?!? termios_locked is NULL.\n");
+ return;
+ }
+
+ NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
+ NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
+ NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
+ NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
+ termios->c_line = locked->c_line ? old->c_line : termios->c_line;
+ for (i=0; i < NCCS; i++)
+ termios->c_cc[i] = locked->c_cc[i] ?
+ old->c_cc[i] : termios->c_cc[i];
+}
+
static int get_termios(struct tty_struct * tty, struct termios * termios)
{
int i;
@@ -173,6 +198,15 @@ static int set_termios(struct tty_struct * tty, struct termios * termios,
copy characters back and forth. -RAB */
if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;
+ unset_locked_termios(tty->termios, &old_termios,
+ termios_locked[tty->line]);
+
+#if 0
+ retval = tty_set_ldisc(tty, tty->termios->c_line);
+ if (retval)
+ return retval;
+#endif
+
if (tty->set_termios)
(*tty->set_termios)(tty, &old_termios);
@@ -234,16 +268,52 @@ static int set_termio(struct tty_struct * tty, struct termio * termio,
*(unsigned short *)&tty->termios->c_oflag = tmp_termio.c_oflag;
*(unsigned short *)&tty->termios->c_cflag = tmp_termio.c_cflag;
*(unsigned short *)&tty->termios->c_lflag = tmp_termio.c_lflag;
- tty->termios->c_line = tmp_termio.c_line;
for(i=0 ; i < NCC ; i++)
tty->termios->c_cc[i] = tmp_termio.c_cc[i];
+ unset_locked_termios(tty->termios, &old_termios,
+ termios_locked[tty->line]);
+
+#if 0
+ retval = tty_set_ldisc(tty, tmp_termio.c_line);
+ if (retval)
+ return retval;
+#endif
+
if (tty->set_termios)
(*tty->set_termios)(tty, &old_termios);
return 0;
}
+static int get_lcktrmios(struct tty_struct * tty, struct termios * termios,
+ int channel)
+{
+ int i;
+
+ i = verify_area(VERIFY_WRITE, termios, sizeof (*termios));
+ if (i)
+ return i;
+ for (i=0 ; i< (sizeof (*termios)) ; i++)
+ put_fs_byte( ((char *)termios_locked[channel])[i],
+ i+(char *)termios);
+ return 0;
+}
+
+static int set_lcktrmios(struct tty_struct * tty, struct termios * termios,
+ int channel)
+{
+ int i;
+
+ if (!suser())
+ return -EPERM;
+ for (i=0 ; i< (sizeof (*termios)) ; i++)
+ ((char *)termios_locked[channel])[i] =
+ get_fs_byte(i+(char *)termios);
+
+ return 0;
+}
+
static int set_window_size(struct tty_struct * tty, struct winsize * ws)
{
int i,changed;
@@ -299,6 +369,7 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
/* Now set up the new line discipline. */
tty->disc = ldisc;
+ tty->termios->c_line = ldisc;
if (ldiscs[tty->disc].open)
return(ldiscs[tty->disc].open(tty));
else
@@ -359,10 +430,14 @@ int tty_ioctl(struct inode * inode, struct file * file,
switch (arg) {
case TCOOFF:
tty->stopped = 1;
+ if (tty->stop)
+ (tty->stop)(tty);
TTY_WRITE_FLUSH(tty);
return 0;
case TCOON:
tty->stopped = 0;
+ if (tty->start)
+ (tty->start)(tty);
TTY_WRITE_FLUSH(tty);
return 0;
case TCIOFF:
@@ -507,7 +582,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
case TIOCSETD:
arg = get_fs_long((unsigned long *) arg);
return tty_set_ldisc(tty, arg);
- case TIOCPKT:
+ case TIOCGLCKTRMIOS:
+ arg = get_fs_long((unsigned long *) arg);
+ return get_lcktrmios(tty, (struct termios *) arg,
+ termios_dev);
+ case TIOCSLCKTRMIOS:
+ arg = get_fs_long((unsigned long *) arg);
+ return set_lcktrmios(tty, (struct termios *) arg,
+ termios_dev);
+ case TIOCPKT:
{
int on;
if (!IS_A_PTY_MASTER(dev))
diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c
index 1e27f23..b1f9c59 100644
--- a/kernel/chr_drv/vt.c
+++ b/kernel/chr_drv/vt.c
@@ -13,6 +13,7 @@
#include <linux/keyboard.h>
#include <linux/kd.h>
#include <linux/vt.h>
+#include <linux/string.h>
#include <asm/io.h>
#include <asm/segment.h>
@@ -258,6 +259,60 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
}
+ case KDGKBSENT:
+ {
+ const struct kbsentry *a = (struct kbsentry *)arg;
+ u_char i;
+ char *p;
+ u_char *q;
+
+ verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
+ if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC)
+ return -EINVAL;
+ q = a->kb_string;
+ for (p = func_table[i]; *p; p++)
+ put_fs_byte(*p, q++);
+ put_fs_byte(0, q);
+ return 0;
+ }
+
+ case KDSKBSENT:
+ {
+ struct kbsentry * const a = (struct kbsentry *)arg;
+ int delta;
+ char *first_free;
+ u_char i;
+ int k;
+ u_char *p;
+ char *q;
+
+ verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
+ if ((i = get_fs_byte(&a->kb_func)) >= NR_FUNC)
+ return -EINVAL;
+ delta = -strlen(func_table[i]);
+ for (p = a->kb_string; get_fs_byte(p); p++)
+ delta++;
+ first_free = func_table[NR_FUNC - 1] +
+ strlen(func_table[NR_FUNC - 1]) + 1;
+ if (
+ delta > 0 &&
+ first_free + delta > func_buf + FUNC_BUFSIZE
+ )
+ return -EINVAL;
+ if (i < NR_FUNC - 1) {
+ memmove(
+ func_table[i + 1] + delta,
+ func_table[i + 1],
+ first_free - func_table[i + 1]);
+ for (k = i + 1; k < NR_FUNC; k++)
+ func_table[k] += delta;
+ }
+ for (p = a->kb_string, q = func_table[i]; ; p++, q++)
+ if (!(*q = get_fs_byte(p)))
+ break;
+ 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 8882395..09b905e 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -62,8 +62,7 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv)
if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
p->signal &= ~(1<<(SIGCONT-1));
/* Actually generate the signal */
- if (!generate(sig,p))
- return 0;
+ generate(sig,p);
return 0;
}
@@ -196,17 +195,17 @@ void audit_ptree(void)
*/
int session_of_pgrp(int pgrp)
{
- struct task_struct **p;
+ struct task_struct *p;
int fallback;
fallback = -1;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!*p || (*p)->session <= 0)
+ for_each_task(p) {
+ if (p->session <= 0)
continue;
- if ((*p)->pgrp == pgrp)
- return (*p)->session;
- if ((*p)->pid == pgrp)
- fallback = (*p)->session;
+ if (p->pgrp == pgrp)
+ return p->session;
+ if (p->pid == pgrp)
+ fallback = p->session;
}
return fallback;
}
@@ -217,19 +216,20 @@ int session_of_pgrp(int pgrp)
*/
int kill_pg(int pgrp, int sig, int priv)
{
- struct task_struct **p;
+ struct task_struct *p;
int err,retval = -ESRCH;
int found = 0;
if (sig<0 || sig>32 || pgrp<=0)
return -EINVAL;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if (*p && (*p)->pgrp == pgrp) {
- if ((err = send_sig(sig,*p,priv)) != 0)
+ for_each_task(p) {
+ if (p->pgrp == pgrp) {
+ if ((err = send_sig(sig,p,priv)) != 0)
retval = err;
else
found++;
}
+ }
return(found ? 0 : retval);
}
@@ -240,31 +240,33 @@ int kill_pg(int pgrp, int sig, int priv)
*/
int kill_sl(int sess, int sig, int priv)
{
- struct task_struct **p;
+ struct task_struct *p;
int err,retval = -ESRCH;
int found = 0;
if (sig<0 || sig>32 || sess<=0)
return -EINVAL;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if (*p && (*p)->session == sess && (*p)->leader) {
- if ((err = send_sig(sig,*p,priv)) != 0)
+ for_each_task(p) {
+ if (p->session == sess && p->leader) {
+ if ((err = send_sig(sig,p,priv)) != 0)
retval = err;
else
found++;
}
+ }
return(found ? 0 : retval);
}
int kill_proc(int pid, int sig, int priv)
{
- struct task_struct **p;
+ struct task_struct *p;
if (sig<0 || sig>32)
return -EINVAL;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if (*p && (*p)->pid == pid)
- return send_sig(sig,*p,priv);
+ for_each_task(p) {
+ if (p && p->pid == pid)
+ return send_sig(sig,p,priv);
+ }
return(-ESRCH);
}
@@ -274,18 +276,19 @@ int kill_proc(int pid, int sig, int priv)
*/
extern "C" int sys_kill(int pid,int sig)
{
- struct task_struct **p = NR_TASKS + task;
int err, retval = 0, count = 0;
if (!pid)
return(kill_pg(current->pgrp,sig,0));
if (pid == -1) {
- while (--p > &FIRST_TASK)
- if (*p && (*p)->pid > 1 && *p != current) {
+ struct task_struct * p;
+ for_each_task(p) {
+ if (p->pid > 1 && p != current) {
++count;
- if ((err = send_sig(sig,*p,0)) != -EPERM)
+ if ((err = send_sig(sig,p,0)) != -EPERM)
retval = err;
}
+ }
return(count ? retval : -ESRCH);
}
if (pid < 0)
@@ -304,16 +307,15 @@ extern "C" int sys_kill(int pid,int sig)
*/
int is_orphaned_pgrp(int pgrp)
{
- struct task_struct **p;
+ struct task_struct *p;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!(*p) ||
- ((*p)->pgrp != pgrp) ||
- ((*p)->state == TASK_ZOMBIE) ||
- ((*p)->p_pptr->pid == 1))
+ for_each_task(p) {
+ if ((p->pgrp != pgrp) ||
+ (p->state == TASK_ZOMBIE) ||
+ (p->p_pptr->pid == 1))
continue;
- if (((*p)->p_pptr->pgrp != pgrp) &&
- ((*p)->p_pptr->session == (*p)->session))
+ if ((p->p_pptr->pgrp != pgrp) &&
+ (p->p_pptr->session == p->session))
return 0;
}
return(1); /* (sighing) "Often!" */
@@ -321,12 +323,12 @@ int is_orphaned_pgrp(int pgrp)
static int has_stopped_jobs(int pgrp)
{
- struct task_struct ** p;
+ struct task_struct * p;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!*p || (*p)->pgrp != pgrp)
+ for_each_task(p) {
+ if (p->pgrp != pgrp)
continue;
- if ((*p)->state == TASK_STOPPED)
+ if (p->state == TASK_STOPPED)
return(1);
}
return(0);
@@ -334,14 +336,15 @@ static int has_stopped_jobs(int pgrp)
static void forget_original_parent(struct task_struct * father)
{
- struct task_struct ** p;
+ struct task_struct * p;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if (*p && (*p)->p_opptr == father)
+ for_each_task(p) {
+ if (p->p_opptr == father)
if (task[1])
- (*p)->p_opptr = task[1];
+ p->p_opptr = task[1];
else
- (*p)->p_opptr = task[0];
+ p->p_opptr = task[0];
+ }
}
volatile void do_exit(long code)
@@ -380,6 +383,17 @@ fake_volatile:
}
}
+ if (current->ldt) {
+ free_page((unsigned long) current->ldt);
+ current->ldt = NULL;
+ for (i=1 ; i<NR_TASKS ; i++) {
+ if (task[i] == current) {
+ set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, &default_ldt, 1);
+ load_ldt(i);
+ }
+ }
+ }
+
current->state = TASK_ZOMBIE;
current->exit_code = code;
current->rss = 0;
@@ -438,7 +452,7 @@ fake_volatile:
}
}
if (current->leader) {
- struct task_struct **p;
+ struct task_struct *p;
struct tty_struct *tty;
if (current->tty >= 0) {
@@ -450,9 +464,10 @@ fake_volatile:
tty->session = 0;
}
}
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if (*p && (*p)->session == current->session)
- (*p)->tty = -1;
+ for_each_task(p) {
+ if (p->session == current->session)
+ p->tty = -1;
+ }
}
if (last_task_used_math == current)
last_task_used_math = NULL;
diff --git a/kernel/fork.c b/kernel/fork.c
index fa5acff..848615d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -23,7 +23,6 @@
#include <asm/segment.h>
#include <asm/system.h>
-extern "C" void lcall7(void);
extern "C" void ret_from_sys_call(void) __asm__("ret_from_sys_call");
#define MAX_TASKS_PER_USER (NR_TASKS/2)
@@ -122,8 +121,7 @@ extern "C" int sys_fork(struct pt_regs regs)
struct file *f;
unsigned long clone_flags = COPYVM | SIGCHLD;
- p = (struct task_struct *) __get_free_page(GFP_KERNEL);
- if (!p)
+ if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL)))
goto bad_fork;
nr = find_empty_process();
if (nr < 0)
@@ -150,8 +148,7 @@ extern "C" int sys_fork(struct pt_regs regs)
/*
* set up new TSS and kernel stack
*/
- p->kernel_stack_page = __get_free_page(GFP_KERNEL);
- if (!p->kernel_stack_page)
+ if (!(p->kernel_stack_page = __get_free_page(GFP_KERNEL)))
goto bad_fork_cleanup;
p->tss.es = KERNEL_DS;
p->tss.cs = KERNEL_CS;
@@ -178,8 +175,11 @@ extern "C" int sys_fork(struct pt_regs regs)
}
p->exit_signal = clone_flags & CSIGNAL;
p->tss.ldt = _LDT(nr);
+ if (p->ldt) {
+ if (p->ldt = (struct desc_struct*) __get_free_page(GFP_KERNEL))
+ memcpy(p->ldt, current->ldt, PAGE_SIZE);
+ }
p->tss.bitmap = offsetof(struct tss_struct,io_bitmap);
- set_call_gate(p->ldt+0,lcall7);
for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */
p->tss.io_bitmap[i] = ~0;
if (last_task_used_math == current)
@@ -204,7 +204,11 @@ extern "C" int sys_fork(struct pt_regs regs)
current->executable->i_count++;
dup_mmap(p);
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
- set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
+ if (p->ldt)
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
+ else
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
+
p->counter = current->counter >> 1;
p->state = TASK_RUNNING; /* do this last, just in case */
return p->pid;
diff --git a/kernel/ldt.c b/kernel/ldt.c
new file mode 100644
index 0000000..021151e
--- /dev/null
+++ b/kernel/ldt.c
@@ -0,0 +1,96 @@
+/*
+ * linux/kernel/ldt.c
+ *
+ * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/ldt.h>
+
+static int read_ldt(void * ptr, unsigned long bytecount)
+{
+ int error;
+ void * address = current->ldt;
+ unsigned long size;
+
+ if (!ptr)
+ return -EINVAL;
+ size = PAGE_SIZE;
+ if (!address) {
+ address = &default_ldt;
+ size = sizeof(default_ldt);
+ }
+ if (size > bytecount)
+ size = bytecount;
+ error = verify_area(VERIFY_WRITE, ptr, size);
+ if (error)
+ return error;
+ memcpy_tofs(ptr, address, size);
+ return size;
+}
+
+static int write_ldt(void * ptr, unsigned long bytecount)
+{
+ struct modify_ldt_ldt_s ldt_info;
+ unsigned long *lp;
+ unsigned long base, limit;
+ int error, i;
+
+ if (bytecount != sizeof(ldt_info))
+ return -EINVAL;
+ error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info));
+ if (error)
+ return error;
+
+ memcpy_fromfs(&ldt_info, ptr, sizeof(ldt_info));
+
+ if (ldt_info.contents == 3 || ldt_info.entry_number >= 512)
+ return -EINVAL;
+
+ limit = ldt_info.limit;
+ base = ldt_info.base_addr;
+ if (ldt_info.limit_in_pages)
+ limit *= PAGE_SIZE;
+
+ limit += base;
+ if (limit < base || limit >= 0xC0000000)
+ return -EINVAL;
+
+ if (!current->ldt) {
+ for (i=1 ; i<NR_TASKS ; i++) {
+ if (task[i] == current) {
+ if (!(current->ldt = (struct desc_struct*) get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, 512);
+ load_ldt(i);
+ }
+ }
+ }
+
+ lp = (unsigned long *) &current->ldt[ldt_info.entry_number];
+ *lp = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+ (ldt_info.limit & 0x0ffff);
+ *(lp+1) = (ldt_info.base_addr & 0xff000000) |
+ ((ldt_info.base_addr & 0x00ff0000)>>16) |
+ (ldt_info.limit & 0xf0000) |
+ (ldt_info.contents << 10) |
+ ((ldt_info.read_exec_only ^ 1) << 9) |
+ (ldt_info.seg_32bit << 22) |
+ (ldt_info.limit_in_pages << 23) |
+ 0xf000;
+ return 0;
+}
+
+extern "C" int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
+{
+ if (func == 0)
+ return read_ldt(ptr, bytecount);
+ if (func == 1)
+ return write_ldt(ptr, bytecount);
+ return -ENOSYS;
+}
diff --git a/kernel/printk.c b/kernel/printk.c
index f577da1..3854604 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -20,13 +20,15 @@
#include <linux/sched.h>
#include <linux/kernel.h>
+#define LOG_BUF_LEN 4096
+
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
extern void console_print(const char *);
static void (*console_print_proc)(const char *) = 0;
-static char log_buf[4096];
+static char log_buf[LOG_BUF_LEN];
static unsigned long log_start = 0;
static unsigned long logged_chars = 0;
unsigned long log_size = 0;
@@ -73,15 +75,19 @@ extern "C" int sys_syslog(int type, char * buf, int len)
sti();
}
i = 0;
+ cli();
while (log_size && i < len) {
+ cli();
c = *((char *) log_buf+log_start);
log_start++;
log_size--;
- log_start &= 4095;
+ log_start &= LOG_BUF_LEN-1;
+ sti();
put_fs_byte(c,buf);
buf++;
i++;
}
+ sti();
return i;
case 4: /* Read/clear last 4k of kernel messages */
do_clear = 1;
@@ -92,13 +98,13 @@ extern "C" int sys_syslog(int type, char * buf, int len)
return 0;
verify_area(VERIFY_WRITE,buf,len);
count = len;
- if (count > 4096)
- count = 4096;
+ if (count > LOG_BUF_LEN)
+ count = LOG_BUF_LEN;
if (count > logged_chars)
count = logged_chars;
j = log_start + log_size - count;
for (i = 0; i < count; i++) {
- c = *((char *) log_buf + (j++ & 4095));
+ c = *((char *) log_buf + (j++ & LOG_BUF_LEN-1));
put_fs_byte(c, buf++);
}
if (do_clear)
@@ -127,8 +133,8 @@ extern "C" int printk(const char *fmt, ...)
i=vsprintf(buf,fmt,args);
va_end(args);
for (j = 0; j < i ; j++) {
- log_buf[(log_start+log_size) & 4095] = buf[j];
- if (log_size < 4096)
+ log_buf[(log_start+log_size) & LOG_BUF_LEN-1] = buf[j];
+ if (log_size < LOG_BUF_LEN)
log_size++;
else
log_start++;
@@ -156,7 +162,7 @@ void register_console(void (*proc)(const char *))
for (i=0,j=0; i < log_size; i++) {
buf[j++] = log_buf[p];
- p++; p &= 4095;
+ p++; p &= LOG_BUF_LEN-1;
if (j < sizeof(buf)-1)
continue;
buf[j] = 0;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 1e70ff9..71626a4 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -19,7 +19,7 @@
/* determines which flags the user has access to. */
/* 1 = access 0 = no access */
-#define FLAG_MASK 0x00000dd9
+#define FLAG_MASK 0x00044dd5
/* set's the trap flag. */
#define TRAP_FLAG 0x100
@@ -89,19 +89,18 @@ static unsigned long get_long(struct task_struct * tsk,
unsigned long page;
repeat:
- page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
- page = *(unsigned long *) page;
+ page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
if (page & PAGE_PRESENT) {
- page &= 0xfffff000;
- page += (addr >> 10) & 0xffc;
+ page &= PAGE_MASK;
+ page += PAGE_PTR(addr);
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
do_no_page(0,addr,tsk,0);
goto repeat;
}
- page &= 0xfffff000;
- page += addr & 0xfff;
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
return *(unsigned long *) page;
}
@@ -117,11 +116,10 @@ static void put_long(struct task_struct * tsk, unsigned long addr,
unsigned long page, pte;
repeat:
- page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
- page = *(unsigned long *) page;
+ page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
if (page & PAGE_PRESENT) {
- page &= 0xfffff000;
- page += (addr >> 10) & 0xffc;
+ page &= PAGE_MASK;
+ page += PAGE_PTR(addr);
pte = page;
page = *((unsigned long *) page);
}
@@ -135,8 +133,8 @@ 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;
+ page &= PAGE_MASK;
+ page += addr & ~PAGE_MASK;
*(unsigned long *) page = data;
}
@@ -149,12 +147,12 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
{
unsigned long low,high;
- if (addr > TASK_SIZE-4)
+ if (addr > TASK_SIZE-sizeof(long))
return -EIO;
- if ((addr & 0xfff) > PAGE_SIZE-4) {
- low = get_long(tsk,addr & 0xfffffffc);
- high = get_long(tsk,(addr+4) & 0xfffffffc);
- switch (addr & 3) {
+ if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
+ low = get_long(tsk,addr & ~(sizeof(long)-1));
+ high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+ switch (addr & sizeof(long)-1) {
case 1:
low >>= 8;
low |= high << 24;
@@ -183,36 +181,36 @@ static int write_long(struct task_struct * tsk, unsigned long addr,
{
unsigned long low,high;
- if (addr > TASK_SIZE-4)
+ if (addr > TASK_SIZE-sizeof(long))
return -EIO;
- if ((addr & 0xfff) > PAGE_SIZE-4) {
- low = get_long(tsk,addr & 0xfffffffc);
- high = get_long(tsk,(addr+4) & 0xfffffffc);
- switch (addr & 3) {
+ if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
+ low = get_long(tsk,addr & ~(sizeof(long)-1));
+ high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
+ switch (addr & sizeof(long)-1) {
case 0: /* shouldn't happen, but safety first */
low = data;
break;
case 1:
low &= 0x000000ff;
low |= data << 8;
- high &= 0xffffff00;
+ high &= ~0xff;
high |= data >> 24;
break;
case 2:
low &= 0x0000ffff;
low |= data << 16;
- high &= 0xffff0000;
+ high &= ~0xffff;
high |= data >> 16;
break;
case 3:
low &= 0x00ffffff;
low |= data << 24;
- high &= 0xff000000;
+ high &= ~0xffffff;
high |= data >> 8;
break;
}
- put_long(tsk,addr & 0xfffffffc,low);
- put_long(tsk,(addr+4) & 0xfffffffc,high);
+ put_long(tsk,addr & ~(sizeof(long)-1),low);
+ put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
put_long(tsk,addr,data);
return 0;
@@ -269,7 +267,7 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
res = read_long(child, addr, &tmp);
if (res < 0)
return res;
- res = verify_area(VERIFY_WRITE, (void *) data, 4);
+ res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
if (!res)
put_fs_long(tmp,(unsigned long *) data);
return res;
@@ -283,10 +281,10 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
addr = addr >> 2; /* temporary hack. */
if (addr < 0 || addr >= 17)
return -EIO;
- res = verify_area(VERIFY_WRITE, (void *) data, 4);
+ res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
if (res)
return res;
- tmp = get_stack_long(child, 4*addr - MAGICNUMBER);
+ tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);
put_fs_long(tmp,(unsigned long *) data);
return 0;
}
@@ -302,11 +300,15 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
return -EIO;
if (addr == ORIG_EAX)
return -EIO;
+ if (addr == DS || addr == ES ||
+ addr == FS || addr == GS ||
+ addr == CS || addr == SS)
+ return -EIO;
if (addr == EFL) { /* flags. */
data &= FLAG_MASK;
- data |= get_stack_long(child, EFL*4-MAGICNUMBER) & ~FLAG_MASK;
+ data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER) & ~FLAG_MASK;
}
- if (put_stack_long(child, 4*addr-MAGICNUMBER, data))
+ if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
return -EIO;
return 0;
@@ -323,8 +325,8 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
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;
- put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
+ tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+ put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
@@ -339,8 +341,8 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
child->state = TASK_RUNNING;
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);
+ tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+ put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
@@ -350,8 +352,8 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
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);
+ tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
+ put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
child->state = TASK_RUNNING;
child->exit_code = data;
/* give it a chance to run. */
@@ -370,8 +372,8 @@ extern "C" int sys_ptrace(long request, long pid, long addr, long data)
child->p_pptr = child->p_opptr;
SET_LINKS(child);
/* 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);
+ tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+ put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
return 0;
}
diff --git a/kernel/sched.c b/kernel/sched.c
index 7c46bb0..55d60d6 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -31,8 +31,14 @@
#define TIMER_IRQ 0
int need_resched = 0;
+
+/*
+ * Tell us the machine setup..
+ */
int hard_math = 0; /* set by boot/head.S */
+int x86 = 0; /* set by boot/head.S to 3 or 4 */
int ignore_irq13 = 0; /* set if exception 16 works */
+int wp_works_ok = 0; /* not used currently */
extern int _setitimer(int, struct itimerval *, struct itimerval *);
unsigned long * prof_buffer = NULL;
@@ -148,8 +154,7 @@ confuse_gcc1:
}
confuse_gcc2:
if (!c) {
- p = &init_task;
- while ((p = p->next_task) != &init_task)
+ for_each_task(p)
p->counter = (p->counter >> 1) + p->priority;
}
switch_to(next);
@@ -277,9 +282,10 @@ void add_timer(struct timer_list * timer)
restore_flags(flags);
}
-void del_timer(struct timer_list * timer)
+int del_timer(struct timer_list * timer)
{
unsigned long flags;
+ unsigned long expires = 0;
struct timer_list **p;
p = &next_timer;
@@ -289,11 +295,15 @@ void del_timer(struct timer_list * timer)
if (*p == timer) {
if ((*p = timer->next) != NULL)
(*p)->expires += timer->expires;
- break;
+ timer->expires += expires;
+ restore_flags(flags);
+ return 1;
}
+ expires += (*p)->expires;
p = &(*p)->next;
}
restore_flags(flags);
+ return 0;
}
unsigned long timer_active = 0;
@@ -345,7 +355,7 @@ static void do_timer(struct pt_regs * regs)
{
unsigned long mask;
struct timer_struct *tp = timer_table+0;
- struct task_struct ** task_p;
+ struct task_struct * task_p;
jiffies++;
calc_load();
@@ -372,13 +382,15 @@ static void do_timer(struct pt_regs * regs)
need_resched = 1;
}
/* Update ITIMER_REAL for every task */
- for (task_p = &LAST_TASK; task_p >= &FIRST_TASK; task_p--)
- if (*task_p && (*task_p)->it_real_value
- && !(--(*task_p)->it_real_value)) {
- send_sig(SIGALRM,*task_p,1);
- (*task_p)->it_real_value = (*task_p)->it_real_incr;
- need_resched = 1;
- }
+ for_each_task(task_p) {
+ if (!task_p->it_real_value)
+ continue;
+ if (--task_p->it_real_value)
+ continue;
+ send_sig(SIGALRM,task_p,1);
+ task_p->it_real_value = task_p->it_real_incr;
+ need_resched = 1;
+ }
/* Update ITIMER_PROF for the current task */
if (current->it_prof_value && !(--current->it_prof_value)) {
current->it_prof_value = current->it_prof_incr;
@@ -473,7 +485,7 @@ static void show_task(int nr,struct task_struct * p)
printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid,
p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i = 0;
- j = 4096;
+ j = PAGE_SIZE;
if (!(stack = (unsigned char *) p->kernel_stack_page)) {
stack = (unsigned char *) init_kernel_stack;
j = sizeof(init_kernel_stack);
@@ -508,7 +520,7 @@ void sched_init(void)
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
- set_ldt_desc(gdt+FIRST_LDT_ENTRY,&init_task.ldt);
+ set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1);
set_system_gate(0x80,&system_call);
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1 ; i<NR_TASKS ; i++) {
diff --git a/kernel/signal.c b/kernel/signal.c
index 50bc1c1..6f6aeef 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -317,6 +317,8 @@ extern "C" int do_signal(unsigned long oldmask, struct pt_regs * regs)
frame = (unsigned long *) regs->esp;
signr = 1;
sa = current->sigaction;
+ if (regs->cs != USER_CS || regs->ss != USER_DS)
+ printk("Warning: signal handler with nonstandard code/stack segment\n");
for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
if (mask > handler_signal)
break;
diff --git a/kernel/sys.c b/kernel/sys.c
index a4a5372..de25852 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -16,6 +16,7 @@
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/ptrace.h>
+#include <linux/stat.h>
#include <asm/segment.h>
#include <asm/io.h>
@@ -156,7 +157,7 @@ static void mark_screen_rdonly(struct task_struct * tsk)
if ((tmp = tsk->tss.cr3) != 0) {
tmp = *(unsigned long *) tmp;
if (tmp & PAGE_PRESENT) {
- tmp &= 0xfffff000;
+ tmp &= PAGE_MASK;
pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp;
tmp = 32;
while (tmp--) {
@@ -189,7 +190,7 @@ extern "C" int sys_vm86(struct vm86_struct * v86)
* inherited from protected mode.
*/
info.regs.eflags &= 0x00000dd5;
- info.regs.eflags |= 0xfffff22a & pt_regs->eflags;
+ info.regs.eflags |= ~0x00000dd5 & pt_regs->eflags;
info.regs.eflags |= VM_MASK;
current->saved_kernel_stack = current->tss.esp0;
current->tss.esp0 = (unsigned long) pt_regs;
@@ -433,8 +434,8 @@ extern "C" int sys_brk(unsigned long brk)
if (brk < current->end_code)
return current->brk;
- newbrk = (brk + 0x00000fff) & 0xfffff000;
- oldbrk = (current->brk + 0x00000fff) & 0xfffff000;
+ newbrk = PAGE_ALIGN(brk);
+ oldbrk = PAGE_ALIGN(current->brk);
/*
* Always allow shrinking brk
*/
@@ -448,7 +449,7 @@ extern "C" int sys_brk(unsigned long brk)
*/
rlim = current->rlim[RLIMIT_DATA].rlim_cur;
if (rlim >= RLIM_INFINITY)
- rlim = 0xffffffff;
+ rlim = ~0;
if (brk - current->end_code > rlim || brk >= current->start_stack - 16384)
return current->brk;
/*
@@ -891,7 +892,7 @@ extern "C" int sys_umask(int mask)
{
int old = current->umask;
- current->umask = mask & 0777;
+ current->umask = mask & S_IRWXUGO;
return (old);
}
diff --git a/kernel/sys_call.S b/kernel/sys_call.S
index 3ecd6fb..1fefe05 100644
--- a/kernel/sys_call.S
+++ b/kernel/sys_call.S
@@ -194,10 +194,8 @@ ret_from_sys_call:
9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $(VM_MASK),%eax # different then
jne 1f
- cmpw $(USER_CS),CS(%esp) # was old code segment supervisor ?
- jne 2f
- cmpw $(USER_DS),OLDSS(%esp) # was stack segment user segment ?
- jne 2f
+ cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
+ je 2f
1: sti
orl $(IF_MASK),%eax # these just try to make sure
andl $~NT_MASK,%eax # the program doesn't do anything
diff --git a/lib/malloc.c b/lib/malloc.c
index a3d8127..789e5d3 100644
--- a/lib/malloc.c
+++ b/lib/malloc.c
@@ -146,10 +146,8 @@ static inline void init_bucket_desc(unsigned long page)
int i;
bdesc = (struct bucket_desc *) page;
- for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) {
+ for (i = PAGE_SIZE/sizeof(struct bucket_desc); --i > 0; bdesc++ )
bdesc->next = bdesc+1;
- bdesc++;
- }
/*
* This is done last, to avoid race conditions in case
* get_free_page() sleeps and this routine gets called again....
@@ -214,8 +212,7 @@ kmalloc(unsigned int len, int priority)
*/
if (!free_bucket_desc) {
restore_flags(flags);
- page = get_free_page(priority);
- if (!page)
+ if(!(page=__get_free_page(priority)))
return NULL;
init_bucket_desc(page);
}
@@ -224,12 +221,10 @@ kmalloc(unsigned int len, int priority)
free_bucket_desc = bdesc->next;
restore_flags(flags);
- page = get_free_page(priority);
-
+ if(!(page=__get_free_page(priority))) {
/*
* Out of memory? Put the bucket descriptor back on the free list
*/
- if (!page) {
cli();
bdesc->next = free_bucket_desc;
free_bucket_desc = bdesc;
@@ -345,23 +340,25 @@ void deb_kcheck_s(const char *deb_file, unsigned short deb_line,
hd--;
if(hd->magic != DEB_MAGIC_ALLOC) {
- if(hd->magic != DEB_MAGIC_USED && hd->magic != DEB_MAGIC_FREED)
- printk("DEB_MALLOC Using %s block of 0x%x from %s:%d, by %s:%d, last %s:%d\n",
- (hd->magic == DEB_MAGIC_FREE)?"free":"bad",obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line);
- if(hd->magic == DEB_MAGIC_FREE)
+ if(hd->magic == DEB_MAGIC_FREE) {
+ printk("DEB_MALLOC Using free block of 0x%x at %s:%d, by %s:%d, wasOK %s:%d\n",
+ obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line);
+ /* For any other condition it is either superfluous or dangerous to print something. */
hd->magic = DEB_MAGIC_FREED;
+ }
return;
}
if(hd->size != size) {
if(size != 0) {
- printk("DEB_MALLOC size for 0x%x given as %d, stored %d, from %s:%d, last %s:%d\n",
+ printk("DEB_MALLOC size for 0x%x given as %d, stored %d, at %s:%d, wasOK %s:%d\n",
obj,size,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line);
}
size = hd->size;
}
he = (struct hdr_end *)(((char *)obj)+size);
if(he->magic != DEB_MAGIC_END) {
- printk("DEB_MALLOC overran block 0x%x:%d, free at %s:%d\n",obj,hd->size,deb_file,deb_line);
+ printk("DEB_MALLOC overran block 0x%x:%d, at %s:%d, wasOK %s:%d\n",
+ obj,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line);
hd->magic = DEB_MAGIC_USED;
return;
}
@@ -398,17 +395,15 @@ void kfree_s(void *obj, int size)
hd = (struct hdr_start *) obj;
hd--;
- if(hd->magic != DEB_MAGIC_ALLOC && hd->magic != DEB_MAGIC_USED) {
- if(hd->magic != DEB_MAGIC_FREED)
- printk("DEB_MALLOC %s free of 0x%x from %s:%d by %s:%d, last %s:%d\n",
- (hd->magic == DEB_MAGIC_FREE)?"dup":"bad",
+ if(hd->magic == DEB_MAGIC_FREE) {
+ printk("DEB_MALLOC dup free of 0x%x at %s:%d by %s:%d, wasOK %s:%d\n",
obj,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line);
return;
}
if(hd->size != size) {
if(size != 0) {
if(hd->magic != DEB_MAGIC_USED)
- printk("DEB_MALLOC size for 0x%x given as %d, stored %d, from %s:%d, last %s:%d\n",
+ printk("DEB_MALLOC size for 0x%x given as %d, stored %d, at %s:%d, wasOK %s:%d\n",
obj,size,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line);
}
size = hd->size;
@@ -416,16 +411,15 @@ void kfree_s(void *obj, int size)
he = (struct hdr_end *)(((char *)obj)+size);
if(he->magic != DEB_MAGIC_END) {
if(hd->magic != DEB_MAGIC_USED)
- printk("DEB_MALLOC overran block 0x%x:%d, free at %s:%d, last %s:%d\n",
- obj,hd->size,deb_file,deb_line,hd->ok_file,hd->ok_line);
- return;
+ printk("DEB_MALLOC overran block 0x%x:%d, at %s:%d, from %s:%d, wasOK %s:%d\n",
+ obj,hd->size,deb_file,deb_line,hd->file,hd->line,hd->ok_file,hd->ok_line);
}
size += sizeof(struct hdr_start)+sizeof(struct hdr_end);
}
#endif
save_flags(flags);
/* Calculate what page this object lives in */
- page = (void *) ((unsigned long) obj & 0xfffff000);
+ page = (void *) ((unsigned long) obj & PAGE_MASK);
/* Now search the buckets looking for that page */
for (bdir = bucket_dir; bdir->size; bdir++) {
@@ -528,7 +522,7 @@ int get_malloc(char *buffer)
if(hd->magic == DEB_MAGIC_ALLOC) {
if(len > PAGE_SIZE-80) {
restore_flags(flags);
- len += sprintf(buffer+len,"+++\n");
+ len += sprintf(buffer+len,"...\n");
return len;
}
len += sprintf(buffer+len,"%08x:%03x %s:%d %s:%d\n",
diff --git a/mm/memory.c b/mm/memory.c
index 6b31b29..33ae7c1 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -91,19 +91,19 @@ static void free_one_table(unsigned long * page_dir)
}
if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
return;
- page_table = (unsigned long *) (pg_table & 0xfffff000);
- for (j = 0 ; j < 1024 ; j++,page_table++) {
+ page_table = (unsigned long *) (pg_table & PAGE_MASK);
+ for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++) {
unsigned long pg = *page_table;
if (!pg)
continue;
*page_table = 0;
if (pg & PAGE_PRESENT)
- free_page(0xfffff000 & pg);
+ free_page(PAGE_MASK & pg);
else
swap_free(pg);
}
- free_page(0xfffff000 & pg_table);
+ free_page(PAGE_MASK & pg_table);
}
/*
@@ -130,19 +130,16 @@ void clear_page_tables(struct task_struct * tsk)
return;
}
if (mem_map[MAP_NR(pg_dir)] > 1) {
- unsigned long page;
unsigned long * new_pg;
- page = get_free_page(GFP_KERNEL);
- if (!page) {
+ if (!(new_pg = (unsigned long*) get_free_page(GFP_KERNEL))) {
oom(tsk);
return;
}
- new_pg = (unsigned long *) page;
for (i = 768 ; i < 1024 ; i++)
new_pg[i] = page_dir[i];
free_page(pg_dir);
- tsk->tss.cr3 = page;
+ tsk->tss.cr3 = (unsigned long) new_pg;
return;
}
for (i = 0 ; i < 768 ; i++,page_dir++)
@@ -179,7 +176,7 @@ void free_page_tables(struct task_struct * tsk)
return;
}
page_dir = (unsigned long *) pg_dir;
- for (i = 0 ; i < 1024 ; i++,page_dir++)
+ for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++)
free_one_table(page_dir);
free_page(pg_dir);
invalidate();
@@ -212,14 +209,13 @@ int copy_page_tables(struct task_struct * tsk)
unsigned long old_pg_dir, *old_page_dir;
unsigned long new_pg_dir, *new_page_dir;
- new_pg_dir = get_free_page(GFP_KERNEL);
- if (!new_pg_dir)
+ if (!(new_pg_dir = get_free_page(GFP_KERNEL)))
return -ENOMEM;
old_pg_dir = current->tss.cr3;
tsk->tss.cr3 = new_pg_dir;
old_page_dir = (unsigned long *) old_pg_dir;
new_page_dir = (unsigned long *) new_pg_dir;
- for (i = 0 ; i < 1024 ; i++,old_page_dir++,new_page_dir++) {
+ for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) {
int j;
unsigned long old_pg_table, *old_page_table;
unsigned long new_pg_table, *new_page_table;
@@ -237,14 +233,13 @@ int copy_page_tables(struct task_struct * tsk)
*new_page_dir = old_pg_table;
continue;
}
- new_pg_table = get_free_page(GFP_KERNEL);
- if (!new_pg_table) {
+ if (!(new_pg_table = get_free_page(GFP_KERNEL))) {
free_page_tables(tsk);
return -ENOMEM;
}
- old_page_table = (unsigned long *) (0xfffff000 & old_pg_table);
- new_page_table = (unsigned long *) (0xfffff000 & new_pg_table);
- for (j = 0 ; j < 1024 ; j++,old_page_table++,new_page_table++) {
+ old_page_table = (unsigned long *) (PAGE_MASK & old_pg_table);
+ new_page_table = (unsigned long *) (PAGE_MASK & new_pg_table);
+ for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) {
unsigned long pg;
pg = *old_page_table;
if (!pg)
@@ -277,18 +272,18 @@ int unmap_page_range(unsigned long from, unsigned long size)
unsigned long *page_table, *dir;
unsigned long poff, pcnt, pc;
- if (from & 0xfff) {
+ if (from & ~PAGE_MASK) {
printk("unmap_page_range called with wrong alignment\n");
return -EINVAL;
}
- size = (size + 0xfff) >> PAGE_SHIFT;
- dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
- poff = (from >> PAGE_SHIFT) & 0x3ff;
- if ((pcnt = 1024 - poff) > size)
+ size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
+ dir = PAGE_DIR_OFFSET(current->tss.cr3,from);
+ poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
+ if ((pcnt = PTRS_PER_PAGE - poff) > size)
pcnt = size;
for ( ; size > 0; ++dir, size -= pcnt,
- pcnt = (size > 1024 ? 1024 : size)) {
+ pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) {
if (!(page_dir = *dir)) {
poff = 0;
continue;
@@ -297,7 +292,7 @@ int unmap_page_range(unsigned long from, unsigned long size)
printk("unmap_page_range: bad page directory.");
continue;
}
- page_table = (unsigned long *)(0xfffff000 & page_dir);
+ page_table = (unsigned long *)(PAGE_MASK & page_dir);
if (poff) {
page_table += poff;
poff = 0;
@@ -309,14 +304,14 @@ int unmap_page_range(unsigned long from, unsigned long size)
if (!(mem_map[MAP_NR(page)]
& MAP_PAGE_RESERVED))
--current->rss;
- free_page(0xfffff000 & page);
+ free_page(PAGE_MASK & page);
} else
swap_free(page);
}
}
- if (pcnt == 1024) {
- free_page(0xfffff000 & page_dir);
+ if (pcnt == PTRS_PER_PAGE) {
*dir = 0;
+ free_page(PAGE_MASK & page_dir);
}
}
invalidate();
@@ -330,35 +325,36 @@ int zeromap_page_range(unsigned long from, unsigned long size, int mask)
unsigned long page;
if (mask) {
- if ((mask & 0xfffff001) != PAGE_PRESENT) {
+ if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) {
printk("zeromap_page_range: mask = %08x\n",mask);
return -EINVAL;
}
mask |= ZERO_PAGE;
}
- if (from & 0xfff) {
+ if (from & ~PAGE_MASK) {
printk("zeromap_page_range: from = %08x\n",from);
return -EINVAL;
}
- dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
- size = (size + 0xfff) >> PAGE_SHIFT;
- poff = (from >> PAGE_SHIFT) & 0x3ff;
- if ((pcnt = 1024 - poff) > size)
+ dir = PAGE_DIR_OFFSET(current->tss.cr3,from);
+ size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
+ poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
+ if ((pcnt = PTRS_PER_PAGE - poff) > size)
pcnt = size;
while (size > 0) {
if (!(PAGE_PRESENT & *dir)) {
- if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) {
+ /* clear page needed here? SRB. */
+ if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) {
invalidate();
return -ENOMEM;
}
if (PAGE_PRESENT & *dir) {
free_page((unsigned long) page_table);
- page_table = (unsigned long *)(0xfffff000 & *dir++);
+ page_table = (unsigned long *)(PAGE_MASK & *dir++);
} else
*dir++ = ((unsigned long) page_table) | PAGE_TABLE;
} else
- page_table = (unsigned long *)(0xfffff000 & *dir++);
+ page_table = (unsigned long *)(PAGE_MASK & *dir++);
page_table += poff;
poff = 0;
for (size -= pcnt; pcnt-- ;) {
@@ -368,13 +364,13 @@ int zeromap_page_range(unsigned long from, unsigned long size, int mask)
if (!(mem_map[MAP_NR(page)]
& MAP_PAGE_RESERVED))
--current->rss;
- free_page(0xfffff000 & page);
+ free_page(PAGE_MASK & page);
} else
swap_free(page);
}
*page_table++ = mask;
}
- pcnt = (size > 1024 ? 1024 : size);
+ pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size);
}
invalidate();
return 0;
@@ -392,31 +388,32 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
unsigned long page;
if (mask) {
- if ((mask & 0xfffff001) != PAGE_PRESENT) {
+ if ((mask & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT) {
printk("remap_page_range: mask = %08x\n",mask);
return -EINVAL;
}
}
- if ((from & 0xfff) || (to & 0xfff)) {
+ if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) {
printk("remap_page_range: from = %08x, to=%08x\n",from,to);
return -EINVAL;
}
- dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
- size = (size + 0xfff) >> PAGE_SHIFT;
- poff = (from >> PAGE_SHIFT) & 0x3ff;
- if ((pcnt = 1024 - poff) > size)
+ dir = PAGE_DIR_OFFSET(current->tss.cr3,from);
+ size = (size + ~PAGE_MASK) >> PAGE_SHIFT;
+ poff = (from >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
+ if ((pcnt = PTRS_PER_PAGE - poff) > size)
pcnt = size;
while (size > 0) {
if (!(PAGE_PRESENT & *dir)) {
- if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) {
+ /* clearing page here, needed? SRB. */
+ if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) {
invalidate();
return -1;
}
*dir++ = ((unsigned long) page_table) | PAGE_TABLE;
}
else
- page_table = (unsigned long *)(0xfffff000 & *dir++);
+ page_table = (unsigned long *)(PAGE_MASK & *dir++);
if (poff) {
page_table += poff;
poff = 0;
@@ -429,7 +426,7 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
if (!(mem_map[MAP_NR(page)]
& MAP_PAGE_RESERVED))
--current->rss;
- free_page(0xfffff000 & page);
+ free_page(PAGE_MASK & page);
} else
swap_free(page);
}
@@ -455,7 +452,7 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
}
to += PAGE_SIZE;
}
- pcnt = (size > 1024 ? 1024 : size);
+ pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size);
}
invalidate();
return 0;
@@ -467,27 +464,27 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i
* out of memory (either when trying to access page-table or
* page.)
*/
-static unsigned long put_page(struct task_struct * tsk,unsigned long page,
+unsigned long put_page(struct task_struct * tsk,unsigned long page,
unsigned long address,int prot)
{
unsigned long *page_table;
- if ((prot & 0xfffff001) != PAGE_PRESENT)
+ if ((prot & (PAGE_MASK|PAGE_PRESENT)) != PAGE_PRESENT)
printk("put_page: prot = %08x\n",prot);
if (page >= high_memory) {
printk("put_page: trying to put page %p at %p\n",page,address);
return 0;
}
- page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
+ page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
if ((*page_table) & PAGE_PRESENT)
- page_table = (unsigned long *) (0xfffff000 & *page_table);
+ page_table = (unsigned long *) (PAGE_MASK & *page_table);
else {
printk("put_page: bad page directory entry\n");
oom(tsk);
*page_table = BAD_PAGETABLE | PAGE_TABLE;
return 0;
}
- page_table += ((address & 0x003ff000) >> PAGE_SHIFT);
+ page_table += (address >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
if (*page_table) {
printk("put_page: page already exists\n");
*page_table = 0;
@@ -512,21 +509,21 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
printk("put_dirty_page: trying to put page %p at %p\n",page,address);
if (mem_map[MAP_NR(page)] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
- page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
+ page_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
if (PAGE_PRESENT & *page_table)
- page_table = (unsigned long *) (0xfffff000 & *page_table);
+ page_table = (unsigned long *) (PAGE_MASK & *page_table);
else {
- if (!(tmp=get_free_page(GFP_KERNEL)))
+ if (!(tmp = get_free_page(GFP_KERNEL)))
return 0;
if (PAGE_PRESENT & *page_table) {
free_page(tmp);
- page_table = (unsigned long *) (0xfffff000 & *page_table);
+ page_table = (unsigned long *) (PAGE_MASK & *page_table);
} else {
*page_table = tmp | PAGE_TABLE;
page_table = (unsigned long *) tmp;
}
}
- page_table += (address >> PAGE_SHIFT) & 0x3ff;
+ page_table += (address >> PAGE_SHIFT) & PTRS_PER_PAGE-1;
if (*page_table) {
printk("put_dirty_page: page already exists\n");
*page_table = 0;
@@ -551,18 +548,18 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
static void __do_wp_page(unsigned long error_code, unsigned long address,
struct task_struct * tsk, unsigned long user_esp)
{
- unsigned long pde, pte, old_page, prot;
+ unsigned long *pde, pte, old_page, prot;
unsigned long new_page;
new_page = __get_free_page(GFP_KERNEL);
- pde = tsk->tss.cr3 + ((address>>20) & 0xffc);
- pte = *(unsigned long *) pde;
+ pde = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
+ pte = *pde;
if (!(pte & PAGE_PRESENT))
goto end_wp_page;
if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory)
goto bad_wp_pagetable;
- pte &= 0xfffff000;
- pte += (address>>10) & 0xffc;
+ pte &= PAGE_MASK;
+ pte += PAGE_PTR(address);
old_page = *(unsigned long *) pte;
if (!(old_page & PAGE_PRESENT))
goto end_wp_page;
@@ -571,8 +568,8 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
if (old_page & PAGE_RW)
goto end_wp_page;
tsk->min_flt++;
- prot = (old_page & 0x00000fff) | PAGE_RW;
- old_page &= 0xfffff000;
+ prot = (old_page & ~PAGE_MASK) | PAGE_RW;
+ old_page &= PAGE_MASK;
if (mem_map[MAP_NR(old_page)] != 1) {
if (new_page) {
if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
@@ -601,7 +598,7 @@ bad_wp_page:
goto end_wp_page;
bad_wp_pagetable:
printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte);
- *(unsigned long *) pde = BAD_PAGETABLE | PAGE_TABLE;
+ *pde = BAD_PAGETABLE | PAGE_TABLE;
send_sig(SIGKILL, tsk, 1);
end_wp_page:
if (new_page)
@@ -619,12 +616,12 @@ void do_wp_page(unsigned long error_code, unsigned long address,
unsigned long page;
unsigned long * pg_table;
- pg_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
+ pg_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
page = *pg_table;
if (!page)
return;
if ((page & PAGE_PRESENT) && page < high_memory) {
- pg_table = (unsigned long *) ((page & 0xfffff000) + ((address>>10) & 0xffc));
+ pg_table = (unsigned long *) ((page & PAGE_MASK) + PAGE_PTR(address));
page = *pg_table;
if (!(page & PAGE_PRESENT))
return;
@@ -662,22 +659,21 @@ int verify_area(int type, void * addr, unsigned long size)
if (!size)
return 0;
size--;
- size += start & 0xfff;
- size >>= 12;
- start &= 0xfffff000;
+ size += start & ~PAGE_MASK;
+ size >>= PAGE_SHIFT;
+ start &= PAGE_MASK;
do {
do_wp_page(1,start,current,0);
- start += 4096;
+ start += PAGE_SIZE;
} while (size--);
return 0;
}
-static void get_empty_page(struct task_struct * tsk, unsigned long address)
+static inline void get_empty_page(struct task_struct * tsk, unsigned long address)
{
unsigned long tmp;
- tmp = get_free_page(GFP_KERNEL);
- if (!tmp) {
+ if (!(tmp = get_free_page(GFP_KERNEL))) {
oom(tsk);
tmp = BAD_PAGE;
}
@@ -707,14 +703,14 @@ static int try_to_share(unsigned long address, struct task_struct * tsk,
unsigned long from_page;
unsigned long to_page;
- from_page = p->tss.cr3 + ((address>>20) & 0xffc);
- to_page = tsk->tss.cr3 + ((address>>20) & 0xffc);
+ from_page = (unsigned long)PAGE_DIR_OFFSET(p->tss.cr3,address);
+ to_page = (unsigned long)PAGE_DIR_OFFSET(tsk->tss.cr3,address);
/* is there a page-directory at from? */
from = *(unsigned long *) from_page;
if (!(from & PAGE_PRESENT))
return 0;
- from &= 0xfffff000;
- from_page = from + ((address>>10) & 0xffc);
+ from &= PAGE_MASK;
+ from_page = from + PAGE_PTR(address);
from = *(unsigned long *) from_page;
/* is the page clean and present? */
if ((from & (PAGE_PRESENT | PAGE_DIRTY)) != PAGE_PRESENT)
@@ -727,19 +723,22 @@ static int try_to_share(unsigned long address, struct task_struct * tsk,
to = *(unsigned long *) to_page;
if (!(to & PAGE_PRESENT))
return 0;
- to &= 0xfffff000;
- to_page = to + ((address>>10) & 0xffc);
+ to &= PAGE_MASK;
+ to_page = to + PAGE_PTR(address);
if (*(unsigned long *) to_page)
return 0;
/* share them if read - do COW immediately otherwise */
if (error_code & PAGE_RW) {
- copy_page((from & 0xfffff000),newpage);
+ if(!newpage) /* did the page exist? SRB. */
+ return 0;
+ copy_page((from & PAGE_MASK),newpage);
to = newpage | PAGE_PRIVATE;
} else {
mem_map[MAP_NR(from)]++;
from &= ~PAGE_RW;
to = from;
- free_page(newpage);
+ if(newpage) /* only if it existed. SRB. */
+ free_page(newpage);
}
*(unsigned long *) from_page = from;
*(unsigned long *) to_page = to;
@@ -755,7 +754,7 @@ static int try_to_share(unsigned long address, struct task_struct * tsk,
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
-static int share_page(struct vm_area_struct * area, struct task_struct * tsk,
+int share_page(struct vm_area_struct * area, struct task_struct * tsk,
struct inode * inode,
unsigned long address, unsigned long error_code, unsigned long newpage)
{
@@ -799,7 +798,7 @@ static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned
unsigned long page;
unsigned long *p;
- p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc));
+ p = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
if (PAGE_PRESENT & *p)
return *p;
if (*p) {
@@ -807,7 +806,7 @@ static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned
*p = 0;
}
page = get_free_page(GFP_KERNEL);
- p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc));
+ p = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
if (PAGE_PRESENT & *p) {
free_page(page);
return *p;
@@ -828,18 +827,15 @@ static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned
void do_no_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk, unsigned long user_esp)
{
- int nr[8], prot;
unsigned long tmp;
unsigned long page;
- unsigned int block,i, j;
- struct inode * inode;
struct vm_area_struct * mpnt;
page = get_empty_pgtable(tsk,address);
if (!page)
return;
- page &= 0xfffff000;
- page += (address >> 10) & 0xffc;
+ page &= PAGE_MASK;
+ page += PAGE_PTR(address);
tmp = *(unsigned long *) page;
if (tmp & PAGE_PRESENT)
return;
@@ -850,67 +846,25 @@ void do_no_page(unsigned long error_code, unsigned long address,
return;
}
address &= 0xfffff000;
- inode = NULL;
- block = 0xffffffff;
- if (address < tsk->end_data) {
- inode = tsk->executable;
- block = 1 + address / BLOCK_SIZE;
- } else {
- for (mpnt = tsk->mmap ; mpnt ; mpnt = mpnt->vm_next) {
- if (address < mpnt->vm_start)
- continue;
- if (address >= ((mpnt->vm_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
- continue;
- mpnt->vm_ops->nopage(error_code, mpnt, address);
- return;
- }
- }
- if (!inode) {
- ++tsk->min_flt;
- get_empty_page(tsk,address);
- if (tsk != current)
- return;
- if (address < tsk->brk)
- return;
- if (address+8192 >= (user_esp & 0xfffff000) &&
- address <= current->start_stack)
- return;
- send_sig(SIGSEGV,tsk,1);
- return;
- }
- page = get_free_page(GFP_KERNEL);
- if (share_page(NULL, tsk,inode,address,error_code,page)) {
- ++tsk->min_flt;
+ for (mpnt = tsk->mmap ; mpnt ; mpnt = mpnt->vm_next) {
+ if (address < mpnt->vm_start)
+ continue;
+ if (address >= ((mpnt->vm_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ continue;
+ mpnt->vm_ops->nopage(error_code, mpnt, address);
return;
}
- if (!page) {
- oom(current);
- put_page(tsk,BAD_PAGE,address,PAGE_PRIVATE);
+ ++tsk->min_flt;
+ get_empty_page(tsk,address);
+ if (tsk != current)
return;
- }
- prot = PAGE_PRIVATE;
- if (CODE_SPACE(address, tsk))
- prot = PAGE_READONLY;
- if (block != 0xffffffff) {
- for (i=0, j=0; i< PAGE_SIZE ; j++, block++,i +=inode->i_sb->s_blocksize)
- nr[j] = bmap(inode,block);
- page = bread_page(page,inode->i_dev,nr,
- inode->i_sb->s_blocksize,prot);
- }
- if (!(error_code & PAGE_RW) && share_page(NULL, tsk,inode,address, error_code,page))
+ if (address < tsk->brk)
return;
- i = address + PAGE_SIZE - tsk->end_data;
- if (i > PAGE_SIZE-1)
- i = 0;
- tmp = page + PAGE_SIZE;
- while (i--) {
- tmp--;
- *(char *)tmp = 0;
- }
- if (put_page(tsk,page,address,prot))
+ if (address+8192 >= (user_esp & 0xfffff000) &&
+ address <= current->start_stack)
return;
- free_page(page);
- oom(current);
+ send_sig(SIGSEGV,tsk,1);
+ return;
}
/*
@@ -977,7 +931,7 @@ unsigned long __bad_pagetable(void)
__asm__ __volatile__("cld ; rep ; stosl":
:"a" (BAD_PAGE + PAGE_TABLE),
"D" ((long) empty_bad_page_table),
- "c" (1024)
+ "c" (PTRS_PER_PAGE)
:"di","cx");
return (unsigned long) empty_bad_page_table;
}
@@ -989,7 +943,7 @@ unsigned long __bad_page(void)
__asm__ __volatile__("cld ; rep ; stosl":
:"a" (0),
"D" ((long) empty_bad_page),
- "c" (1024)
+ "c" (PTRS_PER_PAGE)
:"di","cx");
return (unsigned long) empty_bad_page;
}
@@ -1001,7 +955,7 @@ unsigned long __zero_page(void)
__asm__ __volatile__("cld ; rep ; stosl":
:"a" (0),
"D" ((long) empty_zero_page),
- "c" (1024)
+ "c" (PTRS_PER_PAGE)
:"di","cx");
return (unsigned long) empty_zero_page;
}
@@ -1012,9 +966,9 @@ void show_mem(void)
int shared = 0;
printk("Mem-info:\n");
- printk("Free pages: %6dkB\n",nr_free_pages<<2);
- printk("Secondary pages: %6dkB\n",nr_secondary_pages<<2);
- printk("Free swap: %6dkB\n",nr_swap_pages<<2);
+ printk("Free pages: %6dkB\n",nr_free_pages<<PAGE_SHIFT-10);
+ printk("Secondary pages: %6dkB\n",nr_secondary_pages<<PAGE_SHIFT-10);
+ printk("Free swap: %6dkB\n",nr_swap_pages<<PAGE_SHIFT-10);
printk("Buffer memory: %6dkB\n",buffermem>>10);
printk("Buffer heads: %6d\n",nr_buffer_heads);
printk("Buffer blocks: %6d\n",nr_buffers);
@@ -1053,9 +1007,8 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
* stay that way - it's write-protected and when there is a c-o-w, the
* mm handler treats it specially.
*/
- memset((void *) 0, 0, 4096);
- start_mem += 4095;
- start_mem &= 0xfffff000;
+ memset((void *) 0, 0, PAGE_SIZE);
+ start_mem = PAGE_ALIGN(start_mem);
address = 0;
pg_dir = swapper_pg_dir;
while (address < end_mem) {
@@ -1063,17 +1016,17 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
if (!tmp) {
tmp = start_mem | PAGE_TABLE;
*(pg_dir + 768) = tmp;
- start_mem += 4096;
+ start_mem += PAGE_SIZE;
}
*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++) {
+ pg_table = (unsigned long *) (tmp & PAGE_MASK);
+ for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) {
if (address && address < end_mem)
*pg_table = address | PAGE_SHARED;
else
*pg_table = 0;
- address += 4096;
+ address += PAGE_SIZE;
}
}
invalidate();
@@ -1091,32 +1044,30 @@ void mem_init(unsigned long start_low_mem,
extern int etext;
cli();
- end_mem &= 0xfffff000;
+ end_mem &= PAGE_MASK;
high_memory = end_mem;
- start_mem += 0x0000000f;
- start_mem &= 0xfffffff0;
+ start_mem += 0x0000000f;
+ start_mem &= ~0x0000000f;
tmp = MAP_NR(end_mem);
mem_map = (unsigned short *) start_mem;
p = mem_map + tmp;
start_mem = (unsigned long) p;
while (p > mem_map)
*--p = MAP_PAGE_RESERVED;
- start_low_mem += 0x00000fff;
- start_low_mem &= 0xfffff000;
- start_mem += 0x00000fff;
- start_mem &= 0xfffff000;
+ start_low_mem = PAGE_ALIGN(start_low_mem);
+ start_mem = PAGE_ALIGN(start_mem);
while (start_low_mem < 0xA0000) {
mem_map[MAP_NR(start_low_mem)] = 0;
- start_low_mem += 4096;
+ start_low_mem += PAGE_SIZE;
}
while (start_mem < end_mem) {
mem_map[MAP_NR(start_mem)] = 0;
- start_mem += 4096;
+ start_mem += PAGE_SIZE;
}
sound_mem_init();
free_page_list = 0;
nr_free_pages = 0;
- for (tmp = 0 ; tmp < end_mem ; tmp += 4096) {
+ for (tmp = 0 ; tmp < end_mem ; tmp += PAGE_SIZE) {
if (mem_map[MAP_NR(tmp)]) {
if (tmp >= 0xA0000 && tmp < 0x100000)
reservedpages++;
@@ -1134,9 +1085,9 @@ void mem_init(unsigned long start_low_mem,
printk("Memory: %dk/%dk available (%dk kernel code, %dk reserved, %dk data)\n",
tmp >> 10,
end_mem >> 10,
- codepages << 2,
- reservedpages << 2,
- datapages << 2);
+ codepages << PAGE_SHIFT-10,
+ reservedpages << PAGE_SHIFT-10,
+ datapages << PAGE_SHIFT-10);
return;
}
@@ -1178,7 +1129,7 @@ void file_mmap_nopage(int error_code, struct vm_area_struct * area, unsigned lon
int i, j;
int prot = area->vm_page_prot; /* prot for buffer cache.. */
- address &= 0xfffff000;
+ address &= PAGE_MASK;
block = address - area->vm_start + area->vm_offset;
block >>= inode->i_sb->s_blocksize_bits;
@@ -1217,8 +1168,7 @@ void file_mmap_nopage(int error_code, struct vm_area_struct * area, unsigned lon
tmp = page + PAGE_SIZE;
while (clear--) {
- tmp--;
- *(char *)tmp = 0;
+ *(char *)--tmp = 0;
}
if (put_page(area->vm_task,page,address,area->vm_page_prot))
return;
diff --git a/mm/mmap.c b/mm/mmap.c
index ebfa63d..60942be 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -7,6 +7,7 @@
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
@@ -31,8 +32,8 @@
*
*/
-#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
- current->start_code + current->end_code)
+#define CODE_SPACE(addr) \
+ (PAGE_ALIGN(addr) < current->start_code + current->end_code)
int do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off)
@@ -68,7 +69,7 @@ int do_mmap(struct file * file, unsigned long addr, unsigned long len,
*/
if (flags & MAP_FIXED) {
- if ((addr & 0xfff) || addr == 0)
+ if (addr & ~PAGE_MASK)
return -EINVAL;
if (len > TASK_SIZE || addr > TASK_SIZE - len)
return -ENOMEM;
@@ -76,20 +77,20 @@ int do_mmap(struct file * file, unsigned long addr, unsigned long len,
struct vm_area_struct * vmm;
/* Maybe this works.. Ugly it is. */
- addr = 0x40000000;
- while (addr+len < 0x60000000) {
+ addr = SHM_RANGE_START;
+ while (addr+len < SHM_RANGE_END) {
for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next) {
if (addr >= vmm->vm_end)
continue;
if (addr + len <= vmm->vm_start)
continue;
- addr = (vmm->vm_end + 0xfff) & 0xfffff000;
+ addr = PAGE_ALIGN(vmm->vm_end);
break;
}
if (!vmm)
break;
}
- if (addr+len >= 0x60000000)
+ if (addr+len >= SHM_RANGE_END)
return -ENOMEM;
}
@@ -136,7 +137,7 @@ extern "C" int sys_munmap(unsigned long addr, size_t len)
{
struct vm_area_struct *mpnt, **p, *free;
- if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 || addr + len > TASK_SIZE)
+ if ((addr & ~PAGE_MASK) || addr > LONG_MAX || addr == 0 || addr + len > TASK_SIZE)
return -EINVAL;
/* This needs a bit of work - we need to figure out how to
@@ -198,25 +199,16 @@ int generic_mmap(struct inode * inode, struct file * file,
if (off & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
-
if (len > high_memory || off > high_memory - len) /* avoid overflow */
return -ENXIO;
-
if (get_limit(USER_DS) != TASK_SIZE)
return -EINVAL;
-
- if (!inode->i_sb || !S_ISREG(inode->i_mode) || !permission(inode,MAY_READ)) {
- iput(inode);
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
- }
- if (!inode->i_op || !inode->i_op->bmap) {
- iput(inode);
+ if (!inode->i_op || !inode->i_op->bmap)
return -ENOEXEC;
- }
- if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) {
- iput(inode);
+ if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize)))
return -EACCES;
- }
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
@@ -224,10 +216,8 @@ int generic_mmap(struct inode * inode, struct file * file,
brelse(bh);
mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
- if (!mpnt){
- iput(inode);
+ if (!mpnt)
return -ENOMEM;
- }
unmap_page_range(addr, len);
mpnt->vm_task = current;
diff --git a/mm/swap.c b/mm/swap.c
index 4658de6..c5b4ea5 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -37,7 +37,7 @@ static struct swap_info_struct {
struct inode * swap_file;
unsigned int swap_device;
unsigned char * swap_map;
- char * swap_lockmap;
+ unsigned char * swap_lockmap;
int pages;
int lowest_bit;
int highest_bit;
@@ -53,7 +53,7 @@ extern int shm_swap (int);
#define NR_LAST_FREE_PAGES 32
static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,};
-#define SWAP_BITS 4096
+#define SWAP_BITS PAGE_SIZE
void rw_swap_page(int rw, unsigned long entry, char * buf)
{
@@ -208,8 +208,7 @@ void swap_in(unsigned long *table_ptr)
shm_no_page ((unsigned long *) table_ptr);
return;
}
- page = get_free_page(GFP_KERNEL);
- if (!page) {
+ if (!(page = get_free_page(GFP_KERNEL))) {
oom(current);
page = BAD_PAGE;
} else
@@ -222,7 +221,7 @@ void swap_in(unsigned long *table_ptr)
swap_free(entry);
}
-static int try_to_swap_out(unsigned long * table_ptr)
+static inline int try_to_swap_out(unsigned long * table_ptr)
{
int i;
unsigned long page;
@@ -240,10 +239,10 @@ static int try_to_swap_out(unsigned long * table_ptr)
return 0;
}
for (i = 0; i < NR_LAST_FREE_PAGES; i++)
- if (last_free_pages[i] == (page & 0xfffff000))
+ if (last_free_pages[i] == (page & PAGE_MASK))
return 0;
if (PAGE_DIRTY & page) {
- page &= 0xfffff000;
+ page &= PAGE_MASK;
if (mem_map[MAP_NR(page)] != 1)
return 0;
if (!(entry = get_swap_page()))
@@ -254,7 +253,7 @@ static int try_to_swap_out(unsigned long * table_ptr)
free_page(page);
return 1;
}
- page &= 0xfffff000;
+ page &= PAGE_MASK;
*table_ptr = 0;
invalidate();
free_page(page);
@@ -273,6 +272,136 @@ extern "C" int sys_idle(void)
}
/*
+ * A new implementation of swap_out(). We do not swap complete processes,
+ * but only a small number of blocks, before we continue with the next
+ * process. The number of blocks actually swapped is determined on the
+ * number of page faults, that this process actually had in the last time,
+ * so we won't swap heavily used processes all the time ...
+ *
+ * Note: the priority argument is a hint on much CPU to waste with the
+ * swap block search, not a hint, of how much blocks to swap with
+ * each process.
+ *
+ * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ */
+#ifdef NEW_SWAP
+/*
+ * These are the miminum and maximum number of pages to swap from one process,
+ * before proceeding to the next:
+ */
+#define SWAP_MIN 4
+#define SWAP_MAX 32
+
+/*
+ * The actual number of pages to swap is determined as:
+ * SWAP_RATIO / (number of recent major page faults)
+ */
+#define SWAP_RATIO 128
+
+static int swap_out(unsigned int priority)
+{
+ static int swap_task;
+ int table;
+ int page;
+ long pg_table;
+ int loop;
+ int counter = NR_TASKS * 2 >> priority;
+ struct task_struct *p;
+
+ counter = NR_TASKS * 2 >> priority;
+ for(; counter >= 0; counter--, swap_task++) {
+ /*
+ * Check that swap_task is suitable for swapping. If not, look for
+ * the next suitable process.
+ */
+ loop = 0;
+ while(1) {
+ if(swap_task >= NR_TASKS) {
+ swap_task = 1;
+ if(loop)
+ /* all processes are unswappable or already swapped out */
+ return 0;
+ loop = 1;
+ }
+
+ p = task[swap_task];
+ if(p && p->swappable && p->rss)
+ break;
+
+ swap_task++;
+ }
+
+ /*
+ * Determine the number of pages to swap from this process.
+ */
+ if(! p -> swap_cnt) {
+ p->dec_flt = (p->dec_flt * 3) / 4 + p->maj_flt - p->old_maj_flt;
+ p->old_maj_flt = p->maj_flt;
+
+ if(p->dec_flt >= SWAP_RATIO / SWAP_MIN) {
+ p->dec_flt = SWAP_RATIO / SWAP_MIN;
+ p->swap_cnt = SWAP_MIN;
+ } else if(p->dec_flt <= SWAP_RATIO / SWAP_MAX)
+ p->swap_cnt = SWAP_MAX;
+ else
+ p->swap_cnt = SWAP_RATIO / p->dec_flt;
+ }
+
+ /*
+ * Go through process' page directory.
+ */
+ for(table = p->swap_table; table < 1024; table++) {
+ pg_table = ((unsigned long *) p->tss.cr3)[table];
+ if(pg_table >= high_memory)
+ continue;
+ if(mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
+ continue;
+ if(!(PAGE_PRESENT & pg_table)) {
+ printk("swap_out: bad page-table at pg_dir[%d]: %08x\n",
+ table, pg_table);
+ ((unsigned long *) p->tss.cr3)[table] = 0;
+ continue;
+ }
+ pg_table &= 0xfffff000;
+
+ /*
+ * Go through this page table.
+ */
+ for(page = p->swap_page; page < 1024; page++) {
+ switch(try_to_swap_out(page + (unsigned long *) pg_table)) {
+ case 0:
+ break;
+
+ case 1:
+ p->rss--;
+ /* continue with the following page the next time */
+ p->swap_table = table;
+ p->swap_page = page + 1;
+ if((--p->swap_cnt) == 0)
+ swap_task++;
+ return 1;
+
+ default:
+ p->rss--;
+ break;
+ }
+ }
+
+ p->swap_page = 0;
+ }
+
+ /*
+ * Finish work with this process, if we reached the end of the page
+ * directory. Mark restart from the beginning the next time.
+ */
+ p->swap_table = 0;
+ }
+ return 0;
+}
+
+#else /* old swapping procedure */
+
+/*
* Go through the page tables, searching for a user page that
* we can swap out.
*
@@ -303,7 +432,7 @@ check_task:
goto check_task;
}
check_dir:
- if (swap_table >= 1024) {
+ if (swap_table >= PTRS_PER_PAGE) {
swap_table = 0;
swap_task++;
goto check_task;
@@ -320,9 +449,9 @@ check_dir:
swap_table++;
goto check_dir;
}
- pg_table &= 0xfffff000;
+ pg_table &= PAGE_MASK;
check_table:
- if (swap_page >= 1024) {
+ if (swap_page >= PTRS_PER_PAGE) {
swap_page = 0;
swap_table++;
goto check_dir;
@@ -336,6 +465,8 @@ check_table:
goto check_table;
}
+#endif
+
static int try_to_free_page(void)
{
int i=6;
@@ -358,7 +489,7 @@ static int try_to_free_page(void)
*/
static inline void add_mem_queue(unsigned long addr, unsigned long * queue)
{
- addr &= 0xfffff000;
+ addr &= PAGE_MASK;
*(unsigned long *) addr = *queue;
*queue = addr;
}
@@ -418,7 +549,7 @@ void free_page(unsigned long addr)
#define REMOVE_FROM_MEM_QUEUE(queue,nr) \
cli(); \
if ((result = queue) != 0) { \
- if (!(result & 0xfff) && result < high_memory) { \
+ if (!(result & ~PAGE_MASK) && result < high_memory) { \
queue = *(unsigned long *) result; \
if (!mem_map[MAP_NR(result)]) { \
mem_map[MAP_NR(result)] = 1; \
@@ -492,7 +623,7 @@ repeat:
p = task[nr];
if (!p)
continue;
- for (pgt = 0 ; pgt < 1024 ; pgt++) {
+ for (pgt = 0 ; pgt < PTRS_PER_PAGE ; pgt++) {
ppage = pgt + ((unsigned long *) p->tss.cr3);
page = *ppage;
if (!page)
@@ -501,8 +632,8 @@ repeat:
continue;
if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)
continue;
- ppage = (unsigned long *) (page & 0xfffff000);
- for (pg = 0 ; pg < 1024 ; pg++,ppage++) {
+ ppage = (unsigned long *) (page & PAGE_MASK);
+ for (pg = 0 ; pg < PTRS_PER_PAGE ; pg++,ppage++) {
page = *ppage;
if (!page)
continue;
@@ -511,8 +642,7 @@ repeat:
if (SWP_TYPE(page) != type)
continue;
if (!tmp) {
- tmp = get_free_page(GFP_KERNEL);
- if (!tmp)
+ if (!(tmp = __get_free_page(GFP_KERNEL)))
return -ENOMEM;
goto repeat;
}
@@ -588,7 +718,7 @@ extern "C" int sys_swapon(const char * specialfile)
struct swap_info_struct * p;
struct inode * swap_inode;
unsigned int type;
- char * tmp;
+ unsigned char * tmp;
int i,j;
if (!suser())
@@ -641,8 +771,8 @@ extern "C" int sys_swapon(const char * specialfile)
p->flags = 0;
return -EINVAL;
}
- tmp = (char *) get_free_page(GFP_USER);
- p->swap_lockmap = (char *) get_free_page(GFP_USER);
+ tmp = (unsigned char *) get_free_page(GFP_USER);
+ p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
if (!tmp || !p->swap_lockmap) {
printk("Unable to start swapping: out of memory :-)\n");
free_page((long) tmp);
@@ -655,8 +785,8 @@ extern "C" int sys_swapon(const char * specialfile)
p->flags = 0;
return -ENOMEM;
}
- read_swap_page(SWP_ENTRY(type,0),tmp);
- if (strncmp("SWAP-SPACE",tmp+4086,10)) {
+ read_swap_page(SWP_ENTRY(type,0), (char *) tmp);
+ if (memcmp("SWAP-SPACE",tmp+4086,10)) {
printk("Unable to find swap-space signature\n");
free_page((long) tmp);
free_page((long) p->swap_lockmap);
@@ -668,7 +798,7 @@ extern "C" int sys_swapon(const char * specialfile)
p->flags = 0;
return -EINVAL;
}
- memset(tmp+4086,0,10);
+ memset(tmp+PAGE_SIZE-10,0,10);
j = 0;
p->lowest_bit = 0;
p->highest_bit = 0;
@@ -696,9 +826,9 @@ extern "C" int sys_swapon(const char * specialfile)
if (test_bit(i,tmp))
tmp[i] = 0;
else
- tmp[i] = 128;
- tmp[0] = 128;
- p->swap_map = (unsigned char *) tmp;
+ tmp[i] = 0x80;
+ tmp[0] = 0x80;
+ p->swap_map = tmp;
p->flags = SWP_WRITEOK;
p->pages = j;
nr_swap_pages += j;
@@ -714,7 +844,7 @@ void si_swapinfo(struct sysinfo *val)
for (i = 0; i < nr_swapfiles; i++) {
if (!(swap_info[i].flags & SWP_USED))
continue;
- for (j = 0; j < 4096; ++j)
+ for (j = 0; j < SWAP_BITS; ++j)
switch (swap_info[i].swap_map[j]) {
case 128:
continue;
diff --git a/net/inet/3c509.c b/net/inet/3c509.c
new file mode 100644
index 0000000..fd5fccb
--- /dev/null
+++ b/net/inet/3c509.c
@@ -0,0 +1,594 @@
+/* el3.c: An 3c509 EtherLink3 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 driver should work with the 3Com EtherLinkIII series.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version = "el3.c: v0.02 8/13/93 becker@super.org\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+/*#include <asm/system.h>*/
+#include <asm/io.h>
+#ifndef port_read
+#include "iow.h"
+#endif
+
+#include "dev.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+
+/* From auto_irq.c, should be in a *.h file. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+extern struct device *irq2dev_map[16];
+
+/* These should be in <asm/io.h>. */
+#define port_read_l(port,buf,nr) \
+__asm__("cld;rep;insl": :"d" (port),"D" (buf),"c" (nr):"cx","di")
+#define port_write_l(port,buf,nr) \
+__asm__("cld;rep;outsl": :"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+
+#ifdef EL3_DEBUG
+int el3_debug = EL3_DEBUG;
+#else
+int el3_debug = 1;
+#endif
+
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define ID_PORT 0x100
+#define EEPROM_READ 0x80
+
+/* Register window 1 offsets, used in normal operation. */
+#define TX_FREE 0x0C
+#define TX_STATUS 0x0B
+#define TX_FIFO 0x00
+#define RX_STATUS 0x08
+#define RX_FIFO 0x00
+
+struct el3_private {
+ struct enet_statistics stats;
+};
+
+static int el3_init(struct device *dev);
+static int read_eeprom(int index);
+static int el3_open(struct device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
+static void el3_interrupt(int reg_ptr);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *el3_get_stats(struct device *dev);
+static int el3_rx(struct device *dev);
+static int el3_close(struct device *dev);
+
+
+
+int el3_probe(struct device *dev)
+{
+ short lrs_state = 0xff, i;
+ unsigned short iobase = 0;
+
+ /* Send the ID sequence to the ID_PORT. */
+ outb(0x00, ID_PORT);
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+
+ /* The current Space.c initialization makes it difficult to have more
+ than one adaptor initialized. Send me email if you have a need for
+ multiple adaptors. */
+
+ /* Read in EEPROM data.
+ Only the highest address board will stay on-line. */
+
+ {
+ short *phys_addr = (short *)dev->dev_addr;
+ phys_addr[0] = htons(read_eeprom(0));
+ if (phys_addr[0] != 0x6000)
+ return 1;
+ phys_addr[1] = htons(read_eeprom(1));
+ phys_addr[2] = htons(read_eeprom(2));
+ }
+
+ iobase = read_eeprom(8);
+ dev->irq = read_eeprom(9) >> 12;
+
+ /* Activate the adaptor at the EEPROM location (if set), else 0x320. */
+
+ if (iobase == 0x0000) {
+ dev->base_addr = 0x320;
+ outb(0xf2, ID_PORT);
+ } else {
+ dev->base_addr = 0x200 + ((iobase & 0x1f) << 4);
+ outb(0xff, ID_PORT);
+ }
+
+ outw(0x0800, dev->base_addr + EL3_CMD); /* Window 0. */
+ printk("%s: 3c509 at %#3.3x key %4.4x iobase %4.4x.\n",
+ dev->name, dev->base_addr, inw(dev->base_addr), iobase);
+
+ if (inw(dev->base_addr) == 0x6d50) {
+ el3_init(dev);
+ return 0;
+ } else
+ return -ENODEV;
+}
+
+static int
+read_eeprom(int index)
+{
+ int timer, bit, word = 0;
+
+ /* Issue read command, and pause for at least 162 us. for it to complete.
+ Assume extra-fast 16Mhz bus. */
+ outb(EEPROM_READ + index, ID_PORT);
+
+ for (timer = 0; timer < 162*4 + 400; timer++)
+ SLOW_DOWN_IO;
+
+ for (bit = 15; bit >= 0; bit--)
+ word = (word << 1) + (inb(ID_PORT) & 0x01);
+
+ if (el3_debug > 3)
+ printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+ return word;
+}
+
+static int
+el3_init(struct device *dev)
+{
+ struct el3_private *lp;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ printk("%s: EL3 at %#3x, address", dev->name, ioaddr);
+
+ /* Read in the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i]);
+ printk(", IRQ %d.\n", dev->irq);
+
+ /* Make up a EL3-specific-data structure. */
+ dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct el3_private));
+ lp = (struct el3_private *)dev->priv;
+
+ if (el3_debug > 1)
+ printk(version);
+
+ /* The EL3-specific entries in the device structure. */
+ dev->open = &el3_open;
+ dev->hard_start_xmit = &el3_start_xmit;
+ dev->stop = &el3_close;
+ dev->get_stats = &el3_get_stats;
+
+ /* Fill in the generic field of the device structure. */
+ 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->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;
+}
+
+
+static int
+el3_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ if (request_irq(dev->irq, &el3_interrupt)) {
+ return -EAGAIN;
+ }
+
+ if (el3_debug > 3)
+ printk("%s: Opening, IRQ %d status@%x %4.4x reg4 %4.4x.\n",
+ dev->name, dev->irq, ioaddr + EL3_STATUS,
+ inw(ioaddr + EL3_STATUS), inw(ioaddr + 4));
+ outw(0x0800, ioaddr + EL3_CMD); /* Make certain we are in window 0. */
+
+ /* This is probably unnecessary. */
+ outw(0x0001, ioaddr + 4);
+
+ outw((dev->irq << 12) | 0x0f00, ioaddr + 8);
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Set the station address in window 2 each time opened. */
+ outw(0x0802, ioaddr + EL3_CMD);
+
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ outw(0x1000, ioaddr + EL3_CMD); /* Start the thinnet transceiver. */
+
+ outw(0x8005, ioaddr + EL3_CMD); /* Accept b-case and phys addr only. */
+ outw(0xA800, ioaddr + EL3_CMD); /* Turn on statistics. */
+ outw(0x2000, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(0x4800, ioaddr + EL3_CMD); /* Enable transmitter. */
+ outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
+ outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */
+
+ /* Switch to register set 1 for normal use. */
+ outw(0x0801, ioaddr + EL3_CMD);
+
+ if (el3_debug > 3)
+ printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ return 0; /* Always succeed */
+}
+
+static int
+el3_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk("%s: transmit timed out, tx_status %4.4x status %4.4x.\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
+ dev->trans_start = jiffies;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */
+ outw(0x4800, ioaddr + EL3_CMD); /* TX_START */
+ dev->tbusy = 0;
+ }
+
+ 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;
+
+ if (el3_debug > 4) {
+ printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ }
+
+ if (inw(ioaddr + EL3_STATUS) & 0x0001) { /* IRQ line active, missed one. */
+ printk("%s: Missed interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
+ outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
+ outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
+ }
+
+ /* Avoid timer-based retransmission conflicts. */
+ dev->tbusy=1;
+
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ port_write(ioaddr + TX_FIFO, (void *)(skb+1),
+ ((skb->len + 3) >> 1) & ~0x1);
+
+ dev->trans_start = jiffies;
+ if (skb->free)
+ kfree_skb (skb, FREE_WRITE);
+
+ if (inw(ioaddr + TX_FREE) > 1536) {
+ dev->tbusy=0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(0x9000 + 1536, ioaddr + EL3_CMD);
+
+ if (el3_debug > 4)
+ printk(" Finished queueing packet, FIFO room remaining %d.\n",
+ inw(ioaddr + TX_FREE));
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (el3_debug > 5)
+ printk(" Tx status %4.4x.\n", tx_status);
+ if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+ }
+ return 0;
+}
+
+/* The EL3 interrupt handler. */
+static void
+el3_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int ioaddr, status;
+
+ if (dev == NULL) {
+ printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (el3_debug > 4)
+ printk("%s: interrupt, status %4.4x.\n", dev->name, status);
+
+ while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {
+
+ if (status & 0x08) {
+ if (el3_debug > 5)
+ printk(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
+ dev->tbusy = 0;
+ mark_bh(INET_BH);
+ }
+ if (status & 0x80) /* Statistics full. */
+ update_stats(ioaddr, dev);
+
+ if (status & 0x10)
+ el3_rx(dev);
+
+ /* Clear the interrupts we've handled. */
+ outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
+ }
+
+ if (el3_debug > 4) {
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ }
+
+ if (inw(ioaddr + EL3_STATUS) & 0x01) {
+ int i = 100000;
+ printk("%s: exiting interrupt with status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ while (i--) /* Delay loop to see the message. */
+ inw(ioaddr + EL3_STATUS);
+ while ((inw(ioaddr + EL3_STATUS) & 0x0010) && i++ < 20)
+ outw(0x00, ioaddr + RX_STATUS);
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+
+static struct enet_statistics *
+el3_get_stats(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+
+ sti();
+ update_stats(dev->base_addr, dev);
+ cli();
+ return &lp->stats;
+}
+
+/* Update statistics. We change to register window 6, so this
+ must be run single-threaded. */
+static void update_stats(int ioaddr, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+
+ if (el3_debug > 5)
+ printk(" Updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(0xB000, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ outw(0x0806, ioaddr + EL3_CMD);
+ lp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ lp->stats.collisions += inb(ioaddr + 3);
+ lp->stats.tx_window_errors += inb(ioaddr + 4);
+ lp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ lp->stats.tx_packets += inb(ioaddr + 6);
+ lp->stats.rx_packets += inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ outw(0x0801, ioaddr + EL3_CMD);
+ outw(0xA800, ioaddr + EL3_CMD);
+ return;
+}
+
+/* Print statistics on the kernel error output. */
+void printk_stats(struct enet_statistics *stats)
+{
+
+ printk(" Ethernet statistics: Rx packets %6d Tx packets %6d.\n",
+ stats->rx_packets, stats->tx_packets);
+ printk(" Carrier errors: %6d.\n", stats->tx_carrier_errors);
+ printk(" Heartbeat errors: %6d.\n", stats->tx_heartbeat_errors);
+ printk(" Collisions: %6d.\n", stats->collisions);
+ printk(" Rx FIFO problems: %6d.\n", stats->rx_fifo_errors);
+
+ return;
+}
+
+static int
+el3_rx(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short rx_status;
+
+ if (el3_debug > 5)
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3C00;
+ lp->stats.rx_errors++;
+ switch (error) {
+ case 0x2000: lp->stats.rx_over_errors++; break;
+ case 0x2C00: lp->stats.rx_length_errors++; break;
+ case 0x3400: lp->stats.rx_crc_errors++; break;
+ case 0x2400: lp->stats.rx_length_errors++; break;
+ case 0x3000: lp->stats.rx_frame_errors++; break;
+ case 0x0800: lp->stats.rx_frame_errors++; break;
+ }
+ }
+ if ( (! (rx_status & 0x4000))
+ || ! (rx_status & 0x2000)) { /* Dribble bits are OK. */
+ short length = rx_status & 0x3ff;
+ int sksize = sizeof(struct sk_buff) + length + 3;
+ struct sk_buff *skb;
+ skb = (struct sk_buff *) kmalloc(sksize, GFP_ATOMIC);
+
+ if (el3_debug > 4)
+ printk(" Receiving packet size %d status %4.4x.\n",
+ length, rx_status);
+ 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. */
+ port_read(ioaddr+RX_FIFO, (void *)(skb+1), ((length + 3) >> 2) << 1);
+ if (dev_rint((unsigned char *)skb, length, IN_SKBUFF,dev)== 0){
+ if (el3_debug > 6)
+ printk(" dev_rint() happy, status %4.4x.\n",
+ inb(ioaddr + EL3_STATUS));
+ outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ printk(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS) );
+ if (el3_debug > 6)
+ printk(" discarded packet, status %4.4x.\n",
+ inb(ioaddr + EL3_STATUS));
+ continue;
+ } else {
+ printk("%s: receive buffers full.\n", dev->name);
+ kfree_s(skb, sksize);
+ }
+ } else if (el3_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize);
+ }
+ lp->stats.rx_dropped++;
+ outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ printk(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS) );
+ }
+
+ if (el3_debug > 5)
+ printk(" Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+8));
+
+ return 0;
+}
+
+static int
+el3_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 2)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Turn off statistics. We update lp->stats below. */
+ outw(0xB000, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(0x1800, ioaddr + EL3_CMD);
+ outw(0x5000, ioaddr + EL3_CMD);
+
+ /* Turn off thinnet power. */
+ outw(0xb800, ioaddr + EL3_CMD);
+
+ if (el3_debug > 2) {
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ printk("%s: Status was %4.4x.\n", dev->name, inw(ioaddr + EL3_STATUS));
+ printk_stats(&lp->stats);
+ }
+
+ /* Free the interrupt line. */
+ free_irq(dev->irq);
+ outw(0x1000, ioaddr + EL3_CMD);
+ outw(0x0f00, ioaddr + 8);
+
+ /* Switch back to register window 0. */
+ outw(0x0800, ioaddr + EL3_CMD);
+
+ irq2dev_map[dev->irq] = 0;
+
+ update_stats(ioaddr, dev);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c 3c509.c"
+ * End:
+ */
diff --git a/net/inet/8390.c b/net/inet/8390.c
index 500227a..15bb757 100644
--- a/net/inet/8390.c
+++ b/net/inet/8390.c
@@ -1,19 +1,20 @@
/* 8390.c: A general NS8390 ethernet driver core for linux. */
/*
- Written 1992,1993 by Donald Becker. This is alpha test code.
+ Written 1992,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 driver should work with many 8390-based ethernet adaptors.
+
+ This is the chip-specific code for 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";
+ "8390.c:v0.99-12 8/9/93 for 0.99.12+ 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! */
@@ -31,15 +32,11 @@ static char *version =
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>
@@ -60,7 +57,6 @@ Sources:
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
@@ -78,24 +74,21 @@ Sources:
#ifdef EI_DEBUG
int ei_debug = EI_DEBUG;
#else
-int ei_debug = 2;
+int ei_debug = 1;
#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);
+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,
@@ -113,7 +106,7 @@ struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
up anew at each open, even though many of these registers should only
need to be set once at boot.
*/
-static int
+int
ei_open(struct device *dev)
{
struct ei_device *ei_local = (struct ei_device *) dev->priv;
@@ -195,7 +188,7 @@ ei_start_xmit(struct sk_buff *skb, struct device *dev)
return 1;
}
outb(0x00, e8390_base + EN0_IMR);
- tmp_tbusy=dev->tbusy;
+ tmp_tbusy=dev->tbusy;
dev->tbusy = 1; /* lock dev_tint() in dev.c */
ei_local->irqlock = 1;
sti();
@@ -243,7 +236,8 @@ ei_start_xmit(struct sk_buff *skb, struct device *dev)
tmp_tbusy = 1;
} else {
dev->trans_start = jiffies;
- ei_block_output(dev, length, (unsigned char *)(skb+1), ei_local->tx_start_page);
+ ei_block_output(dev, length, (unsigned char *)(skb+1),
+ ei_local->tx_start_page);
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
tmp_tbusy = 1;
} /* PINGPONG */
@@ -290,7 +284,7 @@ ei_interrupt(int reg_ptr)
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)
@@ -315,9 +309,9 @@ ei_interrupt(int reg_ptr)
ei_tx_intr(dev);
} else if (interrupts & ENISR_COUNTERS) {
struct ei_device *ei_local = (struct ei_device *) dev->priv;
- 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);
+ ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+ ei_local->stat.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
+ ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
}
@@ -347,10 +341,6 @@ ei_tx_intr(struct device *dev)
struct ei_device *ei_local = (struct ei_device *) dev->priv;
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--;
@@ -389,6 +379,25 @@ ei_tx_intr(struct device *dev)
ei_local->txing = 0;
dev->tbusy = 0;
}
+
+ /* Do the statistics _after_ we start the next TX. */
+ if (status & ENTSR_PTX)
+ ei_local->stat.tx_packets++;
+ else
+ ei_local->stat.tx_errors++;
+ if (status & ENTSR_COL)
+ ei_local->stat.collisions++;
+ if (status & ENTSR_ABT)
+ ei_local->stat.tx_aborted_errors++;
+ if (status & ENTSR_CRS)
+ ei_local->stat.tx_carrier_errors++;
+ if (status & ENTSR_FU)
+ ei_local->stat.tx_fifo_errors++;
+ if (status & ENTSR_CDH)
+ ei_local->stat.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC)
+ ei_local->stat.tx_window_errors++;
+
mark_bh (INET_BH);
}
@@ -450,19 +459,19 @@ ei_receive(struct device *dev)
dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
current_offset);
- if (ei_local->rx_packets != last_rx_bogosity) {
+ if (ei_local->stat.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;
+ last_rx_bogosity = ei_local->stat.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);
+ dev->name, ei_local->stat.rx_packets);
sti();
ei_reset_8390(dev);
NS8390_init(dev, 1);
@@ -488,6 +497,7 @@ ei_receive(struct device *dev)
current_offset + sizeof(rx_frame));
if (dev_rint((unsigned char *)skb, size, IN_SKBUFF, dev)) {
printk("%s: receive buffers full.\n", dev->name);
+ kfree_s(skb, sksize);
break;
}
} else if (ei_debug) {
@@ -495,13 +505,14 @@ ei_receive(struct device *dev)
dev->name, sksize);
break;
}
- ei_local->rx_packets++;
+ ei_local->stat.rx_packets++;
} else {
+ int errs = rx_frame.status;
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++;
+ if (errs & ENRSR_FO)
+ ei_local->stat.rx_fifo_errors++;
}
next_frame = rx_frame.next;
@@ -537,7 +548,7 @@ ei_rx_overrun(struct device *dev)
if (ei_debug)
printk("%s: Receiver overrun.\n", dev->name);
- ei_local->rx_overruns++;
+ ei_local->stat.rx_over_errors++;
/* 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?*/
@@ -553,59 +564,44 @@ ei_rx_overrun(struct device *dev)
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);
- }
+ /* Remove packets right away. */
+ ei_receive(dev);
+
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. */
}
+static struct enet_statistics *
+get_stats(struct device *dev)
+{
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ return &ei_local->stat;
+}
+
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->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct ei_device));
- ei_local = (struct ei_device *)dev->priv;
-#ifndef NO_PINGPONG
- ei_local->pingpong = 1;
-#endif
-
if (1
#ifdef WD80x3
&& ! wdprobe(dev->base_addr, dev)
-#endif
+#endif
#ifdef EL2
&& ! el2autoprobe(dev->base_addr, dev)
#endif
#ifdef NE2000
&& ! neprobe(dev->base_addr, dev)
-#endif
+#endif
#ifdef HPLAN
&& ! hpprobe(dev->base_addr, dev)
-#endif
+#endif
&& 1 ) {
- printk("No ethernet device found.\n");
- kfree(dev->priv);
- dev->priv = NULL;
- return 1; /* ENODEV or EAGAIN would be more accurate. */
+ return 1; /* -ENODEV or -EAGAIN would be more accurate. */
}
-
- return ethdev_init(dev);
+ if (ei_debug > 1)
+ printk(version);
+ return 0;
}
/* Initialize the rest of the device structure. */
@@ -617,6 +613,11 @@ ethdev_init(struct device *dev)
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
+ /* The open call may be overridden by the card-specific code. */
+ if (dev->open == NULL)
+ dev->open = &ei_open;
+
+ dev->get_stats = get_stats;
dev->hard_header = eth_header;
dev->add_arp = eth_add_arp;
dev->queue_xmit = dev_queue_xmit;
@@ -624,8 +625,14 @@ ethdev_init(struct device *dev)
dev->type_trans = eth_type_trans;
if (dev->priv == NULL) {
+ struct ei_device *ei_local;
+
dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct ei_device));
+ ei_local = (struct ei_device *)dev->priv;
+#ifndef NO_PINGPONG
+ ei_local->pingpong = 1;
+#endif
}
dev->hard_start_xmit = &ei_start_xmit;
@@ -727,7 +734,7 @@ static void NS8390_trigger_send(struct device *dev, unsigned int length,
/*
* Local variables:
- * compile-command: "gcc -DKERNEL -Wall -O6 -I/usr/src/linux/net/tcp -c 8390.c"
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c 8390.c"
* version-control: t
* kept-new-versions: 5
* End:
diff --git a/net/inet/8390.h b/net/inet/8390.h
index 97ab1be..5d7cae8 100644
--- a/net/inet/8390.h
+++ b/net/inet/8390.h
@@ -1,10 +1,13 @@
/* 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. */
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
-#ifndef e8390_h
-#define e8390_h
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
#define TX_2X_PAGES 12
#define TX_1X_PAGES 6
@@ -13,8 +16,17 @@
#define ETHER_ADDR_LEN 6
/* From 8390.c */
-void ei_interrupt(int reg_ptr);
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+extern int ethif_init(struct device *dev);
+extern int ethdev_init(struct device *dev);
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_open(struct device *dev);
+extern void ei_interrupt(int reg_ptr);
+
/* From auto_irq.c */
+extern struct device *irq2dev_map[16];
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);
@@ -39,15 +51,11 @@ struct ei_device {
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;
+ unsigned char reg0; /* Register '0' in a WD8013 */
+ unsigned char reg5; /* Register '5' in a WD8013 */
+ unsigned char saved_irq; /* Original dev->irq value. */
+ /* The new statistics table. */
+ struct enet_statistics stat;
};
#define ei_status (*(struct ei_device *)(dev->priv))
@@ -130,16 +138,14 @@ struct ei_device {
#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.
- */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occured during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
/* The per-packet-header format. */
struct e8390_pkt_hdr {
@@ -147,4 +153,4 @@ struct e8390_pkt_hdr {
unsigned char next; /* pointer to next packet. */
unsigned short count; /* header + packet lenght in bytes */
};
-#endif /* e8390_h */
+#endif /* _8390_h */
diff --git a/net/inet/CONFIG b/net/inet/CONFIG
index 9040714..99900b7 100644
--- a/net/inet/CONFIG
+++ b/net/inet/CONFIG
@@ -26,7 +26,17 @@
# 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
+
+# Comment out the lines you don't want..
+
+#CARDS := $(CARDS) -DSLIP
+#CARDS := $(CARDS) -DPLIP
+CARDS := $(CARDS) -DWD80x3
+CARDS := $(CARDS) -DNE2000
+CARDS := $(CARDS) -DHPLAN
+CARDS := $(CARDS) -DEL2
+CARDS := $(CARDS) -DD_LINK
+CARDS := $(CARDS) -DCONFIG_AT1500
# For WD and SMC cards:
#OPTS = -DEI8390=0x280 -DEI8390_IRQ=15
@@ -38,9 +48,10 @@ WD_OPTS = -DWD_SHMEM=0
#OPTS = -DEI8390=0 -DEI8390_IRQ=0
#WD_OPTS = -DUD_SHMEM=0xCC000 -UFORCE_8BIT
-EL_OPTS = -UEL2_AUI
+EL2_OPTS = #-UEL2_AUI
NE_OPTS =
HP_OPTS =
PLIP_OPTS =
SLIP_OPTS = -DSL_DUMP -DSL_COMPRESSED
DL_OPTS = -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
+AT_OPTS = # -DLANCE_DMA=5
diff --git a/net/inet/LICENSE.SRC b/net/inet/LICENSE.SRC
new file mode 100644
index 0000000..500940a
--- /dev/null
+++ b/net/inet/LICENSE.SRC
@@ -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
index 7ba4fe6..1c7a6d7 100644
--- a/net/inet/Makefile
+++ b/net/inet/Makefile
@@ -18,8 +18,9 @@
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 slhc.o d_link.o auto_irq.o ip.o raw.o icmp.o tcp.o udp.o
+ eth.o packet.o arp.o dev.o 8390.o wd.o ne.o el2.o hp.o plip.o \
+ slip.o slhc.o d_link.o auto_irq.o ip.o raw.o icmp.o tcp.o udp.o\
+ lance.o
ifdef CONFIG_INET
@@ -33,6 +34,7 @@ inet.o:
endif
+CARDS :=
include CONFIG
@@ -46,8 +48,8 @@ Space.o: CONFIG Space.c Makefile
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) $(EL_OPTS) -c el.c -o $@
+el2.o: CONFIG el2.c el2reg.h Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c el2.c -o $@
ne.o: CONFIG ne.c Makefile
$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c ne.c -o $@
@@ -64,6 +66,9 @@ slip.o: CONFIG slip.c Makefile
d_link.o: CONFIG d_link.c Makefile
$(CC) $(CPPFLAGS) $(CFLAGS) $(DL_OPTS) -c d_link.c -o $@
+lance.o: CONFIG lance.c Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(AT_OPTS) -c lance.c -o $@
+
subdirs: dummy
for i in $(SUBDIRS); do (cd $$i; $(MAKE)); done
diff --git a/net/inet/README.8390 b/net/inet/README.8390
index 0bb97df..87af97b 100644
--- a/net/inet/README.8390
+++ b/net/inet/README.8390
@@ -1,37 +1,60 @@
-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.
+These drivers support all common 8390-based ethernet boards. Currently
+"common" is defined as:
-Use Linux 0.99.5 or later. Make certain you can make a working kernel
-_before_ you install the ethercard driver.
+ 3Com Products:
+* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!)
+ 3Com 3c503/16 and excellent documentation provided by 3Com.
-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.
+ Clones-n-things
+ NE1000 Novell and Eagle are useless for documentation,
+* NE2000 but copied the designs directly from NatSemi;->.
-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'.
+ WD/SMC products
+ WD8003
+* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks!
-Make and install your new kernel.
+* I've seen it work myself!
-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".)
+There is support for the following boards, but since I've only been
+able to borrow a thinnet of an HP ethercard I've relied upon reports
+from others:
-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.
+ HP LAN adaptors
+** HP27245
+** HP27247
+** HP27250
+
+Thanks are due to the dozens of alpha testers. Special thanks are due
+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:
+ 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
+ N.B. The E2100 will not work with Linux until Cabletron
+ releases the programming information!
-________________
Important defines
For Space.c
diff --git a/net/inet/README.DRIVERS b/net/inet/README.DRIVERS
index 0e19811..1a769a1 100644
--- a/net/inet/README.DRIVERS
+++ b/net/inet/README.DRIVERS
@@ -48,7 +48,6 @@ static char *version =
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
diff --git a/net/inet/Space.c b/net/inet/Space.c
index a2ae890..a547399 100644
--- a/net/inet/Space.c
+++ b/net/inet/Space.c
@@ -13,7 +13,7 @@
* field of the 'device' structure to store the unit number...
* -FvK
*
- * Version: @(#)Space.c 1.0.7 05/28/93
+ * Version: @(#)Space.c 1.0.7 08/12/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -28,14 +28,12 @@
#include <linux/ddi.h>
#include "dev.h"
-
#define LOOPBACK /* always present, right? */
-
#define NEXT_DEV NULL
-#ifdef D_LINK
+#if defined(D_LINK) || defined(CONFIG_DE600)
extern int d_link_init(struct device *);
static struct device d_link_dev = {
"dl0",
@@ -53,30 +51,79 @@
# define NEXT_DEV (&d_link_dev)
#endif
-
-#ifdef EL1
+#ifdef CONFIG_EL1
+#error
# ifndef EL1_IRQ
# define EL1_IRQ 9
# endif
+# ifndef EL1
+# define EL1 0
+# 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
+ "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)
+#ifdef CONFIG_DEPCA
+ extern int depca_probe(struct device *);
+ static struct device depca_dev = {
+ "depca0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, depca_probe,
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&depca_dev)
+#endif /* CONFIG_DEPCA */
+
+
+#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
+ extern int atp_probe(struct device *);
+ static struct device atp_dev = {
+ "atp0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, atp_probe,
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&atp_dev)
+#endif /* CONFIG_ATP */
+
+#ifdef CONFIG_EL3
+ extern int el3_probe(struct device *);
+ static struct device eliii0_dev = {
+ "eliii0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, el3_probe,
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&eliii0_dev)
+#endif /* CONFIG_3C509 aka EL3 */
+
+#ifdef CONFIG_ZNET
+ extern int znet_probe(struct device *);
+ static struct device znet_dev = {
+ "znet", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, znet_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&znet_dev)
+#endif /* CONFIG_ZNET */
+
+#ifdef CONFIG_EEXPRESS
+ extern int express_probe(struct device *);
+ static struct device express0_dev = {
+ "exp0", 0,0,0,0, 0, 0, 0, 0, 0, NEXT_DEV, express_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&express0_dev)
+#endif /* CONFIG_EEPRESS */
+
+#ifdef CONFIG_AT1500
+ extern int at1500_probe(struct device *);
+ static struct device lance_dev = {
+ "le0",
+ 0,0,0,0, 0 /* I/O Base */, 0 /* pre-set IRQ */,
+ 0, 0, 0, NEXT_DEV, at1500_probe,
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&lance_dev)
+#endif /* AT1500BT */
+
+#if defined(EI8390) || defined(CONFIG_EL2) || defined(CONFIG_NE2000) \
+ || defined(CONFIG_WD80x3) || defined(CONFIG_HPLAN)
# ifndef EI8390
# define EI8390 0
# endif
@@ -100,50 +147,19 @@
# define NEXT_DEV (&ei8390_dev)
#endif /* The EI8390 drivers. */
-#ifdef PLIP
+#if defined(PLIP) || defined(CONFIG_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
- };
+ "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
- };
+ "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
- };
+ "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
+#if defined(SLIP) || defined(CONFIG_SLIP)
extern int slip_init(struct device *);
static struct device slip3_dev = {
"sl3", /* Internal SLIP driver, channel 3 */
diff --git a/net/inet/arp.c b/net/inet/arp.c
index 8c5c587..e78bc0e 100644
--- a/net/inet/arp.c
+++ b/net/inet/arp.c
@@ -39,7 +39,6 @@
#include <asm/segment.h>
#include <stdarg.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "eth.h"
#include "ip.h"
@@ -410,7 +409,7 @@ arp_create(unsigned long paddr, unsigned char *addr, int hlen, int htype)
apt->htype = htype;
apt->flags = (ATF_INUSE | ATF_COM); /* USED and COMPLETED entry */
memcpy(apt->ha, addr, hlen);
- apt->last_used = timer_seq;
+ apt->last_used = jiffies;
cli();
apt->next = arp_tables[hash];
arp_tables[hash] = apt;
@@ -469,7 +468,7 @@ arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
memcpy(tbl->ha, ptr, arp->ar_hln);
tbl->hlen = arp->ar_hln;
tbl->flags |= ATF_COM;
- tbl->last_used = timer_seq;
+ tbl->last_used = jiffies;
} else {
memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
if (chk_addr(dst) != IS_MYADDR) {
@@ -531,6 +530,7 @@ arp_send(unsigned long paddr, struct device *dev, unsigned long saddr)
skb = (struct sk_buff *) kmalloc(sizeof(struct sk_buff) +
sizeof(struct arphdr) + (2 * dev->addr_len) +
+ dev->hard_header_len +
(2 * 4 /* arp->plen */), GFP_ATOMIC);
if (skb == NULL) {
printk("ARP: No memory available for REQUEST %s\n", in_ntoa(paddr));
@@ -603,8 +603,8 @@ arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
* 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;
+ (!before(apt->last_used, jiffies+ARP_TIMEOUT) && apt->hlen != 0)) {
+ apt->last_used = jiffies;
memcpy(haddr, apt->ha, dev->addr_len);
return(0);
} else {
@@ -647,7 +647,7 @@ arp_add(unsigned long addr, unsigned char *haddr, struct device *dev)
apt = arp_lookup(addr);
if (apt != NULL) {
DPRINTF((DBG_ARP, "ARP: updating entry for %s\n", in_ntoa(addr)));
- apt->last_used = timer_seq;
+ apt->last_used = jiffies;
memcpy(apt->ha, haddr , dev->addr_len);
return;
}
@@ -789,7 +789,7 @@ arp_req_set(struct arpreq *req)
/* 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->last_used = jiffies;
apt->flags = r.arp_flags;
return(0);
diff --git a/net/inet/auto_irq.c b/net/inet/auto_irq.c
index b053354..e3be4cb 100644
--- a/net/inet/auto_irq.c
+++ b/net/inet/auto_irq.c
@@ -27,7 +27,7 @@
#ifdef version
-static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
+static char *version="auto_irq.c:v0.02 1993 Donald Becker (becker@super.org)";
#endif
/*#include <linux/config.h>*/
@@ -35,8 +35,11 @@ static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
#include <linux/sched.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#include "dev.h"
/*#include <asm/system.h>*/
+struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
+
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. */
diff --git a/net/inet/d_link.c b/net/inet/d_link.c
index f8b42dd..22d6248 100644
--- a/net/inet/d_link.c
+++ b/net/inet/d_link.c
@@ -93,7 +93,6 @@ static char *version =
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
diff --git a/net/inet/dev.c b/net/inet/dev.c
index e1ec89e..78e7223 100644
--- a/net/inet/dev.c
+++ b/net/inet/dev.c
@@ -32,7 +32,6 @@
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
@@ -253,30 +252,21 @@ dev_get(char *name)
/* Find an interface that can handle addresses for a certain address. */
struct device *
-dev_check(int which, unsigned long addr)
+dev_check(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);
+ for (dev = dev_base; dev; dev = dev->next)
+ if ((dev->flags & IFF_UP) && (dev->flags & IFF_POINTOPOINT) &&
+ (addr == dev->pa_dstaddr))
+ return dev;
+ for (dev = dev_base; dev; dev = dev->next)
+ if ((dev->flags & IFF_UP) && !(dev->flags & IFF_POINTOPOINT) &&
+ addr == (dev->flags & IFF_LOOPBACK ? dev->pa_addr : dev->pa_addr &
+ dev->pa_mask))
+ break;
+ /* no need to check broadcast addresses */
+ return dev;
}
diff --git a/net/inet/dev.h b/net/inet/dev.h
index 2b2d242..316e18c 100644
--- a/net/inet/dev.h
+++ b/net/inet/dev.h
@@ -5,11 +5,12 @@
*
* Definitions for the Interfaces handler.
*
- * Version: @(#)dev.h 1.0.9 05/31/93
+ * Version: @(#)dev.h 1.0.10 08/12/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>
+ * 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
@@ -20,6 +21,7 @@
#define _DEV_H
#include <linux/if.h>
+#include <linux/if_ether.h>
/* for future expansion when we will have different priorities. */
@@ -73,18 +75,28 @@ struct device {
/* The device initialization function. Called only once. */
int (*init)(struct device *dev);
+ /* Some hardware also needs these fields, but they are not part of the
+ usual set specified in Space.c. */
+ unsigned char if_port; /* Selectable AUI, TP,..*/
+ unsigned char dma; /* DMA channel */
+
+ struct enet_statistics* (*get_stats)(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).
*/
+ /* These may be needed for future network-power-down code. */
+ unsigned long trans_start; /* Time (in jiffies) of last Tx */
+ unsigned long last_rx; /* Time of last Rx */
+
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 *priv; /* pointer to private data */
@@ -147,7 +159,7 @@ 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 struct device *dev_check(unsigned long daddr);
extern unsigned long my_addr(void);
extern void dev_add_pack(struct packet_type *pt);
diff --git a/net/inet/el2.c b/net/inet/el2.c
new file mode 100644
index 0000000..b5adbff
--- /dev/null
+++ b/net/inet/el2.c
@@ -0,0 +1,428 @@
+/* el2.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992,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 driver should work with the 3c503 and 3c503/16. It should be used
+ in shared memory mode for best performance, although it may also work
+ in programmed-I/O 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 =
+ "el2.c:v0.99.12B 8/12/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 "el2reg.h"
+
+int el2autoprobe(int ioaddr, struct device *dev);
+int el2_pio_autoprobe(struct device *dev);
+int el2probe(int ioaddr, struct device *dev);
+
+static int el2_open(struct device *dev);
+static int el2_close(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);
+
+
+/* This routine probes for a memory-mapped 3c503 board by looking for
+ the "location register" at the end of the jumpered boot PROM space.
+ This works even if a PROM isn't there.
+
+ If the ethercard isn't found there is an optional probe for
+ ethercard jumpered to programmed-I/O mode.
+ */
+
+static int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+int
+el2autoprobe(int ioaddr, struct device *dev)
+{
+ int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+
+ /* Non-autoprobe case first: */
+ if (ioaddr > 0)
+ return el2probe(ioaddr, dev);
+
+ 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;
+ }
+#ifdef probe_nonshared_memory
+ return el2_pio_autoprobe(dev);
+#else
+ return 0;
+#endif
+}
+
+/* Try all of the locations that aren't obviously empty. This touches
+ a lot of locations, and is much riskier than the code above. */
+int
+el2_pio_autoprobe(struct device *dev)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ /* Reset and/or avoid any lurking NE2000 */
+ if (inb_p(ports[i] + 0x408) == 0xff)
+ continue;
+ if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
+ && el2probe(ports[i], dev))
+ return dev->base_addr;
+ }
+ 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(saved_406, ioaddr + 0x406);
+ return 0;
+ }
+
+ ethdev_init(dev);
+
+ /* 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 */
+
+ /* This should be probed for (or set via an ioctl()) at run-time.
+ Right now we use a sleazy hack to pass in the interface number
+ at boot-time via the low bits of the mem_end field. That value is
+ unused, and the low bits would be discarded even if it was used. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+ ei_status.interface_num = 1;
+#else
+ ei_status.interface_num = dev->mem_end & 0xf;
+#endif
+
+ 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;
+
+ if (dev->irq == 2)
+ dev->irq = 9;
+ else if (dev->irq > 5 && dev->irq != 9) {
+ printk("\n3c503: configured interrupt %d invalid, using autoIRQ.\n",
+ dev->irq);
+ dev->irq = 0;
+ }
+
+ ei_status.saved_irq = dev->irq;
+
+ dev->start = 0;
+ dev->open = &el2_open;
+ dev->stop = &el2_close;
+ el2_init_card(dev);
+
+ if (dev->mem_start)
+ printk("\n%s: %s with shared memory at %#6x-%#6x,\n",
+ dev->name, ei_status.name, dev->mem_start, dev->mem_end-1);
+ else
+ printk("\n%s: %s using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+ dev->name, ei_status.name);
+ if (ei_debug > 1)
+ printk(version);
+
+ return ioaddr;
+}
+
+static int
+el2_open(struct device *dev)
+{
+
+ if (dev->irq < 2) {
+ int irqlist[] = {5, 2, 3, 4, 0};
+ int *irqp = irqlist;
+
+ outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
+ do {
+ if (request_irq (*irqp, NULL) != -EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen. */
+ autoirq_setup(0);
+ outb_p(0x04 << *irqp, E33G_IDCFR);
+ outb_p(0x00, E33G_IDCFR);
+ if (*irqp == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (dev->irq = *irqp, &ei_interrupt) == 0)
+ break;
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+ return -EAGAIN;
+ }
+ } else {
+ if (request_irq(dev->irq, &ei_interrupt)) {
+ return -EAGAIN;
+ }
+ }
+ return ei_open(dev);
+}
+
+static int
+el2_close(struct device *dev)
+{
+ free_irq(dev->irq);
+ dev->irq = ei_status.saved_irq;
+ irq2dev_map[dev->irq] = NULL;
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+/* 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/el2reg.h b/net/inet/el2reg.h
new file mode 100644
index 0000000..a1c8575
--- /dev/null
+++ b/net/inet/el2reg.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
index 94b80f1..da06dea 100644
--- a/net/inet/eth.c
+++ b/net/inet/eth.c
@@ -28,7 +28,6 @@
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
diff --git a/net/inet/hp.c b/net/inet/hp.c
index a41d37f..18a49a3 100644
--- a/net/inet/hp.c
+++ b/net/inet/hp.c
@@ -1,6 +1,6 @@
/* hp.c: A HP LAN ethernet driver for linux. */
/*
- Written 1993 by Donald Becker. This is alpha test code.
+ 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,
@@ -12,29 +12,22 @@
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";
+static char *version =
+ "hp.c:v0.99.12+ 8/12/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 <asm/io.h>
+#ifndef port_read
+#include "iow.h"
+#endif
#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. */
@@ -47,10 +40,6 @@ __asm__("cld;rep;outsb": :"d" (port),"S" (buf),"c" (nr):"cx","si")
#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);
@@ -87,48 +76,40 @@ 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");
+ /* Check for the HP physical address, 08 00 09 xx xx xx. */
+ if (inb(ioaddr) != 0x08
+ || inb(ioaddr+1) != 0x00
+ || inb(ioaddr+2) != 0x09)
return 0;
- }
+
+ /* Good enough, we will assume everything works. */
+ ethdev_init(dev);
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) {
+ if ((tmp = inb(ioaddr + HP_ID)) & 0x80) {
ei_status.name = "HP27247";
ei_status.word16 = 1;
- ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
+ ei_status.stop_page = HP_16BSTOP_PG; /* Safe (if small) value */
} else {
ei_status.name = "HP27250";
ei_status.word16 = 0;
- ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
+ ei_status.stop_page = HP_8BSTOP_PG;
}
- /* Set the base address to point to the NIC! */
+ printk("%s: %s at %#3x,", dev->name, ei_status.name, ioaddr);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ printk(" %2.2x", station_addr[i] = inb(ioaddr + i));
+
+
+ /* Set the base address to point to the NIC, not the "real" base! */
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. */
+ /* Snarf the interrupt now. Someday this could be moved to open(). */
if (dev->irq < 2) {
int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
int irq_8list[] = { 7, 5, 3, 4, 9, 0};
@@ -141,14 +122,13 @@ int hpprobe1(int ioaddr, struct device *dev)
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);
+ printk(" selecting IRQ %d.\n", dev->irq);
break;
- } else
- printk(" IRQ%d busy..", dev->irq);
+ }
}
} while (*++irqp);
if (*irqp == 0) {
- printk(" unable to find an free IRQ line.\n");
+ printk(" no free IRQ lines.\n");
return 0;
}
} else {
@@ -160,7 +140,6 @@ int hpprobe1(int ioaddr, struct device *dev)
}
}
- printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq);
if (ei_debug > 1)
printk(version);
diff --git a/net/inet/icmp.c b/net/inet/icmp.c
index 2c2050d..bd7bed5 100644
--- a/net/inet/icmp.c
+++ b/net/inet/icmp.c
@@ -23,7 +23,6 @@
#include <linux/socket.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "route.h"
diff --git a/net/inet/iow.h b/net/inet/iow.h
new file mode 100644
index 0000000..e186c45
--- /dev/null
+++ b/net/inet/iow.h
@@ -0,0 +1,126 @@
+#ifndef _ASM_IOW_H
+#define _ASM_IOW_H
+/* I added a few. Please add to the distributed files. -djb. */
+/* This file is copied 1:1 from /linux/include/asm/io.h, and changed all
+ al to ax, all inb to inw and all outb to outw (to get word in/out)
+ the four inlines here should be added to the original, and
+ then this one rm'd (and the #include "iow.h" in depca.c removed)...
+ Gruss PB
+*/
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe abou using 0x80.
+ *
+ * Linus
+ */
+
+/* This is the more general version of outw.. */
+extern inline void __outw(unsigned short value, unsigned short port)
+{
+__asm__ __volatile__ ("outw %w0,%w1"
+ : /* no outputs */
+ :"a" (value),"d" (port));
+}
+
+/* this is used for constant port numbers < 256.. */
+extern inline void __outwc(unsigned short value, unsigned short port)
+{
+__asm__ __volatile__ ("outw %w0,%1"
+ : /* no outputs */
+ :"a" (value),"i" (port));
+}
+
+/* general version of inw */
+extern inline unsigned int __inw(unsigned short port)
+{
+ unsigned int _v;
+__asm__ __volatile__ ("inw %w1,%w0"
+ :"=a" (_v):"d" (port),"0" (0));
+ return _v;
+}
+
+/* inw with constant port nr 0-255 */
+extern inline unsigned int __inwc(unsigned short port)
+{
+ unsigned int _v;
+__asm__ __volatile__ ("inw %1,%w0"
+ :"=a" (_v):"i" (port),"0" (0));
+ return _v;
+}
+
+extern inline void __outw_p(unsigned short value, unsigned short port)
+{
+__asm__ __volatile__ ("outw %w0,%w1"
+ : /* no outputs */
+ :"a" (value),"d" (port));
+ SLOW_DOWN_IO;
+}
+
+extern inline void __outwc_p(unsigned short value, unsigned short port)
+{
+__asm__ __volatile__ ("outw %w0,%1"
+ : /* no outputs */
+ :"a" (value),"i" (port));
+ SLOW_DOWN_IO;
+}
+
+extern inline unsigned int __inw_p(unsigned short port)
+{
+ unsigned int _v;
+__asm__ __volatile__ ("inw %w1,%w0"
+ :"=a" (_v):"d" (port),"0" (0));
+ SLOW_DOWN_IO;
+ return _v;
+}
+
+extern inline unsigned int __inwc_p(unsigned short port)
+{
+ unsigned int _v;
+__asm__ __volatile__ ("inw %1,%w0"
+ :"=a" (_v):"i" (port),"0" (0));
+ SLOW_DOWN_IO;
+ return _v;
+}
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ * - can't use it inside a inlien function (it will never be true)
+ * - you don't have to worry about side effects within the __builtin..
+ */
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc((val),(port)) : \
+ __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc(port) : \
+ __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc_p((val),(port)) : \
+ __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc_p(port) : \
+ __inw_p(port))
+
+#endif
+
+/* The word-wide I/O operations are more general, but require a halved
+ count. */
+#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")
diff --git a/net/inet/ip.c b/net/inet/ip.c
index 66a3df1..636a0b3 100644
--- a/net/inet/ip.c
+++ b/net/inet/ip.c
@@ -26,7 +26,6 @@
#include <linux/sockios.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "eth.h"
#include "ip.h"
@@ -38,6 +37,8 @@
#include "arp.h"
#include "icmp.h"
+#define CONFIG_IP_FORWARD
+
extern int last_retran;
extern void sort_send(struct sock *sk);
@@ -578,7 +579,9 @@ ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
/* Do any IP forwarding required. */
if ((brd = chk_addr(iph->daddr)) == 0) {
+#ifdef CONFIG_IP_FORWARD
ip_forward(skb, dev);
+#endif
skb->sk = NULL;
kfree_skb(skb, FREE_WRITE);
return(0);
@@ -718,9 +721,8 @@ ip_queue_xmit(struct sock *sk, struct device *dev,
}
}
sti();
- sk->time_wait.len = backoff(sk->backoff) * (2 * sk->mdev + sk->rtt);
- sk->timeout = TIME_WRITE;
- reset_timer ((struct timer *)&sk->time_wait);
+ reset_timer(sk, TIME_WRITE,
+ backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
} else {
skb->sk = sk;
}
@@ -787,10 +789,7 @@ ip_retransmit(struct sock *sk, int all)
* back down reasonably quickly.
*/
sk->backoff++;
- sk->time_wait.len = backoff(sk->backoff) * (2 * sk->mdev + sk->rtt);
- sk->timeout = TIME_WRITE;
- reset_timer((struct timer *)&sk->time_wait);
-
+ reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
}
/* Backoff function - the subject of much research */
diff --git a/net/inet/lance.c b/net/inet/lance.c
new file mode 100644
index 0000000..0ae14c6
--- /dev/null
+++ b/net/inet/lance.c
@@ -0,0 +1,630 @@
+/* lance.c: An AMD LANCE 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 driver should work with the Allied Telesis 1500, and NE2100 clones.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+*/
+
+static char *version = "lance.c:v0.08 8/12/93 becker@super.org\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+/*#include <linux/interrupt.h>*/
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+/*#include <asm/system.h>*/
+
+#include "dev.h"
+#include "iow.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+
+/* From auto_irq.c, should be in a *.h file. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+extern struct device *irq2dev_map[16];
+
+extern void printk_stats(struct enet_statistics *stats);
+
+#ifdef LANCE_DEBUG
+int lance_debug = LANCE_DEBUG;
+#else
+int lance_debug = 1;
+#endif
+
+#ifndef DEFAULT_DMA
+#define DEFAULT_DMA 5
+#endif
+
+/* Bitfield in the high bits of init block ring buffer. */
+#define RING_LEN_BITS 0x80000000
+#define RING_MOD_MASK 0x0f
+#define RING_SIZE 16
+
+
+/* Offsets from base I/O address. */
+#define LANCE_DATA 0x10
+#define LANCE_ADDR 0x12
+#define LANCE_RESET 0x14
+#define LANCE_BUS_IF 0x16
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ int base;
+ short buf_length; /* This length is 2's complement (negative)! */
+ short msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ int base;
+ short length; /* Length is 2's complement (negative)! */
+ short misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init {
+ unsigned short mode; /* Pre-set mode (reg. 15) */
+ unsigned char phys_addr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned rx_ring; /* Tx and Rx ring base pointers */
+ unsigned tx_ring;
+};
+
+struct lance_private {
+ /* These must aligned on 8-byte boundaries. */
+ struct lance_rx_head rx_ring[RING_SIZE];
+ struct lance_tx_head tx_ring[RING_SIZE];
+ struct lance_init init_block;
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ int dma;
+ struct enet_statistics stats;
+ int pad0, pad1; /* Used for alignment */
+};
+
+/* This is a temporary solution to the lack of a ethercard low-memory
+ allocation scheme. We need it for bus-master or DMA ethercards if
+ they are to work >16M memory systems. */
+#define PKT_BUF_SZ 1550
+static char rx_buffs[PKT_BUF_SZ][RING_SIZE];
+
+int at1500_init(int ioaddr, struct device *dev);
+static int lance_open(struct device *dev);
+static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
+static int lance_rx(struct device *dev);
+static void lance_interrupt(int reg_ptr);
+static int lance_close(struct device *dev);
+static struct enet_statistics *lance_get_stats(struct device *dev);
+
+#ifdef notdef
+static struct sigaction lance_sigaction = { &lance_interrupt, 0, 0, NULL, };
+#endif
+
+
+
+int at1500_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
+ int ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x100)
+ return ! at1500_init(ioaddr, dev);
+
+ for (port = &ports[0]; *port; port++) {
+ /* Probe for the Allied-Telesys vendor ID. This will not detect
+ other NE2100-like ethercards, which must use a hard-wired ioaddr.
+ There must be a better way to detect a LANCE... */
+ int ioaddr = *port;
+ if (inb(ioaddr) != 0x00
+ || inb(ioaddr+1) != 0x00
+ || inb(ioaddr+2) != 0xF4)
+ continue;
+ if (at1500_init(ioaddr, dev))
+ return 0;
+ }
+ return 1; /* ENODEV would be more accurate. */
+}
+
+int
+at1500_init(int ioaddr, struct device *dev)
+{
+ struct lance_private *lp;
+ int i;
+
+ dev->base_addr = ioaddr;
+ printk("%s: LANCE at %#3x, address", dev->name, ioaddr);
+
+ /* There is a 16 byte station address PROM at the base address.
+ The first six bytes are the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ /* Reset the LANCE */
+ inw(ioaddr+LANCE_RESET);
+
+ /* Make up a LANCE-specific-data structure. */
+ dev->priv = kmalloc(sizeof(struct lance_private), GFP_KERNEL);
+ /* Align on 8-byte boundary. */
+ dev->priv = (void *)(((int)dev->priv + 7) & ~0x07);
+
+ if ((int)dev->priv & 0xff000000 || (int) rx_buffs & 0xff000000) {
+ printk(" disabled (buff %#x > 16M).\n", (int)rx_buffs);
+ return 0;
+ }
+
+ memset(dev->priv, 0, sizeof(struct lance_private));
+ lp = (struct lance_private *)dev->priv;
+
+ /* Un-Reset the LANCE, needed only for the NE2100. */
+ outw(0, ioaddr+LANCE_RESET);
+
+ lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
+
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ outw((short) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+ outw(0x0000, ioaddr+LANCE_ADDR);
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(0x0041, ioaddr+LANCE_DATA);
+
+ dev->irq = autoirq_report(1);
+ if (dev->irq)
+ printk(", using IRQ %d.\n", dev->irq);
+ else {
+ printk(", failed to detect IRQ line.\n");
+ return 0;
+ }
+ } else
+ printk(" assigned IRQ %d.\n", dev->irq);
+
+#ifndef NE2100 /* The NE2100 might not understand */
+ /* Turn on auto-select of media (10baseT or BNC) so that the user
+ can watch the LEDs even if the board isn't opened. */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+#endif
+
+ if ((int)(lp->rx_ring) & 0x07)
+ printk("%s: LANCE Rx and Tx rings not on even boundary.\n",
+ dev->name);
+
+ if (lance_debug > 0)
+ printk(version);
+
+ /* The LANCE-specific entries in the device structure. */
+ dev->open = &lance_open;
+ dev->hard_start_xmit = &lance_start_xmit;
+ dev->stop = &lance_close;
+ dev->get_stats = &lance_get_stats;
+
+ dev->mem_start = 0;
+ dev->rmem_end = 0x00ffffff; /* Bogus, needed for dev_rint(). */
+
+ /* Fill in the generic field of the device structure. */
+ 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->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 ioaddr;
+}
+
+
+static int
+lance_open(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ if (request_irq(dev->irq, &lance_interrupt)) {
+ return -EAGAIN;
+ }
+
+ if (lp->dma < 1)
+ lp->dma = DEFAULT_DMA;
+
+ if (request_dma(lp->dma)) {
+ free_irq(dev->irq);
+ return -EAGAIN;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the LANCE */
+ inw(ioaddr+LANCE_RESET);
+
+ /* The DMA controller is used as a no-operation slave, "cascade mode". */
+ enable_dma(lp->dma);
+ set_dma_mode(lp->dma, DMA_MODE_CASCADE);
+
+ /* Un-Reset the LANCE, needed only for the NE2100. */
+ outw(0, ioaddr+LANCE_RESET);
+
+#ifndef NE2100 /* The NE2100 might not understand */
+ /* Turn on auto-select of media (10baseT or BNC). */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+#endif
+
+ if (lance_debug > 1)
+ printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
+ dev->name, dev->irq, lp->dma, lp->tx_ring, lp->rx_ring,
+ &lp->init_block);
+
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < RING_SIZE; i++) {
+ lp->rx_ring[i].base = (int)rx_buffs | 0x80000000 + i*PKT_BUF_SZ;
+ lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+ lp->tx_ring[i].base = 0;
+ }
+
+ lp->init_block.mode = 0x0000;
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | RING_LEN_BITS;
+
+ /* Re-initialize the LANCE, and start it when done. */
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ outw((short) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+
+ outw(0x0004, ioaddr+LANCE_ADDR);
+ outw(0x0d15, ioaddr+LANCE_DATA);
+
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0001, ioaddr+LANCE_DATA);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ i = 0;
+ while (i++ < 100)
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ break;
+ outw(0x0142, ioaddr+LANCE_DATA);
+
+ if (lance_debug > 2)
+ printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
+ dev->name, i, &lp->init_block, inw(ioaddr+LANCE_DATA));
+
+ return 0; /* Always succeed */
+}
+
+static int
+lance_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int entry = lp->cur_tx++;
+ if (tickssofar < 5)
+ return 1;
+ outw(0, ioaddr+LANCE_ADDR);
+ printk("%s: transmit timed out, status %4.4x.\n", dev->name,
+ inw(ioaddr+LANCE_DATA));
+
+ if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0)
+ dev->tbusy=0;
+ else
+ outw(0x00, ioaddr+LANCE_DATA),
+ outw(0x43, ioaddr+LANCE_DATA);;
+ dev->trans_start = jiffies;
+
+ return 0;
+ }
+
+ 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;
+
+ if (lance_debug > 3) {
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
+ inw(ioaddr+LANCE_DATA));
+ outw(0x0000, ioaddr+LANCE_DATA);
+ }
+
+ /* Avoid timer-based retransmission conflicts. */
+ dev->tbusy=1;
+
+ /* This code is broken for >16M RAM systems.
+ There are two ways to fix it:
+ Keep around several static low-memory buffers, and deal with
+ the book-keeping (bad for small systems).
+ Make static Tx buffers that are optionally used.
+ (Even worse.)
+ */
+ { /* Fill in a Tx ring entry */
+ int entry = lp->cur_tx++;
+
+ entry &= RING_MOD_MASK; /* Ring buffer. */
+ /* Caution: the write order is important here. */
+ lp->tx_ring[entry].length = -skb->len;
+
+ /* This shouldn't be necessary... */
+ lp->tx_ring[entry].length =
+ -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+
+ lp->tx_ring[entry].misc = 0x0000;
+ lp->tx_ring[entry].base = (int)(skb+1) | 0x83000000;
+
+ /* Trigger an immediate send poll. */
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0048, ioaddr+LANCE_DATA);
+
+ if (lance_debug > 4) {
+ unsigned char *pkt =
+ (unsigned char *)(lp->tx_ring[entry].base & 0x00ffffff);
+
+ printk("%s: tx ring[%d], %#x, sk_buf %#x len %d.\n",
+ dev->name, entry, &lp->tx_ring[entry],
+ lp->tx_ring[entry].base, -lp->tx_ring[entry].length);
+ printk("%s: Tx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n",
+ dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6],
+ pkt[7], pkt[8], pkt[11], pkt[12], pkt[13],
+ pkt[14], pkt[15]);
+ }
+
+ dev->trans_start = jiffies;
+
+ if (lp->tx_ring[(entry+1) & RING_MOD_MASK].base >= 0)
+ dev->tbusy=0;
+ }
+
+ return 0;
+}
+
+/* The LANCE interrupt handler. */
+static void
+lance_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ struct lance_private *lp;
+ int csr0, ioaddr;
+
+ if (dev == NULL) {
+ printk ("lance_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct lance_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ outw(0x00, dev->base_addr + LANCE_ADDR);
+ csr0 = inw(dev->base_addr + LANCE_DATA);
+
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+
+ if (lance_debug > 5)
+ printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
+ dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
+
+ if (csr0 & 0x0400) /* Rx interrupt */
+ lance_rx(dev);
+
+ if (csr0 & 0x0200) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx & RING_MOD_MASK;
+
+ if (lance_debug > 5)
+ printk("%s: Cleaning tx ring, dirty %d clean %d.\n",
+ dev->name, dirty_tx, (lp->cur_tx & RING_MOD_MASK));
+
+ /* This code is broken for >16M RAM systems. */
+ while (dirty_tx != (lp->cur_tx & RING_MOD_MASK)
+ && lp->tx_ring[dirty_tx].base > 0) {
+ sk_buff *skb =
+ (sk_buff *)(lp->tx_ring[dirty_tx].base & 0x00ffffff);
+ unsigned short *tmdp = (unsigned short *)(&lp->tx_ring[dirty_tx]);
+ int status = lp->tx_ring[dirty_tx].base >> 24;
+
+ if (status & 0x40) { /* There was an major error, log it. */
+ int err_status = lp->tx_ring[dirty_tx].misc;
+ lp->stats.tx_errors++;
+ if (err_status & 0x0400) lp->stats.tx_aborted_errors++;
+ if (err_status & 0x0800) lp->stats.tx_carrier_errors++;
+ if (err_status & 0x1000) lp->stats.tx_window_errors++;
+ if (err_status & 0x4000) lp->stats.tx_fifo_errors++;
+ /* We should re-init() after the FIFO error. */
+ } else if (status & 0x18)
+ lp->stats.collisions++;
+ else
+ lp->stats.tx_packets++;
+ if (lance_debug > 5)
+ printk("%s: Tx done entry %d, %4.4x %4.4x %4.4x %4.4x.\n",
+ dev->name, dirty_tx,
+ tmdp[0], tmdp[1], tmdp[2], tmdp[3]);
+ if ((skb-1)->free)
+ kfree_skb (skb-1, FREE_WRITE);
+ dirty_tx = ++lp->dirty_tx & RING_MOD_MASK;
+ }
+ }
+
+ /* Clear the interrupts we've handled. */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x7f40, dev->base_addr + LANCE_DATA);
+
+ if (lance_debug > 4)
+ printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+ dev->name, inw(ioaddr + LANCE_ADDR),
+ inw(dev->base_addr + LANCE_DATA));
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+lance_rx(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int entry = lp->cur_rx & RING_MOD_MASK;
+
+ /* Check to see if we own this entry. */
+ while (lp->rx_ring[entry].base >= 0) {
+ int status = lp->rx_ring[entry].base >> 24;
+ if (lance_debug > 5) {
+ unsigned char *pkt =
+ (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff);
+ printk("%s: Rx packet at ring entry %d, len %d status %2.2x.\n",
+ dev->name, entry, lp->rx_ring[entry].msg_length,
+ lp->rx_ring[entry].base >> 24);
+ printk("%s: Rx %2.2x %2.2x %2.2x ... %2.2x %2.2x %2.2x %2.2x...%2.2x len %2.2x %2.2x %2.2x %2.2x.\n",
+ dev->name, pkt[0], pkt[1], pkt[2], pkt[5], pkt[6],
+ pkt[7], pkt[8], pkt[11], pkt[12], pkt[13],
+ pkt[14], pkt[15]);
+ }
+ /* If so, copy it to the upper layers. */
+ if (status & 0x40) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ } else {
+ if (dev_rint((unsigned char *)(lp->rx_ring[entry].base
+ & 0x00ffffff),
+ lp->rx_ring[entry].msg_length, 0, dev)) {
+ lp->stats.rx_dropped++;
+ break;
+ }
+ lp->stats.rx_packets++;
+ }
+
+ lp->rx_ring[entry].base |= 0x80000000;
+ entry = (entry+1) & RING_MOD_MASK;
+ }
+ lp->cur_rx = entry;
+
+ return 0;
+}
+
+static int
+lance_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+
+ outw(0, ioaddr+LANCE_ADDR);
+
+ if (lance_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+#ifdef PRINTK_STATS
+ if (lance_debug > 2)
+ printk_stats(&lp->stats);
+#endif
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ outw(0x0004, ioaddr+LANCE_DATA);
+
+ disable_dma(lp->dma);
+
+ free_irq(dev->irq);
+ free_dma(lp->dma);
+
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+static struct enet_statistics *
+lance_get_stats(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short saved_addr;
+
+ cli();
+ saved_addr = inw(ioaddr+LANCE_ADDR);
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ outw(saved_addr, ioaddr+LANCE_ADDR);
+ sti();
+
+ return &lp->stats;
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -x c++ -c lance.c"
+ * End:
+ */
diff --git a/net/inet/loopback.c b/net/inet/loopback.c
index d370cfc..27fc00e 100644
--- a/net/inet/loopback.c
+++ b/net/inet/loopback.c
@@ -33,7 +33,6 @@
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
diff --git a/net/inet/ne.c b/net/inet/ne.c
index 83b0575..28093eb 100644
--- a/net/inet/ne.c
+++ b/net/inet/ne.c
@@ -1,13 +1,14 @@
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
/*
- Written 1992,1993 by Donald Becker. This is alpha test code.
+ Written 1992,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 driver should work with many 8390-based ethernet boards. Currently
- it support the NE1000, NE2000 (and clones), and some Cabletron products.
+ it support the NE1000, NE2000, 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
@@ -16,30 +17,22 @@
/* 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";
+ "ne.c:v0.99-12B 8/12/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 <asm/io.h>
+#ifndef port_read
+#include "iow.h"
+#endif
#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_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
@@ -48,10 +41,6 @@ __asm__("cld;rep;outsb": :"d" (port),"S" (buf),"c" (nr):"cx","si")
#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);
@@ -63,7 +52,7 @@ static void ne_block_output(struct device *dev, const int count,
/* 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
@@ -101,15 +90,29 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
int i;
unsigned char SA_prom[32];
int wordlength = 2;
+ char *name;
+ int start_page, stop_page;
int neX000, ctron, dlink;
-
-
- if ( inb_p(ioaddr) == 0xFF) {
- if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr);
+ int reg0 = inb(ioaddr);
+
+ if ( reg0 == 0xFF)
return 0;
+
+ /* Do a quick preliminary check that we have a 8390. */
+ { int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + 0x0d);
+ outb_p(0xff, ioaddr + 0x0d);
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr);
+ outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ return 0;
+ }
}
- printk("8390 ethercard probe at %#3x:", ioaddr);
+ printk("NE*000 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.
@@ -118,7 +121,7 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
(I learned the hard way!). */
{
struct {char value, offset; } program_seq[] = {
- {E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
{0x00, EN0_RCNTHI},
@@ -130,7 +133,7 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
{0x00, EN0_RCNTHI},
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
{0x00, EN0_RSARHI},
- {E8390_RREAD+E8390_START, EN_CMD},
+ {E8390_RREAD+E8390_START, E8390_CMD},
};
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
@@ -141,19 +144,17 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
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. */
@@ -165,43 +166,38 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
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;
+ if (neX000 || dlink) {
+ if (wordlength == 2) {
+ name = dlink ? "DE200" : "NE2000";
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ name = dlink ? "DE100" : "D-Link";
+ start_page = NE1SM_START_PG;
+ 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;
+ name = "Cabletron";
+ start_page = 0x01;
+ 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. */
+ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, ioaddr + EN0_RCNTLO);
+ outb_p(0x00, ioaddr + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
dev->irq = autoirq_report(0);
if (ei_debug > 2)
printk(" autoirq is %d", dev->irq);
@@ -212,17 +208,35 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
/* 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)
+ {
+ 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 at %#x, using IRQ %d.\n",
+ dev->name, name, ioaddr, dev->irq);
+
+ dev->base_addr = ioaddr;
+
+ ethdev_init(dev);
+
+ if (ei_debug > 0)
printk(version);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = (wordlength == 2);
+
+ ei_status.rx_start_page = 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
+
ei_status.reset_8390 = &ne_reset_8390;
ei_status.block_input = &ne_block_input;
ei_status.block_output = &ne_block_output;
@@ -269,12 +283,12 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
return 0;
}
ei_status.dmaing |= 0x01;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_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);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
if (count & 0x01)
@@ -284,8 +298,10 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
}
/* 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... */
+ encountering problems that it is still here. If you see
+ this message you either 1) have an slightly imcompatible clone
+ or 2) have noise/speed problems with your bus. */
+ if (ei_debug > 1) { /* DMA termination address check... */
int addr, tries = 20;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
@@ -298,7 +314,7 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
} while (--tries > 0);
if (tries <= 0)
printk("%s: RX transfer address mismatch,"
- "%#4.4x (should be) vs. %#4.4x (actual).\n",
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
dev->name, ring_offset + xfer_count, addr);
}
ei_status.dmaing &= ~0x01;
@@ -318,7 +334,7 @@ ne_block_output(struct device *dev, int count,
if (ei_status.word16 && (count & 0x01))
count++;
if (ei_status.dmaing) {
- if (ei_debug > 0)
+ 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);
@@ -326,7 +342,7 @@ ne_block_output(struct device *dev, int count,
}
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);
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
retry:
#if defined(rw_bugfix)
@@ -338,7 +354,7 @@ ne_block_output(struct device *dev, int count,
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);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
/* Make certain that the dummy read has occured. */
SLOW_DOWN_IO;
SLOW_DOWN_IO;
@@ -351,7 +367,7 @@ ne_block_output(struct device *dev, int count,
outb_p(0x00, nic_base + EN0_RSARLO);
outb_p(start_page, nic_base + EN0_RSARHI);
- outb_p(E8390_RWRITE+E8390_START, EN_CMD);
+ outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
} else {
@@ -360,7 +376,7 @@ ne_block_output(struct device *dev, int 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... */
+ if (ei_debug > 1) { /* DMA termination address check... */
int addr, tries = 20;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
@@ -372,8 +388,8 @@ ne_block_output(struct device *dev, int count,
break;
} while (--tries > 0);
if (tries <= 0) {
- printk("%s: Packet buffer transfer address mismatch on TX,"
- "%#4.4x vs. %#4.4x.\n",
+ printk("%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
dev->name, (start_page << 8) + count, addr);
if (retries++ == 0)
goto retry;
diff --git a/net/inet/packet.c b/net/inet/packet.c
index 19800d8..3aeb5fb 100644
--- a/net/inet/packet.c
+++ b/net/inet/packet.c
@@ -21,7 +21,6 @@
#include <linux/socket.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
diff --git a/net/inet/plip.c b/net/inet/plip.c
index 2522d0a..48cc7c8 100644
--- a/net/inet/plip.c
+++ b/net/inet/plip.c
@@ -1,17 +1,31 @@
-/* plip.c: A parallel port "network" driver for linux. */
+/* Plip.c: A parallel port "network" driver for linux. */
/*
- Written 1993 by Donald Becker. This is unreleased software.
+ Written 1993 by Donald Becker and TANABE Hiroyasu.
+ This code is distributed under the GPL.
+ The current author is reached as hiro@sanpo.t.u-tokyo.ac.jp .
+ For more information do 'whois -h whois.nic.ad.jp HT043JP'
+
+ The original author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
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
+ This driver was first developed by D. Becker, when he was inspired by
+ Russ Nelson's parallel port packet driver. He also did the update
+ to 0.99.10.
+
+ It was further developed by Tommy Thorn (tthorn@daimi.aau.dk).
+
+ Recent versions were debugged and maintained by TANABE Hiroyasu.
+
+ Updated for 0.99pl12 by Donald Becker.
*/
static char *version =
- "plip.c:v0.04 Mar 19 1993 Donald Becker (becker@super.org)\n";
+ "plip.c:v0.15 for 0.99pl12+, 8/11/93\n";
#include <linux/config.h>
@@ -19,14 +33,17 @@ static char *version =
Sources:
Ideas and protocols came from Russ Nelson's (nelson@crynwr.com)
"parallel.asm" parallel port packet driver.
+ TANABE Hiroyasu changes the protocol.
The "Crynwr" parallel port standard specifies the following protocol:
send header nibble '8'
+ type octet '0xfd' or '0xfc'
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)>
+Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
+ <wait for rx. '0x0?'> <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:
@@ -50,14 +67,14 @@ make one yourself. The wiring is:
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/ptrace.h>
+#include <linux/if_ether.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <linux/in.h>
+#include <netinet/in.h>
#include <errno.h>
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
@@ -65,98 +82,125 @@ make one yourself. The wiring is:
#include "sock.h"
#include "arp.h"
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef PLIP_DEBUG
-#define PLIP_DEBUG 9
+#ifdef PRINTK
+#undef PRINTK
+#endif
+#ifdef PRINTK2
+#undef PRINTK2
+#endif
+
+#define PLIP_DEBUG /* debugging */
+#undef PLIP_DEBUG2 /* debugging with more varbose report */
+
+#ifdef PLIP_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+#ifdef PLIP_DEBUG2
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x) /**/
#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];
+/* Network statistics, with the same names as 'struct enet_statistics'. */
+#define netstats enet_statistics
+
+/* constants */
#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;
+#define PLIP_MTU 1600
+#define PLIP_HEADER_TYPE1 0xfd
+#define PLIP_HEADER_TYPE2 0xfc
/* 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);
+static int plip_header (unsigned char *buff, struct device *dev,
+ unsigned short type, unsigned long h_dest,
+ unsigned long h_source, unsigned len);
+
+/* variables used internally. */
+#define INITIALTIMEOUTFACTOR 4
+#define MAXTIMEOUTFACTOR 20
+static int timeoutfactor = INITIALTIMEOUTFACTOR;
/* 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);
+static void plip_device_clear(struct device *dev);
+static void plip_receiver_error(struct device *dev);
+static void plip_set_physicaladdr(struct device *dev, unsigned long ipaddr);
+static int plip_addrcmp(struct ethhdr *eth);
+static int plip_send_enethdr(struct device *dev, struct ethhdr *eth);
+static int plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
+ unsigned char h_dest, unsigned char h_source,
+ unsigned short type);
+static void cold_sleep(int tics);
+static void plip_interrupt(int reg_ptr); /* Dispatch from interrupts. */
+static int plip_receive_packet(struct device *dev);
+static int plip_send_packet(struct device *dev, unsigned char *buf, int length);
+static int plip_send_start(struct device *dev, struct ethhdr *eth);
+static void double_timeoutfactor(void);
+static struct enet_statistics *plip_get_stats(struct device *dev);
int
plip_init(struct device *dev)
{
+ int port_base = dev->base_addr;
int i;
+ /* Check that there is something at base_addr. */
+ outb(0x00, port_base + PAR_CONTROL);
+ outb(0x55, port_base + PAR_DATA);
+ if (inb(port_base + PAR_DATA) != 0x55)
+ return -ENODEV;
+
/* Alpha testers must have the version number to report bugs. */
- if (plip_debug > 1) {
+#ifdef PLIP_DEBUG
+ {
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. */
+#endif
/* Initialize the device structure. */
dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);
memset(dev->priv, 0, sizeof(struct netstats));
- localstats = (struct netstats*) dev->priv;
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_header = &plip_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;
+ dev->get_stats = &plip_get_stats;
/* These are ethernet specific. */
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
+ dev->mtu = PLIP_MTU; /* PLIP may later negotiate max pkt size */
+ 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! */
+ dev->dev_addr[i] = 0;
}
+ printk("%s: configured for parallel port at %#3x, IRQ %d.\n",
+ dev->name, dev->base_addr, dev->irq);
- /* 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);
-
+ /* initialize internal value */
+ timeoutfactor = INITIALTIMEOUTFACTOR;
return 0;
}
@@ -173,14 +217,13 @@ 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;
+ PRINTK(("%s: couldn't get IRQ %d.\n", dev->name, dev->irq));
+ return -EAGAIN;
}
irq2dev_map[dev->irq] = dev;
- outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
- dev->tbusy = 0; /* Transmit busy... */
+ plip_device_clear(dev);
+ dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
return 0;
@@ -190,6 +233,7 @@ plip_open(struct device *dev)
static int
plip_close(struct device *dev)
{
+ dev->tbusy = 1;
dev->start = 0;
free_irq(dev->irq);
irq2dev_map[dev->irq] = NULL;
@@ -201,6 +245,17 @@ static int
plip_tx_packet(struct sk_buff *skb, struct device *dev)
{
int ret_val;
+
+ if (dev->tbusy || dev->interrupt) { /* Do timeouts, to avoid hangs. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 50)
+ return 1;
+ printk("%s: transmit timed out\n", dev->name);
+ /* Try to restart the adaptor. */
+ plip_device_clear(dev);
+ return 0;
+ }
+
/* If some higher layer thinks we've missed an tx-done interrupt
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
itself. */
@@ -218,174 +273,542 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev)
}
dev->trans_start = jiffies;
- ret_val = plip_write(dev, (unsigned char *)(skb+1), skb->len);
+ ret_val = plip_send_packet(dev, (unsigned char *)(skb+1), skb->len);
if (skb->free)
kfree_skb (skb, FREE_WRITE);
dev->tbusy = 0;
mark_bh (INET_BH);
return ret_val;
}
+
+static int
+plip_header (unsigned char *buff, struct device *dev,
+ unsigned short type, unsigned long h_dest,
+ unsigned long h_source, unsigned len)
+{
+ if (dev->dev_addr[0] == 0) {
+ /* set physical address */
+ plip_set_physicaladdr(dev, h_source);
+ }
+ return eth_header(buff, dev, type, h_dest, h_source, len);
+}
+static void
+ plip_device_clear(struct device *dev)
+{
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ outb(0x00, dev->base_addr + PAR_DATA);
+ outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
+}
+
+static void
+ plip_receiver_error(struct device *dev)
+{
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ outb(0x02, dev->base_addr + PAR_DATA);
+ outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */
+}
-static inline int get_byte(struct device *dev)
+static int
+ get_byte(struct device *dev)
{
- unsigned char val;
+ unsigned char val, oldval;
unsigned char low_nibble;
- int boguscount = 1500;
+ int timeout;
+ int error = 0;
+ val = inb(dev->base_addr + PAR_STATUS);
+ timeout = jiffies + timeoutfactor * 2;
do {
+ oldval = val;
val = inb(dev->base_addr + PAR_STATUS);
- } while ( ! (val & 0x80) && --boguscount > 0);
+ if ( oldval != val ) continue; /* it's unstable */
+ if ( timeout < jiffies ) {
+ error++;
+ break;
+ }
+ } while ( (val & 0x80) );
+ val = inb(dev->base_addr + PAR_STATUS);
low_nibble = (val >> 3) & 0x0f;
- if (plip_debug > 8)
- printk("%1x", low_nibble);
- outb(0x10, dev->base_addr + PAR_DATA);
+ outb(0x11, dev->base_addr + PAR_DATA);
+ timeout = jiffies + timeoutfactor * 2;
do {
+ oldval = val;
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);
+ if (oldval != val) continue; /* it's unstable */
+ if ( timeout < jiffies ) {
+ error++;
+ break;
+ }
+ } while ( !(val & 0x80) );
+ val = inb(dev->base_addr + PAR_STATUS);
+ PRINTK2(("%02x %s ", low_nibble | ((val << 1) & 0xf0),
+ error ? "t":""));
+ outb(0x01, dev->base_addr + PAR_DATA);
+ if (error) {
+ /* timeout error */
+ double_timeoutfactor();
+ return -1;
+ }
return low_nibble | ((val << 1) & 0xf0);
}
/* The typical workload of the driver:
Handle the parallel port interrupts. */
static void
-plip_interrupt(int reg_ptr)
+ 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;
+ struct netstats *localstats;
if (dev == NULL) {
- printk ("plip_interrupt(): irq %d for unknown device.\n", irq);
+ PRINTK(("plip_interrupt(): irq %d for unknown device.\n", irq));
return;
}
+ localstats = (struct netstats*) dev->priv;
+ if (dev->tbusy || dev->interrupt) return;
dev->interrupt = 1;
- outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable the rx interrupt. */
+ 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);
-
+ PRINTK2(("%s: interrupt. ", dev->name));
+
+ {
+ /* check whether the interrupt is valid or not.*/
+ int timeout = jiffies + timeoutfactor;
+ while ((inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0xc0) {
+ if ( timeout < jiffies ) {
+ PRINTK2(("%s: No interrupt (status=%#02x)!\n",
+ dev->name, inb(dev->base_addr + PAR_STATUS)));
+ plip_device_clear(dev);
+ return;
+ }
+ }
+ }
+ if (plip_receive_packet(dev)) {
+ /* get some error while receiving data */
+ localstats->rx_errors++;
+ plip_receiver_error(dev);
+ } else {
+ plip_device_clear(dev);
+ }
+}
+
+static int
+plip_receive_packet(struct device *dev)
+{
+ int plip_type;
+ unsigned length;
+ int checksum = 0;
+ struct sk_buff *skb;
+ struct netstats *localstats;
+ struct ethhdr eth;
+
localstats = (struct netstats*) dev->priv;
- /* 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;
+
+ {
+ /* get header octet and length of packet */
+ plip_type = get_byte(dev);
+ if (plip_type < 0) return 1; /* probably wrong interrupt */
+ length = get_byte(dev) << 8;
+ length |= get_byte(dev);
+ switch ( plip_type ) {
+ case PLIP_HEADER_TYPE1:
+ {
+ int i;
+ unsigned char *eth_p = (unsigned char*)&eth;
+ for ( i = 0; i < sizeof(eth); i++, eth_p++) {
+ *eth_p = get_byte(dev);
+ }
+ }
+ break;
+ case PLIP_HEADER_TYPE2:
+ {
+ unsigned char h_dest, h_source;
+ unsigned short type;
+ h_dest = get_byte(dev);
+ h_source = get_byte(dev);
+ type = get_byte(dev) << 8;
+ type |= get_byte(dev);
+ plip_rebuild_enethdr(dev, &eth, h_dest, h_source, type);
+ }
+ break;
+ default:
+ PRINTK(("%s: wrong header octet\n", dev->name));
+ }
+ PRINTK2(("length = %d\n", length));
+ if (length > dev->mtu || length < 8) {
+ PRINTK2(("%s: bogus packet size %d.\n", dev->name, length));
+ return 1;
+ }
}
- boguscount = length << 5;
- sksize = sizeof(struct sk_buff) + length;
- skb = (struct sk_buff *) 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;
+ {
+ /* get skb area from kernel and
+ * set appropriate values to skb
+ */
+ int sksize;
+ sksize = sizeof(struct sk_buff) + length;
+ skb = (struct sk_buff *)kmalloc(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTK(("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, sksize));
+ return 1;
+ }
+ skb->lock = 0;
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
}
- skb->lock = 0;
- skb->mem_len = sksize;
- skb->mem_addr = skb;
{
+ /* phase of receiving the data */
/* 'skb+1' points to the start of sk_buff data area. */
unsigned char *buf = (unsigned char *) (skb+1);
- int checksum = 0;
-
- while (length--) {
+ unsigned char *eth_p = (unsigned char *)&eth;
+ int i;
+ for ( i = 0; i < sizeof(eth); i++) {
+ checksum += *eth_p;
+ *buf++ = *eth_p++;
+ }
+ for ( i = 0; i < length - sizeof(eth); i++) {
unsigned char new_byte = get_byte(dev);
- checksum += new_byte, *buf++ = new_byte;
+ checksum += new_byte;
+ *buf++ = new_byte;
}
- if (checksum != get_byte(dev))
- localstats->soft_rx_errors++;
- else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) {
- printk("%s: receive buffers full.\n", dev->name);
- localstats->rx_errors++;
- return;
+ checksum &= 0xff;
+ if (checksum != get_byte(dev)) {
+ localstats->rx_crc_errors++;
+ PRINTK(("checksum error\n"));
+ return 1;
+ } else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) {
+ printk("%s: rcv buff full.\n", dev->name);
+ localstats->rx_dropped++;
+ return 1;
+ }
+ }
+ {
+ /* phase of terminating this connection */
+ int timeout;
+
+ timeout = jiffies + length * timeoutfactor / 16;
+ outb(0x00, dev->base_addr + PAR_DATA);
+ /* Wait for the remote end to reset. */
+ while ( (inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0x80 ) {
+ if (timeout < jiffies ) {
+ double_timeoutfactor();
+ PRINTK(("remote end is not reseted.\n"));
+ break;
+ }
}
}
- /* 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;
+ return 0;
}
-
-static inline int send_byte(struct device *dev, unsigned char val)
+
+static int send_byte(struct device *dev, unsigned char val)
{
- int boguscount = 1500;
- if (plip_debug > 8)
- printk("send(%02x) ", val);
+ int timeout;
+ int error = 0;
+ if (!(inb(dev->base_addr+PAR_STATUS) & 0x08)) {
+ PRINTK(("remote end become unready while sending\n"));
+ return -1;
+ }
+ PRINTK2((" S%02x", val));
+ outb(val, dev->base_addr); /* this makes data bits more stable */
outb(0x10 | val, dev->base_addr);
- while(inb(dev->base_addr+PAR_STATUS) & 0x80)
- if (--boguscount <= 0) break;
+ timeout = jiffies + timeoutfactor;
+ while( inb(dev->base_addr+PAR_STATUS) & 0x80 )
+ if ( timeout < jiffies ) {
+ error++;
+ break;
+ }
+ outb(0x10 | (val >> 4), dev->base_addr);
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");
+ timeout = jiffies + timeoutfactor;
+ while( (inb(dev->base_addr+PAR_STATUS) & 0x80) == 0 )
+ if ( timeout < jiffies ) {
+ error++;
+ break;
+ }
+ if (error) {
+ /* timeout error */
+ double_timeoutfactor();
+ PRINTK2(("t"));
+ return -1;
+ }
+ return 0;
+}
+/*
+ * plip_send_start
+ * trigger remoto rx interrupt and establish a connection.
+ *
+ * return value
+ * 0 : establish the connection
+ * -1 : connection failed.
+ */
+static int
+plip_send_start(struct device *dev, struct ethhdr *eth)
+{
+ int timeout;
+ int status;
+ int lasttrigger;
+ struct netstats *localstats = (struct netstats*) dev->priv;
+
+ /* This starts the packet protocol by triggering a remote IRQ. */
+ timeout = jiffies + timeoutfactor * 16;
+ lasttrigger = jiffies;
+ while ( ((status = inb(dev->base_addr+PAR_STATUS)) & 0x08) == 0 ) {
+ dev->tbusy = 1;
+ outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable my rx intr. */
+ outb(0x08, dev->base_addr + PAR_DATA); /* Trigger remote rx intr. */
+ if (status & 0x40) {
+ /* The remote end is also trying to send a packet.
+ * Only one end may go to the receiving phase,
+ * so we use the "ethernet" address (set from the IP address)
+ * to determine which end dominates.
+ */
+ if ( plip_addrcmp(eth) > 0 ) {
+ localstats->collisions++;
+ PRINTK2(("both ends are trying to send a packet.\n"));
+ if (plip_receive_packet(dev)) {
+ /* get some error while receiving data */
+ localstats->rx_errors++;
+ outb(0x02, dev->base_addr + PAR_DATA);
+ } else {
+ outb(0x00, dev->base_addr + PAR_DATA);
+ }
+ cold_sleep(2); /* make sure that remote end is ready */
+ }
+ continue; /* restart send sequence */
+ }
+ if (lasttrigger != jiffies) {
+ /* trigger again */
+ outb(0x00, dev->base_addr + PAR_DATA);
+ cold_sleep(1);
+ lasttrigger = jiffies;
+ }
+ if (timeout < jiffies) {
+ double_timeoutfactor();
+ plip_device_clear(dev);
+ localstats->tx_errors++;
+ PRINTK(("%s: Connect failed in send_packet().\n",
+ dev->name));
+ /* We failed to send the packet. To emulate the ethernet we
+ should pretent the send worked fine */
+ return -1;
+ }
+ }
+ return 0;
}
static int
-plip_write(struct device *dev, unsigned char *buf, int length)
+plip_send_packet(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]);
+ int error = 0;
+ int plip_type;
+ struct netstats *localstats;
+
+ PRINTK2(("%s: plip_send_packet(%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 0;
+ }
+ localstats = (struct netstats*) dev->priv;
+
+ {
+ /* phase of checking remote status */
+ int i;
+ int timeout = jiffies + timeoutfactor * 8;
+ while ( (i = (inb(dev->base_addr+PAR_STATUS) & 0xe8)) != 0x80 ) {
+ if (i == 0x78) {
+ /* probably cable is not connected */
+ /* Implementation Note:
+ * This status should result in 'Network unreachable'.
+ * but I don't know the way.
+ */
+ return 0;
+ }
+ if (timeout < jiffies) {
+ /* remote end is not ready */
+ double_timeoutfactor();
+ localstats->tx_errors++;
+ PRINTK(("remote end is not ready.\n"));
+ return 1; /* Failed to send the packet */
+ }
+ }
+ }
+ /* phase of making a connection */
+ if (plip_send_start(dev, (struct ethhdr *)buf) < 0)
return 1;
+
+ /* select plip type */
+ {
+ /* Use stripped ethernet header if each first 5 octet of eth
+ * address is same.
+ */
+ int i;
+ struct ethhdr *eth = (struct ethhdr *)buf;
+
+ plip_type = PLIP_HEADER_TYPE2;
+ for ( i = 0; i < ETH_ALEN - 1; i++)
+ if (eth->h_dest[i] != eth->h_source[i])
+ plip_type = PLIP_HEADER_TYPE1;
}
- /* 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, plip_type); /* send header octet */
+
+ {
+ /* send packet's length */
+ /*
+ * in original plip (before v0.1), it was sent with little endian.
+ * but in internet, network byteorder is big endian,
+ * so changed to use big endian.
+ * maybe using 'ntos()' is better.
+ */
+ send_byte(dev, length >> 8); send_byte(dev, length);
+ }
+ {
+ /* phase of sending data */
+ int i;
+ int checksum = 0;
+
+ if (plip_type == PLIP_HEADER_TYPE2) {
+ plip_send_enethdr(dev, (struct ethhdr*)buf);
}
- 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. */
+ for ( i = 0; i < sizeof(struct ethhdr); i++ ) {
+ if (plip_type == PLIP_HEADER_TYPE1) {
+ send_byte(dev, *buf);
+ }
+ checksum += *buf++;
+ }
+
+ for (i = 0; i < length - sizeof(struct ethhdr); i++) {
+ checksum += buf[i];
+ if (send_byte(dev, buf[i]) < 0) {
+ error++;
+ break;
+ }
+ }
+ send_byte(dev, checksum & 0xff);
+ }
+ {
+ /* phase of terminating this connection */
+ int timeout;
+
+ outb(0x00, dev->base_addr + PAR_DATA);
+ /* Wait for the remote end to reset. */
+ timeout = (jiffies + length * timeoutfactor) >> 4;
+ while ((inb(dev->base_addr + PAR_STATUS) & 0xe8) != 0x80) {
+ if (timeout < jiffies ) {
+ double_timeoutfactor();
+ PRINTK(("remote end is not reseted.\n"));
+ error++;
+ break;
+ }
+ }
+ if (inb(dev->base_addr + PAR_STATUS) & 0x10) {
+ /* receiver reports error */
+ error++;
+ }
+ }
+ plip_device_clear(dev);
localstats->tx_packets++;
- if (plip_debug > 5)
- printk("plip_write(%d) done.\n", length);
+ PRINTK2(("plip_send_packet(%d) done.\n", length));
+ return error?1:0;
+}
+
+/*
+ * some trivial functions
+ */
+static void
+plip_set_physicaladdr(struct device *dev, unsigned long ipaddr)
+{
+ /*
+ * set physical address to
+ * 0xfd.0xfd.ipaddr
+ */
+
+ unsigned char *addr = dev->dev_addr;
+ int i;
+
+ if ((ipaddr >> 24) == 0 || (ipaddr >> 24) == 0xff) return;
+ PRINTK2(("%s: set physical address to %08x\n", dev->name, ipaddr));
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++) {
+ addr[i] = 0xfd;
+ }
+ memcpy(&(addr[i]), &ipaddr, sizeof(unsigned long));
+}
+
+static int
+plip_addrcmp(struct ethhdr *eth)
+{
+ int i;
+ for ( i = ETH_ALEN - 1; i >= 0; i-- ) {
+ if (eth->h_dest[i] > eth->h_source[i]) return -1;
+ if (eth->h_dest[i] < eth->h_source[i]) return 1;
+ }
+ PRINTK2(("h_dest = %08x%04x h_source = %08x%04x\n",
+ *(long*)&eth->h_dest[2],*(short*)&eth->h_dest[0],
+ *(long*)&eth->h_source[2],*(short*)&eth->h_source[0]));
return 0;
}
+
+static int
+plip_send_enethdr(struct device *dev, struct ethhdr *eth)
+{
+ send_byte(dev, eth->h_dest[ETH_ALEN-1]);
+ send_byte(dev, eth->h_source[ETH_ALEN-1]);
+ send_byte(dev, eth->h_proto >> 8);
+ send_byte(dev, eth->h_proto);
+ return 0;
+}
+
+static int
+plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth,
+ unsigned char dest, unsigned char source,
+ unsigned short type)
+{
+ eth->h_proto = type;
+ memcpy(eth->h_dest, dev->dev_addr, ETH_ALEN-1);
+ eth->h_dest[ETH_ALEN-1] = dest;
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN-1);
+ eth->h_source[ETH_ALEN-1] = source;
+ return 0;
+}
+
+/* This function is evil, evil, evil. This should be a
+ _kernel_, rescheduling sleep!. */
+static void
+cold_sleep(int tics)
+{
+ int start = jiffies;
+ while(jiffies < start + tics)
+ ; /* do nothing */
+ return;
+}
+
+static void
+ double_timeoutfactor()
+{
+ timeoutfactor *= 2;
+ if (timeoutfactor >= MAXTIMEOUTFACTOR) {
+ timeoutfactor = MAXTIMEOUTFACTOR;
+ }
+ return;
+}
+
+static struct enet_statistics *
+plip_get_stats(struct device *dev)
+{
+ struct netstats *localstats = (struct netstats*) dev->priv;
+ return localstats;
+}
/*
* Local variables:
- * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c plip.c"
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -fomit-frame-pointer -x c++ -c plip.c"
* version-control: t
* kept-new-versions: 5
* End:
diff --git a/net/inet/proc.c b/net/inet/proc.c
index b15cbc3..9137548 100644
--- a/net/inet/proc.c
+++ b/net/inet/proc.c
@@ -27,7 +27,6 @@
#include <linux/in.h>
#include <linux/param.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
@@ -37,8 +36,6 @@
#include "sock.h"
#include "raw.h"
-extern struct timer *timer_base;
-
/*
* Get__netinfo returns the length of that string.
*
@@ -53,9 +50,9 @@ get__netinfo(struct proto *pro, char *buffer)
struct sock *sp;
char *pos=buffer;
int i;
+ int timer_active;
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");
@@ -71,21 +68,15 @@ get__netinfo(struct proto *pro, char *buffer)
destp = ntohs(destp);
srcp = ntohs(srcp);
- pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X",
+ timer_active = del_timer(&sp->timer);
+ if (!timer_active)
+ sp->timer.expires = 0;
+ pos+=sprintf(pos, "%2d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08X %02X\n",
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");
+ timer_active, sp->timer.expires, sp->retransmits);
+ if (timer_active)
+ add_timer(&sp->timer);
/* Is place in buffer too rare? then abort. */
if (pos > buffer+PAGE_SIZE-80) {
diff --git a/net/inet/protocol.c b/net/inet/protocol.c
index fa03491..360962c 100644
--- a/net/inet/protocol.c
+++ b/net/inet/protocol.c
@@ -24,7 +24,6 @@
#include <linux/socket.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
diff --git a/net/inet/raw.c b/net/inet/raw.c
index 67d2e39..0822c48 100644
--- a/net/inet/raw.c
+++ b/net/inet/raw.c
@@ -27,7 +27,6 @@
#include <linux/socket.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
diff --git a/net/inet/route.c b/net/inet/route.c
index c4b91b2..374befe 100644
--- a/net/inet/route.c
+++ b/net/inet/route.c
@@ -26,7 +26,6 @@
#include <linux/errno.h>
#include <linux/in.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
@@ -216,65 +215,12 @@ rt_new(struct rtentry *r)
*/
/* 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;
- }
- }
- }
- }
+ if (!(r->rt_flags & RTF_GATEWAY))
+ dev = dev_check(((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr);
+ else
+ if ((rt = rt_route(((struct sockaddr_in *) &r->rt_gateway)->sin_addr.
+ s_addr,NULL))) dev = rt->rt_dev;
+ else dev = NULL;
DPRINTF((DBG_RT, "RT: dev for %s gw ",
in_ntoa((*(struct sockaddr_in *)&r->rt_dst).sin_addr.s_addr)));
@@ -339,15 +285,23 @@ rt_route(unsigned long daddr, struct options *opt)
* 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)
+ if ((rt->rt_flags & RTF_HOST) && rt->rt_dst == daddr) {
+ DPRINTF((DBG_RT, "%s (%s)\n",
+ rt->rt_dev->name, in_ntoa(rt->rt_gateway)));
+ rt->rt_use++;
+ return(rt);
+ }
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)) {
+ if (!(rt->rt_flags & RTF_HOST) && 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)) {
+ if ((rt->rt_dev->flags & IFF_BROADCAST) &&
+ 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++;
diff --git a/net/inet/slhc.c b/net/inet/slhc.c
index 5e597f6..10931c3 100644
--- a/net/inet/slhc.c
+++ b/net/inet/slhc.c
@@ -48,7 +48,6 @@
#include <linux/in.h>
#include <linux/fcntl.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
diff --git a/net/inet/slip.c b/net/inet/slip.c
index 00ea959..e7d03d1 100644
--- a/net/inet/slip.c
+++ b/net/inet/slip.c
@@ -27,7 +27,6 @@
#include "inet.h"
#include "dev.h"
#include "eth.h"
-#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
@@ -41,12 +40,6 @@
#define SLIP_VERSION "0.7.5"
-#ifdef SL_COMPRESSED
-#define COMPRESSED_SLIP 1
-#else
-#define COMPRESSED_SLIP 0
-#endif
-
/* Define some IP layer stuff. Not all systems have it. */
#ifdef SL_DUMP
# define IP_VERSION 4 /* version# of our IP software */
@@ -104,6 +97,7 @@ ip_dump(unsigned char *ptr, int len)
#endif
}
+#if 0
void clh_dump(unsigned char *cp, int len)
{
if (len > 60)
@@ -115,6 +109,7 @@ void clh_dump(unsigned char *cp, int len)
}
printk("\n\n");
}
+#endif
/* Initialize a SLIP control block for use. */
static void
@@ -167,11 +162,11 @@ sl_alloc(void)
struct slip *sl;
int i;
+ save_flags (flags);
+ cli();
for (i = 0; i < SL_NRUNIT; i++) {
sl = &sl_ctrl[i];
if (sl->inuse == 0) {
- save_flags(flags);
- cli();
sl->inuse = 1;
sl->tty = NULL;
restore_flags(flags);
@@ -268,7 +263,7 @@ sl_bump(struct slip *sl)
int count;
count = sl->rcount;
- if (COMPRESSED_SLIP) {
+ if (1) {
if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
/* make sure we've reserved enough space for uncompress to use */
save_flags(flags);
@@ -285,15 +280,14 @@ sl_bump(struct slip *sl)
if (! done) /* not enough space available */
return;
- if ((count = slhc_uncompress((struct slcompress *)sl->slcomp,
- sl->rbuff, count)) <= 0 ) {
+ count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
+ if (count <= 0) {
sl->errors++;
return;
}
} else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
sl->rbuff[0] &= 0x4f;
- if ( slhc_remember((struct slcompress *)sl->slcomp, sl->rbuff,
- count ) <= 0 ) {
+ if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
sl->errors++;
return;
}
@@ -334,20 +328,21 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len)
unsigned char c;
int count;
- DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", p, len));
+ DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", icp, len));
DPRINTF((DBG_SLIP, ">> \"%s\" sent:\r\n", sl->dev->name));
ip_dump(icp, len);
p = icp;
- len = slhc_compress((struct slcompress *)sl->slcomp, p, len,
- sl->cbuff, &p, 1);
+#ifdef SL_COMPRESSED
+ len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+#endif
/*
* 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 = sl->xbuff;
*bp++ = END;
count = 1;
@@ -357,7 +352,7 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len)
*/
while(len-- > 0) {
c = *p++;
- switch((c & 0377)) {
+ switch(c) {
case END:
*bp++ = ESC;
*bp++ = ESC_END;
@@ -376,7 +371,7 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len)
*bp++ = END;
count++;
sl->spacket++;
- bp = (unsigned char *) sl->xbuff;
+ bp = 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));
@@ -566,36 +561,28 @@ slip_recv(struct tty_struct *tty)
/* 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) {
+ while (count--) {
c = *p++;
- switch((c & 0377)) {
- case ESC:
+ if (sl->escape) {
+ if (c == ESC_ESC)
+ sl_enqueue(sl, ESC);
+ else if (c == ESC_END)
+ sl_enqueue(sl, END);
+ else
+ printk ("SLIP: received wrong character\n");
+ sl->escape = 0;
+ } else {
+ if (c == 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:
+ else if (c == END) {
if (sl->rcount > 2) sl_bump(sl);
sl_dequeue(sl, sl->rcount);
sl->rcount = 0;
- sl->escape = 0;
- break;
- default:
- sl_enqueue(sl, c);
- sl->escape = 0;
+ } else sl_enqueue(sl, c);
}
}
} while(1);
diff --git a/net/inet/sock.c b/net/inet/sock.c
index a5f30b8..c7dd815 100644
--- a/net/inet/sock.c
+++ b/net/inet/sock.c
@@ -9,6 +9,7 @@
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Florian La Roche, <flla@stud.uni-sb.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -27,7 +28,6 @@
#include <linux/sockios.h>
#include <linux/net.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
@@ -50,8 +50,6 @@ 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;
@@ -318,7 +316,7 @@ destroy_sock(struct sock *sk)
remove_sock(sk);
/* Now we can no longer get new packets. */
- delete_timer((struct timer *)&sk->time_wait);
+ delete_timer(sk);
if (sk->send_tmp != NULL) kfree_skb(sk->send_tmp, FREE_WRITE);
@@ -478,9 +476,7 @@ destroy_sock(struct sock *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);
+ reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
}
DPRINTF((DBG_INET, "leaving destroy_sock\n"));
}
@@ -805,11 +801,9 @@ inet_create(struct socket *sock, int protocol)
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->timer.data = (unsigned long)sk;
+ sk->timer.function = &net_timer;
sk->back_log = NULL;
sk->blog = 0;
sock->data =(void *) sk;
@@ -1506,7 +1500,7 @@ sock_wfree(struct sock *sk, void *mem, unsigned long size)
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);
+ delete_timer(sk);
kfree_s((void *)sk, sizeof(*sk));
}
return;
@@ -1523,7 +1517,7 @@ sock_rfree(struct sock *sk, void *mem, unsigned long 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);
+ delete_timer(sk);
kfree_s((void *)sk, sizeof(*sk));
}
}
@@ -1614,9 +1608,7 @@ void release_sock(struct sock *sk)
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);
+ reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
}
}
@@ -1695,7 +1687,9 @@ static struct proto_ops inet_proto_ops = {
inet_fcntl,
};
+extern unsigned long seq_offset;
+/* Called by ddi.c on kernel startup. */
void inet_proto_init(struct ddi_proto *pro)
{
struct inet_protocol *p;
@@ -1703,7 +1697,7 @@ void inet_proto_init(struct ddi_proto *pro)
/* 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",
+ printk("%s: cannot register major device %d!\n",
pro->name, AF_INET_MAJOR);
return;
}
@@ -1732,5 +1726,4 @@ void inet_proto_init(struct ddi_proto *pro)
/* 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
index c51db07..4b89e25 100644
--- a/net/inet/sock.h
+++ b/net/inet/sock.h
@@ -10,6 +10,7 @@
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche <flla@stud.uni-sb.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,6 +20,7 @@
#ifndef _SOCK_H
#define _SOCK_H
+#include <linux/timer.h>
#define SOCK_ARRAY_SIZE 64
@@ -53,7 +55,6 @@ struct sock {
keepopen,
linger,
delay_acks,
- timeout,
destroy,
ack_timed,
no_check,
@@ -93,7 +94,10 @@ struct sock {
unsigned char max_ack_backlog;
unsigned char priority;
struct tcphdr dummy_th;
- struct timer time_wait;
+
+ /* This part is used for the timeout functions (timer.c). */
+ int timeout; /* What are we waiting for? */
+ struct timer_list timer;
};
struct proto {
@@ -187,4 +191,13 @@ extern void sock_rfree(struct sock *sk, void *mem,
extern unsigned long sock_rspace(struct sock *sk);
extern unsigned long sock_wspace(struct sock *sk);
+
+/* declarations from timer.c */
+extern struct sock *timer_base;
+
+void delete_timer (struct sock *);
+void reset_timer (struct sock *, int, unsigned long);
+void net_timer (unsigned long);
+
+
#endif /* _SOCK_H */
diff --git a/net/inet/tcp.c b/net/inet/tcp.c
index bbc9654..0dffa08 100644
--- a/net/inet/tcp.c
+++ b/net/inet/tcp.c
@@ -11,6 +11,7 @@
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Mark Evans, <evansmp@uhura.aston.ac.uk>
* Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche, <flla@stud.uni-sb.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -27,7 +28,6 @@
#include <linux/in.h>
#include <linux/fcntl.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
@@ -42,12 +42,10 @@
#include <asm/segment.h>
#include <linux/mm.h>
+#define SEQ_TICK 3
+unsigned long seq_offset;
-#define tmax(a,b)(before((a),(b)) ?(b) :(a))
-#define swap(a,b) {unsigned long c; c=a; a=b; b=c;}
-
-
-static int
+static __inline__ int
min(unsigned int a, unsigned int b)
{
if (a < b) return(a);
@@ -115,10 +113,9 @@ 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);
+ if (!sk->dead)
+ wake_up(sk->sleep);
+ reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
}
@@ -503,9 +500,7 @@ tcp_send_ack(unsigned long sequence, unsigned long ack,
/* 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);
+ reset_timer(sk, TIME_WRITE, 10);
}
if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
return;
@@ -551,8 +546,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_ack: build_header failed\n");
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;
+ delete_timer(sk);
}
}
t1->ack_seq = ntohl(ack);
@@ -894,9 +888,7 @@ tcp_read_wakeup(struct sock *sk)
buff = (struct sk_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);
+ reset_timer(sk, TIME_WRITE, 10);
return;
}
@@ -996,11 +988,11 @@ cleanup_rbuf(struct sock *sk)
tcp_read_wakeup(sk);
} else {
/* Force it to send an ack soon. */
- if (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);
- }
+ int was_active = del_timer(&sk->timer);
+ if (!was_active || TCP_ACK_TIME < sk->timer.expires) {
+ reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
+ } else
+ add_timer(&sk->timer);
}
}
}
@@ -1542,13 +1534,13 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
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->send_seq = jiffies * 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->timer.data = (unsigned long)newsk;
+ newsk->timer.function = &net_timer;
newsk->dummy_th.source = skb->h.th->dest;
newsk->dummy_th.dest = skb->h.th->source;
@@ -1644,9 +1636,7 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
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);
+ reset_timer(newsk, TIME_WRITE /* -1 ? FIXME ??? */, TCP_CONNECT_TIME);
skb->sk = newsk;
/* Charge the sock_buff to newsk. */
@@ -1717,9 +1707,7 @@ tcp_close(struct sock *sk, int timeout)
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);
+ reset_timer(sk, TIME_CLOSE, 4 * sk->rtt);
if (timeout) tcp_time_wait(sk);
release_sock(sk);
break;
@@ -1747,9 +1735,7 @@ tcp_close(struct sock *sk, int timeout)
/* 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);
+ reset_timer(sk, TIME_CLOSE, 100);
return;
}
buff->lock = 0;
@@ -1791,10 +1777,8 @@ tcp_close(struct sock *sk, int timeout)
if (sk->wfront == NULL) {
prot->queue_xmit(sk, dev, buff, 0);
} else {
- sk->time_wait.len = backoff(sk->backoff) *
- (2 * sk->mdev + sk->rtt);
- sk->timeout = TIME_WRITE;
- reset_timer((struct timer *)&sk->time_wait);
+ reset_timer(sk, TIME_WRITE,
+ backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
buff->next = NULL;
if (sk->wback == NULL) {
sk->wfront=buff;
@@ -1904,9 +1888,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
return(0);
}
if (sk->keepopen) {
- sk->time_wait.len = TCP_TIMEOUT_LEN;
- sk->timeout = TIME_KEEPOPEN;
- reset_timer((struct timer *)&sk->time_wait);
+ reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
}
return(1);
}
@@ -2106,19 +2088,14 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
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;
+ delete_timer(sk);
} else {
if (sk->state != (unsigned char) sk->keepopen) {
- sk->timeout = TIME_WRITE;
- sk->time_wait.len = backoff(sk->backoff) *
- (2 * sk->mdev + sk->rtt);
- reset_timer((struct timer *)&sk->time_wait);
+ reset_timer(sk, TIME_WRITE,
+ backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
}
if (sk->state == TCP_TIME_WAIT) {
- sk->time_wait.len = TCP_TIMEWAIT_LEN;
- reset_timer((struct timer *)&sk->time_wait);
- sk->timeout = TIME_CLOSE;
+ reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
}
}
}
@@ -2305,9 +2282,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
/* 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);
+ reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
}
}
}
@@ -2344,9 +2319,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
}
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);
+ reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
} 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);
@@ -2439,9 +2412,7 @@ tcp_fin(struct sock *sk, struct tcphdr *th,
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);
+ reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
return(0);
}
sk->ack_backlog++;
@@ -2529,7 +2500,7 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
}
sk->inuse = 1;
sk->daddr = sin.sin_addr.s_addr;
- sk->send_seq = timer_seq*SEQ_TICK-seq_offset;
+ sk->send_seq = jiffies * SEQ_TICK - seq_offset;
sk->rcv_ack_seq = sk->send_seq -1;
sk->err = 0;
sk->dummy_th.dest = sin.sin_port;
@@ -2588,9 +2559,8 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
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);
+ reset_timer(sk, TIME_WRITE /* -1 FIXME ??? */, TCP_CONNECT_TIME);
sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
release_sock(sk);
return(0);
diff --git a/net/inet/timer.c b/net/inet/timer.c
index e7bc3e2..00c524b 100644
--- a/net/inet/timer.c
+++ b/net/inet/timer.c
@@ -11,12 +11,14 @@
* 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>
+ * Florian La Roche, <flla@stud.uni-sb.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>
@@ -27,7 +29,6 @@
#include <asm/system.h>
#include <linux/interrupt.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
@@ -36,117 +37,35 @@
#include "sock.h"
#include "arp.h"
-
-struct timer *timer_base = NULL;
-unsigned long seq_offset;
-
-
void
-delete_timer(struct timer *t)
+delete_timer (struct sock *t)
{
- struct timer *tm;
+ unsigned long flags;
- DPRINTF((DBG_TMR, "delete_timer(t=%X)\n", t));
+ save_flags (flags);
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;
-}
+ t->timeout = 0;
+ del_timer (&t->timer);
+
+ restore_flags (flags);
+}
void
-reset_timer(struct timer *t)
+reset_timer (struct sock *t, int timeout, unsigned long len)
{
- 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);
- cli();
- 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();
+ delete_timer (t);
+
+ if (timeout != -1)
+ t->timeout = timeout;
+
+#if 1
+ /* FIXME: ??? */
+ if ((int) len < 0) /* prevent close to infinite timers. THEY _DO_ */
+ len = 3; /* happen (negative values ?) - don't ask me why ! -FB */
+#endif
+ t->timer.expires = len;
+ add_timer (&t->timer);
}
@@ -156,184 +75,135 @@ reset_timer(struct timer *t)
* sockets that need it.
*/
void
-net_timer(void)
+net_timer (unsigned long data)
{
- struct sock *sk;
- struct timer *tm;
+ struct sock *sk = (struct sock*)data;
+ int why = sk->timeout;
+ /* timeout is overwritten by 'delete_timer' and 'reset_timer' */
+
+ if (sk->inuse) {
+ sk->timer.expires = 10;
+ add_timer(&sk->timer);
+ return;
+ }
+ sk->inuse = 1;
- 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;
+ DPRINTF ((DBG_TMR, "net_timer: found sk=%X why = %d\n", sk, why));
+ if (sk->keepopen)
+ reset_timer (sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
- sk = tm->sk;
- if (sk->inuse) {
- break;
- }
- sk->inuse = 1;
- sti();
- why = sk->timeout;
+ /* 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);
+ }
- 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);
+ /* 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;
}
-
- /* 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);
+ destroy_sock (sk);
+ break;
+ case TIME_DESTROY:
+ /* We've waited for a while for all the memory associated 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);
+ /* 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;
+ reset_timer (sk, TIME_DESTROY, TCP_DONE_TIME);
+ 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) {
+ if (jiffies < (sk->send_head->when + backoff (sk->backoff)
+ * (2 * sk->mdev + sk->rtt))) {
+ reset_timer (sk, TIME_WRITE, (sk->send_head->when
+ + backoff (sk->backoff) * (2 * sk->mdev + sk->rtt)) - jiffies);
+ release_sock (sk);
+ break;
+ }
+ /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
+ sk->retransmits, sk->packets_out, sk->cong_window); */
+ DPRINTF ((DBG_TMR, "retransmitting.\n"));
+ sk->prot->retransmit (sk, 0);
+ if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+ || (sk->state != TCP_ESTABLISHED && 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->state != TCP_ESTABLISHED && 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;
+ reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+ } else {
+ sk->prot->close (sk, 1);
+ break;
+ }
+ }
}
-
- /* 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 (jiffies < (sk->send_head->when +
- backoff(sk->backoff) *
- (2 * sk->mdev + sk->rtt))) {
-/* printk("timer: not yet\n"); */
- sk->time_wait.len =
- (sk->send_head->when +
- backoff(sk->backoff) *
- (2 * sk->mdev + sk->rtt)) - jiffies;
- sk->timeout = TIME_WRITE;
- reset_timer(&sk->time_wait);
- release_sock(sk);
- break;
- }
-/* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
- sk->retransmits, sk->packets_out, sk->cong_window); */
- DPRINTF((DBG_TMR, "retransmitting.\n"));
- sk->prot->retransmit(sk, 0);
- if ((sk->state == TCP_ESTABLISHED &&
- sk->retransmits != 0 &&
- (sk->retransmits & 7) == 0) ||
- (sk->state != TCP_ESTABLISHED &&
- 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->state != TCP_ESTABLISHED &&
- 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->state == TCP_ESTABLISHED &&
- sk->retransmits != 0 &&
- (sk->retransmits & 7) == 0) ||
- (sk->state != TCP_ESTABLISHED &&
- 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->state != TCP_ESTABLISHED &&
- 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;
+ release_sock (sk);
+ break;
+ case TIME_KEEPOPEN:
+ /* Send something to keep the connection open. */
+ if (sk->prot->write_wakeup)
+ sk->prot->write_wakeup (sk);
+ sk->retransmits++;
+ if (sk->shutdown == SHUTDOWN_MASK) {
+ sk->prot->close (sk, 1);
+ sk->state = TCP_CLOSE;
}
- 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;
+ if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+ || (sk->state != TCP_ESTABLISHED && 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->state != TCP_ESTABLISHED && 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;
}
- sti();
}
+
diff --git a/net/inet/timer.h b/net/inet/timer.h
deleted file mode 100644
index 39f6bd7..0000000
--- a/net/inet/timer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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
index 1ea33bd..3f9e047 100644
--- a/net/inet/udp.c
+++ b/net/inet/udp.c
@@ -28,7 +28,6 @@
#include <linux/termios.h>
#include <linux/mm.h>
#include "inet.h"
-#include "timer.h"
#include "dev.h"
#include "ip.h"
#include "protocol.h"
diff --git a/net/inet/utils.c b/net/inet/utils.c
index ddbebe3..58a6cd7 100644
--- a/net/inet/utils.c
+++ b/net/inet/utils.c
@@ -30,12 +30,10 @@
#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"
diff --git a/net/inet/wd.c b/net/inet/wd.c
index c99af3c..bbb3e06 100644
--- a/net/inet/wd.c
+++ b/net/inet/wd.c
@@ -15,7 +15,7 @@
*/
static char *version =
- "wd.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n";
+ "wd.c:v0.99-12 8/12/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
@@ -27,14 +27,10 @@ static char *version =
#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 int wd_open(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);
@@ -84,128 +80,88 @@ int wdprobe1(int ioaddr, struct device *dev)
unsigned char *station_addr = dev->dev_addr;
int checksum = 0;
int ancient = 0; /* An old card without config registers. */
+ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
+ char *model_name;
-#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);
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xFF)
return 0;
- }
-
- ei_status.name = "WD8003";
- ei_status.word16 = 0;
+
+ printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
/* 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.
- */
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata seem to do software
+ configuration differently from others so we have to check for them.
+ This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+ */
if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
- ei_status.name = "PDI8023";
+ unsigned char reg5 = inb(ioaddr+5);
- /* 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;
+ case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
+ case 0x05: word16 = 0; model_name = "PDUC8023"; break;
+ case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+ /* Either 0x01 (dumb) or they've released a new version. */
+ default: word16 = 0; model_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! */
+ dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else { /* 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. */
+ /* Check for the old 8 bit board - it has register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ model_name = "WD8003-old";
+ word16 = 0;
+ } 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( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ model_name = "WD8013";
+ word16 = 1; /* We have a 16bit board here! */
+ } else {
+ model_name = "WD8003";
+ word16 = 0;
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
}
- 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);
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+ printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
#endif
+ }
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
- /* Allow an override. */
+ /* Allow a compile-time override. */
dev->mem_start = WD_SHMEM;
#else
if (dev->mem_start == 0) {
/* Sanity and old 8003 check */
int reg0 = inb(ioaddr);
- if (reg0 == 0xff) {
+ if (reg0 == 0xff || reg0 == 0) {
/* 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)
+ if (high_addr_bits == 0x1f || word16 == 0)
high_addr_bits = 0x01;
dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
}
@@ -215,26 +171,15 @@ int wdprobe1(int ioaddr, struct device *dev)
/* 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. */
+ if (reg1 == 0xff) /* Ack!! No way to read the IRQ! */
+ dev->irq = word16 ? 10 : 5;
+ else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
dev->irq = 9;
/* Snarf the interrupt now. There's no point in waiting since we cannot
@@ -246,33 +191,51 @@ int wdprobe1(int ioaddr, struct device *dev)
}
}
- 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);
+ /* OK, were are certain this is going to work. Setup the device. */
+ ethdev_init(dev);
-#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
+ ei_status.name = model_name;
+ ei_status.word16 = word16;
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+ ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
- /* 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 */
+ /* Don't map in the shared memory until the board is actually opened. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+
+ printk(" %s, IRQ %d, shared memory at %#x-%#x.\n",
+ model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+ if (ei_debug > 0)
+ printk(version);
ei_status.reset_8390 = &wd_reset_8390;
ei_status.block_input = &wd_block_input;
ei_status.block_output = &wd_block_output;
+ dev->open = &wd_open;
dev->stop = &wd_close_card;
NS8390_init(dev, 0);
return dev->base_addr;
}
+static int
+wd_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+ ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ if (ei_status.word16)
+ outb(ISA16 | ei_status.reg5, ioaddr+WD_CMDREG5);
+ outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+ return ei_open(dev);
+}
+
static void
wd_reset_8390(struct device *dev)
{
@@ -306,20 +269,7 @@ wd_reset_8390(struct device *dev)
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
+ return;
}
/* Block input and output are easy on shared memory ethercards, and trivial
@@ -328,16 +278,18 @@ wd_reset_8390(struct device *dev)
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));
+ void *xfer_start = (void *)(dev->mem_start + ring_offset
+ - (WD_START_PG<<8));
+
+ /* This mapout won't be necessary when wd_close_card is called. */
#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);
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ outb(ei_status.reg0, wd_cmdreg); /* 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;
@@ -353,10 +305,11 @@ wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
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 );
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
#endif
return ring_offset + count;
}
@@ -369,23 +322,24 @@ wd_block_output(struct device *dev, int count, const unsigned char *buf,
{
unsigned char *shmem
= (unsigned char *)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);
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ outb(ei_status.reg0, wd_cmdreg); /* 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 );
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
#endif
}
@@ -399,14 +353,16 @@ wd_close_card(struct device *dev)
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);
+ /* Change from 16-bit to 8-bit shared memory so reboot works. */
+ outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+ /* And disable the shared memory. */
+ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
return 0;
}
-
+
/*
* Local variables:
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
diff --git a/net/unix/sock.c b/net/unix/sock.c
index ddf14d6..7ea64fb 100644
--- a/net/unix/sock.c
+++ b/net/unix/sock.c
@@ -282,7 +282,7 @@ unix_proto_create(struct socket *sock, int protocol)
printk("UNIX: create: can't allocate buffer\n");
return(-ENOMEM);
}
- if (!(upd->buf =(char *)get_free_page(GFP_USER))) {
+ if (!(upd->buf = (char*) get_free_page(GFP_USER))) {
printk("UNIX: create: can't get page!\n");
unix_data_deref(upd);
return(-ENOMEM);
@@ -369,7 +369,7 @@ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
old_fs = get_fs();
set_fs(get_ds());
- i = do_mknod(fname, S_IFSOCK | 0777, 0);
+ i = do_mknod(fname, S_IFSOCK | S_IRWXUGO, 0);
if (i == 0) i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
set_fs(old_fs);
if (i < 0) {
diff --git a/zBoot/misc.c b/zBoot/misc.c
index 0ba96b6..3b6835b 100644
--- a/zBoot/misc.c
+++ b/zBoot/misc.c
@@ -4,7 +4,8 @@
* This is a collection of several routines from gzip-1.0.3
* adapted for Linux.
*
- * malloc and puts by Hannu Savolainen 1993
+ * malloc by Hannu Savolainen 1993
+ * puts by Nick Holloway 1993
*/
#include "gzip.h"
@@ -75,7 +76,6 @@ void makecrc(void);
local int get_method(int);
char *vidmem = (char *)0xb8000;
-int vidp = 0;
int lines, cols;
void *malloc(int size)
@@ -101,20 +101,44 @@ void free(void *where)
{ /* Don't care */
}
+static void scroll()
+{
+ int i;
+
+ memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
+ for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
+ vidmem[i] = ' ';
+}
+
static void puts(char *s)
{
- int i,n;
- for (n = 0; s [n] != '\0'; n++);
- if (!n) n = 10;
-
- for (i=0;i<n;i++)
- if (s[i] == '\n')
- {
- vidp = ((vidp / (cols*2)) + 1) * cols * 2;
- } else {
- vidmem[vidp] = s[i];
- vidp = vidp + 2;
+ int x,y;
+ char c;
+
+ x = SCREEN_INFO.orig_x;
+ y = SCREEN_INFO.orig_y;
+
+ while ( ( c = *s++ ) != '\0' ) {
+ if ( c == '\n' ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ } else {
+ vidmem [ ( x + cols * y ) * 2 ] = c;
+ if ( ++x >= cols ) {
+ x = 0;
+ if ( ++y >= lines ) {
+ scroll();
+ y--;
+ }
+ }
+ }
}
+
+ SCREEN_INFO.orig_x = x;
+ SCREEN_INFO.orig_y = y;
}
__ptr_t memset(__ptr_t s, int c, size_t n)
@@ -279,8 +303,6 @@ void decompress_kernel()
vidmem = (char *) 0xb0000;
else
vidmem = (char *) 0xb8000;
- vidp = 0;
- vidmem[0] = '0';
lines = SCREEN_INFO.orig_video_lines;
cols = SCREEN_INFO.orig_video_cols;
@@ -304,7 +326,7 @@ void decompress_kernel()
work(0, 0);
- puts("done.\n\n");
+ puts("done.\n");
puts("Now booting the kernel\n");
}