aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Benedict Torvalds <torvalds@klaava.Helsinki.FI>1992-05-22 21:01:59 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:04 -0400
commit8404560378fc29692399a65c34d39d80699f7288 (patch)
treee3ef2741645be87422fb3cfcef160aa137c02dac
parent30cd071cd0121a1f0a965ce3a533131d55e9a1b4 (diff)
downloadarchive-8404560378fc29692399a65c34d39d80699f7288.tar.gz
0.96a availablev0.96a
Oh, well, no use in delaying it any more, so I sent out my latest release to nic.funet.fi, tsx-11.mit.edu and banjo.concert.net. They should show up in the next couple of days (they are already visible on banjo: /pub/Linux/Linus). I hope all the bugs got fixed, but I did something potentially stupid: I had expected that lankaster wouldn't get his hd-speedup patches ready for 0.96a, and I was resigned to the same hd-performance as with all older releases. But when I saw them on the newsgroup today I thought I'd try them out just in case, as I could always use my backup-version if they backfired... The point here is that the patch ended up in 0.96a after some minor edits by me (one benign bug and some editing due to changed files). So now hd-performance is much better on most operations. I just hope it doesn't result in yet another release just to fix new bugs (but I doubt it: the patches were really minor and clean - no ugly hacks needed I'm happy to say). Branko already posted benchmarks, and I can only confirm that it's indeed snappier, especially on writes. syncing is no longer a pain even after heavy writing. Other than the hd-performance, there are no new features I haven't already mentioned in other posts. Bug-fixes, rewrites in C, better debugging. I haven't made the cdiffs yet, so right now the new release is only available as complete source and as a binary, but I'll try to get patches done tonight. Possibly tomorrow. The patches will be against the original 0.96, and shouldn't be too big. Linus PS. No need for more 16550 info: even if somebody doesn't implement it for the next release, I think I can get it going. Doesn't seem too hard. I'll also start to look into core-files. Eventually. Promise. PPS. Ja 0.96a on taas saatavilla klaavasta /usr/tmp/linux'ssa yliopistolla kirjoilla oleville. ----- From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) Subject: 0.96a out today/tomorrow Date: 21 May 1992 12:19:55 GMT The subject say it all: I haven't heard any bad results (except somewhat worse performance on the serial lines) from the testimage testers, so I'll make 0.96a available tonight (Finnish time: EST) or tomorrow. tsx-11, nic and banjo will be the sites I'll upload it to. 0.96a doesn't have any major new features: I just tried to correct known bugs and get a stable version out the door. I hope 0.96 won't need any more interim releases, but we'll see. 0.96a has gone back to non-interruptible hd-interrupts, and as long as I'm not sure about the reason for the need of this, it's going to stay that way. I hope this new version will run on all drives that have been supported in the past, and the only known remaining problem is the Kalok drives which have spurious interrupts. That's definitely a hardware problem, and Branko Lankaster has patches that fake away these.. 0.96a should also correct most of the serial line problems, and the keyboard driver is now in C - I expect somebody will change it to allow run-time keyboard changing. Additionally, there are various minor fixes in various places: - the kill fix by tytso that allows any process to wake up a stopped process as long as it's in the same session. - chown fix by card and me. Users can chgrp files they own to any group they belong to etc - _POSIX_CHOWN_RESTRICTED should now be implemented correctly. Also, chown() on a symlink now actually chown's the symlink and not the file it points to (but GNU fileutils don't seem to want to chown symlinks anyway - or maybe I have an older version) - SIGSEGV fix by lankaster (?) which allows for better debugging after protection faults. Most such errors are now trappable in gdb (although some circumstances can still result in the process exiting at once: out-of-memory errors etc) I have also changed the memory manager to check for illegal memory references (ie using the area between brk and stack), so debugging bad pointer references should be much easier. This fix will hopefully also trap the uncompress problem with bad files, but I haven't tried it out. Programs can still call brk() with any value, so it doesn't mean that you can't use up all available memory, but at least this should fix /bad/ memory references. My limited tests with gdb seems to indicate it all works nicely. Core files are still not generated (I haven't got the slightest idea what format they should have), so debugging isn't perfect yet, but it should certainly be much easier. Linus ----- [ Committer's note: this commit also folds the lost 0.96 release. ] From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) Subject: 0.96 uploaded Date: 12 May 1992 16:51:12 GMT Well, the title says it all: I've sent off 0.96 to banjo.concert.net, where it can be found in pub/Linux/Linus along with a new bootimage and a program for formatting floppies under linux (which works only under 0.96). I've also sent it to tsx-11.mit.edu and I'll send it to nic as soon as the lines clear up. General warning about 0.96: - the scsi code is in the kernel, but I haven't personally tested it, so who knows... The SCSI code also results in a 4-5 second pause at bootup with the current bootimage, while it searches for an adapter: if you find this disturbing, you have to recompile the kernel with the appropriate changes to config.h. - The harddisk timings have changed: the testimage got a generally good review, but it hasn't been tested very much. The changes seem to help at least some "HD times out" problems, but there might be new bugs.. - The serial code was totally rewritten this weekend, and I haven't tested it out under any heavier load. I found one bug as late as today, and there might be others lurking around. - There have been generally pretty heavy rewrites: it's binary compatible with the old kernels, but the changes might not all be correct. Oh, well. That said, I hope 0.96 will be an improvement on earlier versions, and most of the old bugs corrected. If the new version still has some problem - please mail me with a new bugreport. Otherwise I'll just assume the problem went away: I'm afraid don't have time to go through old mail searching for any bugs that might still be in there. Partial list of features: - automatic floppy detection. Please add the following devices: mknod /dev/fd0 b 2 0 mknod /dev/fd1 b 2 1 which act as A and B floppies respectively, finding out automatically what kind of disk there is. The floppy driver now also contains a timeout, so an empty diskdrive no longer results in a floppy driver hang. - serial lines now support dropping DTR on closing, and sending SIGHUP to the process group that is logged in on a serial line. It's also a lot easier to change the interrupts etc of the lines. - unix sockets supported for X, as well as mmap() on /dev/mem etc. - pty's corrected. Hopefully no more hangs under X due to pty trouble. - better IO-performance when there are computationally intensive jobs in the background or on another VC. Partly due to new scheduler mechanism, partly due to read-ahead on normal files. - cleaned up vfs layer. - no more mismatched children - minor corrections all over the place. The new release is in fact different enough that there is no use trying to make context diffs: files have disappeared, others are new, others have simply changed a lot. Even compared to pre-0.96 there has been quite a lot of changes. Linus ----- From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) Subject: 0.96 out next week Date: 4 May 1992 07:38:03 GMT Ok, the subject says most of it: I'll send out 0.96 sometimes next week (ie 92.05.11-17), and this is just an announcement to confirm that. 0.96 has a lot of changes (even relative to pre-0.96), and it's entirely possible that making it available as cdiffs isn't feasible. It contains a lot of new files, as well as some re-organizations in the old ones. Main new things are: - The SCSI distribution is now in the standard package. I (obviously) haven't been able to test my patchings, so there might be problems in this first release. I had to do some changes "blind" to the cdiffs, but most of them were pretty trivial. - X11r5 as ported by obz is supported. It's still in beta-testing (join the X11-channel on the original mailing-list), but as I'm writing this from an xterm under linux, it works pretty well. Changes to pre-0.96 are just the socket-code by obz, and some small tweaking by me. - Hopefully better interrupt latency - I've changed select() not to use cli-sti, and most IRQ's to enable interrupts, and instead disable just their own interrupt-line. The interrupt latency has been noticeable at higher serial speeds, and I hope 0.96 will be better in this respect. Again, I only have 2400bps, so I've never seen the problems, and cannot guarantee the new version will help. (btw, I hope the problems with select are gone now) - Reorganisation of the vfs routines and minix filesystem driver. These shouldn't bother anyone but people that have implemented their own filesystems (I know of just 2 to date), but I hope the current vfs-interface will prove to be relatively stable. The new vfs interface has made some things much cleaner, and the promised cleanup of special devices has happened. - ps/uptime patches + added readahead, so having computationally intensive background processes isn't as noticeable any more when doing IO. Additionally, there /might/ be a new floppy-driver that supports formatting and autodetecting floppies, but I haven't had time to check into it yet. There are probably any number of minor changes: I've lost track. People have sent me some diffs, and some of them went in, depending on how energetic I was that day. I've tried to correct all the bugs I've gotten reports on, and hopefully 0.96 will work with just about everything (gdb etc). Things I wanted, but didn't have time for: - The config patches aren't there. Sorry everybody. That means still no wd8003 driver etc. - Any number of minor patches (quota, auto-SVGA etc) Generally, 0.96 is cleaning some things up, but on the other hand the new features can have their share of problems. We'll see. Anyway, most things seem to work, and I hope there won't be the same type of problems as with the first 0.95 release. Linus
-rw-r--r--Makefile79
-rw-r--r--fs/Makefile40
-rw-r--r--fs/buffer.c27
-rw-r--r--fs/exec.c35
-rw-r--r--fs/fcntl.c12
-rw-r--r--fs/inode.c28
-rw-r--r--fs/minix/Makefile40
-rw-r--r--fs/minix/bitmap.c2
-rw-r--r--fs/minix/blkdev.c63
-rw-r--r--fs/minix/chrdev.c63
-rw-r--r--fs/minix/dir.c91
-rw-r--r--fs/minix/file.c221
-rw-r--r--fs/minix/file_dev.c169
-rw-r--r--fs/minix/inode.c21
-rw-r--r--fs/minix/minix_op.c62
-rw-r--r--fs/minix/namei.c94
-rw-r--r--fs/minix/symlink.c100
-rw-r--r--fs/minix/truncate.c47
-rw-r--r--fs/namei.c4
-rw-r--r--fs/open.c89
-rw-r--r--fs/pipe.c22
-rw-r--r--fs/read_write.c1
-rw-r--r--fs/select.c24
-rw-r--r--include/asm/io.h30
-rw-r--r--include/linux/config.dist.h27
-rw-r--r--include/linux/config.h50
-rw-r--r--include/linux/config.site.h9
-rw-r--r--include/linux/config_rel.h2
-rw-r--r--include/linux/config_ver.h2
-rw-r--r--include/linux/fd.h32
-rw-r--r--include/linux/fdreg.h1
-rw-r--r--include/linux/fs.h18
-rw-r--r--include/linux/kernel.h2
-rw-r--r--include/linux/lp.h6
-rw-r--r--include/linux/minix_fs.h11
-rw-r--r--include/linux/sched.h1
-rw-r--r--include/linux/sys.h5
-rw-r--r--include/linux/timer.h3
-rw-r--r--include/linux/tty.h71
-rw-r--r--include/linux/unistd.h206
-rw-r--r--include/sys/resource.h7
-rw-r--r--include/sys/socket.h39
-rw-r--r--include/sys/un.h9
-rw-r--r--include/termios.h1
-rw-r--r--include/unistd.h195
-rw-r--r--init/main.c54
-rw-r--r--kernel/Makefile14
-rw-r--r--kernel/asm.s35
-rw-r--r--kernel/blk_drv/Makefile30
-rw-r--r--kernel/blk_drv/blk.h62
-rw-r--r--kernel/blk_drv/floppy.c583
-rw-r--r--kernel/blk_drv/hd.c134
-rw-r--r--kernel/blk_drv/ll_rw_blk.c26
-rw-r--r--kernel/blk_drv/ramdisk.c5
-rw-r--r--kernel/blk_drv/scsi/Makefile149
-rw-r--r--kernel/blk_drv/scsi/aha1542.c481
-rw-r--r--kernel/blk_drv/scsi/aha1542.h124
-rw-r--r--kernel/blk_drv/scsi/config.in29
-rw-r--r--kernel/blk_drv/scsi/hosts.c142
-rw-r--r--kernel/blk_drv/scsi/hosts.h179
-rw-r--r--kernel/blk_drv/scsi/scsi.c1016
-rw-r--r--kernel/blk_drv/scsi/scsi.h257
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.c153
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.h21
-rw-r--r--kernel/blk_drv/scsi/sd.c380
-rw-r--r--kernel/blk_drv/scsi/sd.h57
-rw-r--r--kernel/blk_drv/scsi/sd_ioctl.c20
-rw-r--r--kernel/blk_drv/scsi/seagate.c607
-rw-r--r--kernel/blk_drv/scsi/seagate.h123
-rw-r--r--kernel/blk_drv/scsi/st.c36
-rw-r--r--kernel/blk_drv/scsi/st.h26
-rw-r--r--kernel/blk_drv/scsi/st_ioctl.c19
-rw-r--r--kernel/blk_drv/scsi/ultrastor.c329
-rw-r--r--kernel/blk_drv/scsi/ultrastor.h84
-rw-r--r--kernel/chr_drv/Makefile50
-rw-r--r--kernel/chr_drv/console.c77
-rw-r--r--kernel/chr_drv/keyboard.S805
-rw-r--r--kernel/chr_drv/keyboard.c1107
-rw-r--r--kernel/chr_drv/lp.c34
-rw-r--r--kernel/chr_drv/mem.c12
-rw-r--r--kernel/chr_drv/pty.c4
-rw-r--r--kernel/chr_drv/rs_io.s164
-rw-r--r--kernel/chr_drv/serial.c157
-rw-r--r--kernel/chr_drv/tty_io.c226
-rw-r--r--kernel/chr_drv/tty_ioctl.c74
-rw-r--r--kernel/chr_drv/vt.c69
-rw-r--r--kernel/chr_drv/vt_kern.h13
-rw-r--r--kernel/exit.c40
-rw-r--r--kernel/fork.c3
-rw-r--r--kernel/printk.c85
-rw-r--r--kernel/ptrace.c6
-rw-r--r--kernel/sched.c60
-rw-r--r--kernel/signal.c4
-rw-r--r--kernel/sys.c66
-rw-r--r--kernel/sys_call.S59
-rw-r--r--kernel/traps.c10
-rw-r--r--lib/Makefile18
-rw-r--r--lib/close.c1
-rw-r--r--lib/dup.c1
-rw-r--r--lib/execve.c1
-rw-r--r--lib/setsid.c1
-rw-r--r--lib/wait.c1
-rw-r--r--lib/write.c1
-rw-r--r--mm/memory.c113
-rw-r--r--mm/swap.c19
-rw-r--r--net/Makefile51
-rw-r--r--net/kern_sock.h67
-rw-r--r--net/socket.c761
-rw-r--r--net/socketcall.h13
-rw-r--r--net/unix.c606
110 files changed, 9771 insertions, 2244 deletions
diff --git a/Makefile b/Makefile
index dc83ace..968f18b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,45 @@
#
-# comment this line if you don't want the emulation-code
+# ROOT_DEV specifies the default root-device when making the image.
+# This can be either FLOPPY, /dev/xxxx or empty, in which case the
+# default of FLOPPY is used by 'build'.
#
-MATH_EMULATION = -DKERNEL_MATH_EMULATION
+ROOT_DEV = /dev/hdb1
#
# uncomment the correct keyboard:
#
+# The value of KBDFLAGS should be or'ed together from the following
+# bits, depending on which features you want enabled.
+# 0x80 - Off: the Alt key will set bit 7 if pressed together with
+# another key.
+# On: the Alt key will NOT set the high bit; an escape
+# character is prepended instead.
+# The least significant bits control if the following keys are "dead".
+# The key is dead by default if the bit is on.
+# 0x01 - backquote (`)
+# 0x02 - accent acute
+# 0x04 - circumflex (^)
+# 0x08 - tilde (~)
+# 0x10 - dieresis (umlaut)
+
+KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
+# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F
+# KEYBOARD = -DKBD_US -DKBDFLAGS=0
+# KEYBOARD = -DKBD_GR -DKBDFLAGS=0
+# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F
+# KEYBOARD = -DKBD_FR -DKBDFLAGS=0
+# KEYBOARD = -DKBD_FR_LATIN1 -DKBDFLAGS=0x9F
+# KEYBOARD = -DKBD_UK -DKBDFLAGS=0
+# KEYBOARD = -DKBD_DK -DKBDFLAGS=0
+# KEYBOARD = -DKBD_DK_LATIN1 -DKBDFLAGS=0x9F
+# KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0
-KEYBOARD = -DKBD_FINNISH
-# KEYBOARD = -DKBD_US
-# KEYBOARD = -DKBD_GR
-# KEYBOARD = -DKBD_FR
-# KEYBOARD = -DKBD_UK
-# KEYBOARD = -DKBD_DK
+#
+# comment this line if you don't want the emulation-code
+#
+
+MATH_EMULATION = -DKERNEL_MATH_EMULATION
#
# uncomment this line if you are using gcc-1.40
@@ -27,14 +53,6 @@ KEYBOARD = -DKBD_FINNISH
CFLAGS =-Wall -O6 -fomit-frame-pointer $(GCC_OPT)
#
-# ROOT_DEV specifies the default root-device when making the image.
-# This can be either FLOPPY, /dev/xxxx or empty, in which case the
-# default of FLOPPY is used by 'build'.
-#
-
-ROOT_DEV = /dev/hdb1
-
-#
# if you want the ram-disk device, define this to be the
# size in blocks.
#
@@ -52,9 +70,10 @@ CC =gcc $(RAMDISK)
MAKE =make CFLAGS="$(CFLAGS)"
CPP =cpp -nostdinc -Iinclude
-ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o
+ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o
FILESYSTEMS =fs/minix/minix.o
-DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
+DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \
+ kernel/blk_drv/scsi/scsi.a
MATH =kernel/math/math.a
LIBS =lib/lib.a
@@ -71,7 +90,7 @@ all: Version Image
Version:
@./makever.sh
- @echo \#define UTS_RELEASE \"0.95c-`cat .version`\" > include/linux/config_rel.h
+ @echo \#define UTS_RELEASE \"0.96a-`cat .version`\" > include/linux/config_rel.h
@echo \#define UTS_VERSION \"`date +%D`\" > include/linux/config_ver.h
touch include/linux/config.h
@@ -107,6 +126,9 @@ kernel/math/math.a: dummy
kernel/blk_drv/blk_drv.a: dummy
(cd kernel/blk_drv; $(MAKE))
+kernel/blk_drv/scsi/scsi.a: dummy
+ (cd kernel/blk_drv/scsi; $(MAKE))
+
kernel/chr_drv/chr_drv.a: dummy
(cd kernel/chr_drv; $(MAKE) KEYBOARD="$(KEYBOARD)")
@@ -119,6 +141,9 @@ mm/mm.o: dummy
fs/fs.o: dummy
(cd fs; $(MAKE))
+net/net.o: dummy
+ (cd net; $(MAKE))
+
fs/minix/minix.o: dummy
(cd fs/minix; $(MAKE))
@@ -147,6 +172,7 @@ clean:
(cd fs;make clean)
(cd kernel;make clean)
(cd lib;make clean)
+ (cd net;make clean)
backup: clean
(cd .. ; tar cf - linux | compress - > backup.Z)
@@ -159,15 +185,16 @@ dep:
(cd fs; make dep)
(cd kernel; make dep)
(cd mm; make dep)
+ (cd net;make dep)
(cd lib; make dep)
dummy:
### Dependencies:
-init/main.o : init/main.c include/unistd.h include/sys/stat.h include/sys/types.h \
- include/sys/time.h include/time.h include/sys/times.h include/sys/utsname.h \
- include/sys/param.h include/sys/resource.h include/utime.h include/linux/sched.h \
- include/linux/head.h include/linux/fs.h include/sys/dirent.h include/limits.h \
- include/linux/mm.h include/linux/kernel.h include/signal.h include/linux/tty.h \
- include/termios.h include/linux/string.h include/asm/system.h include/asm/io.h \
- include/stddef.h include/stdarg.h include/fcntl.h
+init/main.o : init/main.c include/stddef.h include/stdarg.h include/fcntl.h include/sys/types.h \
+ include/time.h include/asm/system.h include/asm/io.h include/linux/config.h \
+ include/linux/config_rel.h include/linux/config_ver.h include/linux/config.dist.h \
+ include/linux/sched.h include/linux/head.h include/linux/fs.h include/sys/dirent.h \
+ include/limits.h include/linux/mm.h include/linux/kernel.h include/signal.h \
+ include/sys/param.h include/sys/time.h include/sys/resource.h include/linux/tty.h \
+ include/termios.h include/linux/unistd.h
diff --git a/fs/Makefile b/fs/Makefile
index 602e09f..87d2b5b 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -47,21 +47,21 @@ block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h ../include
../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/segment.h \
../include/asm/system.h
buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h ../include/linux/config_rel.h \
- ../include/linux/config_ver.h ../include/linux/sched.h ../include/linux/head.h \
- ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
- ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
- ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/system.h \
- ../include/asm/io.h
+ ../include/linux/config_ver.h ../include/linux/config.dist.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \
+ ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
+ ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
+ ../include/asm/system.h ../include/asm/io.h
exec.o : exec.c ../include/signal.h ../include/sys/types.h ../include/errno.h \
../include/linux/string.h ../include/sys/stat.h ../include/a.out.h ../include/linux/fs.h \
../include/sys/dirent.h ../include/limits.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h
-fcntl.o : fcntl.c ../include/linux/string.h ../include/errno.h ../include/linux/sched.h \
- ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \
- ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
- ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
- ../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h
+fcntl.o : fcntl.c ../include/errno.h ../include/fcntl.h ../include/sys/types.h \
+ ../include/sys/stat.h ../include/asm/segment.h ../include/linux/string.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h \
+ ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
+ ../include/sys/time.h ../include/time.h ../include/sys/resource.h
file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \
../include/limits.h
inode.o : inode.c ../include/linux/string.h ../include/sys/stat.h ../include/sys/types.h \
@@ -96,19 +96,19 @@ read_write.o : read_write.c ../include/errno.h ../include/sys/types.h ../include
../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/linux/minix_fs.h ../include/asm/segment.h
select.o : select.c ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \
- ../include/limits.h ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
- ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \
- ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
- ../include/linux/string.h ../include/asm/segment.h ../include/asm/system.h ../include/sys/stat.h \
- ../include/const.h ../include/errno.h
+ ../include/limits.h ../include/linux/kernel.h ../include/linux/tty.h ../include/asm/system.h \
+ ../include/termios.h ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \
+ ../include/signal.h ../include/sys/param.h ../include/sys/time.h ../include/time.h \
+ ../include/sys/resource.h ../include/linux/string.h ../include/asm/segment.h \
+ ../include/sys/stat.h ../include/const.h ../include/errno.h
stat.o : stat.c ../include/errno.h ../include/sys/stat.h ../include/sys/types.h \
../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/asm/segment.h
super.o : super.c ../include/linux/config.h ../include/linux/config_rel.h ../include/linux/config_ver.h \
- ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
- ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \
- ../include/signal.h ../include/sys/param.h ../include/sys/time.h ../include/time.h \
- ../include/sys/resource.h ../include/linux/minix_fs.h ../include/asm/system.h \
- ../include/asm/segment.h ../include/errno.h ../include/sys/stat.h
+ ../include/linux/config.dist.h ../include/linux/sched.h ../include/linux/head.h \
+ ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
+ ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
+ ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/linux/minix_fs.h \
+ ../include/asm/system.h ../include/asm/segment.h ../include/errno.h ../include/sys/stat.h
diff --git a/fs/buffer.c b/fs/buffer.c
index 4b80848..ef54e86 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -47,18 +47,12 @@ static void sync_buffers(int dev)
struct buffer_head * bh;
bh = free_list;
- for (i=0 ; i<NR_BUFFERS ; i++,bh = bh->b_next_free) {
-#if 0
- if (dev && (bh->b_dev != dev))
+ for (i = NR_BUFFERS*2 ; i-- > 0 ; bh = bh->b_next_free) {
+ if (bh->b_lock)
continue;
-#endif
- wait_on_buffer(bh);
-#if 0
- if (dev && (bh->b_dev != dev))
+ if (!bh->b_dirt)
continue;
-#endif
- if (bh->b_dirt)
- ll_rw_block(WRITE,bh);
+ ll_rw_block(WRITE,bh);
}
}
@@ -258,9 +252,7 @@ repeat:
if (bh = get_hash_table(dev,block))
return bh;
buffers = NR_BUFFERS;
- tmp = free_list;
- do {
- tmp = tmp->b_next_free;
+ for (tmp = free_list ; buffers-- > 0 ; tmp = tmp->b_next_free) {
if (tmp->b_count)
continue;
if (!bh || BADNESS(tmp)<BADNESS(bh)) {
@@ -268,10 +260,12 @@ repeat:
if (!BADNESS(tmp))
break;
}
+#if 0
if (tmp->b_dirt)
ll_rw_block(WRITEA,tmp);
+#endif
+ }
/* and repeat until we find something good */
- } while (buffers--);
if (!bh) {
sleep_on(&buffer_wait);
goto repeat;
@@ -280,7 +274,7 @@ repeat:
if (bh->b_count)
goto repeat;
while (bh->b_dirt) {
- sync_dev(bh->b_dev);
+ sync_buffers(bh->b_dev);
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
@@ -384,7 +378,7 @@ struct buffer_head * breada(int dev,int first, ...)
tmp=getblk(dev,first);
if (tmp) {
if (!tmp->b_uptodate)
- ll_rw_block(READA,bh);
+ ll_rw_block(READA,tmp);
tmp->b_count--;
}
}
@@ -420,6 +414,7 @@ void buffer_init(long buffer_end)
h->b_next = NULL;
h->b_prev = NULL;
h->b_data = (char *) b;
+ h->b_reqnext = NULL;
h->b_prev_free = h-1;
h->b_next_free = h+1;
h++;
diff --git a/fs/exec.c b/fs/exec.c
index cb7b2ba..48d727f 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -62,7 +62,7 @@ int sys_uselib(const char * library)
inode = NULL;
if (!inode)
return -ENOENT;
- if (!S_ISREG(inode->i_mode) || !permission(inode,MAY_READ|MAY_EXEC)) {
+ if (!S_ISREG(inode->i_mode) || !permission(inode,MAY_READ)) {
iput(inode);
return -EACCES;
}
@@ -229,6 +229,32 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
return data_limit;
}
+static void read_omagic(struct inode *inode, int bytes)
+{
+ struct buffer_head *bh;
+ int n, blkno, blk = 0;
+ char *dest = (char *) 0;
+
+ while (bytes > 0) {
+ if (!(blkno = bmap(inode, blk)))
+ sys_exit(-1);
+ if (!(bh = bread(inode->i_dev, blkno)))
+ sys_exit(-1);
+ n = (blk ? BLOCK_SIZE : BLOCK_SIZE - sizeof(struct exec));
+ if (bytes < n)
+ n = bytes;
+
+ memcpy_tofs(dest, (blk ? bh->b_data :
+ bh->b_data + sizeof(struct exec)), n);
+ brelse(bh);
+ ++blk;
+ dest += n;
+ bytes -= n;
+ }
+ iput(inode);
+ current->executable = NULL;
+}
+
/*
* 'do_execve()' executes a new program.
*
@@ -359,13 +385,14 @@ restart_interp:
goto restart_interp;
}
brelse(bh);
- if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) ||
+ ex.a_trsize || ex.a_drsize ||
ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
retval = -ENOEXEC;
goto exec_error2;
}
- if (N_TXTOFF(ex) != BLOCK_SIZE) {
+ if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) {
printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
retval = -ENOEXEC;
goto exec_error2;
@@ -422,6 +449,8 @@ restart_interp:
current->rss = (LIBRARY_OFFSET - p + PAGE_SIZE-1) / PAGE_SIZE;
current->suid = current->euid = e_uid;
current->sgid = current->egid = e_gid;
+ if (N_MAGIC(ex) == OMAGIC)
+ read_omagic(inode, ex.a_text+ex.a_data);
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
if (current->flags & PF_PTRACED)
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 9658483..952bf8c 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -4,15 +4,17 @@
* (C) 1991 Linus Torvalds
*/
-#include <linux/string.h>
#include <errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <asm/segment.h>
-
#include <fcntl.h>
+
#include <sys/stat.h>
+#include <asm/segment.h>
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
extern int sys_close(int fd);
static int dupfd(unsigned int fd, unsigned int arg)
diff --git a/fs/inode.c b/fs/inode.c
index 385f2f9..1f144c6 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -39,13 +39,13 @@ static inline void unlock_inode(struct inode * inode)
static void write_inode(struct inode * inode)
{
- lock_inode(inode);
- if (!inode->i_dirt || !inode->i_dev) {
- unlock_inode(inode);
+ if (!inode->i_dirt)
return;
- }
- if (inode->i_op && inode->i_op->write_inode)
- inode->i_op->write_inode(inode);
+ inode->i_dirt = 0;
+ lock_inode(inode);
+ if (inode->i_dev && inode->i_sb &&
+ inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
+ inode->i_sb->s_op->write_inode(inode);
unlock_inode(inode);
}
@@ -110,8 +110,12 @@ void iput(struct inode * inode)
if (!inode)
return;
wait_on_inode(inode);
- if (!inode->i_count)
- panic("iput: trying to free free inode");
+ if (!inode->i_count) {
+ printk("iput: trying to free free inode\n");
+ printk("device %04x, inode %d, mode=%07o\n",inode->i_rdev,
+ inode->i_ino,inode->i_mode);
+ return;
+ }
if (inode->i_pipe) {
wake_up(&inode->i_wait);
wake_up(&inode->i_wait2);
@@ -127,18 +131,14 @@ void iput(struct inode * inode)
inode->i_count--;
return;
}
- if (S_ISBLK(inode->i_mode)) {
- sync_dev(inode->i_rdev);
- wait_on_inode(inode);
- }
repeat:
if (inode->i_count>1) {
inode->i_count--;
return;
}
if (!inode->i_nlink) {
- if (inode->i_op && inode->i_op->put_inode)
- inode->i_op->put_inode(inode);
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode)
+ inode->i_sb->s_op->put_inode(inode);
return;
}
if (inode->i_dirt) {
diff --git a/fs/minix/Makefile b/fs/minix/Makefile
index 81aebc7..fe1b2a5 100644
--- a/fs/minix/Makefile
+++ b/fs/minix/Makefile
@@ -22,7 +22,8 @@ CPP =cpp -nostdinc -I../../include
.s.o:
$(AS) -o $*.o $<
-OBJS= minix_op.o bitmap.o truncate.o namei.o inode.o file_dev.o
+OBJS= bitmap.o truncate.o namei.o inode.o \
+ file.o dir.o symlink.o blkdev.o chrdev.o
minix.o: $(OBJS)
$(LD) -r -o minix.o $(OBJS)
@@ -42,28 +43,47 @@ bitmap.o : bitmap.c ../../include/linux/string.h ../../include/linux/sched.h \
../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/minix_fs.h
-file_dev.o : file_dev.c ../../include/errno.h ../../include/fcntl.h ../../include/sys/types.h \
- ../../include/sys/dirent.h ../../include/limits.h ../../include/sys/stat.h ../../include/linux/sched.h \
- ../../include/linux/head.h ../../include/linux/fs.h ../../include/linux/mm.h \
- ../../include/linux/kernel.h ../../include/signal.h ../../include/sys/param.h \
- ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h ../../include/linux/minix_fs.h \
- ../../include/asm/segment.h
+blkdev.o : blkdev.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
+ ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
+ ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
+ ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
+ ../../include/linux/minix_fs.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/termios.h ../../include/errno.h ../../include/fcntl.h ../../include/sys/stat.h
+chrdev.o : chrdev.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
+ ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
+ ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
+ ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
+ ../../include/linux/minix_fs.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/termios.h ../../include/errno.h ../../include/fcntl.h ../../include/sys/stat.h
+dir.o : dir.c ../../include/errno.h ../../include/sys/stat.h ../../include/sys/types.h \
+ ../../include/asm/segment.h ../../include/linux/fs.h ../../include/sys/dirent.h \
+ ../../include/limits.h ../../include/linux/minix_fs.h
+file.o : file.c ../../include/errno.h ../../include/fcntl.h ../../include/sys/types.h \
+ ../../include/sys/dirent.h ../../include/limits.h ../../include/sys/stat.h ../../include/asm/segment.h \
+ ../../include/asm/system.h ../../include/linux/sched.h ../../include/linux/head.h \
+ ../../include/linux/fs.h ../../include/linux/mm.h ../../include/linux/kernel.h \
+ ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
+ ../../include/sys/resource.h ../../include/linux/minix_fs.h
inode.o : inode.c ../../include/linux/string.h ../../include/sys/stat.h ../../include/sys/types.h \
../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/asm/system.h
-minix_op.o : minix_op.c ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \
- ../../include/limits.h ../../include/linux/minix_fs.h
namei.o : namei.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
../../include/linux/minix_fs.h ../../include/asm/segment.h ../../include/linux/string.h \
../../include/fcntl.h ../../include/errno.h ../../include/const.h ../../include/sys/stat.h
+symlink.o : symlink.c ../../include/errno.h ../../include/sys/stat.h ../../include/sys/types.h \
+ ../../include/asm/segment.h ../../include/linux/sched.h ../../include/linux/head.h \
+ ../../include/linux/fs.h ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.h ../../include/sys/param.h \
+ ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h ../../include/linux/minix_fs.h
truncate.o : truncate.c ../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \
../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/linux/tty.h \
- ../../include/termios.h ../../include/errno.h ../../include/fcntl.h ../../include/sys/stat.h
+ ../../include/asm/system.h ../../include/termios.h ../../include/errno.h ../../include/fcntl.h \
+ ../../include/sys/stat.h
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 6a3ead3..a7fd3c3 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -179,6 +179,6 @@ struct inode * minix_new_inode(int dev)
inode->i_dirt = 1;
inode->i_ino = j + i*8192;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->i_op = &minix_inode_operations;
+ inode->i_op = NULL;
return inode;
}
diff --git a/fs/minix/blkdev.c b/fs/minix/blkdev.c
new file mode 100644
index 0000000..fec1bc9
--- /dev/null
+++ b/fs/minix/blkdev.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/chrdev.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/minix_fs.h>
+#include <linux/tty.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/*
+ * Called every time a minix block special file is opened
+ */
+static int blkdev_open(struct inode * inode, struct file * filp)
+{
+ int i;
+
+ check_disk_change(inode->i_rdev);
+ i = MAJOR(inode->i_rdev);
+ if (i < MAX_BLKDEV) {
+ filp->f_op = blkdev_fops[i];
+ if (filp->f_op && filp->f_op->open)
+ return filp->f_op->open(inode,filp);
+ }
+ return 0;
+}
+
+/*
+ * Dummy default file-operations: the only thing this does
+ * is contain the open that then fills in the correct operations
+ * depending on the special file...
+ */
+static struct file_operations def_blk_fops = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ blkdev_open, /* open */
+ NULL, /* release */
+};
+
+struct inode_operations minix_blkdev_inode_operations = {
+ &def_blk_fops, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ minix_bmap, /* bmap */
+ minix_truncate /* truncate */
+};
diff --git a/fs/minix/chrdev.c b/fs/minix/chrdev.c
new file mode 100644
index 0000000..489849f
--- /dev/null
+++ b/fs/minix/chrdev.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/chrdev.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/minix_fs.h>
+#include <linux/tty.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/*
+ * Called every time a minix character special file is opened
+ */
+static int chrdev_open(struct inode * inode, struct file * filp)
+{
+ int i;
+
+ i = MAJOR(inode->i_rdev);
+ if (i < MAX_CHRDEV) {
+ filp->f_op = chrdev_fops[i];
+ if (filp->f_op && filp->f_op->open)
+ return filp->f_op->open(inode,filp);
+ }
+ return 0;
+}
+
+/*
+ * Dummy default file-operations: the only thing this does
+ * is contain the open that then fills in the correct operations
+ * depending on the special file...
+ */
+static struct file_operations def_chr_fops = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ chrdev_open, /* open */
+ NULL, /* release */
+};
+
+struct inode_operations minix_chrdev_inode_operations = {
+ &def_chr_fops, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ minix_bmap, /* bmap */
+ minix_truncate /* truncate */
+};
+
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
new file mode 100644
index 0000000..d09585a
--- /dev/null
+++ b/fs/minix/dir.c
@@ -0,0 +1,91 @@
+/*
+ * linux/fs/minix/dir.c
+ *
+ * minix directory hadnling functions
+ */
+
+#include <errno.h>
+
+#include <sys/stat.h>
+
+#include <asm/segment.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+static int minix_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations minix_dir_operations = {
+ NULL, /* lseek - default */
+ minix_file_read, /* read */
+ NULL, /* write - bad */
+ minix_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations minix_dir_inode_operations = {
+ &minix_dir_operations, /* default directory file-ops */
+ minix_create, /* create */
+ minix_lookup, /* lookup */
+ minix_link, /* link */
+ minix_unlink, /* unlink */
+ minix_symlink, /* symlink */
+ minix_mkdir, /* mkdir */
+ minix_rmdir, /* rmdir */
+ minix_mknod, /* mknod */
+ minix_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ minix_bmap, /* bmap */
+ minix_truncate /* truncate */
+};
+
+static int minix_readdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ unsigned int block,offset,i;
+ char c;
+ struct buffer_head * bh;
+ struct minix_dir_entry * de;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ if (filp->f_pos & (sizeof (struct minix_dir_entry) - 1))
+ return -EBADF;
+ while (filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & 1023;
+ block = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS);
+ if (!block || !(bh = bread(inode->i_dev,block))) {
+ filp->f_pos += 1024-offset;
+ continue;
+ }
+ de = (struct minix_dir_entry *) (offset + bh->b_data);
+ while (offset < 1024 && filp->f_pos < inode->i_size) {
+ offset += sizeof (struct minix_dir_entry);
+ filp->f_pos += sizeof (struct minix_dir_entry);
+ if (de->inode) {
+ for (i = 0; i < MINIX_NAME_LEN; i++)
+ if (c = de->name[i])
+ put_fs_byte(c,i+dirent->d_name);
+ else
+ break;
+ if (i) {
+ put_fs_long(de->inode,&dirent->d_ino);
+ put_fs_byte(0,i+dirent->d_name);
+ put_fs_word(i,&dirent->d_reclen);
+ brelse(bh);
+ return i;
+ }
+ }
+ de++;
+ }
+ brelse(bh);
+ }
+ return 0;
+}
diff --git a/fs/minix/file.c b/fs/minix/file.c
new file mode 100644
index 0000000..9ebeebb
--- /dev/null
+++ b/fs/minix/file.c
@@ -0,0 +1,221 @@
+/*
+ * linux/fs/minix/file.c
+ *
+ * minix regular file handling primitives
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/minix_fs.h>
+#include <linux/kernel.h>
+
+#define NBUF 16
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+int minix_file_read(struct inode *, struct file *, char *, int);
+static int minix_file_write(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the minix filesystem.
+ */
+static struct file_operations minix_file_operations = {
+ NULL, /* lseek - default */
+ minix_file_read, /* read */
+ minix_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* no special open is needed */
+ NULL /* release */
+};
+
+struct inode_operations minix_file_inode_operations = {
+ &minix_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ minix_bmap, /* bmap */
+ minix_truncate /* truncate */
+};
+
+static inline void wait_on_buffer(struct buffer_head * bh)
+{
+ cli();
+ while (bh->b_lock)
+ sleep_on(&bh->b_wait);
+ sti();
+}
+
+/*
+ * minix_file_read() is also needed by the directory read-routine,
+ * so it's not static. NOTE! reading directories directly is a bad idea,
+ * but has to be supported for now for compatability reasons with older
+ * versions.
+ */
+int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ int read,left,chars,nr;
+ int block, blocks, offset;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * buflist[NBUF];
+
+ if (!inode) {
+ printk("minix_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
+ printk("minix_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ if (filp->f_pos > inode->i_size)
+ left = 0;
+ else
+ left = inode->i_size - filp->f_pos;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = filp->f_pos >> BLOCK_SIZE_BITS;
+ offset = filp->f_pos & (BLOCK_SIZE-1);
+ blocks = (left + offset + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ bhb = bhe = buflist;
+ do {
+ if (blocks) {
+ --blocks;
+ if (nr = minix_bmap(inode,block++)) {
+ *bhb = getblk(inode->i_dev,nr);
+ if (!(*bhb)->b_uptodate)
+ ll_rw_block(READ,*bhb);
+ } else
+ *bhb = NULL;
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ if (bhb != bhe)
+ continue;
+ }
+ if (*bhe) {
+ wait_on_buffer(*bhe);
+ if (!(*bhe)->b_uptodate) {
+ do {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (bhe != bhb);
+ break;
+ }
+ }
+
+ if (left < BLOCK_SIZE - offset)
+ chars = left;
+ else
+ chars = BLOCK_SIZE - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ brelse(*bhe);
+ buf += chars;
+ } else {
+ while (chars-->0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0);
+ if (!read)
+ return -EIO;
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return read;
+}
+
+static int minix_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ off_t pos;
+ int written,block,c;
+ struct buffer_head * bh;
+ char * p;
+
+ if (!inode) {
+ printk("minix_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("minix_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written<count) {
+ if (!(block = minix_create_block(inode,pos/BLOCK_SIZE))) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = BLOCK_SIZE - (pos % BLOCK_SIZE);
+ if (c > count-written)
+ c = count-written;
+ if (c == BLOCK_SIZE)
+ bh = getblk(inode->i_dev, block);
+ else
+ bh = bread(inode->i_dev,block);
+ if (!bh) {
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ p = (pos % BLOCK_SIZE) + bh->b_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs(p,buf,c);
+ buf += c;
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse(bh);
+ }
+ inode->i_mtime = CURRENT_TIME;
+ if (!(filp->f_flags & O_APPEND)) {
+ filp->f_pos = pos;
+ inode->i_ctime = CURRENT_TIME;
+ }
+ inode->i_dirt = 1;
+ return written;
+}
diff --git a/fs/minix/file_dev.c b/fs/minix/file_dev.c
deleted file mode 100644
index 2ae0705..0000000
--- a/fs/minix/file_dev.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * linux/fs/file_dev.c
- *
- * (C) 1991 Linus Torvalds
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/dirent.h>
-#include <sys/stat.h>
-
-#include <linux/sched.h>
-#include <linux/minix_fs.h>
-#include <linux/kernel.h>
-#include <asm/segment.h>
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
-int minix_readdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count)
-{
- unsigned int block,offset,i;
- char c;
- struct buffer_head * bh;
- struct minix_dir_entry * de;
-
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
- if (filp->f_pos & (sizeof (struct minix_dir_entry) - 1))
- return -EBADF;
- while (filp->f_pos < inode->i_size) {
- offset = filp->f_pos & 1023;
- block = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS);
- if (!block || !(bh = bread(inode->i_dev,block))) {
- filp->f_pos += 1024-offset;
- continue;
- }
- de = (struct minix_dir_entry *) (offset + bh->b_data);
- while (offset < 1024 && filp->f_pos < inode->i_size) {
- offset += sizeof (struct minix_dir_entry);
- filp->f_pos += sizeof (struct minix_dir_entry);
- if (de->inode) {
- for (i = 0; i < MINIX_NAME_LEN; i++)
- if (c = de->name[i])
- put_fs_byte(c,i+dirent->d_name);
- else
- break;
- if (i) {
- put_fs_long(de->inode,&dirent->d_ino);
- put_fs_byte(0,i+dirent->d_name);
- put_fs_word(i,&dirent->d_reclen);
- brelse(bh);
- return i;
- }
- }
- de++;
- }
- brelse(bh);
- }
- return 0;
-}
-
-int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
-{
- int read,left,chars,nr;
- struct buffer_head * bh;
-
- if (!inode) {
- printk("minix_file_read: inode = NULL\n");
- return -EINVAL;
- }
- if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
- printk("minix_file_read: mode = %07o\n",inode->i_mode);
- return -EINVAL;
- }
- if (filp->f_pos > inode->i_size)
- left = 0;
- else
- left = inode->i_size - filp->f_pos;
- if (left > count)
- left = count;
- read = 0;
- while (left > 0) {
- if (nr = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS)) {
- if (!(bh=bread(inode->i_dev,nr)))
- return read?read:-EIO;
- } else
- bh = NULL;
- nr = filp->f_pos & (BLOCK_SIZE-1);
- chars = MIN( BLOCK_SIZE-nr , left );
- filp->f_pos += chars;
- left -= chars;
- read += chars;
- if (bh) {
- memcpy_tofs(buf,nr+bh->b_data,chars);
- buf += chars;
- brelse(bh);
- } else {
- while (chars-->0)
- put_fs_byte(0,buf++);
- }
- }
- inode->i_atime = CURRENT_TIME;
- return read;
-}
-
-int minix_file_write(struct inode * inode, struct file * filp, char * buf, int count)
-{
- off_t pos;
- int written,block,c;
- struct buffer_head * bh;
- char * p;
-
- if (!inode) {
- printk("minix_file_write: inode = NULL\n");
- return -EINVAL;
- }
- if (!S_ISREG(inode->i_mode)) {
- printk("minix_file_write: mode = %07o\n",inode->i_mode);
- return -EINVAL;
- }
-/*
- * ok, append may not work when many processes are writing at the same time
- * but so what. That way leads to madness anyway.
- */
- if (filp->f_flags & O_APPEND)
- pos = inode->i_size;
- else
- pos = filp->f_pos;
- written = 0;
- while (written<count) {
- if (!(block = minix_create_block(inode,pos/BLOCK_SIZE))) {
- if (!written)
- written = -ENOSPC;
- break;
- }
- c = BLOCK_SIZE - (pos % BLOCK_SIZE);
- if (c > count-written)
- c = count-written;
- if (c == BLOCK_SIZE)
- bh = getblk(inode->i_dev, block);
- else
- bh = bread(inode->i_dev,block);
- if (!bh) {
- if (!written)
- written = -EIO;
- break;
- }
- p = (pos % BLOCK_SIZE) + bh->b_data;
- pos += c;
- if (pos > inode->i_size) {
- inode->i_size = pos;
- inode->i_dirt = 1;
- }
- written += c;
- memcpy_fromfs(p,buf,c);
- buf += c;
- bh->b_uptodate = 1;
- bh->b_dirt = 1;
- brelse(bh);
- }
- inode->i_mtime = CURRENT_TIME;
- if (!(filp->f_flags & O_APPEND)) {
- filp->f_pos = pos;
- inode->i_ctime = CURRENT_TIME;
- }
- inode->i_dirt = 1;
- return written;
-}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index c635e6e..4ddf5c2 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -15,6 +15,13 @@
int sync_dev(int dev);
+void minix_put_inode(struct inode *inode)
+{
+ inode->i_size = 0;
+ minix_truncate(inode);
+ minix_free_inode(inode);
+}
+
void minix_put_super(struct super_block *sb)
{
int i;
@@ -31,6 +38,8 @@ void minix_put_super(struct super_block *sb)
static struct super_operations minix_sops = {
minix_read_inode,
+ minix_write_inode,
+ minix_put_inode,
minix_put_super
};
@@ -199,7 +208,17 @@ void minix_read_inode(struct inode * inode)
else for (block = 0; block < 9; block++)
inode->i_data[block] = raw_inode->i_zone[block];
brelse(bh);
- inode->i_op = &minix_inode_operations;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &minix_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &minix_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &minix_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &minix_chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &minix_blkdev_inode_operations;
}
void minix_write_inode(struct inode * inode)
diff --git a/fs/minix/minix_op.c b/fs/minix/minix_op.c
deleted file mode 100644
index a588f09..0000000
--- a/fs/minix/minix_op.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * linux/fs/minix/minix_op.c
- *
- * structures for the minix super_block/inode/file-operations
- */
-
-#include <linux/fs.h>
-#include <linux/minix_fs.h>
-
-void minix_put_inode(struct inode *inode)
-{
- inode->i_size = 0;
- minix_truncate(inode);
- minix_free_inode(inode);
-}
-
-/*
- * These are the low-level inode operations for minix filesystem inodes.
- */
-struct inode_operations minix_inode_operations = {
- minix_create,
- minix_lookup,
- minix_link,
- minix_unlink,
- minix_symlink,
- minix_mkdir,
- minix_rmdir,
- minix_mknod,
- minix_rename,
- minix_readlink,
- minix_open,
- minix_release,
- minix_follow_link,
- minix_bmap,
- minix_truncate,
- minix_write_inode,
- minix_put_inode
-};
-
-/*
- * We have mostly NULL's here: the current defaults are ok for
- * the minix filesystem.
- */
-struct file_operations minix_file_operations = {
- NULL, /* lseek - default */
- minix_file_read, /* read */
- minix_file_write, /* write */
- NULL, /* readdir - bad */
- NULL, /* close - default */
- NULL, /* select - default */
- NULL /* ioctl - default */
-};
-
-struct file_operations minix_dir_operations = {
- NULL, /* lseek - default */
- minix_file_read, /* read */
- NULL, /* write - bad */
- minix_readdir, /* readdir */
- NULL, /* close - default */
- NULL, /* select - default */
- NULL /* ioctl - default */
-};
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 912b883..8682e9c 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -103,40 +103,6 @@ static struct buffer_head * minix_find_entry(struct inode * dir,
return NULL;
}
-struct inode * minix_follow_link(struct inode * dir, struct inode * inode)
-{
- unsigned short fs;
- struct buffer_head * bh;
-
- if (!dir) {
- dir = current->root;
- dir->i_count++;
- }
- if (!inode) {
- iput(dir);
- return NULL;
- }
- if (!S_ISLNK(inode->i_mode)) {
- iput(dir);
- return inode;
- }
- __asm__("mov %%fs,%0":"=r" (fs));
- if ((current->link_count > 5) || !inode->i_data[0] ||
- !(bh = bread(inode->i_dev, inode->i_data[0]))) {
- iput(dir);
- iput(inode);
- return NULL;
- }
- iput(inode);
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
- current->link_count++;
- inode = _namei(bh->b_data,dir,1);
- current->link_count--;
- __asm__("mov %0,%%fs"::"r" (fs));
- brelse(bh);
- return inode;
-}
-
int minix_lookup(struct inode * dir,const char * name, int len,
struct inode ** result)
{
@@ -249,11 +215,13 @@ int minix_create(struct inode * dir,const char * name, int len, int mode,
iput(dir);
return -ENOSPC;
}
+ inode->i_op = &minix_file_inode_operations;
inode->i_mode = mode;
inode->i_dirt = 1;
bh = minix_add_entry(dir,name,len,&de);
if (!bh) {
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
iput(dir);
return -ENOSPC;
@@ -287,6 +255,17 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd
}
inode->i_uid = current->euid;
inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &minix_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &minix_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &minix_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &minix_chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &minix_blkdev_inode_operations;
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = rdev;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
@@ -294,6 +273,7 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd
bh = minix_add_entry(dir,name,len,&de);
if (!bh) {
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
iput(dir);
return -ENOSPC;
@@ -323,12 +303,13 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
iput(dir);
return -ENOSPC;
}
+ inode->i_op = &minix_dir_inode_operations;
inode->i_size = 2 * sizeof (struct minix_dir_entry);
- inode->i_dirt = 1;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
if (!(inode->i_data[0] = minix_new_block(inode->i_dev))) {
iput(dir);
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
return -ENOSPC;
}
@@ -336,6 +317,7 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
if (!(dir_block = bread(inode->i_dev,inode->i_data[0]))) {
iput(dir);
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
return -EIO;
}
@@ -517,10 +499,11 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
return -ENOSPC;
}
inode->i_mode = S_IFLNK | 0777;
- inode->i_dirt = 1;
+ inode->i_op = &minix_symlink_inode_operations;
if (!(inode->i_data[0] = minix_new_block(inode->i_dev))) {
iput(dir);
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
return -ENOSPC;
}
@@ -528,6 +511,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
if (!(name_block = bread(inode->i_dev,inode->i_data[0]))) {
iput(dir);
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
return -EIO;
}
@@ -542,6 +526,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
bh = minix_find_entry(dir,name,len,&de);
if (bh) {
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
brelse(bh);
iput(dir);
@@ -550,6 +535,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
bh = minix_add_entry(dir,name,len,&de);
if (!bh) {
inode->i_nlink--;
+ inode->i_dirt = 1;
iput(inode);
iput(dir);
return -ENOSPC;
@@ -667,6 +653,10 @@ start_up:
old_inode = iget(old_dir->i_dev, old_de->inode);
if (!old_inode)
goto end_rename;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->euid != old_inode->i_uid &&
+ current->euid != old_dir->i_uid && !suser())
+ goto end_rename;
new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de);
if (new_bh) {
new_inode = iget(new_dir->i_dev, new_de->inode);
@@ -712,8 +702,10 @@ start_up:
/* ok, that's it */
old_de->inode = 0;
new_de->inode = old_inode->i_ino;
- if (new_inode)
+ if (new_inode) {
new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ }
old_bh->b_dirt = 1;
new_bh->b_dirt = 1;
if (dir_bh) {
@@ -761,31 +753,3 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
wake_up(&wait);
return result;
}
-
-int minix_readlink(struct inode * inode, char * buffer, int buflen)
-{
- struct buffer_head * bh;
- int i;
- char c;
-
- if (!S_ISLNK(inode->i_mode)) {
- iput(inode);
- return -EINVAL;
- }
- if (buflen > 1023)
- buflen = 1023;
- if (inode->i_data[0])
- bh = bread(inode->i_dev, inode->i_data[0]);
- else
- bh = NULL;
- iput(inode);
- if (!bh)
- return 0;
- i = 0;
- while (i<buflen && (c = bh->b_data[i])) {
- i++;
- put_fs_byte(c,buffer++);
- }
- brelse(bh);
- return i;
-}
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
new file mode 100644
index 0000000..70fbeb0
--- /dev/null
+++ b/fs/minix/symlink.c
@@ -0,0 +1,100 @@
+/*
+ * linux/fs/minix/symlink.c
+ *
+ * minix symlink handling code
+ */
+
+#include <errno.h>
+
+#include <sys/stat.h>
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+static int minix_readlink(struct inode *, char *, int);
+static struct inode * minix_follow_link(struct inode *, struct inode *);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations minix_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ minix_readlink, /* readlink */
+ minix_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL /* truncate */
+};
+
+static struct inode * minix_follow_link(struct inode * dir, struct inode * inode)
+{
+ unsigned short fs;
+ struct buffer_head * bh;
+
+ if (!dir) {
+ dir = current->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return NULL;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ return inode;
+ }
+ __asm__("mov %%fs,%0":"=r" (fs));
+ if ((current->link_count > 5) || !inode->i_data[0] ||
+ !(bh = bread(inode->i_dev, inode->i_data[0]))) {
+ iput(dir);
+ iput(inode);
+ return NULL;
+ }
+ iput(inode);
+ __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+ current->link_count++;
+ inode = _namei(bh->b_data,dir,1);
+ current->link_count--;
+ __asm__("mov %0,%%fs"::"r" (fs));
+ brelse(bh);
+ return inode;
+}
+
+static int minix_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ if (buflen > 1023)
+ buflen = 1023;
+ if (inode->i_data[0])
+ bh = bread(inode->i_dev, inode->i_data[0]);
+ else
+ bh = NULL;
+ iput(inode);
+ if (!bh)
+ return 0;
+ i = 0;
+ while (i<buflen && (c = bh->b_data[i])) {
+ i++;
+ put_fs_byte(c,buffer++);
+ }
+ brelse(bh);
+ return i;
+}
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index 7df5268..215daf3 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -149,50 +149,3 @@ void minix_release(struct inode * inode, struct file * filp)
{
printk("minix_release not implemented\n");
}
-
-static int check_char_dev(struct inode * inode, struct file * filp)
-{
- struct tty_struct *tty;
- int min, dev;
-
- dev = inode->i_rdev;
- if (MAJOR(dev) == 4 || MAJOR(dev) == 5) {
- if (MAJOR(dev) == 5)
- min = current->tty;
- else
- min = MINOR(dev);
- if (min < 0)
- return -1;
- if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1))
- return -1;
- tty = TTY_TABLE(min);
- if (!(filp->f_flags & O_NOCTTY) &&
- current->leader &&
- current->tty<0 &&
- tty->session==0) {
- current->tty = min;
- tty->session= current->session;
- tty->pgrp = current->pgrp;
- }
- if (IS_A_SERIAL(min))
- serial_open(min-64);
- }
- return 0;
-}
-
-/*
- * Called every time a minix-file is opened
- */
-int minix_open(struct inode * inode, struct file * filp)
-{
- if (S_ISCHR(inode->i_mode)) {
- if (check_char_dev(inode,filp))
- return -EAGAIN;
- } else if (S_ISBLK(inode->i_mode))
- check_disk_change(inode->i_rdev);
- else if (S_ISREG(inode->i_mode))
- filp->f_op = &minix_file_operations;
- else if (S_ISDIR(inode->i_mode))
- filp->f_op = &minix_dir_operations;
- return 0;
-}
diff --git a/fs/namei.c b/fs/namei.c
index 73c1621..c4f4acc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -167,10 +167,6 @@ struct inode * _namei(const char * pathname, struct inode * base,
inode = follow_link(base,inode);
else
iput(base);
- if (inode) {
- inode->i_atime=CURRENT_TIME;
- inode->i_dirt=1;
- }
return inode;
}
diff --git a/fs/open.c b/fs/open.c
index e2cdce7..3e47592 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -49,9 +49,9 @@ int sys_truncate(const char * path, unsigned int length)
if (!(inode = namei(path)))
return -ENOENT;
- if (!permission(inode,MAY_WRITE)) {
+ if (S_ISDIR(inode->i_mode) || !permission(inode,MAY_WRITE)) {
iput(inode);
- return -EPERM;
+ return -EACCES;
}
inode->i_size = length;
if (inode->i_op && inode->i_op->truncate)
@@ -71,8 +71,8 @@ int sys_ftruncate(unsigned int fd, unsigned int length)
return -EBADF;
if (!(inode = file->f_inode))
return -ENOENT;
- if (!(file->f_flags & 2))
- return -EPERM;
+ if (S_ISDIR(inode->i_mode) || !(file->f_flags & 2))
+ return -EACCES;
inode->i_size = length;
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
@@ -92,7 +92,7 @@ int sys_utime(char * filename, struct utimbuf * times)
if (current->euid != inode->i_uid &&
!permission(inode,MAY_WRITE)) {
iput(inode);
- return -EPERM;
+ return -EACCES;
}
actime = get_fs_long((unsigned long *) &times->actime);
modtime = get_fs_long((unsigned long *) &times->modtime);
@@ -147,6 +147,10 @@ int sys_chdir(const char * filename)
iput(inode);
return -ENOTDIR;
}
+ if (!permission(inode,MAY_EXEC)) {
+ iput(inode);
+ return -EACCES;
+ }
iput(current->pwd);
current->pwd = inode;
return (0);
@@ -162,6 +166,10 @@ int sys_chroot(const char * filename)
iput(inode);
return -ENOTDIR;
}
+ if (!suser()) {
+ iput(inode);
+ return -EPERM;
+ }
iput(current->root);
current->root = inode;
return (0);
@@ -177,7 +185,7 @@ int sys_fchmod(unsigned int fd, mode_t mode)
if (!(inode = file->f_inode))
return -ENOENT;
if ((current->euid != inode->i_uid) && !suser())
- return -EACCES;
+ return -EPERM;
inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
inode->i_dirt = 1;
return 0;
@@ -191,7 +199,7 @@ int sys_chmod(const char * filename, mode_t mode)
return -ENOENT;
if ((current->euid != inode->i_uid) && !suser()) {
iput(inode);
- return -EACCES;
+ return -EPERM;
}
inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
inode->i_dirt = 1;
@@ -208,29 +216,34 @@ int sys_fchown(unsigned int fd, uid_t user, gid_t group)
return -EBADF;
if (!(inode = file->f_inode))
return -ENOENT;
- if (!suser())
- return -EACCES;
- inode->i_uid = user;
- inode->i_gid = group;
- inode->i_dirt=1;
- return 0;
+ if ((current->euid == inode->i_uid && user == inode->i_uid &&
+ (in_group_p(group) || group == inode->i_gid)) ||
+ suser()) {
+ inode->i_uid = user;
+ inode->i_gid = group;
+ inode->i_dirt=1;
+ return 0;
+ }
+ return -EPERM;
}
int sys_chown(const char * filename, uid_t user, gid_t group)
{
struct inode * inode;
- if (!(inode = namei(filename)))
+ if (!(inode = lnamei(filename)))
return -ENOENT;
- if (!suser()) {
+ if ((current->euid == inode->i_uid && user == inode->i_uid &&
+ (in_group_p(group) || group == inode->i_gid)) ||
+ suser()) {
+ inode->i_uid = user;
+ inode->i_gid = group;
+ inode->i_dirt=1;
iput(inode);
- return -EACCES;
+ return 0;
}
- inode->i_uid = user;
- inode->i_gid = group;
- inode->i_dirt = 1;
iput(inode);
- return 0;
+ return -EPERM;
}
int sys_open(const char * filename,int flag,int mode)
@@ -256,24 +269,17 @@ int sys_open(const char * filename,int flag,int mode)
f->f_count=0;
return i;
}
- f->f_op = NULL;
- if (inode)
- if (S_ISCHR(inode->i_mode)) {
- i = MAJOR(inode->i_rdev);
- if (i < MAX_CHRDEV)
- f->f_op = chrdev_fops[i];
- } else if (S_ISBLK(inode->i_mode)) {
- i = MAJOR(inode->i_rdev);
- if (i < MAX_CHRDEV)
- f->f_op = blkdev_fops[i];
- }
f->f_mode = "\001\002\003\000"[flag & O_ACCMODE];
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
- if (inode->i_op && inode->i_op->open)
- if (i = inode->i_op->open(inode,f)) {
+ f->f_reada = 0;
+ f->f_op = NULL;
+ if (inode->i_op)
+ f->f_op = inode->i_op->default_file_ops;
+ if (f->f_op && f->f_op->open)
+ if (i = f->f_op->open(inode,f)) {
iput(inode);
f->f_count=0;
current->filp[fd]=NULL;
@@ -297,12 +303,17 @@ int sys_close(unsigned int fd)
if (!(filp = current->filp[fd]))
return -EINVAL;
current->filp[fd] = NULL;
- if (filp->f_count == 0)
- panic("Close: file count is 0");
- if (--filp->f_count)
- return (0);
- if (filp->f_op && filp->f_op->close)
- return filp->f_op->close(filp->f_inode,filp);
+ if (filp->f_count == 0) {
+ printk("Close: file count is 0\n");
+ return 0;
+ }
+ if (filp->f_count > 1) {
+ filp->f_count--;
+ return 0;
+ }
+ if (filp->f_op && filp->f_op->release)
+ filp->f_op->release(filp->f_inode,filp);
iput(filp->f_inode);
+ filp->f_count--;
return 0;
}
diff --git a/fs/pipe.c b/fs/pipe.c
index 0aef899..1657ccb 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -105,14 +105,27 @@ static int pipe_ioctl(struct inode *pino, struct file * filp,
}
}
+/*
+ * Ok, these two routines should keep track of readers/writers,
+ * but it's currently done with the inode->i_count checking.
+ */
+static void pipe_read_release(struct inode * inode, struct file * filp)
+{
+}
+
+static void pipe_write_release(struct inode * inode, struct file * filp)
+{
+}
+
static struct file_operations read_pipe_fops = {
pipe_lseek,
pipe_read,
bad_pipe_rw,
pipe_readdir,
- NULL, /* pipe_close */
NULL, /* pipe_select */
- pipe_ioctl
+ pipe_ioctl,
+ NULL, /* no special open code */
+ pipe_read_release
};
static struct file_operations write_pipe_fops = {
@@ -120,9 +133,10 @@ static struct file_operations write_pipe_fops = {
bad_pipe_rw,
pipe_write,
pipe_readdir,
- NULL, /* pipe_close */
NULL, /* pipe_select */
- pipe_ioctl
+ pipe_ioctl,
+ NULL, /* no special open code */
+ pipe_write_release
};
int sys_pipe(unsigned long * fildes)
diff --git a/fs/read_write.c b/fs/read_write.c
index 19cbcda..b28d7e7 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -62,6 +62,7 @@ int sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
if (tmp < 0)
return -EINVAL;
file->f_pos = tmp;
+ file->f_reada = 0;
return file->f_pos;
}
diff --git a/fs/select.c b/fs/select.c
index 98e74c4..efd6549 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -127,6 +127,11 @@ static int check_in(select_table * wait, struct inode * inode)
return 1;
else
add_wait(&inode->i_wait, wait);
+ else if (S_ISSOCK(inode->i_mode))
+ if (sock_select(inode, NULL, SEL_IN, wait))
+ return 1;
+ else
+ add_wait(&inode->i_wait, wait);
return 0;
}
@@ -144,6 +149,11 @@ static int check_out(select_table * wait, struct inode * inode)
return 1;
else
add_wait(&inode->i_wait, wait);
+ else if (S_ISSOCK(inode->i_mode))
+ if (sock_select(inode, NULL, SEL_OUT, wait))
+ return 1;
+ else
+ add_wait(&inode->i_wait, wait);
return 0;
}
@@ -161,6 +171,11 @@ static int check_ex(select_table * wait, struct inode * inode)
return 1;
else
add_wait(&inode->i_wait,wait);
+ else if (S_ISSOCK(inode->i_mode))
+ if (sock_select(inode, NULL, SEL_EX, wait))
+ return 1;
+ else
+ add_wait(&inode->i_wait, wait);
return 0;
}
@@ -186,6 +201,8 @@ int do_select(fd_set in, fd_set out, fd_set ex,
continue;
if (S_ISFIFO(current->filp[i]->f_inode->i_mode))
continue;
+ if (S_ISSOCK(current->filp[i]->f_inode->i_mode))
+ continue;
return -EBADF;
}
repeat:
@@ -196,6 +213,7 @@ repeat:
sel_tables = &wait_table;
*inp = *outp = *exp = 0;
count = 0;
+ current->state = TASK_INTERRUPTIBLE;
mask = 1;
for (i = 0 ; i < NR_OPEN ; i++, mask += mask) {
if (mask & in)
@@ -216,14 +234,12 @@ repeat:
}
if (!(current->signal & ~current->blocked) &&
current->timeout && !count) {
- current->state = TASK_INTERRUPTIBLE;
- sti();
schedule();
- cli();
free_wait(&wait_table);
goto repeat;
}
free_wait(&wait_table);
+ current->state = TASK_RUNNING;
return count;
}
@@ -266,13 +282,11 @@ int sys_select( unsigned long *buffer )
timeout += jiffies;
}
current->timeout = timeout;
- cli();
i = do_select(in, out, ex, &res_in, &res_out, &res_ex);
if (current->timeout > jiffies)
timeout = current->timeout - jiffies;
else
timeout = 0;
- sti();
current->timeout = 0;
if (i < 0)
return i;
diff --git a/include/asm/io.h b/include/asm/io.h
index 226f343..af1c72f 100644
--- a/include/asm/io.h
+++ b/include/asm/io.h
@@ -1,6 +1,14 @@
#ifndef _ASM_IO_H
#define _ASM_IO_H
+/*
+ * 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.
+ *
+ * Linus
+ */
+
extern void inline outb(char value, unsigned short port)
{
__asm__ volatile ("outb %0,%1"
@@ -9,12 +17,11 @@ __asm__ volatile ("outb %0,%1"
extern void inline outb_p(char value, unsigned short port)
{
-__asm__ volatile ("outb %0,%1\n"
- "\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:"
+__asm__ volatile ("outb %0,%1\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80"
::"a" ((char) value),"d" ((unsigned short) port));
}
@@ -29,12 +36,11 @@ __asm__ volatile ("inb %1,%0"
extern unsigned char inline inb_p(unsigned short port)
{
unsigned char _v;
-__asm__ volatile ("inb %1,%0\n"
- "\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:\tjmp 1f\n"
- "1:"
+__asm__ volatile ("inb %1,%0\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80\n\t"
+ "outb %0,$0x80"
:"=a" (_v):"d" ((unsigned short) port));
return _v;
}
diff --git a/include/linux/config.dist.h b/include/linux/config.dist.h
new file mode 100644
index 0000000..4a84fbf
--- /dev/null
+++ b/include/linux/config.dist.h
@@ -0,0 +1,27 @@
+#ifndef _CONFIG_DIST_H
+#define _CONFIG_DIST_H
+#ifdef CONFIG_DISTRIBUTION
+
+#undef CONFG_SCSI
+#define CONFIG_SCSI
+
+#undef CONFIG_SCSI_AHA1542
+#define CONFIG_SCSI_AHA1542
+#undef CONFIG_SCSI_CSC
+#define CONFIG_SCSI_CSC
+#undef CONFIG_SCSI_DTC
+#define CONFIG_SCSI_DTC
+#undef CONFIG_SCSI_FUTURE_DOMAIN
+#define CONFIG_SCSI_FUTURE_DOMAIN
+#undef CONFIG_SCSI_SEAGATE
+#define CONFIG_SCSI_SEAGATE
+#undef CONFIG_SCSI_ULTRASTOR
+#define CONFIG_SCSI_ULTRASTOR
+
+#undef CONFIG_BLK_DEV_SD
+#define CONFIG_BLK_DEV_SD
+#undef CONFIG_BLK_DEV_ST
+#define CONFIG_BLK_DEV_ST
+
+#endif
+#endif
diff --git a/include/linux/config.h b/include/linux/config.h
index 8c79f93..db499dc 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -1,6 +1,8 @@
#ifndef _CONFIG_H
#define _CONFIG_H
+#define CONFIG_DISTRIBUTION
+
/*
* Defines for what uname() should return
*/
@@ -62,4 +64,52 @@
leave HD_TYPE undefined. This is the normal thing to do.
*/
+#undef HD_TYPE
+
+#undef CONFIG_BLK_DEV_SD
+#undef CONFIG_BLK_DEV_ST
+
+/*
+ Choose supported SCSI adapters here.
+*/
+
+#undef CONFIG_SCSI_AHA1542
+#undef CONFIG_SCSI_ALWAYS
+#undef CONFIG_SCSI_CSC
+#undef CONFIG_SCSI_DTC
+#undef CONFIG_SCSI_FUTURE_DOMAIN
+#undef CONFIG_SCSI_SEAGATE
+#undef CONFIG_SCSI_ULTRASTOR
+
+#if defined(CONFIG_BLK_DEV_SD) || defined(CONFIG_BLK_DEV_ST)
+ #ifndef CONFIG_SCSI
+ #define CONFIG_SCSI
+ #endif
+
+ #if !defined(CONFIG_SCSI_AHA1542) && !defined(CONFIG_SCSI_CSC) && !defined(CONFIG_SCSI_DTC) && \
+ !defined(CONFIG_SCSI_FUTURE_DOMAIN) && !defined(CONFIG_SCSI_SEAGATE) && !defined(CONFIG_SCSI_ULTRASTOR)
+
+ #error Error : SCSI devices enabled, but no low level drivers have been enabled.
+ #endif
+#endif
+
+#ifdef CONFIG_DISTRIBUTION
+ #include <linux/config.dist.h>
+#else
+ #include <linux/config.site.h>
+#endif
+
+/*
+ File type specific stuff goes into this.
+*/
+
+#ifdef ASM_SRC
+#endif
+
+#ifdef C_SRC
+#endif
+
+#ifdef MAKE
+#endif
+
#endif
diff --git a/include/linux/config.site.h b/include/linux/config.site.h
new file mode 100644
index 0000000..49b731b
--- /dev/null
+++ b/include/linux/config.site.h
@@ -0,0 +1,9 @@
+#ifndef _CONFIG_SITE_H
+#define _CONFIG_SITE_H
+
+/*
+ This configuration file contains site specific things, things
+ that you have added and config.dist will not know about.
+*/
+
+#endif
diff --git a/include/linux/config_rel.h b/include/linux/config_rel.h
index a602c1f..e199879 100644
--- a/include/linux/config_rel.h
+++ b/include/linux/config_rel.h
@@ -1 +1 @@
-#define UTS_RELEASE "0.95c-54"
+#define UTS_RELEASE "0.96a-10"
diff --git a/include/linux/config_ver.h b/include/linux/config_ver.h
index 34e98a9..dde55d7 100644
--- a/include/linux/config_ver.h
+++ b/include/linux/config_ver.h
@@ -1 +1 @@
-#define UTS_VERSION "04/22/92"
+#define UTS_VERSION "05/22/92"
diff --git a/include/linux/fd.h b/include/linux/fd.h
new file mode 100644
index 0000000..990dce5
--- /dev/null
+++ b/include/linux/fd.h
@@ -0,0 +1,32 @@
+#ifndef _FD_H_
+#define _FD_H
+
+#define FDCLRPRM 0 /* clear user-defined parameters */
+#define FDSETPRM 1 /* set user-defined parameters for current media */
+#define FDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
+#define FDGETPRM 3 /* get disk parameters */
+#define FDMSGON 4 /* issue kernel messages on media type change */
+#define FDMSGOFF 5 /* don't issue kernel messages on media type change */
+#define FDFMTBEG 6 /* begin formatting a disk */
+#define FDFMTTRK 7 /* format the specified track */
+#define FDFMTEND 8 /* end formatting a disk */
+
+#define FD_FILL_BYTE 0xF6 /* format fill byte */
+
+#define FORMAT_NONE 0 /* no format request */
+#define FORMAT_WAIT 1 /* format request is waiting */
+#define FORMAT_BUSY 2 /* formatting in progress */
+#define FORMAT_OKAY 3 /* successful completion */
+#define FORMAT_ERROR 4 /* formatting error */
+
+struct floppy_struct {
+ unsigned int size, sect, head, track, stretch;
+ unsigned char gap,rate,spec1,fmt_gap;
+ char *name; /* used only for predefined formats */
+};
+
+struct format_descr {
+ unsigned int device,head,track;
+};
+
+#endif
diff --git a/include/linux/fdreg.h b/include/linux/fdreg.h
index 01355af..77a270e 100644
--- a/include/linux/fdreg.h
+++ b/include/linux/fdreg.h
@@ -63,6 +63,7 @@ extern void floppy_deselect(unsigned int nr);
#define FD_WRITE 0xC5 /* write with MT, MFM */
#define FD_SENSEI 0x08 /* Sense Interrupt Status */
#define FD_SPECIFY 0x03 /* specify HUT etc */
+#define FD_FORMAT 0x4D /* format one track */
/* DMA commands */
#define DMA_READ 0x46
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7e067bd..c4bcb84 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -20,9 +20,11 @@
* 5 - /dev/tty
* 6 - /dev/lp
* 7 - unnamed pipes
+ * 8 - /dev/sd
+ * 9 - /dev/st
*/
-#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3)
+#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3 || (x)==8)
#define MAY_EXEC 1
#define MAY_WRITE 2
@@ -81,6 +83,7 @@ struct buffer_head {
struct buffer_head * b_next;
struct buffer_head * b_prev_free;
struct buffer_head * b_next_free;
+ struct buffer_head * b_reqnext;
};
struct inode {
@@ -113,6 +116,7 @@ struct file {
unsigned short f_mode;
unsigned short f_flags;
unsigned short f_count;
+ unsigned short f_reada;
struct inode * f_inode;
struct file_operations * f_op;
off_t f_pos;
@@ -159,12 +163,14 @@ struct file_operations {
int (*read) (struct inode *, struct file *, char *, int);
int (*write) (struct inode *, struct file *, char *, int);
int (*readdir) (struct inode *, struct file *, struct dirent *, int count);
- int (*close) (struct inode *, struct file *);
int (*select) (struct inode *, struct file *, int, select_table *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int);
+ int (*open) (struct inode *, struct file *);
+ void (*release) (struct inode *, struct file *);
};
struct inode_operations {
+ struct file_operations * default_file_ops;
int (*create) (struct inode *,const char *,int,int,struct inode **);
int (*lookup) (struct inode *,const char *,int,struct inode **);
int (*link) (struct inode *,struct inode *,const char *,int);
@@ -175,18 +181,15 @@ struct inode_operations {
int (*mknod) (struct inode *,const char *,int,int,int);
int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);
int (*readlink) (struct inode *,char *,int);
- int (*open) (struct inode *, struct file *);
- void (*release) (struct inode *, struct file *);
struct inode * (*follow_link) (struct inode *, struct inode *);
int (*bmap) (struct inode *,int);
void (*truncate) (struct inode *);
- /* added by entropy */
- void (*write_inode)(struct inode *inode);
- void (*put_inode)(struct inode *inode);
};
struct super_operations {
void (*read_inode)(struct inode *inode);
+ void (*write_inode) (struct inode *inode);
+ void (*put_inode) (struct inode *inode);
void (*put_super)(struct super_block *sb);
};
@@ -211,7 +214,6 @@ extern int floppy_change(struct buffer_head * first_block);
extern int ticks_to_floppy_on(unsigned int dev);
extern void floppy_on(unsigned int dev);
extern void floppy_off(unsigned int dev);
-extern void truncate(struct inode * inode);
extern void sync_inodes(void);
extern void wait_on(struct inode * inode);
extern int bmap(struct inode * inode,int block);
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2451af9..ef27d83 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -4,9 +4,7 @@
void verify_area(void * addr,int count);
volatile void panic(const char * str);
volatile void do_exit(long error_code);
-int printf(const char * fmt, ...);
int printk(const char * fmt, ...);
-void console_print(const char * str);
void * malloc(unsigned int size);
void free_s(void * obj, int size);
diff --git a/include/linux/lp.h b/include/linux/lp.h
index 6e565da..dd92cf0 100644
--- a/include/linux/lp.h
+++ b/include/linux/lp.h
@@ -31,9 +31,7 @@ $Header: /usr/src/linux/include/linux/lp.h,v 1.2 1992/01/21 23:59:24 james_r_wie
#define LP_B(minor) lp_table[(minor)].base
#define LP_F(minor) lp_table[(minor)].flags
-#define LP_T(minor) lp_table[(minor)].lp_task
#define LP_S(minor) inb(LP_B((minor)) + 1)
-#define LP_R(minor) lp_table[(minor)].remainder
/*
since we are dealing with a horribly slow device
@@ -45,10 +43,6 @@ I don't see the need for a queue
struct lp_struct {
int base;
int flags;
- /* number of characters yet to be printed in current block */
- int remainder;
- /* needed for busy determination */
- int lp_task;
};
/*
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index c427cb1..a51b60c 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -45,7 +45,6 @@ struct minix_dir_entry {
extern int minix_open(struct inode * inode, struct file * filp);
extern void minix_release(struct inode * inode, struct file * filp);
-extern struct inode * minix_follow_link(struct inode * dir, struct inode * inode);
extern int minix_lookup(struct inode * dir,const char * name, int len,
struct inode ** result);
extern int minix_create(struct inode * dir,const char * name, int len, int mode,
@@ -59,7 +58,6 @@ extern int minix_link(struct inode * oldinode, struct inode * dir, const char *
extern int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev);
extern int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
struct inode * new_dir, const char * new_name, int new_len);
-extern int minix_readlink(struct inode * inode, char * buffer, int buflen);
extern struct inode * minix_new_inode(int dev);
extern void minix_free_inode(struct inode * inode);
extern int minix_new_block(int dev);
@@ -73,15 +71,18 @@ extern void minix_put_super(struct super_block *);
extern struct super_block *minix_read_super(struct super_block *,void *);
extern void minix_read_inode(struct inode *);
extern void minix_write_inode(struct inode *);
+extern void minix_put_inode(struct inode *);
extern int minix_lseek(struct inode *, struct file *, off_t, int);
extern int minix_read(struct inode *, struct file *, char *, int);
extern int minix_write(struct inode *, struct file *, char *, int);
-extern int minix_readdir(struct inode *, struct file *, struct dirent *, int);
extern int minix_file_read(struct inode *, struct file *, char *, int);
-extern int minix_file_write(struct inode *, struct file *, char *, int);
-extern struct inode_operations minix_inode_operations;
+extern struct inode_operations minix_file_inode_operations;
+extern struct inode_operations minix_dir_inode_operations;
+extern struct inode_operations minix_symlink_inode_operations;
+extern struct inode_operations minix_chrdev_inode_operations;
+extern struct inode_operations minix_blkdev_inode_operations;
extern struct file_operations minix_file_operations;
extern struct file_operations minix_dir_operations;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 9d744cb..b52578d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -222,6 +222,7 @@ extern int jiffies_offset;
extern void add_timer(long jiffies, void (*fn)(void));
extern void sleep_on(struct task_struct ** p);
+extern int send_sig(long sig,struct task_struct * p,int priv);
extern void interruptible_sleep_on(struct task_struct ** p);
extern void wake_up(struct task_struct ** p);
extern int in_group_p(gid_t grp);
diff --git a/include/linux/sys.h b/include/linux/sys.h
index df8264c..d35b1d7 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -104,6 +104,8 @@ extern int sys_profil();
extern int sys_statfs();
extern int sys_fstatfs();
extern int sys_ioperm();
+extern int sys_socketcall();
+extern int sys_syslog();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
@@ -123,7 +125,8 @@ sys_gettimeofday, sys_settimeofday, sys_getgroups, sys_setgroups,
sys_select, sys_symlink, sys_lstat, sys_readlink, sys_uselib,
sys_swapon, sys_reboot, sys_readdir, sys_mmap, sys_munmap,
sys_truncate, sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority,
-sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm };
+sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm,
+sys_socketcall, sys_syslog };
/* So we don't have to do any more manual updating.... */
int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
diff --git a/include/linux/timer.h b/include/linux/timer.h
index ab3c74e..aa92aa3 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -20,6 +20,8 @@
* HD_TIMER harddisk timer
*
* FLOPPY_TIMER floppy disk timer (not used right now)
+ *
+ * SCSI_TIMER scsi.c timeout timer
*/
#define BLANK_TIMER 0
@@ -37,6 +39,7 @@
#define HD_TIMER 16
#define FLOPPY_TIMER 17
+#define SCSI_TIMER 18
struct timer_struct {
unsigned long expires;
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 22f0f0e..57c3b58 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -9,6 +9,8 @@
#ifndef _TTY_H
#define _TTY_H
+#include <asm/system.h>
+
#define MAX_CONSOLES 8
#define NR_SERIALS 4
#define NR_PTYS 4
@@ -24,7 +26,7 @@ struct tty_queue {
unsigned long head;
unsigned long tail;
struct task_struct * proc_list;
- char buf[TTY_BUF_SIZE];
+ unsigned char buf[TTY_BUF_SIZE];
};
#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
@@ -41,11 +43,31 @@ struct tty_queue {
#define LAST(a) ((a)->buf[(TTY_BUF_SIZE-1)&((a)->head-1)])
#define FULL(a) (!LEFT(a))
#define CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1))
-#define GETCH(queue,c) \
-(void)({c=(queue)->buf[(queue)->tail];INC((queue)->tail);})
-#define PUTCH(c,queue) \
-(void)({(queue)->buf[(queue)->head]=(c);INC((queue)->head);})
+static inline void PUTCH(char c, struct tty_queue * queue)
+{
+ int head;
+
+ cli();
+ head = (queue->head + 1) & (TTY_BUF_SIZE-1);
+ if (head != queue->tail) {
+ queue->buf[queue->head] = c;
+ queue->head = head;
+ }
+ sti();
+}
+
+static inline int GETCH(struct tty_queue * queue)
+{
+ int result = -1;
+
+ if (queue->tail != queue->head) {
+ result = 0xff & queue->buf[queue->tail];
+ queue->tail = (queue->tail + 1) & (TTY_BUF_SIZE-1);
+ }
+ return result;
+}
+
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT])
#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE])
@@ -55,12 +77,43 @@ struct tty_queue {
#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP])
#define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP])
+#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
+#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
+#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)
+
+#define L_CANON(tty) _L_FLAG((tty),ICANON)
+#define L_ISIG(tty) _L_FLAG((tty),ISIG)
+#define L_ECHO(tty) _L_FLAG((tty),ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
+#define L_ECHONL(tty) _L_FLAG((tty),ECHONL)
+#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
+#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
+#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
+
+#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
+#define I_NLCR(tty) _I_FLAG((tty),INLCR)
+#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
+#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
+#define I_IXON(tty) _I_FLAG((tty),IXON)
+#define I_STRP(tty) _I_FLAG((tty),ISTRIP)
+
+#define O_POST(tty) _O_FLAG((tty),OPOST)
+#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
+#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
+#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
+
+#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD)
+#define C_HUP(tty) (C_SPEED((tty)) == B0)
+
struct tty_struct {
struct termios termios;
int pgrp;
int session;
int stopped;
int busy;
+ int count;
struct winsize winsize;
void (*write)(struct tty_struct * tty);
struct tty_queue *read_q;
@@ -103,6 +156,7 @@ do { \
} while (0)
extern struct tty_struct tty_table[];
+extern struct tty_struct * redirect;
extern int fg_console;
extern unsigned long video_num_columns;
extern unsigned long video_num_lines;
@@ -124,7 +178,12 @@ extern void lp_init(void);
extern void con_init(void);
extern void tty_init(void);
+extern void flush(struct tty_queue * queue);
+
extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned int);
+extern int is_orphaned_pgrp(int pgrp);
+extern int is_ignored(int sig);
+extern int tty_signal(int sig, struct tty_struct *tty);
extern void rs_write(struct tty_struct * tty);
extern void con_write(struct tty_struct * tty);
@@ -137,4 +196,6 @@ void copy_to_cooked(struct tty_struct * tty);
void update_screen(int new_console);
+int kill_pg(int pgrp, int sig, int priv);
+
#endif
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
new file mode 100644
index 0000000..21e19ba
--- /dev/null
+++ b/include/linux/unistd.h
@@ -0,0 +1,206 @@
+#ifndef _LINUX_UNISTD_H
+#define _LINUX_UNISTD_H
+
+/*
+ * This file contains the system call numbers and the syscallX
+ * macros
+ */
+
+#define __NR_setup 0 /* used only by init, to get system going */
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_chown 16
+#define __NR_break 17
+#define __NR_stat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_fstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_phys 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_uname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_lstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+/*
+ * Not all of these are implemented yet, but these are the
+ * numbers they will use.
+ */
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+
+extern int errno;
+
+/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name)); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,atype,a) \
+type name(atype a) \
+{ \
+long __res; \
+__asm__ volatile ("movl %2,%%ebx\n\t" \
+ "int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"g" ((long)(a)):"bx"); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall2(type,name,atype,a,btype,b) \
+type name(atype a,btype b) \
+{ \
+long __res; \
+__asm__ volatile ("movl %2,%%ebx\n\t" \
+ "int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)):"bx"); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
+type name(atype a,btype b,ctype c) \
+{ \
+long __res; \
+__asm__ volatile ("movl %2,%%ebx\n\t" \
+ "int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)),"d" ((long)(c)):"bx"); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \
+type name (atype a, btype b, ctype c, dtype d) \
+{ \
+long __res; \
+__asm__ volatile ("movl %2,%%ebx\n\t" \
+ "int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
+ "d" ((long)(c)),"S" ((long)(d))); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#define _syscall5(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e) \
+type name (atype a,btype b,ctype c,dtype d,etype e) \
+{ \
+long __res; \
+__asm__ volatile ("movl %2,%%ebx\n\t" \
+ "int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
+ "d" ((long)(c)),"S" ((long)(d)),"D" ((long)(e))); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#endif /* _LINUX_UNISTD_H */
diff --git a/include/sys/resource.h b/include/sys/resource.h
index e23ec63..3f34379 100644
--- a/include/sys/resource.h
+++ b/include/sys/resource.h
@@ -60,4 +60,11 @@ struct rlimit {
int rlim_max;
};
+#define PRIO_MIN -99
+#define PRIO_MAX 14
+
+#define PRIO_PROCESS 0
+#define PRIO_PGRP 1
+#define PRIO_USER 2
+
#endif /* _SYS_RESOURCE_H */
diff --git a/include/sys/socket.h b/include/sys/socket.h
new file mode 100644
index 0000000..7d840a6
--- /dev/null
+++ b/include/sys/socket.h
@@ -0,0 +1,39 @@
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+struct sockaddr {
+ u_short sa_family; /* address family, AF_xxx */
+ char sa_data[14]; /* 14 bytes of protocol address */
+};
+
+/*
+ * socket types
+ */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (connectionless) socket */
+#define SOCK_SEQPACKET 3 /* sequential packet socket */
+#define SOCK_RAW 4 /* raw socket */
+
+/*
+ * supported address families
+ */
+#define AF_UNSPEC 0
+#define AF_UNIX 1
+#define AF_INET 2
+
+/*
+ * protocol families, same as address families
+ */
+#define PF_UNIX AF_UNIX
+#define PF_INET AF_INET
+
+int socket(int family, int type, int protocol);
+int socketpair(int family, int type, int protocol, int sockvec[2]);
+int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
+int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
+int listen(int sockfd, int backlog);
+int accept(int sockfd, struct sockaddr *peer, int *paddrlen);
+int getsockname(int sockfd, struct sockaddr *addr, int *paddrlen);
+int getpeername(int sockfd, struct sockaddr *peer, int *paddrlen);
+
+#endif /* _SOCKET_H */
diff --git a/include/sys/un.h b/include/sys/un.h
new file mode 100644
index 0000000..26a51f4
--- /dev/null
+++ b/include/sys/un.h
@@ -0,0 +1,9 @@
+#ifndef _UN_H
+#define _UN_H
+
+struct sockaddr_un {
+ u_short sun_family; /* AF_UNIX */
+ char sun_path[108]; /* pathname */
+};
+
+#endif /* _UN_H */
diff --git a/include/termios.h b/include/termios.h
index b04a0e9..740f3d8 100644
--- a/include/termios.h
+++ b/include/termios.h
@@ -34,6 +34,7 @@
#define FIONREAD 0x541B
#define TIOCINQ FIONREAD
#define TIOCLINUX 0x541C
+#define TIOCCONS 0x541D
struct winsize {
unsigned short ws_row;
diff --git a/include/unistd.h b/include/unistd.h
index 6631444..e7a1c6e 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -64,200 +64,7 @@
#include <utime.h>
#ifdef __LIBRARY__
-
-#define __NR_setup 0 /* used only by init, to get system going */
-#define __NR_exit 1
-#define __NR_fork 2
-#define __NR_read 3
-#define __NR_write 4
-#define __NR_open 5
-#define __NR_close 6
-#define __NR_waitpid 7
-#define __NR_creat 8
-#define __NR_link 9
-#define __NR_unlink 10
-#define __NR_execve 11
-#define __NR_chdir 12
-#define __NR_time 13
-#define __NR_mknod 14
-#define __NR_chmod 15
-#define __NR_chown 16
-#define __NR_break 17
-#define __NR_stat 18
-#define __NR_lseek 19
-#define __NR_getpid 20
-#define __NR_mount 21
-#define __NR_umount 22
-#define __NR_setuid 23
-#define __NR_getuid 24
-#define __NR_stime 25
-#define __NR_ptrace 26
-#define __NR_alarm 27
-#define __NR_fstat 28
-#define __NR_pause 29
-#define __NR_utime 30
-#define __NR_stty 31
-#define __NR_gtty 32
-#define __NR_access 33
-#define __NR_nice 34
-#define __NR_ftime 35
-#define __NR_sync 36
-#define __NR_kill 37
-#define __NR_rename 38
-#define __NR_mkdir 39
-#define __NR_rmdir 40
-#define __NR_dup 41
-#define __NR_pipe 42
-#define __NR_times 43
-#define __NR_prof 44
-#define __NR_brk 45
-#define __NR_setgid 46
-#define __NR_getgid 47
-#define __NR_signal 48
-#define __NR_geteuid 49
-#define __NR_getegid 50
-#define __NR_acct 51
-#define __NR_phys 52
-#define __NR_lock 53
-#define __NR_ioctl 54
-#define __NR_fcntl 55
-#define __NR_mpx 56
-#define __NR_setpgid 57
-#define __NR_ulimit 58
-#define __NR_uname 59
-#define __NR_umask 60
-#define __NR_chroot 61
-#define __NR_ustat 62
-#define __NR_dup2 63
-#define __NR_getppid 64
-#define __NR_getpgrp 65
-#define __NR_setsid 66
-#define __NR_sigaction 67
-#define __NR_sgetmask 68
-#define __NR_ssetmask 69
-#define __NR_setreuid 70
-#define __NR_setregid 71
-#define __NR_sigsuspend 72
-#define __NR_sigpending 73
-#define __NR_sethostname 74
-#define __NR_setrlimit 75
-#define __NR_getrlimit 76
-#define __NR_getrusage 77
-#define __NR_gettimeofday 78
-#define __NR_settimeofday 79
-#define __NR_getgroups 80
-#define __NR_setgroups 81
-#define __NR_select 82
-#define __NR_symlink 83
-#define __NR_lstat 84
-#define __NR_readlink 85
-#define __NR_uselib 86
-#define __NR_swapon 87
-#define __NR_reboot 88
-#define __NR_readdir 89
-#define __NR_mmap 90
-#define __NR_munmap 91
-/*
- * Not all of these are implemented yet, but these are the
- * numbers they will use.
- */
-#define __NR_truncate 92
-#define __NR_ftruncate 93
-#define __NR_fchmod 94
-#define __NR_fchown 95
-#define __NR_getpriority 96
-#define __NR_setpriority 97
-#define __NR_profil 98
-#define __NR_statfs 99
-#define __NR_fstatfs 100
-#define __NR_ioperm 101
-
-/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
-#define _syscall0(type,name) \
-type name(void) \
-{ \
-long __res; \
-__asm__ volatile ("int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name)); \
-if (__res >= 0) \
- return (type) __res; \
-errno = -__res; \
-return -1; \
-}
-
-#define _syscall1(type,name,atype,a) \
-type name(atype a) \
-{ \
-long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)):"bx"); \
-if (__res >= 0) \
- return (type) __res; \
-errno = -__res; \
-return -1; \
-}
-
-#define _syscall2(type,name,atype,a,btype,b) \
-type name(atype a,btype b) \
-{ \
-long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)):"bx"); \
-if (__res >= 0) \
- return (type) __res; \
-errno = -__res; \
-return -1; \
-}
-
-#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
-type name(atype a,btype b,ctype c) \
-{ \
-long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)),"d" ((long)(c)):"bx"); \
-if (__res>=0) \
- return (type) __res; \
-errno=-__res; \
-return -1; \
-}
-
-#define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \
-type name (atype a, btype b, ctype c, dtype d) \
-{ \
-long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
- "d" ((long)(c)),"S" ((long)(d))); \
-if (__res>=0) \
- return (type) __res; \
-errno=-__res; \
-return -1; \
-}
-
-#define _syscall5(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e) \
-type name (atype a,btype b,ctype c,dtype d,etype e) \
-{ \
-long __res; \
-__asm__ volatile ("movl %2,%%ebx\n\t" \
- "int $0x80" \
- : "=a" (__res) \
- : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \
- "d" ((long)(c)),"S" ((long)(d)),"D" ((long)(e))); \
-if (__res>=0) \
- return (type) __res; \
-errno=-__res; \
-return -1; \
-}
-
+#include <linux/unistd.h>
#endif /* __LIBRARY__ */
/* XXX - illegal. */
diff --git a/init/main.c b/init/main.c
index 378ca99..839161a 100644
--- a/init/main.c
+++ b/init/main.c
@@ -4,10 +4,22 @@
* (C) 1991 Linus Torvalds
*/
-#define __LIBRARY__
-#include <unistd.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <fcntl.h>
#include <time.h>
+#include <sys/types.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/head.h>
+#include <linux/unistd.h>
+
/*
* we need this inline - forking from kernel space will result
* in NO COPY ON WRITE (!!!), until an execve is executed. This
@@ -24,34 +36,35 @@ static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)
-
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/head.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include <stddef.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
+static inline _syscall0(pid_t,setsid)
+static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
+static inline _syscall1(int,dup,int,fd)
+static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
+static inline _syscall1(int,close,int,fd)
+static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+
+static inline pid_t wait(int * wait_stat)
+{
+ return waitpid(-1,wait_stat,0);
+}
static char printbuf[1024];
-extern char *strcpy();
extern int vsprintf();
extern void init(void);
extern void blk_dev_init(void);
extern void chr_dev_init(void);
extern void hd_init(void);
extern void floppy_init(void);
+extern void sock_init(void);
extern void mem_init(long start, long end);
extern long rd_init(long mem_start, int length);
extern long kernel_mktime(struct tm * tm);
+#ifdef CONFIG_SCSI
+extern void scsi_dev_init(void);
+#endif
+
static int sprintf(char * str, const char *fmt, ...)
{
va_list args;
@@ -154,14 +167,19 @@ void start_kernel(void)
#endif
mem_init(main_memory_start,memory_end);
trap_init();
+ sched_init();
chr_dev_init();
blk_dev_init();
time_init();
- sched_init();
+ printk("Linux version " UTS_RELEASE " " __DATE__ " " __TIME__ "\n");
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
+ sock_init();
sti();
+#ifdef CONFIG_SCSI
+ scsi_dev_init();
+#endif
move_to_user_mode();
if (!fork()) { /* we count on this going ok */
init();
diff --git a/kernel/Makefile b/kernel/Makefile
index 0ebd895..716bcfd 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -62,7 +62,7 @@ exit.s exit.o : exit.c ../include/errno.h ../include/signal.h ../include/sys/typ
../include/sys/wait.h ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
- ../include/linux/tty.h ../include/termios.h ../include/asm/segment.h
+ ../include/linux/tty.h ../include/asm/system.h ../include/termios.h ../include/asm/segment.h
fork.s fork.o : fork.c ../include/errno.h ../include/stddef.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \
../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
@@ -77,7 +77,11 @@ panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h ../
../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
../include/linux/mm.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h
-printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h ../include/linux/kernel.h
+printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h ../include/errno.h \
+ ../include/asm/segment.h ../include/asm/system.h ../include/linux/sched.h ../include/linux/head.h \
+ ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
+ ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
+ ../include/sys/time.h ../include/time.h ../include/sys/resource.h
ptrace.s ptrace.o : ptrace.c ../include/linux/head.h ../include/linux/kernel.h ../include/linux/sched.h \
../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
../include/linux/mm.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \
@@ -98,9 +102,9 @@ sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h ../include/linux
../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/linux/tty.h \
- ../include/termios.h ../include/linux/config.h ../include/linux/config_rel.h \
- ../include/linux/config_ver.h ../include/asm/segment.h ../include/sys/times.h \
- ../include/sys/utsname.h ../include/linux/string.h
+ ../include/asm/system.h ../include/termios.h ../include/linux/config.h ../include/linux/config_rel.h \
+ ../include/linux/config_ver.h ../include/linux/config.dist.h ../include/asm/segment.h \
+ ../include/sys/times.h ../include/sys/utsname.h ../include/linux/string.h
traps.s traps.o : traps.c ../include/linux/string.h ../include/linux/head.h ../include/linux/sched.h \
../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
diff --git a/kernel/asm.s b/kernel/asm.s
index f565060..f62751c 100644
--- a/kernel/asm.s
+++ b/kernel/asm.s
@@ -11,40 +11,7 @@
* care about the stack layout etc.
*/
-.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt
-
-_hd_interrupt:
- cld
- pushl %eax
- pushl %ecx
- pushl %edx
- push %ds
- push %es
- push %fs
- movl $0x10,%eax
- mov %ax,%ds
- mov %ax,%es
- movl $0x17,%eax
- mov %ax,%fs
- movb $0x20,%al
- outb %al,$0xA0 # EOI to interrupt controller #1
- jmp 1f # give port chance to breathe
-1: jmp 1f
-1: outb %al,$0x20
- andl $0xfffeffff,_timer_active
- xorl %edx,%edx
- xchgl _do_hd,%edx
- testl %edx,%edx
- jne 1f
- movl $_unexpected_hd_interrupt,%edx
-1: call *%edx # "interesting" way of handling intr.
- pop %fs
- pop %es
- pop %ds
- popl %edx
- popl %ecx
- popl %eax
- iret
+.globl _floppy_interrupt,_parallel_interrupt
_floppy_interrupt:
cld
diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile
index 3aaf961..58dc4aa 100644
--- a/kernel/blk_drv/Makefile
+++ b/kernel/blk_drv/Makefile
@@ -25,15 +25,19 @@ CPP =cpp -nostdinc -I../../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
-OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o
+OBJS = hd.o ll_rw_blk.o floppy.o ramdisk.o
+
+all: blk_drv.a
blk_drv.a: $(OBJS)
+ rm -f blk_drv.a
$(AR) rcs blk_drv.a $(OBJS)
sync
clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done
+ (cd scsi; make clean)
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
@@ -46,12 +50,13 @@ floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/hea
../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
- ../../include/linux/fdreg.h ../../include/asm/system.h ../../include/asm/io.h \
- ../../include/asm/segment.h blk.h
+ ../../include/linux/timer.h ../../include/linux/fdreg.h ../../include/linux/fd.h \
+ ../../include/asm/system.h ../../include/asm/io.h ../../include/asm/segment.h \
+ ../../include/errno.h blk.h
hd.s hd.o : hd.c ../../include/errno.h ../../include/linux/config.h ../../include/linux/config_rel.h \
- ../../include/linux/config_ver.h ../../include/linux/sched.h ../../include/linux/head.h \
- ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \
- ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
+ ../../include/linux/config_ver.h ../../include/linux/config.dist.h ../../include/linux/sched.h \
+ ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \
+ ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/timer.h ../../include/linux/hdreg.h \
../../include/asm/system.h ../../include/asm/io.h ../../include/asm/segment.h \
@@ -62,9 +67,10 @@ ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/asm/system.h blk.h
ramdisk.s ramdisk.o : ramdisk.c ../../include/linux/string.h ../../include/linux/config.h \
- ../../include/linux/config_rel.h ../../include/linux/config_ver.h ../../include/linux/sched.h \
- ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \
- ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
- ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
- ../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/asm/system.h \
- ../../include/asm/segment.h ../../include/asm/memory.h blk.h
+ ../../include/linux/config_rel.h ../../include/linux/config_ver.h ../../include/linux/config.dist.h \
+ ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
+ ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
+ ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
+ ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
+ ../../include/linux/minix_fs.h ../../include/asm/system.h ../../include/asm/segment.h \
+ ../../include/asm/memory.h blk.h
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index 746f5f0..40a5925 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -1,7 +1,7 @@
#ifndef _BLK_H
#define _BLK_H
-#define NR_BLK_DEV 7
+#define NR_BLK_DEV 10
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
@@ -29,6 +29,7 @@ struct request {
char * buffer;
struct task_struct * waiting;
struct buffer_head * bh;
+ struct buffer_head * bhtail;
struct request * next;
};
@@ -88,6 +89,24 @@ extern int * blk_size[NR_BLK_DEV];
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
+#elif (MAJOR_NR == 8)
+/* scsi disk */
+#define DEVICE_NAME "scsidisk"
+#define DEVICE_INTR do_sd
+#define DEVICE_REQUEST do_sd_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == 9)
+/* scsi tape */
+#define DEVICE_NAME "scsitape"
+#define DEVICE_INTR do_st
+#define DEVICE_REQUEST do_st_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
#elif
/* unknown blk device */
#error "unknown blk device"
@@ -130,22 +149,45 @@ extern inline void unlock_buffer(struct buffer_head * bh)
wake_up(&bh->b_wait);
}
-extern inline void end_request(int uptodate)
+extern inline void next_buffer(int uptodate)
{
- DEVICE_OFF(CURRENT->dev);
- if (CURRENT->bh) {
- CURRENT->bh->b_uptodate = uptodate;
- unlock_buffer(CURRENT->bh);
- }
+ struct buffer_head *tmp;
+
+ CURRENT->bh->b_uptodate = uptodate;
+ unlock_buffer(CURRENT->bh);
if (!uptodate) {
printk(DEVICE_NAME " I/O error\n\r");
printk("dev %04x, block %d\n\r",CURRENT->dev,
CURRENT->bh->b_blocknr);
}
- wake_up(&CURRENT->waiting);
+ tmp = CURRENT->bh;
+ CURRENT->bh = CURRENT->bh->b_reqnext;
+ tmp->b_reqnext = NULL;
+ if (!CURRENT->bh)
+ panic("next_buffer: request buffer list destroyed\r\n");
+ CURRENT->buffer = CURRENT->bh->b_data;
+ CURRENT->errors = 0;
+}
+
+extern inline void end_request(int uptodate)
+{
+ struct request * tmp;
+
+ tmp = CURRENT;
+ DEVICE_OFF(tmp->dev);
+ CURRENT = tmp->next;
+ if (tmp->bh) {
+ tmp->bh->b_uptodate = uptodate;
+ unlock_buffer(tmp->bh);
+ }
+ if (!uptodate) {
+ printk(DEVICE_NAME " I/O error\n\r");
+ printk("dev %04x, block %d\n\r",tmp->dev,
+ tmp->bh->b_blocknr);
+ }
+ wake_up(&tmp->waiting);
+ tmp->dev = -1;
wake_up(&wait_for_request);
- CURRENT->dev = -1;
- CURRENT = CURRENT->next;
}
#ifdef DEVICE_INTR
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index acda4c9..2b9d238 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -32,21 +32,31 @@
* by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
*/
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signa| detection.
+ */
+
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
+#include <linux/timer.h>
#include <linux/fdreg.h>
+#include <linux/fd.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
+#include <errno.h>
#define MAJOR_NR 2
#include "blk.h"
-unsigned int changed_floppies = 0;
+static unsigned int changed_floppies = 0, fake_change = 0;
static int recalibrate = 0;
static int reset = 0;
+static int recover = 0; /* recalibrate immediately after resetting */
static int seek = 0;
extern unsigned char current_DOR;
@@ -64,6 +74,20 @@ __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
#define MAX_ERRORS 12
/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ */
+
+#define MAX_DISK_SIZE 1440
+
+/*
+ * Maximum number of sectors in a track buffer. Track buffering is disabled
+ * if tracks are bigger.
+ */
+
+#define MAX_BUFFER_SECTORS 18
+
+/*
* globals used by 'result()'
*/
#define MAX_REPLIES 7
@@ -83,22 +107,95 @@ static unsigned char reply_buffer[MAX_REPLIES];
* types (ie 360kB diskette in 1.2MB drive etc). Others should
* be self-explanatory.
*/
-struct floppy_struct {
- unsigned int size, sect, head, track, stretch;
- unsigned char gap,rate,spec1;
-};
static struct floppy_struct floppy_type[] = {
- { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */
- { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */
- { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */
- { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */
- { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */
- { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */
- { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */
- { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */
+ { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* no testing */
+ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB PC diskettes */
+ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,NULL }, /* 1.2 MB AT-diskettes */
+ { 720, 9,2,40,1,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB in 720kB drive */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,NULL }, /* 3.5" 720kB diskette */
+ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,NULL }, /* 360kB in 1.2MB drive */
+ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,NULL }, /* 720kB in 1.2MB drive */
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }, /* 1.44MB diskette */
};
+/* For auto-detection. Each drive type has a pair of formats to try. */
+
+static struct floppy_struct floppy_types[] = {
+ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */
+ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */
+ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M" }, /* 1.2 MB AT-diskettes */
+ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"360k/AT" }, /* 360kB in 1.2MB drive */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" }, /* 3.5" 720kB diskette */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" }, /* 3.5" 720kB diskette */
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" }, /* 1.44MB diskette */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */
+};
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+
+struct floppy_struct *current_type[4] = { NULL, NULL, NULL, NULL };
+
+/* This type is tried first. */
+
+struct floppy_struct *base_type[4];
+
+/* User-provided type information. current_type points to the respective entry
+ of this array. */
+
+struct floppy_struct user_params[4];
+
+static int floppy_sizes[] ={
+ MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE,
+ 360, 360 ,360, 360,
+ 1200,1200,1200,1200,
+ 360, 360, 360, 360,
+ 720, 720, 720, 720,
+ 360, 360, 360, 360,
+ 720, 720, 720, 720,
+ 1440,1440,1440,1440
+};
+
+/* The driver is trying to determine the correct media format while probing
+ is set. rw_interrupts clears it after a successful access. */
+
+static int probing = 0;
+
+/* (User-provided) media information is _not_ discarded after a media change
+ if the corresponding keep_data flag is non-zero. Positive values are
+ decremented after each probe. */
+
+static int keep_data[4] = { 0,0,0,0 };
+
+/* Announce successful media type detection and media information loss after
+ disk changes. */
+
+static ftd_msg[4] = { 1,1,1,1 };
+
+/* Synchronization of FDC access. */
+
+static volatile int format_status = FORMAT_NONE, fdc_busy = 0;
+static struct task_struct *fdc_wait = NULL, *format_done = NULL;
+
+/* Errors during formatting are counted here. */
+
+static int format_errors;
+
+/* Format request descriptor. */
+
+static struct format_descr format_req;
+
+/* Current device number. Taken either from the block header or from the
+ format request descriptor. */
+
+#define CURRENT_DEVICE (format_status == FORMAT_BUSY ? format_req.device : \
+ (CURRENT->dev))
+
+/* Current error count. */
+
+#define CURRENT_ERRORS (format_status == FORMAT_BUSY ? format_errors : \
+ (CURRENT->errors))
+
/*
* Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
@@ -110,7 +207,9 @@ static struct floppy_struct floppy_type[] = {
extern void floppy_interrupt(void);
extern char tmp_floppy_area[1024];
-extern char floppy_track_buffer[512*2*18];
+extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
+
+static void redo_fd_request(void);
/*
* These are global variables, as that's the easiest way to give
@@ -143,6 +242,16 @@ void floppy_deselect(unsigned int nr)
wake_up(&wait_on_floppy_select);
}
+void request_done(int uptodate)
+{
+ timer_active &= ~(1 << FLOPPY_TIMER);
+ if (format_status != FORMAT_BUSY) end_request(uptodate);
+ else {
+ format_status = uptodate ? FORMAT_OKAY : FORMAT_ERROR;
+ wake_up(&format_done);
+ }
+}
+
/*
* floppy-change is never called from an interrupt, so we can relax a bit
* here, sleep etc. Note that floppy-on tries to set current_DOR to point
@@ -154,9 +263,15 @@ int floppy_change(struct buffer_head * bh)
unsigned int mask = 1 << (bh->b_dev & 0x03);
if (MAJOR(bh->b_dev) != 2) {
- printk("floppy_changed: not a floppy\n");
+ printk("floppy_changed: not a floppy\r\n");
return 0;
}
+ if (fake_change & mask) {
+ fake_change &= ~mask;
+/* omitting the next line breaks formatting in a horrible way ... */
+ changed_floppies &= ~mask;
+ return 1;
+ }
if (changed_floppies & mask) {
changed_floppies &= ~mask;
recalibrate = 1;
@@ -179,7 +294,7 @@ int floppy_change(struct buffer_head * bh)
changed_floppies &= ~mask;
recalibrate = 1;
return 1;
- }
+ }
return 0;
}
@@ -190,10 +305,16 @@ __asm__("cld ; rep ; movsl" \
static void setup_DMA(void)
{
- unsigned long addr = (long) CURRENT->buffer;
- unsigned long count = 1024;
+ unsigned long addr,count;
- cli();
+ if (command == FD_FORMAT) {
+ addr = (long) tmp_floppy_area;
+ count = floppy->sect*4;
+ }
+ else {
+ addr = (long) CURRENT->buffer;
+ count = 1024;
+ }
if (read_track) {
/* mark buffer-track bad, in case all this fails.. */
buffer_drive = buffer_track = -1;
@@ -205,6 +326,7 @@ static void setup_DMA(void)
copy_buffer(CURRENT->buffer,tmp_floppy_area);
}
/* mask DMA 2 */
+ cli();
immoutb_p(4|2,10);
/* output command byte. I don't know why, but everyone (minix, */
/* sanches & canton) output this twice, first to 12 then to 11 */
@@ -274,12 +396,12 @@ static int result(void)
static void bad_flp_intr(void)
{
current_track = NO_TRACK;
- CURRENT->errors++;
- if (CURRENT->errors > MAX_ERRORS) {
+ CURRENT_ERRORS++;
+ if (CURRENT_ERRORS > MAX_ERRORS) {
floppy_deselect(current_drive);
- end_request(0);
+ request_done(0);
}
- if (CURRENT->errors > MAX_ERRORS/2)
+ if (CURRENT_ERRORS > MAX_ERRORS/2)
reset = 1;
else
recalibrate = 1;
@@ -297,12 +419,22 @@ static void rw_interrupt(void)
if (ST1 & 0x02) {
printk("Drive %d is write protected\n\r",current_drive);
floppy_deselect(current_drive);
- end_request(0);
+ request_done(0);
} else
bad_flp_intr();
- do_fd_request();
+ redo_fd_request();
return;
}
+ if (probing) {
+ int drive = MINOR(CURRENT->dev);
+
+ if (ftd_msg[drive])
+ printk("Auto-detected floppy type %s in fd%d\r\n",
+ floppy->name,drive);
+ current_type[drive] = floppy;
+ floppy_sizes[drive] = floppy->size >> 1;
+ probing = 0;
+ }
if (read_track) {
buffer_track = seek_track;
buffer_drive = current_drive;
@@ -313,8 +445,8 @@ static void rw_interrupt(void)
(unsigned long)(CURRENT->buffer) >= 0x100000)
copy_buffer(tmp_floppy_area,CURRENT->buffer);
floppy_deselect(current_drive);
- end_request(1);
- do_fd_request();
+ request_done(1);
+ redo_fd_request();
}
/*
@@ -329,23 +461,31 @@ inline void setup_rw_floppy(void)
setup_DMA();
do_floppy = rw_interrupt;
output_byte(command);
- if (read_track) {
- output_byte(current_drive);
- output_byte(track);
- output_byte(0);
- output_byte(1);
+ if (command != FD_FORMAT) {
+ if (read_track) {
+ output_byte(current_drive);
+ output_byte(track);
+ output_byte(0);
+ output_byte(1);
+ } else {
+ output_byte(head<<2 | current_drive);
+ output_byte(track);
+ output_byte(head);
+ output_byte(sector);
+ }
+ output_byte(2); /* sector size = 512 */
+ output_byte(floppy->sect);
+ output_byte(floppy->gap);
+ output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
} else {
output_byte(head<<2 | current_drive);
- output_byte(track);
- output_byte(head);
- output_byte(sector);
+ output_byte(2);
+ output_byte(floppy->sect);
+ output_byte(floppy->fmt_gap);
+ output_byte(FD_FILL_BYTE);
}
- output_byte(2); /* sector size = 512 */
- output_byte(floppy->sect);
- output_byte(floppy->gap);
- output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
if (reset)
- do_fd_request();
+ redo_fd_request();
}
/*
@@ -360,7 +500,7 @@ static void seek_interrupt(void)
if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) {
recalibrate = 1;
bad_flp_intr();
- do_fd_request();
+ redo_fd_request();
return;
}
current_track = ST1;
@@ -374,7 +514,8 @@ static void seek_interrupt(void)
*/
static void transfer(void)
{
- read_track = (command == FD_READ) && (CURRENT->errors < 4);
+ read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) &&
+ (floppy->sect <= MAX_BUFFER_SECTORS);
if (cur_spec1 != floppy->spec1) {
cur_spec1 = floppy->spec1;
output_byte(FD_SPECIFY);
@@ -384,7 +525,7 @@ static void transfer(void)
if (cur_rate != floppy->rate)
outb_p(cur_rate = floppy->rate,FD_DCR);
if (reset) {
- do_fd_request();
+ redo_fd_request();
return;
}
if (!seek) {
@@ -399,19 +540,24 @@ static void transfer(void)
output_byte((head<<2) | current_drive);
output_byte(seek_track);
if (reset)
- do_fd_request();
+ redo_fd_request();
}
/*
* Special case - used after a unexpected interrupt (or reset)
*/
+
+static void recalibrate_floppy();
+
static void recal_interrupt(void)
{
output_byte(FD_SENSEI);
current_track = NO_TRACK;
if (result()!=2 || (ST0 & 0xE0) == 0x60)
reset = 1;
- do_fd_request();
+/* Recalibrate until track 0 is reached. Might help on some errors. */
+ if ((ST0 & 0x10) == 0x10) recalibrate_floppy();
+ else redo_fd_request();
}
void unexpected_floppy_interrupt(void)
@@ -432,7 +578,7 @@ static void recalibrate_floppy(void)
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
if (reset)
- do_fd_request();
+ redo_fd_request();
}
static void reset_interrupt(void)
@@ -442,7 +588,11 @@ static void reset_interrupt(void)
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
- do_fd_request();
+ if (!recover) redo_fd_request();
+ else {
+ recalibrate_floppy();
+ recover = 0;
+ }
}
/*
@@ -467,11 +617,72 @@ static void reset_floppy(void)
sti();
}
+static void floppy_shutdown(void)
+{
+ cli();
+ request_done(0);
+ recover = 1;
+ reset_floppy();
+ sti();
+}
+
+static void shake_done(void)
+{
+ current_track = NO_TRACK;
+ if (inb(FD_DIR) & 0x80) request_done(0);
+ redo_fd_request();
+}
+
+static int retry_recal(void (*proc)(void))
+{
+ output_byte(FD_SENSEI);
+ if (result() == 2 && (ST0 & 0x10) != 0x10) return 0;
+ do_floppy = proc;
+ output_byte(FD_RECALIBRATE);
+ output_byte(head<<2 | current_drive);
+ return 1;
+}
+
+static void shake_zero(void)
+{
+ if (!retry_recal(shake_zero)) shake_done();
+}
+
+static void shake_one(void)
+{
+ if (retry_recal(shake_one)) return;
+ do_floppy = shake_done;
+ output_byte(FD_SEEK);
+ output_byte(head << 2 | current_drive);
+ output_byte(1);
+}
+
static void floppy_on_interrupt(void)
{
if (inb(FD_DIR) & 0x80) {
changed_floppies |= 1<<current_drive;
buffer_track = -1;
+ if (keep_data[current_drive]) {
+ if (keep_data[current_drive] > 0)
+ keep_data[current_drive]--;
+ }
+ else {
+ if (ftd_msg[current_drive] && current_type[
+ current_drive] != NULL)
+ printk("Disk type is undefined after disk "
+ "change in fd%d\r\n",current_drive);
+ current_type[current_drive] = NULL;
+ floppy_sizes[current_drive] = MAX_DISK_SIZE;
+ }
+/* Forcing the drive to seek makes the "media changed" condition go away.
+ There should be a cleaner solution for that ... */
+ if (!reset && !recalibrate) {
+ do_floppy = (current_track && current_track != NO_TRACK)
+ ? shake_zero : shake_one;
+ output_byte(FD_RECALIBRATE);
+ output_byte(head<<2 | current_drive);
+ return;
+ }
}
if (reset) {
reset_floppy();
@@ -494,45 +705,109 @@ static void floppy_on_interrupt(void)
transfer();
}
-void do_fd_request(void)
+static void setup_format_params(void)
+{
+ unsigned char *here = (unsigned char *) tmp_floppy_area;
+ int count;
+
+ for (count = 1; count <= floppy->sect; count++) {
+ *here++ = track;
+ *here++ = head;
+ *here++ = count;
+ *here++ = 2; /* 512 bytes */
+ }
+}
+
+static void redo_fd_request(void)
{
unsigned int block;
char * buffer_area;
-
- INIT_REQUEST;
+ int device;
+
+repeat:
+ if (format_status == FORMAT_WAIT) format_status = FORMAT_BUSY;
+ if (format_status != FORMAT_BUSY) {
+ if (!CURRENT) {
+ if (!fdc_busy) panic("FDC access conflict");
+ fdc_busy = 0;
+ wake_up(&fdc_wait);
+ CLEAR_INTR;
+ return;
+ }
+ if (MAJOR(CURRENT->dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed"); \
+ if (CURRENT->bh) {
+ if (!CURRENT->bh->b_lock)
+ panic(DEVICE_NAME ": block not locked");
+ }
+ }
seek = 0;
- floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
- if (current_drive != CURRENT_DEV)
- current_track = NO_TRACK;
- current_drive = CURRENT_DEV;
- block = CURRENT->sector;
- if (block+2 > floppy->size) {
- end_request(0);
- goto repeat;
+ probing = 0;
+ device = MINOR(CURRENT_DEVICE);
+ if (device > 3)
+ floppy = (device >> 2) + floppy_type;
+ else { /* Auto-detection */
+ if ((floppy = current_type[device & 3]) == NULL) {
+ probing = 1;
+ if ((floppy = base_type[device & 3]) ==
+ NULL) {
+ request_done(0);
+ goto repeat;
+ }
+ floppy += CURRENT_ERRORS & 1;
+ }
+ }
+ if (format_status != FORMAT_BUSY) {
+ if (current_drive != CURRENT_DEV)
+ current_track = NO_TRACK;
+ current_drive = CURRENT_DEV;
+ block = CURRENT->sector;
+ if (block+2 > floppy->size) {
+ request_done(0);
+ goto repeat;
+ }
+ sector = block % floppy->sect;
+ block /= floppy->sect;
+ head = block % floppy->head;
+ track = block / floppy->head;
+ seek_track = track << floppy->stretch;
+ if (CURRENT->cmd == READ)
+ command = FD_READ;
+ else if (CURRENT->cmd == WRITE)
+ command = FD_WRITE;
+ else {
+ printk("do_fd_request: unknown command\n");
+ request_done(0);
+ goto repeat;
+ }
}
- sector = block % floppy->sect;
- block /= floppy->sect;
- head = block % floppy->head;
- track = block / floppy->head;
- seek_track = track << floppy->stretch;
- if (CURRENT->cmd == READ)
- command = FD_READ;
- else if (CURRENT->cmd == WRITE)
- command = FD_WRITE;
else {
- printk("do_fd_request: unknown command\n");
- end_request(0);
- goto repeat;
+ if (current_drive != (format_req.device & 3))
+ current_track = NO_TRACK;
+ current_drive = format_req.device & 3;
+ if (format_req.track < 0 || format_req.track >= floppy->track ||
+ (format_req.head & 0xfffe) || probing) {
+ request_done(0);
+ goto repeat;
+ }
+ head = format_req.head;
+ track = format_req.track;
+ seek_track = track << floppy->stretch;
+ if (seek_track == buffer_track) buffer_track = -1;
+ command = FD_FORMAT;
+ setup_format_params();
}
+ timer_table[FLOPPY_TIMER].expires = jiffies+10*HZ;
+ timer_active |= 1 << FLOPPY_TIMER;
if ((seek_track == buffer_track) &&
(current_drive == buffer_drive)) {
buffer_area = floppy_track_buffer +
((sector + head*floppy->sect)<<9);
if (command == FD_READ) {
copy_buffer(buffer_area,CURRENT->buffer);
- end_request(1);
+ request_done(1);
goto repeat;
- } else
+ } else if (command == FD_WRITE)
copy_buffer(CURRENT->buffer,buffer_area);
}
if (seek_track != current_track)
@@ -541,25 +816,162 @@ void do_fd_request(void)
add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
}
-static int floppy_sizes[] ={
- 0, 0, 0, 0,
- 360, 360 ,360, 360,
- 1200,1200,1200,1200,
- 360, 360, 360, 360,
- 720, 720, 720, 720,
- 360, 360, 360, 360,
- 720, 720, 720, 720,
- 1440,1440,1440,1440
-};
+void do_fd_request(void)
+{
+ cli();
+ while (fdc_busy) sleep_on(&fdc_wait);
+ fdc_busy = 1;
+ sti();
+ redo_fd_request();
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned int param)
+{
+ int drive,cnt,okay;
+ struct floppy_struct *this;
+
+ if (!suser()) return -EPERM;
+ drive = MINOR(inode->i_rdev);
+ switch (cmd) {
+ case FDFMTBEG:
+ return 0;
+ case FDFMTEND:
+ cli();
+ fake_change |= 1 << (drive & 3);
+ sti();
+ drive &= 3;
+ cmd = FDCLRPRM;
+ break;
+ case FDGETPRM:
+ if (drive > 3) this = &floppy_type[drive >> 2];
+ else if ((this = current_type[drive & 3]) == NULL)
+ return -ENODEV;
+ verify_area((void *) param,sizeof(struct floppy_struct));
+ for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++)
+ put_fs_byte(((char *) this)[cnt],
+ (char *) param+cnt);
+ return 0;
+ case FDFMTTRK:
+ cli();
+ while (format_status != FORMAT_NONE)
+ sleep_on(&format_done);
+ for (cnt = 0; cnt < sizeof(struct format_descr); cnt++)
+ ((char *) &format_req)[cnt] = get_fs_byte(
+ (char *) param+cnt);
+ format_req.device = drive;
+ format_status = FORMAT_WAIT;
+ format_errors = 0;
+ while (format_status != FORMAT_OKAY && format_status !=
+ FORMAT_ERROR) {
+ if (fdc_busy) sleep_on(&fdc_wait);
+ else {
+ fdc_busy = 1;
+ redo_fd_request();
+ }
+ }
+ while (format_status != FORMAT_OKAY && format_status !=
+ FORMAT_ERROR)
+ sleep_on(&format_done);
+ sti();
+ okay = format_status == FORMAT_OKAY;
+ format_status = FORMAT_NONE;
+ wake_up(&format_done);
+ return okay ? 0 : -EIO;
+ }
+ if (drive < 0 || drive > 3) return -EINVAL;
+ switch (cmd) {
+ case FDCLRPRM:
+ current_type[drive] = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE;
+ keep_data[drive] = 0;
+ break;
+ case FDSETPRM:
+ case FDDEFPRM:
+ for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++)
+ ((char *) &user_params[drive])[cnt] =
+ get_fs_byte((char *) param+cnt);
+ current_type[drive] = &user_params[drive];
+ floppy_sizes[drive] = user_params[drive].size >> 1;
+ if (cmd == FDDEFPRM) keep_data[drive] = -1;
+ else {
+ cli();
+ while (fdc_busy) sleep_on(&fdc_wait);
+ fdc_busy = 1;
+ sti();
+ outb_p((current_DOR & 0xfc) | drive |
+ (0x10 << drive),FD_DOR);
+ for (cnt = 0; cnt < 1000; cnt++) __asm__("nop");
+ keep_data[drive] = (inb(FD_DIR) & 0x80) ? 1 : 0;
+ outb_p(current_DOR,FD_DOR);
+ fdc_busy = 0;
+ wake_up(&fdc_wait);
+ }
+ break;
+ case FDMSGON:
+ ftd_msg[drive] = 1;
+ break;
+ case FDMSGOFF:
+ ftd_msg[drive] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#define CMOS_READ(addr) ({ \
+outb_p(0x80|addr,0x70); \
+inb_p(0x71); \
+})
+
+static struct floppy_struct *find_base(int drive,int code)
+{
+ struct floppy_struct *base;
+
+ if (code > 0 && code < 5) {
+ base = &floppy_types[(code-1)*2];
+ printk("fd%d is %s",drive,base->name);
+ return base;
+ }
+ printk("fd%d is unknown type %d",drive,code);
+ return NULL;
+}
+
+static void config_types(void)
+{
+ printk("Floppy drive(s): ");
+ base_type[0] = find_base(0,(CMOS_READ(0x10) >> 4) & 15);
+ if (((CMOS_READ(0x14) >> 6) & 1) == 0) base_type[0] = NULL;
+ else {
+ printk(", ");
+ base_type[1] = find_base(1,CMOS_READ(0x10) & 15);
+ }
+ base_type[2] = base_type[3] = NULL;
+ printk("\r\n");
+}
+
+static int floppy_open(struct inode * inode, struct file * filp)
+{
+ if (filp->f_mode)
+ check_disk_change(inode->i_rdev);
+ return 0;
+}
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+ sync_dev(inode->i_rdev);
+}
static struct file_operations floppy_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
- NULL, /* close - default */
NULL, /* select */
- NULL /* ioctl */
+ fd_ioctl, /* ioctl */
+ floppy_open, /* open */
+ floppy_release /* release */
};
void floppy_init(void)
@@ -568,6 +980,9 @@ void floppy_init(void)
blk_size[MAJOR_NR] = floppy_sizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
blkdev_fops[MAJOR_NR] = &floppy_fops;
+ timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
+ timer_active &= ~(1 << FLOPPY_TIMER);
+ config_types();
set_intr_gate(0x26,&floppy_interrupt);
outb(inb_p(0x21)&~0x40,0x21);
}
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index 756ebde..43ce9b7 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -37,6 +37,8 @@ static inline unsigned char CMOS_READ(unsigned char addr)
return inb_p(0x71);
}
+#define HD_DELAY 0
+
/* Max read/write errors/sector */
#define MAX_ERRORS 7
#define MAX_HD 2
@@ -47,6 +49,10 @@ static void bad_rw_intr(void);
static int recalibrate = 0;
static int reset = 0;
+#if (HD_DELAY > 0)
+unsigned long last_req, read_timer();
+#endif
+
/*
* This struct defines the HD's and their types.
*/
@@ -276,6 +282,22 @@ int sys_setup(void * BIOS)
return (0);
}
+#if (HD_DELAY > 0)
+unsigned long read_timer(void)
+{
+ unsigned long t;
+ int i;
+
+ cli();
+ outb_p(0xc2, 0x43);
+ t = jiffies * 11931 + (inb_p(0x40) & 0x80 ? 5966 : 11932);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ sti();
+ return(t - i / 2);
+}
+#endif
+
static int controller_ready(void)
{
int retries = 100000;
@@ -308,6 +330,10 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
if (drive>1 || head>15)
panic("Trying to write bad sector");
+#if (HD_DELAY > 0)
+ while (read_timer() - last_req < HD_DELAY)
+ /* nothing */;
+#endif
if (reset || !controller_ready()) {
reset = 1;
return;
@@ -321,7 +347,7 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
- outb(cmd,++port);
+ outb_p(cmd,++port);
}
static int drive_busy(void)
@@ -343,6 +369,7 @@ static void reset_controller(void)
{
int i;
+ printk("HD-controller reset\r\n");
outb(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
@@ -396,49 +423,90 @@ static void bad_rw_intr(void)
if (!CURRENT)
return;
if (++CURRENT->errors >= MAX_ERRORS)
- end_request(0);
+ if (CURRENT->bh && CURRENT->nr_sectors > 2) {
+ CURRENT->nr_sectors &= ~1;
+ next_buffer(0);
+ } else
+ end_request(0);
if (CURRENT->errors > MAX_ERRORS/2)
reset = 1;
else
recalibrate = 1;
}
+#define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
+#define STAT_OK (READY_STAT | SEEK_STAT)
+
static void read_intr(void)
{
- SET_INTR(&read_intr);
- if (win_result()) {
- SET_INTR(NULL);
- bad_rw_intr();
- do_hd_request();
- return;
- }
+ int i;
+
+ i = (unsigned) inb_p(HD_STATUS);
+ if (!(i & DRQ_STAT))
+ goto bad_read;
+ if ((i & STAT_MASK) != STAT_OK)
+ goto bad_read;
port_read(HD_DATA,CURRENT->buffer,256);
+ i = (unsigned) inb_p(HD_STATUS);
+ if (!(i & BUSY_STAT))
+ if ((i & STAT_MASK) != STAT_OK)
+ goto bad_read;
CURRENT->errors = 0;
- CURRENT->buffer += 512;
+ if (CURRENT->bh && (CURRENT->nr_sectors&1) && CURRENT->nr_sectors > 2)
+ next_buffer(1);
+ else
+ CURRENT->buffer += 512;
CURRENT->sector++;
- if (--CURRENT->nr_sectors)
+ if (--CURRENT->nr_sectors) {
+ SET_INTR(&read_intr);
return;
- SET_INTR(NULL);
+ }
end_request(1);
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
do_hd_request();
+ return;
+bad_read:
+ if (i & ERR_STAT)
+ i = (unsigned) inb(HD_ERROR);
+ bad_rw_intr();
+ do_hd_request();
+ return;
}
static void write_intr(void)
{
- if (win_result()) {
- bad_rw_intr();
+ int i;
+
+ i = (unsigned) inb_p(HD_STATUS);
+ if ((i & STAT_MASK) != STAT_OK)
+ goto bad_write;
+ if (CURRENT->nr_sectors < 2) {
+ end_request(1);
+#if (HD_DELAY > 0)
+ last_req = read_timer();
+#endif
do_hd_request();
return;
}
- if (--CURRENT->nr_sectors) {
- CURRENT->sector++;
+ if (!(i & DRQ_STAT))
+ goto bad_write;
+ CURRENT->sector++;
+ CURRENT->nr_sectors--;
+ if (CURRENT->bh && !(CURRENT->nr_sectors & 1))
+ next_buffer(1);
+ else
CURRENT->buffer += 512;
- SET_INTR(&write_intr);
- port_write(HD_DATA,CURRENT->buffer,256);
- return;
- }
- end_request(1);
+ SET_INTR(&write_intr);
+ port_write(HD_DATA,CURRENT->buffer,256);
+ return;
+bad_write:
+ if (i & ERR_STAT)
+ i = (unsigned) inb(HD_ERROR);
+ bad_rw_intr();
do_hd_request();
+ return;
}
static void recal_intr(void)
@@ -461,7 +529,11 @@ static void hd_times_out(void)
printk("HD timeout\n\r");
cli();
if (++CURRENT->errors >= MAX_ERRORS)
- end_request(0);
+ if (CURRENT->bh && CURRENT->nr_sectors > 2) {
+ CURRENT->nr_sectors &= ~1;
+ next_buffer(0);
+ } else
+ end_request(0);
do_hd_request();
}
@@ -494,8 +566,7 @@ static void do_hd_request(void)
}
if (recalibrate) {
recalibrate = 0;
- hd_out(dev,hd_info[dev].sect,0,0,0,
- WIN_RESTORE,&recal_intr);
+ hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
if (reset)
goto repeat;
return;
@@ -532,6 +603,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return -EINVAL;
switch (cmd) {
case HDIO_REQ:
+ verify_area(loc, sizeof(*loc));
put_fs_byte(hd_info[dev].head,
(char *) &loc->heads);
put_fs_byte(hd_info[dev].sect,
@@ -544,14 +616,24 @@ static int hd_ioctl(struct inode * inode, struct file * file,
}
}
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void hd_release(struct inode * inode, struct file * file)
+{
+ sync_dev(inode->i_rdev);
+}
+
static struct file_operations hd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
- NULL, /* close - default */
NULL, /* select */
- hd_ioctl /* ioctl */
+ hd_ioctl, /* ioctl */
+ NULL, /* no special open code */
+ hd_release /* release */
};
void hd_init(void)
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index 475863c..18f58d4 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -36,7 +36,10 @@ struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{ NULL, NULL }, /* dev hd */
{ NULL, NULL }, /* dev ttyx */
{ NULL, NULL }, /* dev tty */
- { NULL, NULL } /* dev lp */
+ { NULL, NULL }, /* dev lp */
+ { NULL, NULL }, /* dev pipes */
+ { NULL, NULL }, /* dev sd */
+ { NULL, NULL } /* dev st */
};
/*
@@ -128,6 +131,23 @@ static void make_request(int major,int rw, struct buffer_head * bh)
return;
}
repeat:
+ cli();
+ if (major == 3 && (req = blk_dev[major].current_request)) {
+ while (req = req->next) {
+ if (req->dev == bh->b_dev &&
+ !req->waiting &&
+ req->cmd == rw &&
+ req->sector + req->nr_sectors == bh->b_blocknr << 1 &&
+ req->nr_sectors < 254) {
+ req->bhtail->b_reqnext = bh;
+ req->bhtail = bh;
+ req->nr_sectors += 2;
+ bh->b_dirt = 0;
+ sti();
+ return;
+ }
+ }
+ }
/* we don't allow the write-requests to fill up the queue completely:
* we want some room for reads: they take precedence. The last third
* of the requests are only for reads.
@@ -137,7 +157,6 @@ repeat:
else
req = request+(NR_REQUEST/2);
/* find an empty request */
- cli();
while (--req >= request)
if (req->dev < 0)
goto found;
@@ -161,6 +180,7 @@ found: sti();
req->buffer = bh->b_data;
req->waiting = NULL;
req->bh = bh;
+ req->bhtail = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
@@ -206,6 +226,8 @@ void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major;
+ if (!bh)
+ return;
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
!(blk_dev[major].request_fn)) {
printk("ll_rw_block: Trying to read nonexistent block-device\n\r");
diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c
index c507631..8448022 100644
--- a/kernel/blk_drv/ramdisk.c
+++ b/kernel/blk_drv/ramdisk.c
@@ -52,9 +52,10 @@ static struct file_operations rd_fops = {
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
- NULL, /* close - default */
NULL, /* select */
- NULL /* ioctl */
+ NULL, /* ioctl */
+ NULL, /* no special open code */
+ NULL /* no special release code */
};
/*
diff --git a/kernel/blk_drv/scsi/Makefile b/kernel/blk_drv/scsi/Makefile
new file mode 100644
index 0000000..acfeec7
--- /dev/null
+++ b/kernel/blk_drv/scsi/Makefile
@@ -0,0 +1,149 @@
+#
+# Makefile for the linux kernel block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+#DEBUG = -DDEBUG=0xffffffff -DDEBUG_NO_CMD
+
+AR =ar
+AS =as
+LD =ld
+LDFLAGS =-s -x
+CC =cc
+
+CPP =cc -E -nostdinc -I../../../include
+
+.c.s:
+ $(CC) -nostdinc $(CFLAGS) $(DEBUG) \
+ -S -o $*.s $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) -nostdinc -I../../../include $(CFLAGS) $(DEBUG) \
+ -c -o $*.o $<
+
+LOWLEVELCSRC = aha1542.c seagate.c ultrastor.c
+LOWLEVELHSRC = aha1542.c seagate.h ultrastor.h
+
+CSRC = hosts.c sd.c st.c scsi.c $(LOWLEVELCSRC)
+HSRC = hosts.h sd.h st.h scsi.h $(LOWLEVELHSRC)
+
+OBJS = scsi.o hosts.o scsi_ioctl.o sd.o sd_ioctl.o st.o st_ioctl.o \
+ aha1542.o seagate.o ultrastor.o
+
+all: scsi.a
+
+config.out : config.in ../../../include/linux/config.h
+ rm -f foo.c
+ ln -s config.in foo.c
+ $(CPP) foo.c | grep '\.o' > config.out
+ rm foo.c
+
+figure : hosts.h ../../../include/linux/config.h hosts.c config.out
+ $(CC) -I../../../include -DFIGURE_MAX_SCSI_HOSTS hosts.c -o figure
+
+max_hosts.h : figure
+ (echo "#ifndef _MAX_HOSTS_H"; \
+ echo "#define _MAX_HOSTS_H"; \
+ echo "#define MAX_SCSI_HOSTS `./figure`";\
+ echo "#endif") > tmp_max
+ cp tmp_max max_hosts.h
+
+scsi.a: $(OBJS)
+ $(AR) rcs scsi.a $(OBJS)
+ sync
+
+scsi.shar: Makefile scsi.doc $(CSRC) $(HSRC) ../ll_rw_blk.c ../blk.h all.diff
+ (cd ..; shar scsi/scsi.doc scsi/Makefile scsi/*.{c,h} scsi/all.diff blk.h ll_rw_blk.c) > scsi.shar;
+
+clean:
+ rm -f core *.o *.a tmp_make tmp_max figure config.out Makefile.tag max_hosts.h
+
+seagate.s seagate.o : seagate.c ../../../include/linux/config.h \
+ ../../../include/linux/config.dist.h ../../../include/linux/sched.h \
+ ../../../include/linux/head.h ../../../include/linux/fs.h \
+ ../../../include/sys/types.h ../../../include/linux/mm.h \
+ ../../../include/linux/kernel.h ../../../include/signal.h \
+ ../../../include/sys/param.h ../../../include/sys/time.h \
+ ../../../include/time.h ../../../include/sys/resource.h \
+ ../../../include/linux/string.h seagate.h scsi.h hosts.h max_hosts.h
+ cc -nostdinc -I../../../include -Wall -c seagate.c $(DEBUG)
+
+dep:
+ sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+ (for i in *.c ;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
+ $(CPP) -M $$i;done) >> tmp_make
+ cp tmp_make Makefile
+
+### Dependencies:
+aha1542.s aha1542.o : aha1542.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/kernel.h \
+ ../../../include/linux/head.h ../../../include/linux/string.h ../../../include/asm/system.h \
+ ../../../include/asm/io.h scsi.h hosts.h max_hosts.h aha1542.h
+hosts.s hosts.o : hosts.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/kernel.h \
+ scsi.h hosts.h max_hosts.h aha1542.h seagate.h ultrastor.h
+scsi.s scsi.o : scsi.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/asm/system.h \
+ ../../../include/linux/sched.h ../../../include/linux/head.h ../../../include/linux/fs.h \
+ ../../../include/sys/types.h ../../../include/sys/dirent.h ../../../include/limits.h \
+ ../../../include/linux/mm.h ../../../include/linux/kernel.h ../../../include/signal.h \
+ ../../../include/sys/param.h ../../../include/sys/time.h ../../../include/time.h \
+ ../../../include/sys/resource.h ../../../include/linux/timer.h ../../../include/linux/string.h \
+ scsi.h hosts.h max_hosts.h sd.h st.h
+scsi_ioctl.s scsi_ioctl.o : scsi_ioctl.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/errno.h \
+ ../../../include/asm/io.h ../../../include/asm/segment.h ../../../include/asm/system.h \
+ ../../../include/linux/kernel.h ../../../include/linux/sched.h ../../../include/linux/head.h \
+ ../../../include/linux/fs.h ../../../include/sys/types.h ../../../include/sys/dirent.h \
+ ../../../include/limits.h ../../../include/linux/mm.h ../../../include/signal.h \
+ ../../../include/sys/param.h ../../../include/sys/time.h ../../../include/time.h \
+ ../../../include/sys/resource.h ../../../include/linux/string.h scsi.h hosts.h \
+ max_hosts.h scsi_ioctl.h
+sd.s sd.o : sd.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/string.h \
+ ../../../include/linux/fs.h ../../../include/sys/types.h ../../../include/sys/dirent.h \
+ ../../../include/limits.h ../../../include/linux/kernel.h ../../../include/linux/sched.h \
+ ../../../include/linux/head.h ../../../include/linux/mm.h ../../../include/signal.h \
+ ../../../include/sys/param.h ../../../include/sys/time.h ../../../include/time.h \
+ ../../../include/sys/resource.h scsi.h sd.h ../blk.h
+sd_ioctl.s sd_ioctl.o : sd_ioctl.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/kernel.h \
+ ../../../include/linux/sched.h ../../../include/linux/head.h ../../../include/linux/fs.h \
+ ../../../include/sys/types.h ../../../include/sys/dirent.h ../../../include/limits.h \
+ ../../../include/linux/mm.h ../../../include/signal.h ../../../include/sys/param.h \
+ ../../../include/sys/time.h ../../../include/time.h ../../../include/sys/resource.h \
+ scsi.h sd.h
+seagate.s seagate.o : seagate.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/sched.h \
+ ../../../include/linux/head.h ../../../include/linux/fs.h ../../../include/sys/types.h \
+ ../../../include/sys/dirent.h ../../../include/limits.h ../../../include/linux/mm.h \
+ ../../../include/linux/kernel.h ../../../include/signal.h ../../../include/sys/param.h \
+ ../../../include/sys/time.h ../../../include/time.h ../../../include/sys/resource.h \
+ seagate.h scsi.h hosts.h max_hosts.h
+st.s st.o : st.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h scsi.h \
+ st.h ../../../include/linux/fs.h ../../../include/sys/types.h ../../../include/sys/dirent.h \
+ ../../../include/limits.h ../../../include/linux/kernel.h ../../../include/linux/sched.h \
+ ../../../include/linux/head.h ../../../include/linux/mm.h ../../../include/signal.h \
+ ../../../include/sys/param.h ../../../include/sys/time.h ../../../include/time.h \
+ ../../../include/sys/resource.h ../blk.h
+st_ioctl.s st_ioctl.o : st_ioctl.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/linux/kernel.h \
+ ../../../include/linux/sched.h ../../../include/linux/head.h ../../../include/linux/fs.h \
+ ../../../include/sys/types.h ../../../include/sys/dirent.h ../../../include/limits.h \
+ ../../../include/linux/mm.h ../../../include/signal.h ../../../include/sys/param.h \
+ ../../../include/sys/time.h ../../../include/time.h ../../../include/sys/resource.h \
+ st.h scsi.h
+ultrastor.s ultrastor.o : ultrastor.c ../../../include/linux/config.h ../../../include/linux/config_rel.h \
+ ../../../include/linux/config_ver.h ../../../include/linux/config.dist.h ../../../include/stddef.h \
+ ../../../include/linux/string.h ../../../include/linux/sched.h ../../../include/linux/head.h \
+ ../../../include/linux/fs.h ../../../include/sys/types.h ../../../include/sys/dirent.h \
+ ../../../include/limits.h ../../../include/linux/mm.h ../../../include/linux/kernel.h \
+ ../../../include/signal.h ../../../include/sys/param.h ../../../include/sys/time.h \
+ ../../../include/time.h ../../../include/sys/resource.h ../../../include/linux/hdreg.h \
+ ../../../include/asm/system.h ../../../include/asm/io.h ../../../include/asm/segment.h \
+ ultrastor.h scsi.h hosts.h max_hosts.h
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
new file mode 100644
index 0000000..9547dda
--- /dev/null
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -0,0 +1,481 @@
+/* $Id: aha1542.c,v 1.1 1992/04/24 18:01:50 root Exp root $
+ * linux/kernel/aha1542.c
+ *
+ * (C) 1992 Tommy Thorn
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aha1542.h"
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1542.c,v 1.1 1992/04/24 18:01:50 root Exp root $";
+*/
+
+#define base 0x330
+#define intr_chan 11
+
+static struct mailbox mb[2];
+static struct ccb ccb;
+
+long WAITtimeout, WAITnexttimeout = 3000000;
+
+void (*do_done)() = NULL;
+extern void aha1542_interrupt();
+
+#define aha1542_intr_reset() outb(IRST, CONTROL)
+#define aha1542_enable_intr() outb(inb_p(0xA1) & ~8, 0xA1)
+#define aha1542_disable_intr() outb(inb_p(0xA1) | 8, 0xA1)
+
+#define WAIT(port, mask, allof, noneof) \
+ { register WAITbits; \
+ register WAITtimeout = WAITnexttimeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+static void aha1542_stat(void)
+{
+ int s = inb(STATUS), i = inb(INTRFLAGS);
+/* printk("status = %x, intrflags = %x served %d last %x timeout %d\n", s, i, intr_flag, intr_last, WAITtimeout); */
+ printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout);
+}
+
+static int aha1542_out(unchar *cmdp, int len)
+{
+ while (len--)
+ {
+ WAIT(STATUS, CDF, 0, CDF);
+ outb(*cmdp++, DATA);
+ }
+ return 0;
+ fail:
+ printk("aha1542_out failed(%d): ", len+1); aha1542_stat();
+ return 1;
+}
+
+int makecode(unsigned hosterr, unsigned scsierr)
+{
+ switch (hosterr) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error, interrupt generated */
+ hosterr = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ hosterr = DID_TIME_OUT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ thean was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+
+ case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was
+ invalid. This usually indicates a software failure. */
+
+ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
+ This usually indicates a software failure. */
+
+ case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set
+ of linked CCB's does not specify the same logical unit number as
+ the first. */
+ case 0x18: /* Invalid Target Direction received from Host-The direction of a
+ Target Mode CCB was invalid. */
+
+ case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was
+ received to service data transfer between the same target LUN
+ and initiator SCSI ID in the same direction. */
+
+ case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero
+ length segment or invalid segment list boundaries was received.
+ A CCB parameter was invalid. */
+ hosterr = DID_ERROR; /* Couldn't find any better */
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ hosterr = DID_RESET;
+ break;
+ default:
+ printk("makecode: unknown hoststatus %x\n", hosterr);
+ break;
+ }
+ return scsierr|(hosterr << 16);
+}
+
+int aha1542_test_port(void)
+{
+ volatile int debug = 0;
+
+ /* Reset the adapter. I ought to make a hard reset, but it's not really nessesary */
+
+ /* DEB(printk("aha1542_test_port called \n")); */
+
+ outb(SRST|IRST/*|SCRST*/, CONTROL);
+
+ debug = 1;
+ /* Expect INIT and IDLE, any of the others are bad */
+ WAIT(STATUS, STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF);
+
+ debug = 2;
+ /* Shouldn't have generated any interrupts during reset */
+ if (inb(INTRFLAGS)&INTRMASK) goto fail;
+
+ debug = 3;
+ /* Test the basic ECHO command */
+ outb(CMD_ECHO, DATA);
+
+ debug = 4;
+ /* Wait for CDF=0. If any of the others are set, it's bad */
+ WAIT(STATUS, STATMASK, 0, STST|DIAGF|INVDCMD|DF|CDF);
+
+ debug = 5;
+ /* The meaning of life */
+ outb(42, DATA);
+
+ debug = 6;
+ /* Expect only DF, that is, data ready */
+ WAIT(STATUS, STATMASK, DF, STST|DIAGF|CDF|INVDCMD);
+
+ debug = 7;
+ /* Is the answer correct? */
+ if (inb(DATA) != 42) goto fail;
+
+ debug = 8;
+ /* Reading port should reset DF */
+ if (inb(STATUS) & DF) goto fail;
+
+ debug = 9;
+ /* When HACC, command is completed, and we're though testing */
+ WAIT(INTRFLAGS, HACC, HACC, 0);
+ /* now initialize adapter */
+
+ debug = 10;
+ /* Clear interrupts */
+ outb(IRST, CONTROL);
+
+ debug = 11;
+
+ return debug; /* 1 = ok */
+ fail:
+ return 0; /* 0 = not ok */
+}
+
+/* What's this little function for? */
+char *aha1542_info(void)
+{
+ static char buffer[] = "Adaptec 1542";
+ return buffer;
+}
+
+/* A "high" level interrupt handler */
+void aha1542_intr_handle(void)
+{
+ int flag = inb(INTRFLAGS);
+ void (*my_done)() = do_done;
+ int errstatus;
+
+ do_done = NULL;
+#ifdef DEBUG
+ printk("aha1542_intr_handle: ");
+ if (!(flag&ANYINTR)) printk("no interrupt?");
+ if (flag&MBIF) printk("MBIF ");
+ if (flag&MBOA) printk("MBOF ");
+ if (flag&HACC) printk("HACC ");
+ if (flag&SCRD) printk("SCRD ");
+ printk("status %02x\n", inb(STATUS));
+ if (ccb.tarstat|ccb.hastat)
+ printk("aha1542_command: returning %x (status %d)\n", ccb.tarstat + ((int) ccb.hastat << 16), mb[1].status);
+#endif
+ aha1542_intr_reset();
+ if (!my_done) {
+ printk("aha1542_intr_handle: Unexpected interrupt\n");
+ return;
+ }
+
+ /* is there mail :-) */
+
+ if (!mb[1].status) {
+ DEB(printk("aha1542_intr_handle: strange: mbif but no mail!\n"));
+ my_done(DID_TIME_OUT << 16);
+ return;
+ }
+
+ /* more error checking left out here */
+ if (mb[1].status != 1)
+ /* This is surely wrong, but I don't know what's right */
+ errstatus = makecode(ccb.hastat, ccb.tarstat);
+ else
+ errstatus = 0;
+
+ mb[1].status = 0;
+
+ if (ccb.tarstat == 2) {
+ int i;
+ DEB(printk("aha1542_intr_handle: sense:"));
+ for (i = 0; i < 12; i++)
+ printk("%02x ", ccb.cdb[ccb.cdblen+i]);
+ printk("\n");
+/*
+ DEB(printk("aha1542_intr_handle: buf:"));
+ for (i = 0; i < bufflen; i++)
+ printk("%02x ", ((unchar *)buff)[i]);
+ printk("\n");
+*/
+ }
+ DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus));
+ my_done(errstatus);
+ return;
+}
+
+int aha1542_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, void (*done)(int))
+{
+ unchar ahacmd = CMD_START_SCSI;
+ int i;
+ unchar *cmd = (unchar *) cmnd;
+
+ DEB(if (target > 1) {done(DID_TIME_OUT << 16); return 0;});
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ if (done)
+ printk("aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ else
+ printk("aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ aha1542_stat();
+ printk("aha1542_queuecommand: dumping scsi cmd:");
+ for (i = 0; i < (*cmd<=0x1f?6:10); i++) printk("%02x ", cmd[i]);
+ printk("\n");
+ if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ return 0; /* we are still testing, so *don't* write */
+#endif
+ memset(&ccb, 0, sizeof ccb);
+
+ ccb.cdblen = (*cmd<=0x1f)?6:10; /* SCSI Command Descriptor Block Length */
+
+ memcpy(ccb.cdb, cmd, ccb.cdblen);
+ ccb.op = 0; /* SCSI Initiator Command */
+ ccb.idlun = (target&7)<<5; /* SCSI Target Id */
+ ccb.rsalen = 12;
+ any2scsi(ccb.datalen, bufflen);
+ any2scsi(ccb.dataptr, buff);
+ ccb.linkptr[0] = ccb.linkptr[1] = ccb.linkptr[2] = 0;
+ ccb.commlinkid = 0;
+
+ mb[0].status = 1;
+ mb[1].status = 0;
+
+#ifdef DEBUGd
+ printk("aha1542_command: sending.. ");
+ for (i = 0; i < sizeof(ccb)-10; i++)
+ printk("%02x ", ((unchar *)&ccb)[i]);
+#endif
+
+ if (done) {
+ DEB(printk("aha1542_queuecommand: now waiting for interrupt "); aha1542_stat());
+ if (do_done)
+ printk("aha1542_queuecommand: Two concurrent queuecommand?\n");
+ else
+ do_done = done;
+ aha1542_out(&ahacmd, 1); /* start scsi command */
+ DEB(aha1542_stat());
+ aha1542_enable_intr();
+ }
+ else
+ printk("aha1542_queuecommand: done can't be NULL\n");
+
+ return 0;
+}
+
+volatile static int internal_done_flag = 0;
+volatile static int internal_done_errcode = 0;
+static void internal_done(int errcode)
+{
+ internal_done_errcode = errcode;
+ ++internal_done_flag;
+}
+
+int aha1542_command(unchar target, const void *cmnd, void *buff, int bufflen)
+{
+ DEB(printk("aha1542_command: ..calling aha1542_queuecommand\n"));
+ aha1542_queuecommand(target, cmnd, buff, bufflen, internal_done);
+
+ while (!internal_done_flag);
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+/* Initialize mailboxes */
+static void setup_mailboxes()
+{
+ static unchar cmd[5] = {CMD_MBINIT, 1};
+
+ mb[0].status = mb[1].status = 0;
+ aha1542_intr_reset(); /* reset interrupts, so they don't block */
+ any2scsi((cmd+2), mb);
+ any2scsi(mb[0].ccbptr, &ccb);
+ aha1542_out(cmd, 5);
+ WAIT(INTRFLAGS, INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: failed setting up mailboxes\n");
+ }
+ aha1542_intr_reset();
+}
+
+/* a hack to avoid a strange compilation error */
+
+void call_buh()
+{
+ set_intr_gate(0x2b,&aha1542_interrupt);
+}
+
+/* return non-zero on detection */
+int aha1542_detect(int hostnum) /* hostnum ignored for now */
+{
+ int i;
+
+ DEB(printk("aha1542_detect: \n"));
+
+ if (!(i = aha1542_test_port())) {
+ return 0;
+ }
+
+ /* Set the Bus on/off-times as not to ruin floppy performens */
+ {
+ static unchar oncmd[] = {CMD_BUSON_TIME, 5};
+ static unchar offcmd[] = {CMD_BUSOFF_TIME, 9};
+
+ aha1542_intr_reset();
+ aha1542_out(oncmd, 2);
+ WAIT(INTRFLAGS, INTRMASK, HACC, 0);
+ aha1542_intr_reset();
+ aha1542_out(offcmd, 2);
+ WAIT(INTRFLAGS, INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: setting bus on/off-time failed\n");
+ }
+ aha1542_intr_reset();
+ }
+
+ aha1542_stat();
+ setup_mailboxes();
+
+ aha1542_stat();
+
+ DEB(printk("aha1542_detect: enable interrupt channel %d\n", intr_chan));
+ call_buh();
+
+ if (intr_chan >= 8)
+ outb(inb_p(0x21)&0xfb,0x21); /* open for slave ?? */
+
+ DEB(printk("aha1542_detect: enabling interrupts\n"));
+ aha1542_enable_intr();
+
+#ifdef DEBUG
+ DEB(printk(" *** READ CAPACITY ***\n"));
+
+ {
+ unchar buf[8];
+ static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i;
+
+ for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87;
+ for (i = 0; i < 2; ++i)
+ if (!aha1542_command(i, cmd, buf, sizeof(buf))) {
+ printk("aha_detect: LU %d sector_size %d device_size %d\n",
+ i, xscsi2int(buf+4), xscsi2int(buf));
+ }
+ }
+
+ DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n"));
+
+ for (i = 0; i < 4; ++i)
+ {
+ unsigned char cmd[10];
+ static buffer[512];
+
+ cmd[0] = READ_10;
+ cmd[1] = 0;
+ xany2scsi(cmd+2, i);
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 1;
+ cmd[9] = 0;
+ aha1542_command(0, cmd, buffer, 512);
+ }
+#endif
+ return 1;
+}
+
+int aha1542_abort(int i)
+{
+ DEB(printk("aha1542_abort\n"));
+ return 0;
+}
+
+int aha1542_reset(void)
+{
+ DEB(printk("aha1542_reset called\n"));
+ return 0;
+}
+
+__asm__("
+_aha1542_interrupt:
+ cld
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ push %ds
+ push %es
+ push %fs
+ movl $0x10,%eax
+ mov %ax,%ds
+ mov %ax,%es
+ movl $0x17,%eax
+ mov %ax,%fs
+ movb $0x20,%al
+ outb %al,$0xA0 # EOI to interrupt controller #1
+ jmp 1f # give port chance to breathe
+1: jmp 1f
+1: outb %al,$0x20
+# Please, someone, change this to use the timer
+# andl $0xfffeffff,_timer_active
+ movl $_aha1542_intr_handle,%edx
+ call *%edx # ``interesting'' way of handling intr.
+ pop %fs
+ pop %es
+ pop %ds
+ popl %edx
+ popl %ecx
+ popl %eax
+ iret
+");
diff --git a/kernel/blk_drv/scsi/aha1542.h b/kernel/blk_drv/scsi/aha1542.h
new file mode 100644
index 0000000..6609a4e
--- /dev/null
+++ b/kernel/blk_drv/scsi/aha1542.h
@@ -0,0 +1,124 @@
+#ifndef _AHA1542_H
+
+/* $Id: aha1542.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+ *
+ * Header file for the adaptec 1542 driver for Linux
+ *
+ * $Log: aha1542.h,v $
+ * Revision 1.1 1992/04/24 18:01:50 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/02 03:23:13 drew
+ * Initial revision
+ *
+ * Revision 1.3 1992/01/27 14:46:29 tthorn
+ * *** empty log message ***
+ *
+ */
+
+typedef unsigned char unchar;
+
+/* I/O Port interface 4.2 */
+/* READ */
+#define STATUS base
+#define STST 0x80 /* Self Test in Progress */
+#define DIAGF 0x40 /* Internal Diagonostic Failure */
+#define INIT 0x20 /* Mailbox Initialization Required */
+#define IDLE 0x10 /* SCSI Host Adapter Idle */
+#define CDF 0x08 /* Command/Data Out Port Full */
+#define DF 0x04 /* Data In Port Full */
+#define INVDCMD 0x01 /* Invalid H A Command */
+#define STATMASK 0xfd /* 0x02 is reserved */
+
+#define INTRFLAGS STATUS+2
+#define ANYINTR 0x80 /* Any Interrupt */
+#define SCRD 0x08 /* SCSI Reset Detected */
+#define HACC 0x04 /* HA Command Complete */
+#define MBOA 0x02 /* MBO Empty */
+#define MBIF 0x01 /* MBI Full */
+#define INTRMASK 0x8f
+
+/* WRITE */
+#define CONTROL STATUS
+#define HRST 0x80 /* Hard Reset */
+#define SRST 0x40 /* Soft Reset */
+#define IRST 0x20 /* Interrupt Reset */
+#define SCRST 0x10 /* SCSI Bus Reset */
+
+/* READ/WRITE */
+#define DATA STATUS+1
+#define CMD_NOP 0x00 /* No Operation */
+#define CMD_MBINIT 0x01 /* Mailbox Initialization */
+#define CMD_START_SCSI 0x02 /* Start SCSI Command */
+#define CMD_INQUIRY 0x04 /* Adapter Inquiry */
+#define CMD_EMBOI 0x05 /* Enable MailBox Out Interrupt */
+#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */
+#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */
+#define CMD_RETDEVS 0x0a /* Return Installed Devices */
+#define CMD_RETCONF 0x0b /* Return Configuration Data */
+#define CMD_RETSETUP 0x0d /* Return Setup Data */
+#define CMD_ECHO 0x1f /* ECHO Command Data */
+
+/* Mailbox Definition 5.2.1 and 5.2.2 */
+struct mailbox {
+ unchar status; /* Command/Status */
+ unchar ccbptr[3]; /* msb, .., lsb */
+};
+
+/* These belong in scsi.h also */
+#define any2scsi(up, p) \
+(up)[0] = (((long)(p)) >> 16) & 0xff; \
+(up)[1] = ((long)(p)) >> 8; \
+(up)[2] = ((long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+struct ccb { /* Command Control Block 5.3 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked*/
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdblen; /* SCSI Command Length */
+ unchar rsalen; /* Request Sense Allocation Length/Disable */
+ unchar datalen[3]; /* Data Length (msb, .., lsb) */
+ unchar dataptr[3]; /* Data Pointer */
+ unchar linkptr[3]; /* Link Pointer */
+ unchar commlinkid; /* Command Linking Identifier */
+ unchar hastat; /* Host Adapter Status (HASTAT) */
+ unchar tarstat; /* Target Device Status */
+ unchar reserved[2];
+ unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */
+ /* REQUEST SENSE */
+};
+
+int aha1542_detect(int);
+int aha1542_command(unsigned char target, const void *cmnd, void *buff, int bufflen);
+/*int aha1542_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, void (*done)(int));*/
+int aha1542_abort(int);
+char *aha1542_info(void);
+int aha1542_reset(void);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#define AHA1542 {"Adaptec 1542", aha1542_detect, \
+ aha1542_info, aha1542_command, \
+ /*aha1542_queuecommand*/ NULL, \
+ aha1542_abort, \
+ aha1542_reset, \
+ 0, 7, 0}
+#endif
diff --git a/kernel/blk_drv/scsi/config.in b/kernel/blk_drv/scsi/config.in
new file mode 100644
index 0000000..75c1fde
--- /dev/null
+++ b/kernel/blk_drv/scsi/config.in
@@ -0,0 +1,29 @@
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI
+scsi.o
+hosts.o
+scsi_ioctl.o
+#endif
+
+#ifdef CONFIG_BLK_DEV_SD
+sd.o
+sd_ioctl.o
+#endif
+
+#ifdef CONFIG_BLK_DEV_ST
+st.o
+st_ioctl.o
+#endif
+
+#ifdef CONFIG_SCSI_AHA1542
+aha1542.o
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+seagate.o
+#endif
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+ultrastor.o
+#endif
diff --git a/kernel/blk_drv/scsi/hosts.c b/kernel/blk_drv/scsi/hosts.c
new file mode 100644
index 0000000..71f7450
--- /dev/null
+++ b/kernel/blk_drv/scsi/hosts.c
@@ -0,0 +1,142 @@
+/*
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * mid to lowlevel SCSI driver interface by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+
+/*
+ This file contains the medium level SCSI
+ host interface initialization, as well as the scsi_hosts array of SCSI
+ hosts currently present in the system.
+*/
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI
+#include <linux/kernel.h>
+#include "scsi.h"
+
+#ifndef NULL
+ #define NULL 0L
+#endif
+
+#ifdef FIGURE_MAX_SCSI_HOSTS
+ #define MAX_SCSI_HOSTS
+#endif
+
+#include "hosts.h"
+
+#ifdef CONFIG_SCSI_AHA1542
+#include "aha1542.h"
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+#include "seagate.h"
+#endif
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+#include "ultrastor.h"
+#endif
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.c,v 1.1 1992/04/24 18:01:50 root Exp root $";
+*/
+
+/*
+ The scsi host entries should be in the order you wish the
+ cards to be detected. A driver may appear more than once IFF
+ it can deal with being detected (and therefore initialized)
+ with more than one simulatenous host number, can handle being
+ rentrant, etc.
+
+ They may appear in any order, as each SCSI host is told which host number it is
+ during detection.
+*/
+
+/*
+ When figure is run, we don't want to link to any object code. Since
+ the macro for each host will contain function pointers, we cannot
+ use it and instead must use a "blank" that does no such
+ idiocy.
+*/
+
+#ifdef FIGURE_MAX_SCSI_HOSTS
+ #define BLANKIFY(what) BLANK_HOST
+#else
+ #define BLANKIFY(what) what
+#endif
+
+Scsi_Host scsi_hosts[] =
+ {
+#ifdef CONFIG_SCSI_AHA1542
+ BLANKIFY(AHA1542),
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+ BLANKIFY(SEAGATE_ST0X),
+#endif
+#ifdef CONFIG_SCSI_ULTRASTOR
+ BLANKIFY(ULTRASTOR_14F),
+#endif
+ };
+
+#ifdef FIGURE_MAX_SCSI_HOSTS
+ #undef MAX_SCSI_HOSTS
+ #define MAX_SCSI_HOSTS (sizeof(scsi_hosts) / sizeof(Scsi_Host))
+#endif
+
+#ifdef FIGURE_MAX_SCSI_HOSTS
+#include <stdio.h>
+void main (void)
+{
+ printf("%d", MAX_SCSI_HOSTS);
+}
+#else
+/*
+ Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here.
+*/
+
+volatile unsigned char host_busy[MAX_SCSI_HOSTS];
+volatile int host_timeout[MAX_SCSI_HOSTS];
+volatile Scsi_Cmnd *host_queue[MAX_SCSI_HOSTS];
+/*
+ scsi_init initializes the scsi hosts.
+*/
+
+void scsi_init(void)
+ {
+ static int called = 0;
+ int i, count;
+ if (!called)
+ {
+ called = 1;
+ for (count = i = 0; i < MAX_SCSI_HOSTS; ++i)
+ {
+ /*
+ Initialize our semaphores. -1 is interpreted to mean
+ "inactive" - where as 0 will indicate a time out condition.
+ */
+
+ host_busy[i] = 0;
+ host_timeout[i] = 0;
+ host_queue[i] = NULL;
+
+ if ((scsi_hosts[i].detect) && (scsi_hosts[i].present = scsi_hosts[i].detect(i)))
+ {
+ printk ("Host %d is detected as a(n) %s.\n\r",
+ count, scsi_hosts[i].name);
+ printk ("%s", scsi_hosts[i].info());
+ ++count;
+ }
+ }
+ printk ("%d host adapters detected. \n\r", count);
+ }
+
+ }
+
+#endif
+
+#endif
diff --git a/kernel/blk_drv/scsi/hosts.h b/kernel/blk_drv/scsi/hosts.h
new file mode 100644
index 0000000..468a044
--- /dev/null
+++ b/kernel/blk_drv/scsi/hosts.h
@@ -0,0 +1,179 @@
+/*
+ * hosts.h Copyright (C) 1992 Drew Eckhardt
+ * mid to low-level SCSI driver interface header by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _HOSTS_H
+ #define _HOSTS_H
+
+#ifndef MAX_SCSI_HOSTS
+ #include "max_hosts.h"
+#endif
+
+/*
+ $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+*/
+
+/*
+ The Scsi_Cmnd structure is used by scsi.c internally, and for communication with
+ low level drivers that support multiple outstanding commands.
+*/
+
+typedef struct scsi_cmnd {
+ int host;
+ unsigned char target, lun;
+ unsigned char cmnd[10];
+ unsigned bufflen;
+ void *buffer;
+
+ unsigned char sense_cmnd[6];
+ unsigned char *sense_buffer;
+
+ unsigned flags;
+
+ int retries;
+ int allowed;
+ int timeout_per_command, timeout_total, timeout;
+
+ void (*done)(int,int);
+ struct scsi_cmnd *next, *prev;
+ } Scsi_Cmnd;
+
+/*
+ The Scsi_Host type has all that is needed to interface with a SCSI
+ host in a device independant matter.
+*/
+
+typedef struct
+ {
+ /*
+ The name pointer is a pointer to the name of the SCSI
+ device detected.
+ */
+
+ char *name;
+
+ /*
+ The detect function shall return non zero on detection,
+ and initialize all data necessary for this particular
+ SCSI driver. It is passed the host number, so this host
+ knows where it is in the hosts array
+ */
+
+ int (* detect)(int);
+
+ /*
+ The info function will return whatever useful
+ information the developer sees fit.
+ */
+
+ char *(* info)(void);
+
+ /*
+ The command function takes a target, a command (this is a SCSI
+ command formatted as per the SCSI spec, nothing strange), a
+ data buffer pointer, and data buffer length pointer. The return
+ is a status int, bit fielded as follows :
+ Byte What
+ 0 SCSI status code
+ 1 SCSI 1 byte message
+ 2 host error return.
+ 3 mid level error return
+ */
+
+ int (* command)(unsigned char target, const void *cmnd,
+ void *buff, int bufflen);
+
+ /*
+ The QueueCommand function works in a similar manner
+ to the command function. It takes an additional parameter,
+ void (* done)(int host, int code) which is passed the host
+ # and exit result when the command is complete.
+ Host number is the POSITION IN THE hosts array of THIS
+ host adapter.
+ */
+
+ int (* queuecommand)(unsigned char target, const void *cmnd,
+ void *buff, int bufflen, void (*done)(int,int));
+
+
+ /*
+ Since the mid level driver handles time outs, etc, we want to
+ be able to abort the current command. Abort returns 0 if the
+ abortion was successful. If non-zero, the code passed to it
+ will be used as the return code, otherwise
+ DID_ABORT should be returned.
+
+ Note that the scsi driver should "clean up" after itself,
+ resetting the bus, etc. if necessary.
+ */
+
+ int (* abort)(int);
+
+ /*
+ The reset function will reset the SCSI bus. Any executing
+ commands should fail with a DID_RESET in the host byte.
+ */
+
+ int (* reset)(void);
+
+ /*
+ This determines if we will use a non-interrupt driven
+ or an interrupt driven scheme, It is set to the maximum number
+ of simulataneous commands a given host adapter will accept.
+ */
+
+ int can_queue;
+
+ /*
+ In many instances, especially where disconnect / reconnect are
+ supported, our host also has an ID on the SCSI bus. If this is
+ the case, then it must be reserved. Please set this_id to -1 if your settup is in single initiator mode, and the host lacks an
+ ID.
+ */
+
+ int this_id;
+
+ /*
+ present contains a flag as to weather we are present -
+ so we don't have to call detect multiple times.
+ */
+
+ unsigned present:1;
+ } Scsi_Host;
+
+/*
+ The scsi_hosts array is the array containing the data for all
+ possible <supported> scsi hosts.
+*/
+
+extern Scsi_Host scsi_hosts[];
+
+/*
+ This is our semaphore array, used by scsi.c, sd.c, st.c.
+ Other routines SHOULD NOT mess with it. Your driver should NOT mess with it.
+ This is used to protect against contention by disk and tape drivers.
+*/
+
+extern volatile unsigned char host_busy[];
+extern volatile int host_timeout[];
+
+/*
+ This is the queue of currently pending commands for a given
+ SCSI host.
+*/
+
+extern volatile Scsi_Cmnd *host_queue[];
+
+/*
+ scsi_init initializes the scsi hosts.
+*/
+
+
+void scsi_init(void);
+
+#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#endif
diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c
new file mode 100644
index 0000000..22f3402
--- /dev/null
+++ b/kernel/blk_drv/scsi/scsi.c
@@ -0,0 +1,1016 @@
+/*
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * generic mid-level SCSI driver by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+
+#include "scsi.h"
+#include "hosts.h"
+
+#ifdef CONFIG_BLK_DEV_SD
+#include "sd.h"
+#endif
+
+#ifdef CONFIG_BLK_DEV_ST
+#include "st.h"
+#endif
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.1 1992/04/24 18:01:50 root Exp root $";
+*/
+
+#define INTERNAL_ERROR (printk ("Internal error in file %s, line %s.\n", __FILE__, __LINE__), panic(""))
+
+static void scsi_done (int host, int result);
+static void update_timeout (void);
+
+static int time_start;
+static int time_elapsed;
+
+/*
+ global variables :
+ NR_SCSI_DEVICES is the number of SCSI devices we have detected,
+ scsi_devices an array of these specifing the address for each
+ (host, id, LUN)
+*/
+
+int NR_SCSI_DEVICES=0;
+Scsi_Device scsi_devices[MAX_SCSI_DEVICE];
+
+#define SENSE_LENGTH 255
+/*
+ As the scsi do command functions are inteligent, and may need to
+ redo a command, we need to keep track of the last command
+ executed on each one.
+*/
+
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
+#define WAS_SENSE 0x04
+#define IS_RESETTING 0x08
+
+static Scsi_Cmnd last_cmnd[MAX_SCSI_HOSTS];
+static int last_reset[MAX_SCSI_HOSTS];
+
+/*
+ This is the number of clock ticks we should wait before we time out
+ and abort the command. This is for where the scsi.c module generates
+ the command, not where it originates from a higher level, in which
+ case the timeout is specified there.
+
+
+ ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
+ respectively.
+*/
+#ifdef DEBUG
+ #define SCSI_TIMEOUT 500
+#else
+ #define SCSI_TIMEOUT 100
+#endif
+#ifdef DEBUG
+ #define SENSE_TIMEOUT SCSI_TIMEOUT
+ #define ABORT_TIMEOUT SCSI_TIMEOUT
+ #define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+
+ #define SENSE_TIMEOUT 50
+ #define RESET_TIMEOUT 50
+ #define ABORT_TIMEOUT 50
+ #define MIN_RESET_DELAY 25
+
+#endif
+/*
+ As the actual SCSI command runs in the background, we must set up a
+ flag that tells scan_scsis() when the result it has is valid.
+ scan_scsis can set the_result to -1, and watch for it to become the
+ actual return code for that call. the scan_scsis_done function() is
+ our user specified completion function that is passed on to the
+ scsi_do_cmd() function.
+*/
+
+static int the_result;
+static unsigned char sense_buffer[SENSE_LENGTH];
+static void scan_scsis_done (int host, int result)
+ {
+
+#ifdef DEBUG
+ printk ("scan_scsis_done(%d, %06x\n\r", host, result);
+#endif
+ the_result = result;
+ }
+/*
+ Detecting SCSI devices :
+ We scan all present host adapter's busses, from ID 0 to ID 6.
+ We use the INQUIRY command, determine device type, and pass the ID /
+ lun address of all sequential devices to the tape driver, all random
+ devices to the disk driver.
+*/
+
+static void scan_scsis (void)
+ {
+ int host_nr , dev, lun, type, maxed;
+ static unsigned char scsi_cmd [12];
+ static unsigned char scsi_result [256];
+
+ for (host_nr = 0; host_nr < MAX_SCSI_HOSTS; ++host_nr)
+ if (scsi_hosts[host_nr].present)
+ {
+ for (dev = 0; dev < 7; ++dev)
+ if (scsi_hosts[host_nr].this_id != dev)
+ #ifdef MULTI_LUN
+ for (lun = 0; lun < 8; ++lun)
+ {
+ #else
+ {
+ lun = 0;
+ #endif
+ /* Build an INQUIRY command block. */
+
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 255;
+ scsi_cmd[5] = 0;
+ the_result = -1;
+#ifdef DEBUG
+ memset ((void *) scsi_result , 0, 255);
+#endif
+ scsi_do_cmd (host_nr, dev, (void *) scsi_cmd, (void *)
+ scsi_result, 256, scan_scsis_done,
+ SCSI_TIMEOUT, sense_buffer, 3);
+
+ /* Wait for valid result */
+
+ while (the_result < 0);
+
+
+ if (!the_result)
+ {
+ 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].
+ removable = (0x80 &
+ scsi_result[1]) >> 7;
+
+
+
+/*
+ Currently, all sequential devices are assumed to be tapes,
+ all random devices disk, with the appropriate read only
+ flags set for ROM / WORM treated as RO.
+*/
+
+ switch (type = scsi_result[0])
+ {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ scsi_devices[NR_SCSI_DEVICES].writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ scsi_devices[NR_SCSI_DEVICES].writeable = 0;
+ break;
+ default:
+ type = -1;
+ }
+
+ scsi_devices[NR_SCSI_DEVICES].random = (type == TYPE_TAPE) ? 0 : 1;
+
+ maxed = 0;
+ switch (type)
+ {
+ case -1:
+ break;
+ case TYPE_TAPE:
+ printk("Detected scsi tape at host %d, ID %d, lun %d \n", host_nr, dev, lun);
+#ifdef CONFIG_BLK_DEV_ST
+ if (!(maxed = (NR_ST == MAX_ST)))
+ scsi_tapes[NR_ST].device = &scsi_devices[NR_SCSI_DEVICES];
+#endif
+ default:
+ printk("Detected scsi disk at host %d, ID %d, lun %d \n", host_nr, dev, lun);
+#ifdef CONFIG_BLK_DEV_SD
+ if (!(maxed = (NR_SD >= MAX_SD)))
+ rscsi_disks[NR_SD].device = &scsi_devices[NR_SCSI_DEVICES];
+#endif
+ }
+
+ if (maxed)
+ {
+ printk ("Already have detected maximum number of SCSI %ss Unable to \n"
+ "add drive at SCSI host %s, ID %d, LUN %d\n\r", (type == TYPE_TAPE) ?
+ "tape" : "disk", scsi_hosts[host_nr].name,
+ dev, lun);
+ type = -1;
+ break;
+ }
+
+ else if (type != -1)
+ {
+ if (type == TYPE_TAPE)
+#ifdef CONFIG_BLK_DEV_ST
+ ++NR_ST;
+#else
+;
+#endif
+
+ else
+#ifdef CONFIG_BLK_DEV_SD
+ ++NR_SD;
+#else
+;
+#endif
+ }
+ ++NR_SCSI_DEVICES;
+ } /* if result == DID_OK ends */
+ } /* for lun ends */
+ } /* if present */
+
+ printk("Detected "
+#ifdef CONFIG_BLK_DEV_SD
+"%d disk%s "
+#endif
+
+#ifdef CONFIG_BLK_DEV_ST
+"%d tape%s "
+#endif
+
+"total.\n",
+
+#ifdef CONFIG_BLK_DEV_SD
+NR_SD, (NR_SD != 1) ? "s" : ""
+#ifdef CONFIG_BLK_DEV_ST
+,
+#endif
+#endif
+
+#ifdef CONFIG_BLK_DEV_ST
+NR_ST, (NR_ST != 1) ? "s" : ""
+#endif
+);
+ } /* scan_scsis ends */
+
+/*
+ We handle the timeout differently if it happens when a reset,
+ abort, etc are in process.
+*/
+
+static unsigned char internal_timeout[MAX_SCSI_HOSTS];
+
+/* Flag bits for the internal_timeout array */
+
+#define NORMAL_TIMEOUT 0
+#define IN_ABORT 1
+#define IN_RESET 2
+/*
+ This is our time out function, called when the timer expires for a
+ given host adapter. It will attempt to abort the currently executing
+ command, that failing perform a kernel panic.
+*/
+
+static void scsi_times_out (int host)
+ {
+
+ switch (internal_timeout[host] & (IN_ABORT | IN_RESET))
+ {
+ case NORMAL_TIMEOUT:
+ printk("SCSI host %d timed out - aborting command \r\n",
+ host);
+
+ if (!scsi_abort (host, DID_TIME_OUT))
+ return;
+ case IN_ABORT:
+ printk("SCSI host %d abort() timed out - reseting \r\n",
+ host);
+ if (!scsi_reset (host))
+ return;
+ case IN_RESET:
+ case (IN_ABORT | IN_RESET):
+ printk("Unable to reset scsi host %d\r\n",host);
+ panic("");
+ default:
+ INTERNAL_ERROR;
+ }
+
+ }
+
+/*
+ This is inline because we have stack problemes if we recurse to deeply.
+*/
+
+static void internal_cmnd (int host, unsigned char target, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(int,int))
+ {
+ int temp;
+
+#ifdef DEBUG_DELAY
+ int clock;
+#endif
+
+ if ((host < 0) || (host > MAX_SCSI_HOSTS))
+ panic ("Host number in internal_cmnd() is out of range.\n");
+
+
+/*
+ We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ we can avoid the drive not being ready.
+*/
+temp = last_reset[host];
+while (jiffies < temp);
+
+host_timeout[host] = last_cmnd[host].timeout_per_command;
+update_timeout();
+
+/*
+ We will use a queued command if possible, otherwise we will emulate the
+ queing and calling of completion function ourselves.
+*/
+#ifdef DEBUG
+ printk("internal_cmnd (host = %d, target = %d, command = %08x, buffer = %08x, \n"
+ "bufflen = %d, done = %08x)\n", host, target, cmnd, buffer, bufflen, done);
+#endif
+
+
+ if (scsi_hosts[host].can_queue)
+ {
+#ifdef DEBUG
+ printk("queuecommand : routine at %08x\n",
+ scsi_hosts[host].queuecommand);
+#endif
+ scsi_hosts[host].queuecommand (target, cmnd, buffer, bufflen,
+ done);
+ }
+ else
+ {
+
+#ifdef DEBUG
+ printk("command() : routine at %08x\n", scsi_hosts[host].command);
+#endif
+ temp=scsi_hosts[host].command (target, cmnd, buffer, bufflen);
+
+#ifdef DEBUG_DELAY
+ clock = jiffies + 400;
+ while (jiffies < clock);
+ printk("done(host = %d, result = %04x) : routine at %08x\n", host, temp, done);
+#endif
+ done(host, temp);
+ }
+#ifdef DEBUG
+ printk("leaving internal_cmnd()\n");
+#endif
+ }
+
+static void scsi_request_sense (int host, unsigned char target,
+ unsigned char lun)
+ {
+ cli();
+ host_timeout[host] = SENSE_TIMEOUT;
+ update_timeout();
+ last_cmnd[host].flags |= WAS_SENSE;
+ sti();
+
+ last_cmnd[host].sense_cmnd[1] = lun << 5;
+
+ internal_cmnd (host, target, (void *) last_cmnd[host].sense_cmnd,
+ (void *) last_cmnd[host].sense_buffer, SENSE_LENGTH,
+ scsi_done);
+ }
+
+
+
+
+
+/*
+ scsi_do_cmd sends all the commands out to the low-level driver. It
+ handles the specifics required for each low level driver - ie queued
+ or non queud. It also prevents conflicts when different high level
+ drivers go for the same host at the same time.
+*/
+
+void scsi_do_cmd (int host, unsigned char target, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(int,int),
+ int timeout, unsigned char *sense_buffer, int retries
+ )
+ {
+ int ok = 0;
+
+#ifdef DEBUG
+ int i;
+ printk ("scsi_do_cmd (host = %d, target = %d, buffer =%08x, "
+ "bufflen = %d, done = %08x, timeout = %d, retries = %d)\n"
+ "command : " , host, target, buffer, bufflen, done, timeout, retries);
+ for (i = 0; i < 10; ++i)
+ printk ("%02x ", ((unsigned char *) cmnd)[i]);
+ printk("\n");
+#endif
+
+ if ((host >= MAX_SCSI_HOSTS) || !scsi_hosts[host].present)
+ {
+ printk ("Invalid or not present host number. %d\n", host);
+ panic("");
+ }
+
+
+/*
+ We must prevent reentrancy to the lowlevel host driver. This prevents
+ it - we enter a loop until the host we want to talk to is not busy.
+ Race conditions are prevented, as interrupts are disabled inbetween the
+ time we check for the host being not busy, and the time we mark it busy
+ ourselves.
+*/
+
+ do {
+ cli();
+ if (host_busy[host])
+ {
+ sti();
+#ifdef DEBUG
+ printk("Host %d is busy.\n" );
+#endif
+ while (host_busy[host]);
+#ifdef DEBUG
+ printk("Host %d is no longer busy.");
+#endif
+ }
+ else
+ {
+ host_busy[host] = 1;
+ ok = 1;
+ sti();
+ }
+ } while (!ok);
+
+
+/*
+ Our own function scsi_done (which marks the host as not busy, disables
+ the timeout counter, etc) will be called by us or by the
+ scsi_hosts[host].queuecommand() function needs to also call
+ the completion function for the high level driver.
+
+*/
+
+ memcpy ((void *) last_cmnd[host].cmnd , (void *) cmnd, 10);
+ last_cmnd[host].host = host;
+ last_cmnd[host].target = target;
+ last_cmnd[host].lun = (last_cmnd[host].cmnd[1] >> 5);
+ last_cmnd[host].bufflen = bufflen;
+ last_cmnd[host].buffer = buffer;
+ last_cmnd[host].sense_buffer = sense_buffer;
+ last_cmnd[host].flags=0;
+ last_cmnd[host].retries=0;
+ last_cmnd[host].allowed=retries;
+ last_cmnd[host].done = done;
+ last_cmnd[host].timeout_per_command = timeout;
+
+ /* Start the timer ticking. */
+
+ internal_timeout[host] = 0;
+ internal_cmnd (host, target, cmnd , buffer, bufflen, scsi_done);
+
+#ifdef DEBUG
+ printk ("Leaving scsi_do_cmd()\n");
+#endif
+ }
+
+
+/*
+ The scsi_done() function disables the timeout timer for the scsi host,
+ marks the host as not busy, and calls the user specified completion
+ function for that host's current command.
+*/
+
+static void reset (int host)
+ {
+ #ifdef DEBUG
+ printk("reset(%d)\n", host);
+ #endif
+
+ last_cmnd[host].flags |= (WAS_RESET | IS_RESETTING);
+ scsi_reset(host);
+
+ #ifdef DEBUG
+ printk("performing request sense\n");
+ #endif
+
+ scsi_request_sense (host, last_cmnd[host].target, last_cmnd[host].lun);
+ }
+
+
+
+static int check_sense (int host)
+ {
+ if (((sense_buffer[0] & 0x70) >> 4) == 7)
+ switch (sense_buffer[2] & 0xf)
+ {
+ case NO_SENSE:
+ case RECOVERED_ERROR:
+ return 0;
+
+ case ABORTED_COMMAND:
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ return SUGGEST_RETRY;
+
+ /* these three are not supported */
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+
+ case MEDIUM_ERROR:
+ return SUGGEST_REMAP;
+ case BLANK_CHECK:
+ case DATA_PROTECT:
+ case HARDWARE_ERROR:
+ case ILLEGAL_REQUEST:
+ default:
+ return SUGGEST_ABORT;
+ }
+ else
+ return SUGGEST_RETRY;
+ }
+
+static void scsi_done (int host, int result)
+ {
+ int status=0;
+ int exit=0;
+ int checked;
+ int oldto;
+ oldto = host_timeout[host];
+ host_timeout[host] = 0;
+ update_timeout();
+
+#define FINISHED 0
+#define MAYREDO 1
+#define REDO 3
+
+#ifdef DEBUG
+ printk("In scsi_done(host = %d, result = %06x)\n", host, result);
+#endif
+ if (host > MAX_SCSI_HOSTS || host < 0)
+ {
+ host_timeout[host] = 0;
+ update_timeout();
+ panic("scsi_done() called with invalid host number.\n");
+ }
+
+ switch (host_byte(result))
+ {
+ case DID_OK:
+ if (last_cmnd[host].flags & IS_RESETTING)
+ {
+ last_cmnd[host].flags &= ~IS_RESETTING;
+ status = REDO;
+ break;
+ }
+
+ if (status_byte(result) && (last_cmnd[host].flags &
+ WAS_SENSE))
+ {
+ last_cmnd[host].flags &= ~WAS_SENSE;
+ cli();
+ internal_timeout[host] &= ~SENSE_TIMEOUT;
+ sti();
+
+ if (!(last_cmnd[host].flags & WAS_RESET))
+ reset(host);
+ else
+ {
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ status = FINISHED;
+ }
+ }
+ else switch(msg_byte(result))
+ {
+ case COMMAND_COMPLETE:
+ switch (status_byte(result))
+ {
+ case GOOD:
+ if (last_cmnd[host].flags & WAS_SENSE)
+ {
+#ifdef DEBUG
+ printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");
+#endif
+
+ last_cmnd[host].flags &= ~WAS_SENSE;
+ cli();
+ internal_timeout[host] &= ~SENSE_TIMEOUT;
+ sti();
+
+ switch (checked = check_sense(host))
+ {
+ case 0:
+#ifdef DEBUG
+ printk("NO SENSE. status = REDO\n");
+#endif
+
+ host_timeout[host] = oldto;
+ update_timeout();
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+#ifdef DEBUG
+ printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");
+#endif
+
+ status = MAYREDO;
+ exit = SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+#ifdef DEBUG
+ printk("SENSE SUGGEST ABORT - status = FINISHED");
+#endif
+
+ status = FINISHED;
+ exit = DRIVER_SENSE;
+ break;
+ default:
+ printk ("Internal error %s %s \n", __FILE__,
+ __LINE__);
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("COMMAND COMPLETE message returned, status = FINISHED. \n");
+#endif
+
+ exit = DRIVER_OK;
+ status = FINISHED;
+ }
+ break;
+
+ case CHECK_CONDITION:
+
+#ifdef DEBUG
+ printk("CHECK CONDITION message returned, performing request sense.\n");
+#endif
+
+ scsi_request_sense (host, last_cmnd[host].target, last_cmnd[host].lun);
+ 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
+ host_timeout[host] = oldto;
+ update_timeout();
+ status = REDO;
+ break;
+
+ case RESERVATION_CONFLICT:
+ reset(host);
+ exit = DRIVER_SOFT | SUGGEST_ABORT;
+ status = MAYREDO;
+ break;
+ default:
+ printk ("Internal error %s %s \n"
+ "status byte = %d \n", __FILE__,
+ __LINE__, status_byte(result));
+
+ }
+ break;
+ default:
+ panic ("unsupported message byte recieved.");
+ }
+ break;
+ case DID_TIME_OUT:
+#ifdef DEBUG
+ printk("Host returned DID_TIME_OUT - ");
+#endif
+
+ if (last_cmnd[host].flags & WAS_TIMEDOUT)
+ {
+#ifdef DEBUG
+ printk("Aborting\n");
+#endif
+ exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
+ }
+ else
+ {
+#ifdef DEBUG
+ printk ("Retrying.\n");
+#endif
+ last_cmnd[host].flags |= WAS_TIMEDOUT;
+ status = REDO;
+ }
+ break;
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ status = REDO;
+ break;
+ case DID_NO_CONNECT:
+#ifdef DEBUG
+ printk("Couldn't connect.\n");
+#endif
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_ERROR:
+ status = MAYREDO;
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ exit = (DRIVER_INVALID | SUGGEST_ABORT);
+ break;
+ default :
+ exit = (DRIVER_ERROR | SUGGEST_DIE);
+ }
+
+ switch (status)
+ {
+ case FINISHED:
+ break;
+ case MAYREDO:
+
+#ifdef DEBUG
+ printk("In MAYREDO, allowing %d retries, have %d\n\r",
+ last_cmnd[host].allowed, last_cmnd[host].retries);
+#endif
+
+ if ((++last_cmnd[host].retries) < last_cmnd[host].allowed)
+ {
+ if ((last_cmnd[host].retries >= (last_cmnd[host].allowed >> 1))
+ && !(last_cmnd[host].flags & WAS_RESET))
+ reset(host);
+ break;
+
+ }
+ else
+ {
+ status = FINISHED;
+ break;
+ }
+ /* fall through to REDO */
+
+ case REDO:
+ if (last_cmnd[host].flags & WAS_SENSE)
+ scsi_request_sense (host, last_cmnd[host].target,
+ last_cmnd[host].lun);
+ else
+ internal_cmnd (host, last_cmnd[host].target,
+ last_cmnd[host].cmnd,
+ last_cmnd[host].buffer,
+ last_cmnd[host].bufflen, scsi_done);
+ break;
+ default:
+ INTERNAL_ERROR;
+ }
+
+ if (status == FINISHED)
+ {
+ #ifdef DEBUG
+ printk("Calling done function - at address %08x\n", last_cmnd[host].done);
+ #endif
+ last_cmnd[host].done (host, (result | ((exit & 0xff) << 24)));
+ host_busy[host] = 0;
+ }
+
+
+#undef FINISHED
+#undef REDO
+#undef MAYREDO
+
+ }
+
+/*
+ The scsi_abort function interfaces with the abort() function of the host
+ we are aborting, and causes the current command to not complete. The
+ caller should deal with any error messages or status returned on the
+ next call.
+
+ This will not be called rentrantly for a given host.
+*/
+
+/*
+ Since we're nice guys and specified that abort() and reset()
+ can be non-reentrant. The internal_timeout flags are used for
+ this.
+*/
+
+
+int scsi_abort (int host, int why)
+ {
+ int temp, oldto;
+
+ while(1)
+ {
+ cli();
+ if (internal_timeout[host] & IN_ABORT)
+ {
+ sti();
+ while (internal_timeout[host] & IN_ABORT);
+ }
+ else
+ {
+ internal_timeout[host] |= IN_ABORT;
+ host_timeout[host] = ABORT_TIMEOUT;
+ update_timeout();
+
+ oldto = host_timeout[host];
+
+ sti();
+ if (!host_busy[host] || !scsi_hosts[host].abort(why))
+ temp = 0;
+ else
+ temp = 1;
+
+ cli();
+ internal_timeout[host] &= ~IN_ABORT;
+ host_timeout[host]=oldto;
+ update_timeout();
+ sti();
+ return temp;
+ }
+ }
+ }
+
+int scsi_reset (int host)
+ {
+ int temp, oldto;
+
+ while (1) {
+ cli();
+ if (internal_timeout[host] & IN_RESET)
+ {
+ sti();
+ while (internal_timeout[host] & IN_RESET);
+ }
+ else
+ {
+ oldto = host_timeout[host];
+ host_timeout[host] = RESET_TIMEOUT;
+ update_timeout();
+ internal_timeout[host] |= IN_RESET;
+
+ if (host_busy[host])
+ {
+ sti();
+ if (!(last_cmnd[host].flags & IS_RESETTING) && !(internal_timeout[host] & IN_ABORT))
+ scsi_abort(host, DID_RESET);
+
+ temp = scsi_hosts[host].reset();
+ }
+ else
+ {
+ host_busy[host]=1;
+
+ sti();
+ temp = scsi_hosts[host].reset();
+ last_reset[host] = jiffies;
+ host_busy[host]=0;
+ }
+
+ cli();
+ host_timeout[host] = oldto;
+ update_timeout();
+ internal_timeout[host] &= ~IN_RESET;
+ sti();
+ return temp;
+ }
+ }
+ }
+
+
+static void scsi_main_timeout(void)
+ {
+ /*
+ We must not enter update_timeout with a timeout condition still pending.
+ */
+
+ int i, timed_out;
+
+ do {
+ cli();
+
+ /*
+ Find all timers such that they have 0 or negative (shouldn't happen)
+ time remaining on them.
+ */
+
+ for (i = timed_out = 0; i < MAX_SCSI_HOSTS; ++i)
+ if (host_timeout[i] != 0 && host_timeout[i] <= time_elapsed)
+ {
+ sti();
+ host_timeout[i] = 0;
+ scsi_times_out(i);
+ ++timed_out;
+ }
+
+ update_timeout();
+ } while (timed_out);
+ sti();
+ }
+
+/*
+ These are used to keep track of things.
+*/
+
+static int time_start, time_elapsed;
+
+/*
+ The strategy is to cause the timer code to call scsi_times_out()
+ when the soonest timeout is pending.
+*/
+
+static void update_timeout(void)
+ {
+ int i, least, used;
+
+ cli();
+
+/*
+ Figure out how much time has passed since the last time the timeouts
+ were updated
+*/
+ used = (time_start) ? (jiffies - time_start) : 0;
+
+/*
+ Find out what is due to timeout soonest, and adjust all timeouts for
+ the amount of time that has passed since the last time we called
+ update_timeout.
+*/
+
+ for (i = 0, least = 0xffffffff; i < MAX_SCSI_HOSTS; ++i)
+ if (host_timeout[i] > 0 && (host_timeout[i] -= used) < least)
+ least = host_timeout[i];
+
+/*
+ If something is due to timeout again, then we will set the next timeout
+ interrupt to occur. Otherwise, timeouts are disabled.
+*/
+
+ if (least != 0xffffffff)
+ {
+ time_start = jiffies;
+ timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+ timer_active |= 1 << SCSI_TIMER;
+ }
+ else
+ {
+ timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
+ timer_active &= ~(1 << SCSI_TIMER);
+ }
+ sti();
+ }
+/*
+ scsi_dev_init() is our initialization routine, which inturn calls host
+ initialization, bus scanning, and sd/st initialization routines. It
+ should be called from main().
+*/
+
+static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
+void scsi_dev_init (void)
+ {
+ int i;
+#ifdef FOO_ON_YOU
+ return;
+#endif
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+
+ scsi_init(); /* initialize all hosts */
+ /*
+ Set up sense command in each host structure.
+ */
+
+ for (i = 0; i < MAX_SCSI_HOSTS; ++i)
+ {
+ memcpy ((void *) last_cmnd[i].sense_cmnd, (void *) generic_sense,
+ 6);
+ last_reset[i] = 0;
+ }
+
+ scan_scsis(); /* scan for scsi devices */
+
+#ifdef CONFIG_BLK_DEV_SD
+ sd_init(); /* init scsi disks */
+#endif
+
+#ifdef CONFIG_BLK_DEV_ST
+ st_init(); /* init scsi tapes */
+#endif
+ }
+#endif
diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h
new file mode 100644
index 0000000..1a949ab
--- /dev/null
+++ b/kernel/blk_drv/scsi/scsi.h
@@ -0,0 +1,257 @@
+/*
+ * scsi.h Copyright (C) 1992 Drew Eckhardt
+ * generic SCSI package header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _SCSI_H
+ #define _SCSI_H
+/*
+ $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+
+ For documentation on the OPCODES, MESSAGES, and SENSE values,
+ please consult the SCSI standard.
+
+*/
+
+/*
+ SCSI opcodes
+*/
+
+#define TEST_UNIT_READY 0x00
+#define REZERO_UNIT 0x01
+#define REQUEST_SENSE 0x03
+#define FORMAT_UNIT 0x04
+#define REASSIGN_BLOCKS 0x07
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define SEEK_6 0x0b
+#define INQUIRY 0x12
+#define MODE_SELECT 0x15
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define COPY 0x18
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RECIEVE_DAIGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define SEEK_10 0x2b
+#define WRITE_VERIFY 0x2e
+#define VERIFY 0x2f
+#define SEARCH_HIGH 0x30
+#define SEARCH_EQUAL 0x31
+#define SEARCH_LOW 0x32
+#define SET_LIMITS 0x33
+#define COMPARE 0x39
+#define COPY_VERIFY 0x3a
+
+#define COMMAND_SIZE(opcode) ((opcode) ? ((opcode) > 0x20 ? 10 : 6) : 0)
+
+/*
+ MESSAGE CODES
+*/
+
+#define COMMAND_COMPLETE 0x00
+#define EXTENDED_MESSAGE 0x01
+#define SAVE_POINTERS 0x02
+#define RESTORE_POINTERS 0x03
+#define DISCONNECT 0x04
+#define INITIATOR_ERROR 0x05
+#define ABORT 0x06
+#define MESSAGE_REJECT 0x07
+#define NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define LINKED_CMD_COMPLETE 0x0a
+#define LINKED_FLG_CMD_COMPLETE 0x0b
+#define BUS_DEVICE_RESET 0x0c
+#define IDENTIFY 0x80
+/*
+ Status codes
+*/
+
+#define GOOD 0x00
+#define CHECK_CONDITION 0x01
+#define CONDITION_GOOD 0x02
+#define BUSY 0x04
+#define INTERMEDIATE_GOOD 0x08
+#define INTERMEDIATE_C_GOOD 0x0a
+#define RESERVATION_CONFLICT 0x0c
+
+#define STATUS_MASK 0x1e
+
+/*
+ the return of the status word will be in the following format :
+ The low byte is the status returned by the SCSI command,
+ with vendor specific bits masked.
+
+ The next byte is the message which followed the SCSI status.
+ This allows a stos to be used, since the Intel is a little
+ endian machine.
+
+ The final byte is a host return code, which is one of the following.
+
+ IE
+ lsb msb
+ status msg host code
+
+ Our errors returned by OUR driver, NOT SCSI message. Orr'd with
+ SCSI message passed back to driver <IF any>.
+*/
+
+/* NO error */
+#define DID_OK 0x00
+/* Couldn't connect before timeout period */
+#define DID_NO_CONNECT 0x01
+/* BUS stayed busy through time out period */
+#define DID_BUS_BUSY 0x02
+/* TIMED OUT for other reason */
+#define DID_TIME_OUT 0x03
+/* BAD target. */
+#define DID_BAD_TARGET 0x04
+/* Told to abort for some other reason */
+#define DID_ABORT 0x05
+/*
+ Parity error
+*/
+#define DID_PARITY 0x06
+/*
+ Internal error
+*/
+#define DID_ERROR 0x07
+/*
+ Reset by somebody.
+*/
+#define DID_RESET 0x08
+
+/*
+ Driver status
+*/
+#define DRIVER_OK 0x00
+
+/*
+ These indicate the error that occured, and what is available.
+*/
+
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+
+#define SUGGEST_RETRY 0x08
+#define SUGGEST_ABORT 0x09
+#define SUGGEST_REMAP 0x0a
+#define SUGGEST_DIE 0x0b
+
+#define DRIVER_SENSE 0x10
+
+#define DRIVER_MASK 0x0f
+#define SUGGEST_MASK 0xf0
+
+/*
+
+ SENSE KEYS
+*/
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define BLANK_CHECK 0x08
+#define COPY_ABORTED 0x0a
+#define ABORTED_COMMAND 0x0b
+#define VOLUME_OVERFLOW 0x0d
+#define MISCOMPARE 0x0e
+
+
+/*
+ DEVICE TYPES
+
+*/
+
+#define TYPE_DISK 0x00
+#define TYPE_TAPE 0x01
+#define TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define TYPE_ROM 0x05
+#define TYPE_NO_LUN 0x7f
+/*
+ Every SCSI command starts with a one byte OP-code.
+ The next byte's high three bits are the LUN of the
+ device. Any multi-byte quantities are stored high byte
+ first, and may have a 5 bit MSB in the same byte
+ as the LUN.
+*/
+
+
+/*
+ The scsi_device struct contains what we know about each given scsi
+ device.
+*/
+
+typedef struct scsi_device {
+ unsigned char host_no, id, lun;
+ unsigned writeable:1;
+ unsigned removable:1;
+ unsigned random:1;
+ } Scsi_Device;
+/*
+ Use these to separate status msg and our bytes
+*/
+
+#define status_byte(result) (((result) >> 1) & 0xf)
+#define msg_byte(result) (((result) >> 8) & 0xff)
+#define host_byte(result) (((result) >> 16) & 0xff)
+#define driver_byte(result) (((result) >> 24) & 0xff)
+#define sugestion(result) (driver_byte(result) & SUGGEST_MASK)
+
+#define sense_class(sense) (((sense) >> 4) & 0x7)
+#define sense_error(sense) ((sense) & 0xf)
+#define sense_valid(sense) ((sense) & 0x80);
+
+/*
+ These are the SCSI devices available on the system.
+*/
+
+#define MAX_SCSI_DEVICE 2
+extern int NR_SCSI_DEVICES;
+extern Scsi_Device scsi_devices[MAX_SCSI_DEVICE];
+/*
+ scsi_abort aborts the current command that is executing on host host.
+ The error code, if non zero is returned in the host byte, otherwise
+ DID_ABORT is returned in the hostbyte.
+*/
+
+extern int scsi_abort (int host, int code);
+
+/*
+ Initializes all SCSI devices. This scans all scsi busses.
+*/
+
+extern void scsi_dev_init (void);
+
+/*
+ You guesed it. This sends a command to the selected SCSI host
+*/
+
+
+
+extern void scsi_do_cmd (int host, unsigned char target, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(int,int),
+ int timeout, unsigned char *sense_buffer, int retries);
+
+int scsi_reset (int host);
+#endif
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c
new file mode 100644
index 0000000..b373c3b
--- /dev/null
+++ b/kernel/blk_drv/scsi/scsi_ioctl.c
@@ -0,0 +1,153 @@
+#include <linux/config.h>
+#ifdef CONFIG_SCSI
+
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_ioctl.h"
+
+#define MAX_RETRIES 5
+#define MAX_TIMEOUT 200
+#define MAX_BUF 1024
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/*
+ * If we are told to probe a host, we will return 0 if the host is not
+ * present, 1 if the host is present, and will return an identifying
+ * string at *arg, if arg is non null, filling to the length stored at
+ * (int *) arg
+ */
+
+static int ioctl_probe(int dev, void *buffer)
+{
+ int temp;
+ int len;
+
+ if ((temp = scsi_hosts[dev].present) && buffer) {
+ len = get_fs_long ((int *) buffer);
+ memcpy_tofs (buffer, scsi_hosts[dev].info(), len);
+ }
+ return temp;
+}
+
+/*
+ *
+ * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
+ * The MAX_TIMEOUT and MAX_RETRIES variables are used.
+ *
+ * dev is the SCSI device number, *(int *) arg the length of the input
+ * data, *((int *)arg + 1) the output buffer.
+ *
+ * *(char *) ((int *) arg)[1] the actual command.
+ *
+ * Note that no more than MAX_BUF bytes will be transfered. Since
+ * SCSI block device size is 512 bytes, I figured 1K was good.
+ *
+ * This size * does * include the initial lengths that were passed.
+ *
+ * The SCSI command is read from the memory location immediately after the
+ * length words, and the out data after the command. The SCSI routines know the
+ * command size based on the length byte.
+ *
+ * The area is then filled in from the byte at offset 0.
+ */
+
+static int the_result[MAX_SCSI_HOSTS];
+
+static void scsi_ioctl_done (int host, int result)
+{
+ the_result[host] = result;
+}
+
+static int ioctl_command(Scsi_Device *dev, void *buffer)
+{
+ char buf[MAX_BUF];
+ char cmd[10];
+ char * cmd_in;
+ unsigned char opcode;
+ int inlen, outlen, cmdlen, temp, host;
+
+ if (!buffer)
+ return -EINVAL;
+
+ inlen = get_fs_long((int *) buffer);
+ outlen = get_fs_long(((int *) buffer) + 1);
+
+ cmd_in = (char *) ( ((int *)buffer) + 2);
+ opcode = get_fs_byte(cmd_in);
+
+ memcpy_fromfs ((void *) cmd, cmd_in, cmdlen = COMMAND_SIZE (opcode));
+ memcpy_fromfs ((void *) buf, (void *) (cmd_in + cmdlen), inlen);
+ host = dev->host_no;
+
+#ifndef DEBUG_NO_CMD
+ do {
+ cli();
+ if (the_result[host]) {
+ sti();
+ while(the_result[host])
+ /* nothing */;
+ } else {
+ the_result[host]=-1;
+ sti();
+ break;
+ }
+ } while (1);
+
+ scsi_do_cmd(host, dev->id, cmd, buf, ((outlen > MAX_BUF) ?
+ MAX_BUF : outlen), scsi_ioctl_done, MAX_TIMEOUT,
+ buf, MAX_RETRIES);
+
+ while (the_result[host] == -1)
+ /* nothing */;
+ temp = the_result[host];
+ the_result[host]=0;
+ memcpy_tofs (buffer, buf, (outlen > MAX_BUF) ? MAX_BUF : outlen);
+ return temp;
+#else
+ {
+ int i;
+ printk("scsi_ioctl : device %d. command = ", dev);
+ for (i = 0; i < 10; ++i)
+ printk("%02x ", cmd[i]);
+ printk("\r\nbuffer =");
+ for (i = 0; i < 20; ++i)
+ printk("%02x ", buf[i]);
+ printk("\r\n");
+ }
+ return 0;
+#endif
+}
+
+
+/*
+ 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
+ an index in scsi_devices[]
+*/
+int scsi_ioctl (int dev, int cmd, void *arg)
+{
+ if ((cmd != 0 && dev > NR_SCSI_DEVICES) || dev < 0)
+ return -ENODEV;
+ if ((cmd == 0 && dev > MAX_SCSI_HOSTS))
+ return -ENODEV;
+
+ switch (cmd) {
+ case SCSI_IOCTL_PROBE_HOST:
+ return ioctl_probe(dev, arg);
+ case SCSI_IOCTL_SEND_COMMAND:
+ return ioctl_command((Scsi_Device *) dev, arg);
+ default :
+ return -EINVAL;
+ }
+}
+#endif
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.h b/kernel/blk_drv/scsi/scsi_ioctl.h
new file mode 100644
index 0000000..2acdb55
--- /dev/null
+++ b/kernel/blk_drv/scsi/scsi_ioctl.h
@@ -0,0 +1,21 @@
+#ifndef _SCSI_IOCTL_H
+#define _SCSI_IOCTL_H
+
+#ifndef _CONFIG_H
+#include <linux/config.h>
+#endif
+
+#define SCSI_IOCTL_PROBE_HOST 0
+#define SCSI_IOCTL_SEND_COMMAND 1
+
+#ifdef CONFIG_BLK_DEV_SD
+/* Should start at 128 */
+#endif
+
+#ifdef CONFIG_BLK_DEV_SD
+/* Should start at 256 */
+#endif
+
+#endif
+
+
diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c
new file mode 100644
index 0000000..0ad47d3
--- /dev/null
+++ b/kernel/blk_drv/scsi/sd.c
@@ -0,0 +1,380 @@
+/*
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Linux scsi disk driver by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_SD
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include "sd.h"
+
+#define MAJOR_NR 8
+
+#include "../blk.h"
+
+/*
+static const char RCSid[] = "$Header:";
+*/
+
+#define MAX_RETRIES 5
+
+/*
+ * Time out in seconds
+ */
+
+#define SD_TIMEOUT 100
+
+Partition scsi_disks[MAX_SD << 4];
+int NR_SD=0;
+Scsi_Disk rscsi_disks[MAX_SD];
+static int sd_sizes[MAX_SD << 4];
+static int this_count;
+static int the_result;
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned long, unsigned long);
+
+static void sd_release(struct inode * inode, struct file * file)
+{
+ sync_dev(inode->i_rdev);
+}
+
+static struct file_operations sd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sd_ioctl, /* ioctl */
+ NULL, /* no special open code */
+ sd_release /* release */
+};
+
+/*
+ The sense_buffer is where we put data for all mode sense commands
+ performed.
+*/
+
+static unsigned char sense_buffer[255];
+
+/*
+ rw_intr is the interrupt routine for the device driver. It will
+ be notified on the end of a SCSI read / write, and
+ will take on of several actions based on success or failure.
+*/
+
+static void rw_intr (int host, int result)
+{
+ if (HOST != host)
+ panic ("sd.o : rw_intr() recieving interrupt for different host.");
+
+/*
+ First case : we assume that the command succeeded. One of two things will
+ happen here. Either we will be finished, or there will be more
+ sectors that we were unable to read last time.
+*/
+
+ if (!result)
+ if (!(CURRENT->nr_sectors -= this_count)) {
+ end_request(1);
+ do_sd_request();
+ } else {
+ CURRENT->nr_sectors -= this_count;
+/*
+ The CURRENT->nr_sectors field is always done in 512 byte sectors,
+ even if this really isn't the case.
+*/
+
+ (char *) CURRENT->buffer += this_count << 9;
+ CURRENT->sector += this_count;
+ CURRENT->errors = 0;
+ do_sd_request();
+ }
+
+/*
+ Now, if we were good little boys and girls, Santa left us a request
+ sense buffer. We can extract information from this, so we
+ can choose a block to remap, etc.
+*/
+
+ else if (driver_byte(result) & DRIVER_SENSE) {
+ if (sugestion(result) == SUGGEST_REMAP) {
+#ifdef REMAP
+/*
+ Not yet implemented. A read will fail after being remapped,
+ a write will call the strategy routine again.
+*/
+
+ if rscsi_disks[DEVICE_NR(CURRENT->dev)].remap
+ {
+ result = 0;
+ }
+ else
+
+#endif
+ }
+/*
+ If we had an ILLEGAL REQUEST returned, then we may have performed
+ an unsupported command. The only thing this should be would be a ten
+ byte read where only a six byte read was supportted. Also, on a
+ system where READ CAPACITY failed, we mave have read past the end of the
+ disk.
+*/
+
+ else if (sense_buffer[7] == ILLEGAL_REQUEST) {
+ if (rscsi_disks[DEVICE_NR(CURRENT->dev)].ten) {
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].ten = 0;
+ do_sd_request();
+ result = 0;
+ } else {
+ }
+ }
+ }
+ if (result) {
+ printk("SCSI disk error : host %d id %d lun %d return code = %03x\n",
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no,
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->id,
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun);
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(sense_buffer[0]),
+ sense_error(sense_buffer[0]),
+ sense_buffer[2] & 0xf);
+
+ end_request(0);
+ }
+}
+
+/*
+ do_sd_request() is the request handler function for the sd driver.
+ Its function in life is to take block device requests, and translate
+ them to SCSI commands.
+*/
+
+void do_sd_request (void)
+{
+ int dev, block;
+ unsigned char cmd[10];
+
+ INIT_REQUEST;
+ dev = MINOR(CURRENT->dev);
+ block = CURRENT->sector;
+
+#ifdef DEBUG
+ printk("Doing sd request, dev = %d, block = %d\n", dev, block);
+#endif
+
+ if (dev >= (NR_SD << 4) || block + 2 > scsi_disks[dev].nr_sects ||
+ (dev % 16) > 5)
+ {
+ end_request(0);
+ goto repeat;
+ }
+
+ block += scsi_disks[dev].start_sect;
+ dev = DEVICE_NR(dev);
+
+#ifdef DEBUG
+ printk("Real dev = %d, block = %d\n", dev, block);
+#endif
+
+ if (!rscsi_disks[dev].use)
+ {
+ end_request(0);
+ goto repeat;
+ }
+
+ this_count = CURRENT->nr_sectors;
+ switch (CURRENT->cmd)
+ {
+ case WRITE :
+ if (!rscsi_disks[dev].device->writeable)
+ {
+ end_request(0);
+ goto repeat;
+ }
+ cmd[0] = WRITE_6;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ printk ("Unknown sd command %d\r\n", CURRENT->cmd);
+ panic("");
+ }
+
+ cmd[1] = (LUN << 5) & 0xe0;
+
+ if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
+ {
+ if (this_count > 0xffff)
+ this_count = 0xffff;
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ cmd[8] = (unsigned char) this_count & 0xff;
+ }
+ else
+ {
+ if (this_count > 0xff)
+ this_count = 0xff;
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) this_count;
+ cmd[5] = 0;
+ }
+
+ scsi_do_cmd (HOST, ID, (void *) cmd, CURRENT->buffer, this_count << 9,
+ rw_intr, SD_TIMEOUT, sense_buffer, MAX_RETRIES);
+}
+
+static void sd_init_done (int host, int result)
+{
+ the_result = result;
+}
+
+/*
+ The sd_init() function looks at all SCSI drives present, determines
+ their size, and reads partition table entries for them.
+*/
+
+void sd_init(void)
+{
+ int i,j,k;
+ unsigned char cmd[10];
+ unsigned char buffer[513];
+
+ Partition *p;
+
+
+ for (i = 0; i < NR_SD; ++i)
+ {
+ cmd[0] = READ_CAPACITY;
+ rscsi_disks[i].use = 1;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ the_result = -1;
+#ifdef DEBUG
+ printk("Read capacity, disk %d at host = %d, id = %d\n", i,
+ rscsi_disks[i].device->host_no, rscsi_disks[i].device->id);
+#endif
+ scsi_do_cmd (rscsi_disks[i].device->host_no ,
+ rscsi_disks[i].device->id,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT, sense_buffer,
+ MAX_RETRIES);
+
+ while(the_result < 0);
+/*
+ The SCSI standard says "READ CAPACITY is necessary for self confuring software"
+ While not mandatory, support of READ CAPACITY is strongly encouraged.
+
+ We used to die if we couldn't successfully do a READ CAPACITY.
+ But, now we go on about our way. The side effects of this are
+
+ 1. We can't know block size with certainty. I have said "512 bytes is it"
+ as this is most common.
+
+ 2. Recovery from when some one attempts to read past the end of the raw device will
+ be slower.
+*/
+
+ if (the_result)
+ {
+ printk ("Warning : SCSI device at host %d, id %d, lun %d failed READ CAPACITY.\n"
+ "status = %x, message = %02x, host = %02x, driver = %02x \n",
+ rscsi_disks[i].device->host_no, rscsi_disks[i].device->id,
+ rscsi_disks[i].device->lun,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result)
+ );
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ printk("Extended sense code = %1x \n", sense_buffer[2] & 0xf);
+ else
+ printk("Sense not available. \n");
+
+ printk("Block size assumed to be 512 bytes, disk size 1GB. \n");
+ rscsi_disks[i].capacity = 0x1fffff;
+ rscsi_disks[i].sector_size = 512;
+ }
+ else
+ {
+ rscsi_disks[i].capacity = (buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3];
+
+ if ((rscsi_disks[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) |
+ (buffer[6] << 8) |
+ buffer[7]) != 512)
+ {
+ printk ("Unsupported sector size %d for sd %d",
+ rscsi_disks[i].sector_size, i);
+ rscsi_disks[i].use = 0;
+ }
+ }
+
+ if (rscsi_disks[i].use)
+ {
+ scsi_disks[j = (i << 4)].start_sect = 0;
+
+ sd_sizes[j]=(scsi_disks[j].nr_sects = rscsi_disks[i].capacity)>>1;
+#ifdef DEBUG
+ printk("/dev/sd%1d size = %d\n", j, sd_sizes[j]);
+#endif
+ cmd[0] = READ_6;
+ cmd[2] = cmd[3] = cmd[5] = 0;
+ cmd[4] = 1;
+ the_result = -1;
+
+ scsi_do_cmd (rscsi_disks[i].device->host_no , rscsi_disks[i].device->id,
+ (void *) cmd, (void *) buffer, 512, sd_init_done, SD_TIMEOUT,
+ sense_buffer, MAX_RETRIES);
+
+ while (the_result < 0);
+
+
+ if (the_result || (0xaa55 != *(unsigned short *)(buffer + 510)))
+ {
+ printk ("Cannot read partition table for sd %d"
+ "\n\r",i);
+ rscsi_disks[i].use = 0;
+ }
+ else
+ for (++j, k=j+4, p=(Partition *) (buffer + 0x1be); j < k; ++j, ++p)
+ {
+ memcpy ((void *) &scsi_disks[j], (void *) p, sizeof(Partition));
+ sd_sizes[j]=(scsi_disks[j].nr_sects)>>1;
+#ifdef DEBUG
+ printk("/dev/sd%1d size = %d (%d blocks), offset = %d\n", j, scsi_disks[j].nr_sects, sd_sizes[j], scsi_disks[j].start_sect);
+#endif
+ }
+
+ rscsi_disks[i].ten = 1;
+ rscsi_disks[i].remap = 1;
+ }
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_size[MAJOR_NR] = sd_sizes;
+ blkdev_fops[MAJOR_NR] = &sd_fops;
+}
+#endif
+
diff --git a/kernel/blk_drv/scsi/sd.h b/kernel/blk_drv/scsi/sd.h
new file mode 100644
index 0000000..e4c4613
--- /dev/null
+++ b/kernel/blk_drv/scsi/sd.h
@@ -0,0 +1,57 @@
+/*
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+#ifndef _SD_H
+ #define _SD_H
+/*
+ $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/sd.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+#define MAX_SD 2
+
+typedef struct partition {
+ unsigned char boot_ind; /* 0x80 - active (unused) */
+ unsigned char head; /* ? */
+ unsigned char sector; /* ? */
+ unsigned char cyl; /* ? */
+ unsigned char sys_ind; /* ? */
+ unsigned char end_head; /* ? */
+ unsigned char end_sector; /* ? */
+ unsigned char end_cyl; /* ? */
+ unsigned int start_sect; /* starting sector counting from 0 */
+ unsigned int nr_sects; /* nr of sectors in partition */
+} Partition;
+
+extern int NR_SD;
+
+extern Partition scsi_disks[MAX_SD << 4] ;
+
+
+typedef struct {
+ unsigned capacity; /* size in blocks */
+ unsigned sector_size; /* size in bytes */
+ Scsi_Device *device;
+ unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */
+ unsigned char sector_bit_shift; /* power of 2 sectors per FS block */
+ unsigned ten:1; /* support ten byte read / write */
+ unsigned remap:1; /* support remapping */
+ unsigned use:1; /* after the initial inquiry, is
+ the device still supported ? */
+ } Scsi_Disk;
+
+extern Scsi_Disk rscsi_disks[MAX_SD];
+
+void sd_init(void);
+
+#define HOST (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no)
+#define ID (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->id)
+#define LUN (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun)
+#endif
diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c
new file mode 100644
index 0000000..087f870
--- /dev/null
+++ b/kernel/blk_drv/scsi/sd_ioctl.c
@@ -0,0 +1,20 @@
+#include <linux/config.h>
+#ifdef CONFIG_BLK_DEV_SD
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include "scsi.h"
+#include "sd.h"
+
+extern int scsi_ioctl (int dev, int cmd, void *arg);
+
+int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg)
+{
+ int dev = inode->i_rdev;
+
+ switch (cmd) {
+ default:
+ return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device,cmd,(void *) arg);
+ }
+}
+#endif
diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c
new file mode 100644
index 0000000..28df162
--- /dev/null
+++ b/kernel/blk_drv/scsi/seagate.c
@@ -0,0 +1,607 @@
+/*
+ * seagate.c Copyright (C) 1992 Drew Eckhardt
+ * low level scsi driver for ST01/ST02 by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_SEAGATE
+#include <linux/sched.h>
+
+#include "seagate.h"
+#include "scsi.h"
+#include "hosts.h"
+
+static int incommand; /*
+ set if arbitration has finished and we are
+ in some command phase.
+ */
+
+static void *base_address = NULL; /*
+ Where the card ROM starts,
+ used to calculate memory mapped
+ register location.
+ */
+static volatile int abort_confirm = 0;
+
+volatile void *st0x_cr_sr; /*
+ control register write,
+ status register read.
+ 256 bytes in length.
+
+ Read is status of SCSI BUS,
+ as per STAT masks.
+
+ */
+
+
+static volatile void *st0x_dr; /*
+ data register, read write
+ 256 bytes in length.
+ */
+
+
+static volatile int st0x_aborted=0; /*
+ set when we are aborted, ie by a time out, etc.
+ */
+
+ /*
+ In theory, we have a nice auto
+ detect routine - but this
+ overides it.
+ */
+
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
+#define STATUS (*(unsigned char *) st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(unsigned char *) st0x_dr)
+
+#ifndef OVERRIDE
+static const char * seagate_bases[] = {(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, (char *) 0xce000, (char *) 0xce000,
+ (char *) 0xdc000, (char *) 0xde000};
+typedef struct
+ {
+ char *signature ;
+ unsigned offset;
+ unsigned length;
+ } Signature;
+
+static const Signature signatures[] = {
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40},
+{"SEAGATE SCSI BIOS ",16, 17},
+{"SEAGATE SCSI BIOS ",17, 17}};
+/*
+ Note that the last signature handles BIOS revisions 3.0.0 and
+ 3.2 - the real ID's are
+
+SEAGATE SCSI BIOS REVISION 3.0.0
+SEAGATE SCSI BIOS REVISION 3.2
+
+*/
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
+#endif
+
+int seagate_st0x_detect (int hostnum)
+ {
+ #ifndef OVERRIDE
+ int i,j;
+ #endif
+
+ /*
+ First, we try for the manual override.
+ */
+ #ifdef DEBUG
+ printk("Autodetecting seagate ST0x\n");
+ #endif
+
+ base_address = NULL;
+ #ifdef OVERRIDE
+ base_address = (void *) OVERRIDE;
+ #ifdef DEBUG
+ printk("Base address overridden to %x\n", base_address);
+ #endif
+ #else
+ /*
+ To detect this card, we simply look for the SEAGATE SCSI
+ from the BIOS version notice in all the possible locations
+ of the ROM's.
+ */
+
+ for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i)
+ for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
+ if (!memcmp ((void *) (seagate_bases[i] +
+ signatures[j].offset), (void *) signatures[j].signature,
+ signatures[j].length))
+ base_address = (void *) seagate_bases[i];
+ #endif
+
+ if (base_address)
+ {
+ st0x_cr_sr =(void *) (((unsigned char *) base_address) + 0x1a00);
+ st0x_dr = (void *) (((unsigned char *) base_address )+ 0x1c00);
+ #ifdef DEBUG
+ printk("ST0x detected. Base address = %x, cr = %x, dr = %x\n", base_address, st0x_cr_sr, st0x_dr);
+ #endif
+ return -1;
+ }
+ else
+ {
+ #ifdef DEBUG
+ printk("ST0x not detected.\n");
+ #endif
+ return 0;
+ }
+ }
+
+
+
+char *seagate_st0x_info(void)
+{
+ static char buffer[] = "Seagate ST-0X SCSI driver by Drew Eckhardt \n"
+"$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/seagate.c,v 1.1 1992/04/24 18:01:50 root Exp root $\n";
+ return buffer;
+}
+
+
+
+int seagate_st0x_command(unsigned char target, const void *cmnd,
+ void *buff, int bufflen)
+ {
+ int len;
+ unsigned char *data;
+
+ int clock; /*
+ We use clock for timeouts, etc. This replaces the
+ seagate_st0x_timeout that we had been using.
+ */
+ #if (DEBUG & PHASE_SELECTION)
+ int temp;
+ #endif
+
+ #if (DEBUG & PHASE_EXIT)
+ void *retaddr, *realretaddr;
+ #endif
+
+ #if ((DEBUG & PHASE_ETC) || (DEBUG & PRINT_COMMAND) || (DEBUG & PHASE_EXIT))
+ int i;
+ #endif
+
+ #if (DEBUG & PHASE_ETC)
+ int phase=0, newphase;
+ #endif
+
+ int done = 0;
+ unsigned char status = 0;
+ unsigned char message = 0;
+ register unsigned char status_read;
+
+ #if (DEBUG & PHASE_EXIT)
+ __asm__("
+movl 4(%%ebp), %%eax
+":"=a" (realretaddr):);
+ printk("return address = %08x\n", realretaddr);
+ #endif
+
+
+ len=bufflen;
+ data=(unsigned char *) buff;
+
+ incommand = 0;
+ st0x_aborted = 0;
+
+ #if (DEBUG & PRINT_COMMAND)
+ printk ("seagate_st0x_command, target = %d, command = ", target);
+ for (i = 0; i < COMMAND_SIZE(((unsigned char *)cmnd)[0]); ++i)
+ printk("%02x ", ((unsigned char *) cmnd)[i]);
+ printk("\n");
+ #endif
+
+ if (target > 6)
+ return DID_BAD_TARGET;
+
+
+ #if (DEBUG & PHASE_BUS_FREE)
+ printk ("SCSI PHASE = BUS FREE \n");
+ #endif
+
+ /*
+
+ BUS FREE PHASE
+
+ On entry, we make sure that the BUS is in a BUS FREE
+ phase, by insuring that both BSY and SEL are low for
+ at least one bus settle delay. The standard requires a
+ minimum of 400 ns, which is 16 clock cycles on a
+ 386-40 .
+
+ This doesn't give us much time - so we'll do two several
+ reads to be sure be sure.
+ */
+
+ clock = jiffies + ST0X_BUS_FREE_DELAY;
+
+ while (((STATUS | STATUS | STATUS) &
+ (STAT_BSY | STAT_SEL)) &&
+ (!st0x_aborted) && (jiffies < clock));
+
+ if (jiffies > clock)
+ 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, plus driver enable,
+ plus raise select signal.
+ */
+
+ #if (DEBUG & PHASE_SELECTION)
+ printk("SCSI PHASE = SELECTION\n");
+ #endif
+
+ clock = jiffies + ST0X_SELECTION_DELAY;
+ DATA = (unsigned char) (1 << target);
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL;
+
+ /*
+ When the SCSI device decides that we're gawking at it, it will respond by asserting BUSY on the bus.
+ */
+ while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock) && !st0x_aborted)
+
+#if (DEBUG & PHASE_SELECTION)
+ {
+ temp = clock - jiffies;
+
+ if (!(jiffies % 5))
+ printk("seagate_st0x_timeout : %d \r",temp);
+
+ }
+ printk("Done. \n\r");
+ printk("Status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n", status_read, temp,
+ st0x_aborted);
+#else
+ ;
+#endif
+
+
+ if ((jiffies > clock) || (!st0x_aborted & !(status_read & STAT_BSY)))
+ {
+ #if (DEBUG & PHASE_SELECT)
+ printk ("NO CONNECT with target %d, status = %x \n", target, STATUS);
+ #endif
+ return retcode(DID_NO_CONNECT);
+ }
+
+ /*
+ If we have been aborted, and we have a command in progress, IE the target still has
+ BSY asserted, then we will reset the bus, and notify the midlevel driver to
+ expect sense.
+ */
+
+ if (st0x_aborted)
+ {
+ CONTROL = BASE_CMD;
+ if (STATUS & STAT_BSY)
+ {
+ seagate_st0x_reset();
+ return retcode(DID_RESET);
+ }
+
+ return retcode(st0x_aborted);
+ }
+
+ /*
+ COMMAND PHASE
+ The device has responded with a BSY, so we may now enter
+ the information transfer phase, where we will send / recieve
+ data and command as directed by the target.
+
+
+ The nasty looking read / write inline assembler loops we use for
+ DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ the 'C' versions - since we're moving 1024 bytes of data, this
+ really adds up.
+ */
+
+ #if (DEBUG & PHASE_ETC)
+ printk("PHASE = information transfer\n");
+ #endif
+
+ incommand = 1;
+
+ /*
+ Enable command
+ */
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+
+ /*
+ Now, we poll the device for status information,
+ and handle any requests it makes. Note that since we are unsure of
+ how much data will be flowing across the system, etc and cannot
+ make reasonable timeouts, that we will instead have the midlevel
+ driver handle any timeouts that occur in this phase.
+ */
+
+ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done)
+ {
+ #ifdef PARITY
+ if (status_read & STAT_PARITY)
+ {
+ done = 1;
+ st0x_aborted = DID_PARITY;
+ }
+ #endif
+
+ if (status_read & STAT_REQ)
+ {
+ #if (DEBUG & PHASE_ETC)
+ if ((newphase = (status_read & REQ_MASK)) != phase)
+ {
+ phase = newphase;
+ switch (phase)
+ {
+ case REQ_DATAOUT : printk("SCSI PHASE = DATA OUT\n"); break;
+ case REQ_DATAIN : printk("SCSI PHASE = DATA IN\n"); break;
+ case REQ_CMDOUT : printk("SCSI PHASE = COMMAND OUT\n"); break;
+ case REQ_STATIN : printk("SCSI PHASE = STATUS IN\n"); break;
+ case REQ_MSGOUT : printk("SCSI PHASE = MESSAGE OUT\n"); break;
+ case REQ_MSGIN : printk("SCSI PHASE = MESSAGE IN\n"); break;
+ default : printk("UNKNOWN PHASE"); st0x_aborted = 1; done = 1;
+ }
+ }
+ #endif
+
+ switch (status_read & REQ_MASK)
+ {
+ case REQ_DATAOUT :
+
+ /*
+ We loop as long as we are in a data out phase, there is data to send, and BSY is still
+ active
+ */
+ __asm__ ("
+
+/*
+ Local variables :
+ len = ecx
+ data = esi
+ st0x_cr_sr = ebx
+ st0x_dr = edi
+
+ Test for any data here at all.
+*/
+ movl %0, %%esi /* local value of data */
+ movl %1, %%ecx /* local value of len */
+ orl %%ecx, %%ecx
+ jz 2f
+
+ cld
+
+ movl _st0x_cr_sr, %%ebx
+ movl _st0x_dr, %%edi
+
+1: movb (%%ebx), %%al
+/*
+ Test for BSY
+*/
+
+ test $1, %%al
+ jz 2f
+
+/*
+ Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.
+*/
+ test $0xe, %%al
+ jnz 2f
+/*
+ Test for REQ
+*/
+ test $0x10, %%al
+ jz 1b
+ lodsb
+ movb %%al, (%%edi)
+ loop 1b
+
+2:
+ movl %%esi, %2
+ movl %%ecx, %3
+ ":
+/* output */
+"=r" (data), "=r" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"ebx", "ecx", "edi", "esi");
+
+ break;
+
+ case REQ_DATAIN :
+ /*
+ We loop as long as we are in a data out phase, there is room to read, and BSY is still
+ active
+ */
+
+ __asm__ ("
+/*
+ Local variables :
+ ecx = len
+ edi = data
+ esi = st0x_cr_sr
+ ebx = st0x_dr
+
+ Test for room to read
+*/
+
+ movl %0, %%edi /* data */
+ movl %1, %%ecx /* len */
+ orl %%ecx, %%ecx
+ jz 2f
+
+ cld
+ movl _st0x_cr_sr, %%esi
+ movl _st0x_dr, %%ebx
+
+1: movb (%%esi), %%al
+/*
+ Test for BSY
+*/
+
+ test $1, %%al
+ jz 2f
+
+/*
+ Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4.
+*/
+ movb $0xe, %%ah
+ andb %%al, %%ah
+ cmpb $0x04, %%ah
+ jne 2f
+
+/*
+ Test for REQ
+*/
+ test $0x10, %%al
+ jz 1b
+
+ movb (%%ebx), %%al
+ stosb
+ loop 1b
+
+2: movl %%edi, %2 /* data */
+ movl %%ecx, %3 /* len */
+ ":
+/* output */
+"=r" (data), "=r" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"ebx", "ecx", "edi", "esi");
+ break;
+
+ case REQ_CMDOUT :
+ while (((status_read = STATUS) & STAT_BSY) && ((status_read & REQ_MASK) ==
+ REQ_CMDOUT))
+ DATA = *(unsigned char *) cmnd ++;
+ break;
+
+ case REQ_STATIN :
+ status = DATA;
+ break;
+
+ case REQ_MSGOUT :
+ DATA = MESSAGE_REJECT;
+ break;
+
+ case REQ_MSGIN :
+ if ((message = DATA) == COMMAND_COMPLETE)
+ done=1;
+
+ break;
+
+ default : printk("UNKNOWN PHASE"); st0x_aborted = DID_ERROR;
+ }
+ }
+
+ }
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
+ printk("Transfered %d bytes, allowed %d additional bytes\n", (bufflen - len), len);
+#endif
+
+#if (DEBUG & PHASE_EXIT)
+ printk("Buffer : \n");
+ for (i = 0; i < 20; ++i)
+ printk ("%02x ", ((unsigned char *) buff)[i]);
+ printk("\n");
+ printk("Status = %02x, message = %02x\n", status, message);
+#endif
+
+
+ if (st0x_aborted)
+ {
+ if (STATUS & STAT_BSY)
+ {
+ seagate_st0x_reset();
+ st0x_aborted = DID_RESET;
+ }
+ abort_confirm = 1;
+ }
+
+ CONTROL = BASE_CMD;
+
+#if (DEBUG & PHASE_EXIT)
+ __asm__("
+mov 4(%%ebp), %%eax
+":"=a" (retaddr):);
+
+ printk("Exiting seagate_st0x_command() - return address is %08x \n", retaddr);
+ if (retaddr != realretaddr)
+ panic ("Corrupted stack : return address on entry != return address on exit.\n");
+
+#endif
+
+ return retcode (st0x_aborted);
+ }
+
+int seagate_st0x_abort (int code)
+ {
+ if (code)
+ st0x_aborted = code;
+ else
+ st0x_aborted = DID_ABORT;
+
+ return 0;
+ }
+
+/*
+ the seagate_st0x_reset function resets the SCSI bus
+*/
+
+int seagate_st0x_reset (void)
+ {
+ unsigned clock;
+ /*
+ No timeouts - this command is going to fail because
+ it was reset.
+ */
+
+#ifdef DEBUG
+ printk("In seagate_st0x_reset()\n");
+#endif
+
+
+ /* assert RESET signal on SCSI bus. */
+
+ CONTROL = BASE_CMD | CMD_RST;
+ clock=jiffies+2;
+
+
+ /* Wait. */
+
+ while (jiffies < clock);
+
+ CONTROL = BASE_CMD;
+
+ st0x_aborted = DID_RESET;
+
+#ifdef DEBUG
+ printk("SCSI bus reset.\n");
+#endif
+ return 0;
+ }
+#endif
diff --git a/kernel/blk_drv/scsi/seagate.h b/kernel/blk_drv/scsi/seagate.h
new file mode 100644
index 0000000..d625446
--- /dev/null
+++ b/kernel/blk_drv/scsi/seagate.h
@@ -0,0 +1,123 @@
+/*
+ * seagate.h Copyright (C) 1992 Drew Eckhardt
+ * low level scsi driver header for ST01/ST02 by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _SEAGATE_H
+ #define SEAGATE_H
+/*
+ $Header
+*/
+#ifndef ASM
+int seagate_st0x_detect(int);
+int seagate_st0x_command(unsigned char target, const void *cmnd, void *buff, int bufflen);
+int seagate_st0x_abort(int);
+char *seagate_st0x_info(void);
+int seagate_st0x_reset(void);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \
+ seagate_st0x_info, seagate_st0x_command, \
+ NULL, seagate_st0x_abort, seagate_st0x_reset, \
+ 0, 7, 0}
+#endif
+
+
+/*
+ defining PARITY causes parity data to be checked
+*/
+
+#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
+ driver, and Mitsugu Suzuki for information on the ST-01
+ SCSI host.
+*/
+
+/*
+ CONTROL defines
+*/
+
+#define CMD_RST 0x01
+#define CMD_SEL 0x02
+#define CMD_BSY 0x04
+#define CMD_ATTN 0x08
+#define CMD_START_ARB 0x10
+#define CMD_EN_PARITY 0x20
+#define CMD_INTR 0x40
+#define CMD_DRVR_ENABLE 0x80
+
+/*
+ STATUS
+*/
+
+#define STAT_BSY 0x01
+#define STAT_MSG 0x02
+#define STAT_IO 0x04
+#define STAT_CD 0x08
+#define STAT_REQ 0x10
+#define STAT_SEL 0x20
+#define STAT_PARITY 0x40
+#define STAT_ARB_CMPL 0x80
+
+/*
+ REQUESTS
+*/
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+extern volatile int seagate_st0x_timeout;
+
+#ifdef PARITY
+ #define BASE_CMD CMD_EN_PARITY
+#else
+ #define BASE_CMD 0
+#endif
+
+/*
+ Debugging code
+*/
+
+#define PHASE_BUS_FREE 1
+#define PHASE_ARBITRATION 2
+#define PHASE_SELECTION 4
+#define PHASE_DATAIN 8
+#define PHASE_DATAOUT 0x10
+#define PHASE_CMDOUT 0x20
+#define PHASE_MSGIN 0x40
+#define PHASE_MSGOUT 0x80
+#define PHASE_STATUSIN 0x100
+#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
+#define PRINT_COMMAND 0x200
+#define PHASE_EXIT 0x400
+
+/*
+ Control options - these are timeouts specified in .01 seconds.
+*/
+
+#define ST0X_BUS_FREE_DELAY 25
+#define ST0X_SELECTION_DELAY 25
+
+#endif
+
diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c
new file mode 100644
index 0000000..bef3ae8
--- /dev/null
+++ b/kernel/blk_drv/scsi/st.c
@@ -0,0 +1,36 @@
+/*
+ The st.c file is a sub-stub file. I just wanted to have all the detect code, etc in the
+ mid level driver present and working. If no one else volunteers for this, I'll
+ do it - but it's low on my list of priorities.
+*/
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_ST
+#include "scsi.h"
+#include "st.h"
+
+#define MAJOR_NR 9
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include "../blk.h"
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/st.c,v 1.1 1992/04/24 18:01:50 root Exp root $";
+*/
+
+Scsi_Tape scsi_tapes[MAX_ST];
+static int st_sizes[MAX_ST];
+int NR_ST=0;
+
+void do_st_request(void)
+{
+ panic("There is no st driver.\n\r");
+}
+
+void st_init(void)
+{
+ blk_dev[MAJOR_NR].request_fn = do_st_request;
+ blk_size[MAJOR_NR] = st_sizes;
+}
+#endif
diff --git a/kernel/blk_drv/scsi/st.h b/kernel/blk_drv/scsi/st.h
new file mode 100644
index 0000000..1a7c044
--- /dev/null
+++ b/kernel/blk_drv/scsi/st.h
@@ -0,0 +1,26 @@
+
+#ifndef _ST_H
+ #define _ST_H
+/*
+ $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/st.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+#define MAX_ST 1
+
+typedef struct
+ {
+ /*
+ Undecided goodies go here!!!
+ */
+ Scsi_Device* device;
+ } Scsi_Tape;
+
+
+extern int NR_ST;
+extern Scsi_Tape scsi_tapes[MAX_ST];
+void st_init(void);
+#endif
diff --git a/kernel/blk_drv/scsi/st_ioctl.c b/kernel/blk_drv/scsi/st_ioctl.c
new file mode 100644
index 0000000..4bc7a39
--- /dev/null
+++ b/kernel/blk_drv/scsi/st_ioctl.c
@@ -0,0 +1,19 @@
+#include <linux/config.h>
+#ifdef CONFIG_BLK_DEV_ST
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include "st.h"
+
+extern int scsi_ioctl(int dev, int cmd, void *arg);
+
+int st_ioctl(struct inode * inode,struct file * file, unsigned long cmd, unsigned long arg)
+{
+ int dev = inode->i_rdev;
+
+ switch (cmd) {
+ default:
+ return scsi_ioctl(scsi_tapes[MINOR(dev)].device,cmd,(void *) arg);
+ }
+}
+#endif
diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c
new file mode 100644
index 0000000..049fdd8
--- /dev/null
+++ b/kernel/blk_drv/scsi/ultrastor.c
@@ -0,0 +1,329 @@
+/*
+ * ultrastor.c (C) 1991 David B. Gentzel
+ * Low-level scsi driver for UltraStor 14F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+/* ??? Caveats:
+ This driver is VERY stupid. It takes no advantage of much of the power of
+ the UltraStor controller. We just sit-and-spin while waiting for commands
+ to complete. I hope to go back and beat it into shape, but PLEASE, anyone
+ else who would like to, please make improvements! */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+
+#include <stddef.h>
+
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include "ultrastor.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#define VERSION "1.0 alpha"
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))
+
+/* Simply using "unsigned long" in these structures won't work as it causes
+ alignment. Perhaps the "aligned" attribute may be used in GCC 2.0 to get
+ around this, but for now I use this hack. */
+typedef struct {
+ unsigned char bytes[4];
+} Longword;
+
+/* Used to store configuration info read from config i/o registers. Most of
+ this is not used yet, but might as well save it. */
+struct config {
+ struct {
+ unsigned char bios_segment: 3;
+ unsigned char reserved: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+ struct {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+};
+
+/* MailBox SCSI Command Packet. Basic command structure for communicating
+ with controller. */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target_id: 3; /* target SCSI id */
+ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
+ unsigned char lun: 3; /* logical unit number */
+ Longword transfer_data; /* transfer data pointer */
+ Longword transfer_data_length; /* length in bytes */
+ Longword command_link; /* for linking command chains */
+ unsigned char scsi_command_link_id; /* identifies command in chain */
+ unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */
+ unsigned char length_of_sense_byte;
+ unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ Longword sense_data;
+};
+
+/* Allowed BIOS base addresses for 14f (NULL indicates reserved) */
+static const void *const bios_segment_table[8] = {
+ NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
+ (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
+};
+
+/* Allowed IRQs for 14f */
+static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
+
+/* Allowed DMA channels for 14f (0 indicates reserved) */
+static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+#if 0 /* Not currently used, head/sector mappings allowed by 14f */
+static const struct {
+ unsigned char heads;
+ unsigned char sectors;
+} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
+#endif
+
+/* Config info */
+static struct config config;
+
+/* Our index in the host adapter array maintained by higher-level driver */
+static int host_number;
+
+/* PORT_ADDRESS is first port address used for i/o of messages. */
+#ifdef PORT_OVERRIDE
+# define PORT_ADDRESS PORT_OVERRIDE
+#else
+static unsigned short port_address = 0;
+# define PORT_ADDRESS port_address
+#endif
+
+static volatile int aborted = 0;
+
+#ifndef PORT_OVERRIDE
+static const unsigned short ultrastor_ports[] = {
+ 0x330, 0x340, 0x310, 0x230, 0x240, 0x210, 0x130, 0x140,
+};
+#endif
+
+static const struct {
+ const char *signature;
+ size_t offset;
+ size_t length;
+} signatures[] = {
+ { "SBIOS 1.01 COPYRIGHT (C) UltraStor Corporation,1990-1992.", 0x10, 57 },
+};
+
+int ultrastor_14f_detect(int hostnum)
+{
+ size_t i;
+ unsigned char in_byte;
+ const void *base_address;
+
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: called\n");
+#endif
+
+#ifndef PORT_OVERRIDE
+/* ??? This is easy to implement, but I'm not sure how "friendly" it is to
+ go off and read random i/o ports. */
+# error Not implemented!
+#endif
+
+ if (!PORT_ADDRESS) {
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: no port address found!\n");
+#endif
+ return FALSE;
+ }
+
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: port address = %X\n", PORT_ADDRESS);
+#endif
+
+ in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 0));
+ if (in_byte != US14F_PRODUCT_ID_0) {
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: unknown product ID 0 - %02X\n", in_byte);
+#endif
+ return FALSE;
+ }
+ in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1));
+ /* Only upper nibble is defined for Product ID 1 */
+ if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: unknown product ID 1 - %02X\n", in_byte);
+#endif
+ return FALSE;
+ }
+
+ /* All above tests passed, must be the right thing. Get some useful
+ info. */
+ *(char *)&config.config_1 = inb(CONFIG(PORT_ADDRESS + 0));
+ *(char *)&config.config_2 = inb(CONFIG(PORT_ADDRESS + 1));
+
+ /* To verify this card, we simply look for the UltraStor SCSI from the
+ BIOS version notice. */
+ base_address = bios_segment_table[config.config_1.bios_segment];
+ if (base_address != NULL) {
+ int found = 0;
+
+ for (i = 0; !found && i < ARRAY_SIZE(signatures); i++)
+ if (memcmp((char *)base_address + signatures[i].offset,
+ signatures[i].signature, signatures[i].length))
+ found = 1;
+ if (!found)
+ base_address = NULL;
+ }
+ if (!base_address) {
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: not detected.\n");
+#endif
+ return FALSE;
+ }
+
+ /* Final consistancy check, verify previous info. */
+ if (!dma_channel_table[config.config_1.dma_channel]
+ || !(config.config_2.tfr_port & 0x2)) {
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: consistancy check failed\n");
+#endif
+ return FALSE;
+ }
+
+ /* If we were TRULY paranoid, we could issue a host adapter inquiry
+ command here and verify the data returned. But frankly, I'm
+ exhausted! */
+
+ /* Finally! Now I'm satisfied... */
+#ifdef DEBUG
+ printk("ultrastor_14f_detect: detect succeeded\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %d\n"
+ " DMA channel: %d\n"
+ " H/A SCSI ID: %d\n",
+ base_address, interrupt_table[config.config_1.interrupt],
+ dma_channel_table[config.config_1.dma_channel],
+ config.config_2.ha_scsi_id);
+#endif
+ host_number = hostnum;
+ scsi_hosts[hostnum].this_id = config.config_2.ha_scsi_id;
+ return TRUE;
+}
+
+const char *ultrastor_14f_info(void)
+{
+ return "UltraStor 14F SCSI driver version "
+ VERSION
+ " by David B. Gentzel\n";
+}
+
+#if 0
+int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd,
+ void *buff, int bufflen, void (*done)(int, int))
+#else
+int ultrastor_14f_command(unsigned char target, const void *cmnd,
+ void *buff, int bufflen)
+#endif
+{
+ struct mscp mscp = {
+ OP_SCSI, DTD_SCSI, FALSE, TRUE, FALSE,
+ target, 0, 0 /* LUN??? */,
+ *(Longword *)&buff,
+ *(Longword *)&bufflen,
+ { 0, 0, 0, 0 },
+ 0,
+ 0,
+ 0,
+ ((*(char *)cmnd <= 0x1F) ? 6 : 10),
+ { 0 }, /* Filled in via memcpy below */
+ 0,
+ 0,
+ { 0, 0, 0, 0 }
+ };
+ unsigned char in_byte;
+
+ memcpy(mscp.scsi_cdbs, cmnd, mscp.length_of_scsi_cdbs);
+
+ /* Find free OGM slot (OGMINT bit is 0) */
+ do
+ in_byte = inb(LCL_DOORBELL_INTR(PORT_ADDRESS));
+ while (!aborted && (in_byte & 1));
+ if (aborted)
+ /* ??? is this right? */
+ return (aborted << 16);
+
+ /* Store pointer in OGM address bytes */
+ outb(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0));
+ outb(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1));
+ outb(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2));
+ outb(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3));
+
+ /* Issue OGM interrupt */
+ outb(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS));
+
+ /* Wait for ICM interrupt */
+ do
+ in_byte = inb(SYS_DOORBELL_INTR(PORT_ADDRESS));
+ while (!aborted && !(in_byte & 1));
+ if (aborted)
+ /* ??? is this right? */
+ return (aborted << 16);
+
+ /* Clean ICM slot (set ICMINT bit to 0) */
+ outb(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
+
+ /* ??? not right, but okay for now? */
+ return (mscp.adapter_status << 16) | mscp.target_status;
+}
+
+int ultrastor_14f_abort(int code)
+{
+ aborted = (code ? code : DID_ABORT);
+ return 0;
+}
+
+int ultrastor_14f_reset(void)
+{
+ unsigned char in_byte;
+
+#ifdef DEBUG
+ printk("ultrastor_14f_reset: called\n");
+#endif
+
+ /* Issue SCSI BUS reset */
+ outb(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS));
+ /* Wait for completion... */
+ do
+ in_byte = inb(LCL_DOORBELL_INTR(PORT_ADDRESS));
+ while (in_byte & 0x20);
+
+ aborted = DID_RESET;
+
+#ifdef DEBUG
+ printk("ultrastor_14f_reset: returning\n");
+#endif
+ return 0;
+}
+
+#endif
diff --git a/kernel/blk_drv/scsi/ultrastor.h b/kernel/blk_drv/scsi/ultrastor.h
new file mode 100644
index 0000000..13ded23
--- /dev/null
+++ b/kernel/blk_drv/scsi/ultrastor.h
@@ -0,0 +1,84 @@
+/*
+ * ultrastor.c (C) 1991 David B. Gentzel
+ * Low-level scsi driver for UltraStor 14F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+#ifndef _ULTRASTOR_H
+#define _ULTRASTOR_H
+
+/* ??? Some of the stuff in this file is really private to ultrastor.c and
+ should be moved elsewhere (as this file is included by higher-level driver
+ files). */
+
+/* ??? These don't really belong here */
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+int ultrastor_14f_detect(int);
+const char *ultrastor_14f_info(void);
+#if 0 /* ??? Future direction... */
+int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd,
+ void *buff, int bufflen,
+ void (*done)(int, int));
+#else
+int ultrastor_14f_command(unsigned char target, const void *cmnd,
+ void *buff, int bufflen);
+#endif
+int ultrastor_14f_abort(int);
+int ultrastor_14f_reset(void);
+
+#if 0 /* ??? Future direction... */
+# define ULTRASTOR_14F \
+ { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, 0, \
+ ultrastor_14f_queuecommand, ultrastor_14f_abort, ultrastor_14f_reset, \
+ TRUE, 0, 0 }
+#else
+# define ULTRASTOR_14F \
+ { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, \
+ ultrastor_14f_command, 0, ultrastor_14f_abort, ultrastor_14f_reset, \
+ FALSE, 0, 0 }
+#endif
+
+#define PORT_OVERRIDE 0x330
+
+/* Port addresses (relative to the base address) */
+#define LCL_DOORBELL_MASK(port) ((port) + 0x0)
+#define LCL_DOORBELL_INTR(port) ((port) + 0x1)
+#define SYS_DOORBELL_MASK(port) ((port) + 0x2)
+#define SYS_DOORBELL_INTR(port) ((port) + 0x3)
+#define PRODUCT_ID(port) ((port) + 0x4)
+#define CONFIG(port) ((port) + 0x6)
+#define OGM_DATA_PTR(port) ((port) + 0x8)
+#define ICM_DATA_PTR(port) ((port) + 0xC)
+
+/* Values for the PRODUCT_ID ports for the 14F */
+#define US14F_PRODUCT_ID_0 0x56
+#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */
+
+/* MSCP field values */
+
+/* Opcode */
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+
+/* Date Transfer Direction */
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+
+/* Host Adapter command subcodes */
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#endif
diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile
index 8049f2f..9eb9435 100644
--- a/kernel/chr_drv/Makefile
+++ b/kernel/chr_drv/Makefile
@@ -25,15 +25,15 @@ CPP =cpp -nostdinc -I../../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
-OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \
+OBJS = tty_io.o console.o keyboard.o serial.o \
tty_ioctl.o pty.o lp.o vt.o mem.o
chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
-keyboard.s: keyboard.S
- $(CPP) $(KEYBOARD) -traditional keyboard.S -o keyboard.s
+keyboard.o: keyboard.c
+ $(CC) $(CFLAGS) $(KEYBOARD) -c -o keyboard.o keyboard.c
clean:
rm -f core *.o *.a tmp_make keyboard.s
@@ -42,7 +42,7 @@ clean:
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
- $(CPP) -M $$i;done) >> tmp_make
+ $(CPP) -M -DKBD_FINNISH $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
@@ -51,10 +51,16 @@ console.s console.o : console.c ../../include/linux/sched.h ../../include/linux/
../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/timer.h ../../include/linux/tty.h \
- ../../include/termios.h ../../include/linux/config.h ../../include/linux/config_rel.h \
- ../../include/linux/config_ver.h ../../include/asm/io.h ../../include/asm/system.h \
- ../../include/asm/segment.h ../../include/linux/string.h ../../include/errno.h \
- ../../include/sys/kd.h vt_kern.h
+ ../../include/asm/system.h ../../include/termios.h ../../include/linux/config.h \
+ ../../include/linux/config_rel.h ../../include/linux/config_ver.h ../../include/linux/config.dist.h \
+ ../../include/asm/io.h ../../include/asm/segment.h ../../include/linux/string.h \
+ ../../include/errno.h ../../include/sys/kd.h vt_kern.h
+keyboard.s keyboard.o : keyboard.c ../../include/linux/sched.h ../../include/linux/head.h \
+ ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \
+ ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
+ ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
+ ../../include/sys/resource.h ../../include/linux/ctype.h ../../include/linux/tty.h \
+ ../../include/asm/system.h ../../include/termios.h ../../include/asm/io.h
lp.s lp.o : lp.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
@@ -64,38 +70,38 @@ mem.s mem.o : mem.c ../../include/errno.h ../../include/sys/types.h ../../includ
../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \
../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
- ../../include/sys/resource.h ../../include/linux/tty.h ../../include/termios.h \
- ../../include/asm/segment.h ../../include/asm/io.h
+ ../../include/sys/resource.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/termios.h ../../include/asm/segment.h ../../include/asm/io.h
pty.s pty.o : pty.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
- ../../include/linux/tty.h ../../include/termios.h ../../include/asm/system.h \
+ ../../include/linux/tty.h ../../include/asm/system.h ../../include/termios.h \
../../include/asm/io.h
-serial.s serial.o : serial.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
- ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \
- ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \
+serial.s serial.o : serial.c ../../include/signal.h ../../include/sys/types.h ../../include/linux/sched.h \
+ ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \
+ ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \
- ../../include/linux/timer.h ../../include/linux/tty.h ../../include/termios.h \
- ../../include/asm/system.h ../../include/asm/io.h
+ ../../include/linux/timer.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/termios.h ../../include/asm/io.h
tty_io.s tty_io.o : tty_io.c ../../include/linux/ctype.h ../../include/errno.h ../../include/signal.h \
../../include/sys/types.h ../../include/unistd.h ../../include/sys/stat.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/times.h ../../include/sys/utsname.h ../../include/sys/param.h \
../../include/sys/resource.h ../../include/utime.h ../../include/fcntl.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \
../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
- ../../include/linux/tty.h ../../include/termios.h ../../include/asm/segment.h \
- ../../include/asm/system.h
+ ../../include/linux/tty.h ../../include/asm/system.h ../../include/termios.h \
+ ../../include/asm/io.h ../../include/asm/segment.h
tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h ../../include/sys/types.h \
../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
- ../../include/sys/resource.h ../../include/linux/tty.h ../../include/asm/io.h \
- ../../include/asm/segment.h ../../include/asm/system.h
+ ../../include/sys/resource.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/asm/io.h ../../include/asm/segment.h
vt.s vt.o : vt.c ../../include/errno.h ../../include/sys/types.h ../../include/sys/kd.h \
../../include/sys/vt.h ../../include/asm/io.h ../../include/asm/segment.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \
../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
- ../../include/sys/resource.h ../../include/linux/tty.h ../../include/termios.h \
- vt_kern.h
+ ../../include/sys/resource.h ../../include/linux/tty.h ../../include/asm/system.h \
+ ../../include/termios.h vt_kern.h
diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c
index 01421e5..824f1ad 100644
--- a/kernel/chr_drv/console.c
+++ b/kernel/chr_drv/console.c
@@ -124,11 +124,9 @@ static struct {
unsigned int vc_saved_y;
unsigned int vc_iscolor;
unsigned char vc_kbdapplic;
- unsigned char vc_kbdleds;
unsigned char vc_kbdmode;
- unsigned char vc_kbdraw;
- unsigned char vc_kbde0;
char * vc_translate;
+ /* additional information is in vt_kern.h */
} vc_cons [MAX_CONSOLES];
#define MEM_BUFFER_SIZE (2*80*50*8)
@@ -161,9 +159,9 @@ static int console_blanked = 0;
#define iscolor (vc_cons[currcons].vc_iscolor)
#define kbdapplic (vc_cons[currcons].vc_kbdapplic)
#define kbdmode (vc_cons[currcons].vc_kbdmode)
-#define kbdraw (vc_cons[currcons].vc_kbdraw)
-#define kbde0 (vc_cons[currcons].vc_kbde0)
-#define kbdleds (vc_cons[currcons].vc_kbdleds)
+#define kbdraw (vt_cons[currcons].vc_kbdraw)
+#define kbde0 (vt_cons[currcons].vc_kbde0)
+#define kbdleds (vt_cons[currcons].vc_kbdleds)
int blankinterval = 5*60*HZ;
static int screen_size = 0;
@@ -475,12 +473,10 @@ static void respond(int currcons, struct tty_struct * tty)
{
char * p = RESPONSE;
- cli();
while (*p) {
PUTCH(*p,tty->read_q);
p++;
}
- sti();
TTY_READ_FLUSH(tty);
}
@@ -579,7 +575,7 @@ enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
void con_write(struct tty_struct * tty)
{
- unsigned char c;
+ int c;
unsigned int currcons;
wake_up(&tty->write_q->proc_list);
@@ -588,10 +584,11 @@ void con_write(struct tty_struct * tty)
printk("con_write: illegal tty\n\r");
return;
}
- while (!EMPTY(tty->write_q)) {
- if (tty->stopped)
- break;
- GETCH(tty->write_q,c);
+ if (vt_cons[currcons].vt_mode == KD_GRAPHICS) {
+ flush(tty->write_q);
+ return; /* no output in graphics mode */
+ }
+ while (!tty->stopped && (c = GETCH(tty->write_q)) >= 0) {
if (c == 24 || c == 26)
state = ESnormal;
switch(state) {
@@ -788,6 +785,7 @@ void con_write(struct tty_struct * tty)
par[1] <= video_num_lines) {
top=par[0];
bottom=par[1];
+ gotoxy(currcons,0,0);
}
break;
case 's':
@@ -838,19 +836,22 @@ void con_write(struct tty_struct * tty)
}
set_cursor(currcons);
timer_active &= ~(1<<BLANK_TIMER);
- if (console_blanked) {
- timer_table[BLANK_TIMER].expires = 0;
- timer_active |= 1<<BLANK_TIMER;
- } else if (blankinterval) {
- timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
- timer_active |= 1<<BLANK_TIMER;
- }
+ if (currcons == fg_console)
+ if (console_blanked) {
+ timer_table[BLANK_TIMER].expires = 0;
+ timer_active |= 1<<BLANK_TIMER;
+ } else if (blankinterval) {
+ timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
+ timer_active |= 1<<BLANK_TIMER;
+ }
}
void do_keyboard_interrupt(void)
{
TTY_READ_FLUSH(TTY_TABLE(0));
timer_active &= ~(1<<BLANK_TIMER);
+ if (vt_cons[fg_console].vt_mode == KD_GRAPHICS)
+ return;
if (console_blanked) {
timer_table[BLANK_TIMER].expires = 0;
timer_active |= 1<<BLANK_TIMER;
@@ -886,9 +887,7 @@ void con_init(void)
char *display_desc = "????";
char *display_ptr;
int currcons = 0;
- long base, term;
- long video_memory;
- long saveterm, savebase;
+ long base;
video_num_columns = ORIG_VIDEO_COLS;
video_size_row = video_num_columns * 2;
@@ -949,25 +948,20 @@ void con_init(void)
display_ptr++;
}
- savebase = video_mem_base;
- saveterm = video_mem_term;
memsetw(vc_scrmembuf,video_erase_char,MEM_BUFFER_SIZE/2);
- video_mem_base = (long)vc_scrmembuf;
- video_mem_term = (long)&(vc_scrmembuf[MEM_BUFFER_SIZE/2]);
- video_memory = video_mem_term - video_mem_base;
+ base = (long)vc_scrmembuf;
screen_size = (video_num_lines * video_size_row);
- NR_CONSOLES = video_memory / screen_size;
+ NR_CONSOLES = MEM_BUFFER_SIZE / screen_size;
if (NR_CONSOLES > MAX_CONSOLES)
NR_CONSOLES = MAX_CONSOLES;
if (!NR_CONSOLES)
NR_CONSOLES = 1;
- video_memory = screen_size;
/* Initialize the variables used for scrolling (mostly EGA/VGA) */
- base = origin = video_mem_start = video_mem_base;
- term = video_mem_end = base + video_memory;
- scr_end = video_mem_start + screen_size;
+ base = origin = video_mem_start = (long)vc_scrmembuf;
+ scr_end = video_mem_end = base + screen_size;
+ vc_scrbuf[0] = (unsigned short *) origin;
top = 0;
bottom = video_num_lines;
attr = 0x07;
@@ -983,21 +977,20 @@ void con_init(void)
kbdraw = 0;
kbde0 = 0;
kbdapplic = 0;
+ vt_cons[0].vt_mode = KD_TEXT;
vc_cons[0].vc_bold_attr = -1;
gotoxy(currcons,ORIG_X,ORIG_Y);
for (currcons = 1; currcons<NR_CONSOLES; currcons++) {
vc_cons[currcons] = vc_cons[0];
- origin = video_mem_start = (base += video_memory);
- scr_end = origin + video_num_lines * video_size_row;
- video_mem_end = (term += video_memory);
+ vt_cons[currcons] = vt_cons[0];
+ base += screen_size;
+ origin = video_mem_start = base;
+ scr_end = video_mem_end = base + screen_size;
+ vc_scrbuf[currcons] = (unsigned short *) origin;
gotoxy(currcons,0,0);
}
- for (currcons = 0; currcons<NR_CONSOLES; currcons++)
- vc_scrbuf[currcons] = (unsigned short *)origin;
currcons = 0;
- video_mem_base = savebase;
- video_mem_term = saveterm;
video_mem_start = video_mem_base;
video_mem_end = video_mem_term;
@@ -1012,8 +1005,6 @@ void con_init(void)
a=inb_p(0x61);
outb_p(a|0x80,0x61);
outb_p(a,0x61);
-
- vt_init();
}
void kbdsave(int new_console)
@@ -1138,7 +1129,7 @@ void console_print(const char * b)
if (currcons<0 || currcons>=NR_CONSOLES)
currcons = 0;
- if (vt_info[currcons].mode == KD_GRAPHICS)
+ if (vt_cons[currcons].vt_mode == KD_GRAPHICS)
return; /* no output in graphics mode */
while (c = *(b++)) {
if (c == 10) {
diff --git a/kernel/chr_drv/keyboard.S b/kernel/chr_drv/keyboard.S
deleted file mode 100644
index 099cfc9..0000000
--- a/kernel/chr_drv/keyboard.S
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * linux/kernel/keyboard.S
- *
- * (C) 1991 Linus Torvalds
- */
-
-/*
- * NOTE! This file no longer contains the low-level keyboard interrupt:
- * that is now in kernel/sys_call.S. The reason is that the low-level
- * interrupt must keep track of stack frames for signals etc.
- *
- * Thus it should be very easy to rewrite this in C - I just haven't
- * bothered.
- */
-
-/*
- * Thanks to Alfred Leung for US keyboard patches
- * Wolfgang Thiel for German keyboard patches
- * Marc Corsini for the French keyboard
- * LeBlanc@mcc.ac.uk for the UK keyboard
- * Tommy Thorn (tthorn@daimi.aau.dk) for Danish keyboard
- */
-
-/* KBD_FINNISH for Finnish keyboards
- * KBD_US for US-type
- * KBD_GR for German keyboards
- * KBD_FR for Frech keyboard
- * KBD_UK for British extended keyboard
- * KBD_DK for Danish keyboard
- */
-
-.text
-.globl _hard_reset_now
-.globl _do_keyboard_interrupt
-.globl _do_keyboard
-.globl _kapplic
-.globl _kmode
-.globl _kleds
-.globl _set_leds
-.globl _kraw
-.globl _ke0
-
-/*
- * these are for the keyboard read functions
- */
-size = 2048 /* must be a power of two ! And MUST be the same
- as in tty_io.c !!!! */
-head = 4
-tail = 8
-proc_list = 12
-buf = 16
-
-_kapplic: .byte 0
-_kmode: .byte 0 /* caps, alt, ctrl and shift mode */
-_kleds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
-_ke0: .byte 0
-_kraw: .byte 0
-
-/*
- * do_keyboard is the real interrupt routine that reads the
- * keyboard scan-code and converts it into the appropriate
- * ascii character(s).
- */
-_do_keyboard:
- xorl %eax,%eax /* %eax is scan code */
- inb $0x60,%al
- pushl %eax
- inb $0x61,%al
- jmp 1f
-1: jmp 1f
-1: orb $0x80,%al
- jmp 1f
-1: jmp 1f
-1: outb %al,$0x61
- jmp 1f
-1: jmp 1f
-1: andb $0x7F,%al
- outb %al,$0x61
- jmp 1f
-1: jmp 1f
-1: movb $0x20,%al
- outb %al,$0x20
- cmpb $0,_kraw
- je 1f
- xorl %ebx,%ebx /* if raw kbd, just put scancode */
- popl %eax
- sti
- call put_queue
- jmp postkey
-1: popl %eax
- movl $1,%ebx
- cmpb $0xE0,%al
- je end_intr
- movl $2,%ebx
- cmpb $0xE1,%al
- je end_intr
- sti
- call key_table(,%eax,4)
-postkey:
- call _do_keyboard_interrupt
- movl $0,%ebx
-end_intr:
- movb %bl,_ke0
- ret
-
-/*
- * This routine fills the buffer with max 8 bytes, taken from
- * %ebx:%eax. (%edx is high). The bytes are written in the
- * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
- */
-put_queue:
- pushl %ecx
- pushl %edx
- movl _table_list,%edx # read-queue for console
- movl head(%edx),%ecx
-1: movb %al,buf(%edx,%ecx)
- incl %ecx
- andl $size-1,%ecx
- cmpl tail(%edx),%ecx # buffer full - discard everything
- je 3f
- shrdl $8,%ebx,%eax
- je 2f
- shrl $8,%ebx
- jmp 1b
-2: movl %ecx,head(%edx)
- movl proc_list(%edx),%ecx
- testl %ecx,%ecx
- je 3f
- movl $0,(%ecx)
-3: popl %edx
- popl %ecx
- ret
-
-ctrl: movb $0x04,%al
- jmp 1f
-alt: movb $0x10,%al
-1: cmpb $0,_ke0
- je 2f
- addb %al,%al
-2: orb %al,_kmode
- ret
-unctrl: movb $0x04,%al
- jmp 1f
-unalt: movb $0x10,%al
-1: cmpb $0,_ke0
- je 2f
- addb %al,%al
-2: notb %al
- andb %al,_kmode
- ret
-
-lshift:
- orb $0x01,_kmode
- ret
-unlshift:
- andb $0xfe,_kmode
- ret
-rshift:
- orb $0x02,_kmode
- ret
-unrshift:
- andb $0xfd,_kmode
- ret
-
-old_leds:
- .byte 2
-
-caps: testb $0x80,_kmode
- jne 1f
- xorb $4,_kleds
- xorb $0x40,_kmode
- orb $0x80,_kmode
-_set_leds:
- movb _kleds,%al
- cmpb old_leds,%al
- je 1f
- movb %al,old_leds
- call kb_wait
- movb $0xed,%al /* set leds command */
- outb %al,$0x60
- call kb_wait
- movb _kleds,%al
- outb %al,$0x60
-1: ret
-uncaps: andb $0x7f,_kmode
- ret
-scroll:
- testb $0x03,_kmode
- je 1f
- call _show_mem
- jmp 2f
-1: call _show_state
-2: xorb $1,_kleds
- jmp _set_leds
-
-num: cmpb $0x01,_kapplic
- jne notappl
- movw $0x0050,%ax
-applkey:
- shll $16,%eax
- movw $0x4f1b,%ax
- xorl %ebx,%ebx
- jmp put_queue
-
-notappl:
- xorb $2,_kleds
- jmp _set_leds
-
-/*
- * cursor-key/numeric keypad cursor keys are handled here.
- * checking for numeric keypad etc.
- */
-cursor:
- subb $0x47,%al
- jb 1f
- cmpb $12,%al
- ja 1f
- jne cur2 /* check for ctrl-alt-del */
- testb $0x0c,_kmode
- je cur2
- testb $0x30,_kmode
- jne _ctrl_alt_del
-cur2: cmpb $0x01,_ke0 /* _ke0 forces cursor movement */
- je cur
- testb $0x03,_kmode /* shift forces cursor */
- jne cur
- cmpb $0x01,_kapplic
- jne notcappl
- movb appl_table(%eax),%al
- jmp applkey
-notcappl:
- testb $0x02,_kleds /* not num-lock forces cursor */
- je cur
- xorl %ebx,%ebx
- movb num_table(%eax),%al
- jmp put_queue
-1: ret
-
-/*
- * cursor keys send ^[ [ x if normal, ^[ O x if application mode
- */
-cur: movb cur_table(%eax),%al
- cmpb $'9,%al
- ja ok_cur
- movb $'~,%ah
-ok_cur: shll $16,%eax
- movw $0x5b1b,%ax
- xorl %ebx,%ebx
- cmpb $0x01,_kapplic
- jne put_queue
- movb $0x4f,%ah
- jmp put_queue
-
-#if defined(KBD_FR) /* || defined(KBD_DK) correct, but .. */
-num_table:
- .ascii "789-456+1230."
-#else
-num_table:
- .ascii "789-456+1230,"
-#endif
-cur_table:
- .ascii "HA5-DGC+YB623"
-
-/*
- Keypad / 35 B7 Q
- Keypad * (PrtSc) 37 B7 R
- Keypad NumLock 45 ?? P
- Keypad 7 (Home) 47 C7 w
- Keypad 8 (Up arrow) 48 C8 x
- Keypad 9 (PgUp) 49 C9 y
- Keypad - 4A CA S
- Keypad 4 (Left arrow) 4B CB t
- Keypad 5 4C CC u
- Keypad 6 (Right arrow) 4D CD v
- Keypad + 4E CE l
- Keypad 1 (End) 4F CF q
- Keypad 2 (Down arrow) 50 D0 r
- Keypad 3 (PgDn) 51 D1 s
- Keypad 0 (Ins) 52 D2 p
- Keypad . (Del) 53 D3 n
-*/
-
-appl_table:
- .ascii "wxyStuvlqrspn"
-
-/*
- * this routine handles function keys
- */
-func:
- subb $0x3B,%al
- jb end_func
- cmpb $9,%al
- jbe ok_func
- subb $18,%al
- cmpb $10,%al
- jb end_func
- cmpb $11,%al
- ja end_func
-ok_func:
- testb $0x10,_kmode
- jne alt_func
- cmpl $4,%ecx /* check that there is enough room */
- jl end_func
- movl func_table(,%eax,4),%eax
- xorl %ebx,%ebx
- jmp put_queue
-alt_func:
- pushl %eax
- call _change_console
- popl %eax
-end_func:
- ret
-
-/*
- * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
- */
-func_table:
- .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
- .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
- .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
-
-#if defined(KBD_FINNISH)
-key_map:
- .byte 0,27
- .ascii "1234567890+'"
- .byte 127,9
- .ascii "qwertyuiop}"
- .byte 0,13,0
- .ascii "asdfghjkl|{"
- .byte 0,0
- .ascii "'zxcvbnm,.-"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '<
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "!\"#$%&/()=?`"
- .byte 127,9
- .ascii "QWERTYUIOP]^"
- .byte 13,0
- .ascii "ASDFGHJKL\\["
- .byte 0,0
- .ascii "*ZXCVBNM;:_"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '>
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0@\243$\0\0{[]}\\\0"
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-#elif defined(KBD_US)
-
-key_map:
- .byte 0,27
- .ascii "1234567890-="
- .byte 127,9
- .ascii "qwertyuiop[]"
- .byte 13,0
- .ascii "asdfghjkl;'"
- .byte '`,0
- .ascii "\\zxcvbnm,./"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '<
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "!@#$%^&*()_+"
- .byte 127,9
- .ascii "QWERTYUIOP{}"
- .byte 13,0
- .ascii "ASDFGHJKL:\""
- .byte '~,0
- .ascii "|ZXCVBNM<>?"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '>
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0@\0$\0\0{[]}\\\0"
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-#elif defined(KBD_UK)
-
-key_map:
- .byte 0,27
- .ascii "1234567890-="
- .byte 127,9
- .ascii "qwertyuiop[]"
- .byte 13,0
- .ascii "asdfghjkl;'"
- .byte '`,0
- .ascii "#zxcvbnm,./"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .ascii "\\"
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "!\"\243$%^&*()_+"
- .byte 127,9
- .ascii "QWERTYUIOP{}"
- .byte 13,0
- .ascii "ASDFGHJKL:@"
- .byte '~,0
- .ascii "~ZXCVBNM<>?"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0@\0$\0\0{[]}\\\0"
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-#elif defined(KBD_GR)
-
-key_map:
- .byte 0,27
- .ascii "1234567890\\'"
- .byte 127,9
- .ascii "qwertzuiop@+"
- .byte 13,0
- .ascii "asdfghjkl[]^"
- .byte 0,'#
- .ascii "yxcvbnm,.-"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '<
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "!\"#$%&/()=?`"
- .byte 127,9
- .ascii "QWERTZUIOP\\*"
- .byte 13,0
- .ascii "ASDFGHJKL{}~"
- .byte 0,''
- .ascii "YXCVBNM;:_"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '>
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0@\0$\0\0{[]}\\\0"
- .byte 0,0
- .byte '@,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-#elif defined(KBD_FR)
-
-key_map:
- .byte 0,27
- .ascii "&{\"'(-}_/@)="
- .byte 127,9
- .ascii "azertyuiop^$"
- .byte 13,0
- .ascii "qsdfghjklm|"
- .byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */
- .ascii "wxcvbn,;:!"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '<
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "1234567890]+"
- .byte 127,9
- .ascii "AZERTYUIOP<>"
- .byte 13,0
- .ascii "QSDFGHJKLM%"
- .byte '~,0,'#
- .ascii "WXCVBN?./\\"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '>
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0~#{[|`\\^@]}"
- .byte 0,0
- .byte '@,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '|
- .fill 10,1,0
-
-#elif defined(KBD_DK)
-
-key_map:
- .byte 0,27
- .ascii "1234567890+'"
- .byte 127,9
- .ascii "qwertyuiop"
- .byte 134,0,13,0 /* This is IBM-PC, change it to latin-1 */
- .ascii "asdfghjkl"
- .byte 145,155,0,0
- .ascii "'zxcvbnm,.-"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '<
- .fill 10,1,0
-
-shift_map:
- .byte 0,27
- .ascii "!\"#$%&/()=?`"
- .byte 127,9
- .ascii "QWERTYUIOP"
- .byte 143,94,13,0
- .ascii "ASDFGHJKL"
- .byte 146,157,0,0
- .ascii "*ZXCVBNM;:_"
- .byte 0,'*,0,32 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte '-,0,0,0,'+ /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .byte '>
- .fill 10,1,0
-
-alt_map:
- .byte 0,0
- .ascii "\0@\0$\0\0{[]}\0"
- .byte '|,0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte '~,13,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0
- .byte 0,0,0,0,0,0,0,0,0,0,0
- .byte 0,0,0,0 /* 36-39 */
- .fill 16,1,0 /* 3A-49 */
- .byte 0,0,0,0,0 /* 4A-4E */
- .byte 0,0,0,0,0,0,0 /* 4F-55 */
- .ascii "\\"
- .fill 10,1,0
-
-#else
-#error "KBD-type not defined"
-#endif
-/*
- * do_self handles "normal" keys, ie keys that don't change meaning
- * and which have just one character returns.
- */
-do_self:
- lea alt_map,%ebx
- testb $0x20,_kmode /* alt-gr */
- jne 1f
- lea shift_map,%ebx
- testb $0x0f,_kmode
- jne 1f
- lea key_map,%ebx
-1: movb (%ebx,%eax),%al
- orb %al,%al
- je none
- testb $0x4c,_kmode /* ctrl or caps */
- je 2f
- cmpb $'a,%al
- jb 2f
- cmpb $'},%al
- ja 2f
- subb $32,%al
-2: testb $0x0c,_kmode /* ctrl */
- je 3f
- andb $0x1f,%al
-3: testb $0x10,_kmode /* left alt */
- je 4f
- orb $0x80,%al
-4: andl $0xff,%eax
- xorl %ebx,%ebx
- call put_queue
-none: ret
-
-/*
- * slash and star have routines of their own, as a 'E0h' before
- * the scan code for slash means that the numeric keypad
- * slash was pushed.
- */
-slash: cmpb $1,_ke0
- jne do_self
- cmpb $1,_kapplic
- jne notmapplic
- movw $'Q,%ax
- jmp applkey
-
-notmapplic:
- movl $'/,%eax
- xorl %ebx,%ebx
- jmp put_queue
-
-star: cmpb $1,_kapplic
- jne do_self
- movw $'R,%ax
- jmp applkey
-
-notsapplic:
- movl $'*,%eax
- xorl %ebx,%ebx
- jmp put_queue
-
-enter: cmpb $1,_ke0
- jne do_self
- cmpb $1,_kapplic
- jne do_self
- movw $'M,%ax
- jmp applkey
-
-minus: cmpb $1,_kapplic
- jne do_self
- movw $'S,%ax
- jmp applkey
-
-plus: cmpb $1,_kapplic
- jne do_self
- movw $'l,%ax
- jmp applkey
-
-/*
- * This table decides which routine to call when a scan-code has been
- * gotten. Most routines just call do_self, or none, depending if
- * they are make or break.
- */
-key_table:
- .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
- .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
- .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
- .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
- .long do_self,do_self,do_self,do_self /* 10-13 q w e r */
- .long do_self,do_self,do_self,do_self /* 14-17 t y u i */
- .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
- .long enter,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
- .long do_self,do_self,do_self,do_self /* 20-23 d f g h */
- .long do_self,do_self,do_self,do_self /* 24-27 j k l | */
- .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
- .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
- .long do_self,do_self,do_self,do_self /* 30-33 b n m , */
- .long do_self,slash,rshift,star /* 34-37 . - rshift * */
- .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */
- .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
- .long func,func,func,func /* 40-43 f6 f7 f8 f9 */
- .long func,num,scroll,cursor /* 44-47 f10 num scr home */
- .long cursor,cursor,minus,cursor /* 48-4B up pgup - left */
- .long cursor,cursor,plus,cursor /* 4C-4F n5 right + end */
- .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
- .long none,none,do_self,func /* 54-57 sysreq ? < f11 */
- .long func,none,none,none /* 58-5B f12 ? ? ? */
- .long none,none,none,none /* 5C-5F ? ? ? ? */
- .long none,none,none,none /* 60-63 ? ? ? ? */
- .long none,none,none,none /* 64-67 ? ? ? ? */
- .long none,none,none,none /* 68-6B ? ? ? ? */
- .long none,none,none,none /* 6C-6F ? ? ? ? */
- .long none,none,none,none /* 70-73 ? ? ? ? */
- .long none,none,none,none /* 74-77 ? ? ? ? */
- .long none,none,none,none /* 78-7B ? ? ? ? */
- .long none,none,none,none /* 7C-7F ? ? ? ? */
- .long none,none,none,none /* 80-83 ? br br br */
- .long none,none,none,none /* 84-87 br br br br */
- .long none,none,none,none /* 88-8B br br br br */
- .long none,none,none,none /* 8C-8F br br br br */
- .long none,none,none,none /* 90-93 br br br br */
- .long none,none,none,none /* 94-97 br br br br */
- .long none,none,none,none /* 98-9B br br br br */
- .long none,unctrl,none,none /* 9C-9F br unctrl br br */
- .long none,none,none,none /* A0-A3 br br br br */
- .long none,none,none,none /* A4-A7 br br br br */
- .long none,none,unlshift,none /* A8-AB br br unlshift br */
- .long none,none,none,none /* AC-AF br br br br */
- .long none,none,none,none /* B0-B3 br br br br */
- .long none,none,unrshift,none /* B4-B7 br br unrshift br */
- .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */
- .long none,none,none,none /* BC-BF br br br br */
- .long none,none,none,none /* C0-C3 br br br br */
- .long none,none,none,none /* C4-C7 br br br br */
- .long none,none,none,none /* C8-CB br br br br */
- .long none,none,none,none /* CC-CF br br br br */
- .long none,none,none,none /* D0-D3 br br br br */
- .long none,none,none,none /* D4-D7 br br br br */
- .long none,none,none,none /* D8-DB br ? ? ? */
- .long none,none,none,none /* DC-DF ? ? ? ? */
- .long none,none,none,none /* E0-E3 e0 e1 ? ? */
- .long none,none,none,none /* E4-E7 ? ? ? ? */
- .long none,none,none,none /* E8-EB ? ? ? ? */
- .long none,none,none,none /* EC-EF ? ? ? ? */
- .long none,none,none,none /* F0-F3 ? ? ? ? */
- .long none,none,none,none /* F4-F7 ? ? ? ? */
- .long none,none,none,none /* F8-FB ? ? ? ? */
- .long none,none,none,none /* FC-FF ? ? ? ? */
-
-/*
- * kb_wait waits for the keyboard controller buffer to empty.
- */
-kb_wait:
- pushl %eax
- pushl %ebx
- movl $10000,%ebx
-1: inb $0x64,%al
- testb $0x02,%al
- je 2f
- decl %ebx
- jne 1b
-2: popl %ebx
- popl %eax
- ret
-
-no_idt:
- .long 0,0
-/*
- * This routine reboots the machine by asking the keyboard
- * controller to pulse the reset-line low. We try that for a while,
- * and if it doesn't work, we do some other stupid things.
- */
-_hard_reset_now:
- sti
- movl $100,%ebx
-1: call kb_wait
- movw $0x1234,0x472 /* don't do memory check */
- movb $0xfe,%al /* pulse reset low */
- outb %al,$0x64
- decl %ebx
- jne 1b
- lidt no_idt /* zero-length idt: should triple-fault */
- jmp _hard_reset_now
diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c
new file mode 100644
index 0000000..1398059
--- /dev/null
+++ b/kernel/chr_drv/keyboard.c
@@ -0,0 +1,1107 @@
+/*
+ * linux/kernel/chr_drv/keyboard.c
+ *
+ * Keyboard driver for Linux v0.96 using Latin-1.
+ *
+ * Written for linux by Johan Myreen as a translation from
+ * the assembly version by Linus (with diacriticals added)
+ */
+
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/tty.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define LSHIFT 0x01
+#define RSHIFT 0x02
+#define LCTRL 0x04
+#define RCTRL 0x08
+#define ALT 0x10
+#define ALTGR 0x20
+#define CAPS 0x40
+#define CAPSDOWN 0x80
+
+#define SCRLED 0x01
+#define NUMLED 0x02
+#define CAPSLED 0x04
+
+#define NO_META_BIT 0x80
+
+unsigned char kapplic = 0;
+unsigned char kmode = 0;
+unsigned char kleds = NUMLED;
+unsigned char ke0 = 0;
+unsigned char kraw = 0;
+unsigned char kbd_flags = KBDFLAGS;
+
+extern void do_keyboard_interrupt(void);
+extern void ctrl_alt_del(void);
+extern void show_mem(void), show_state(void);
+extern void change_console(unsigned int new_console);
+extern struct tty_queue *table_list[];
+
+typedef void (*fptr)(int);
+
+static unsigned char old_leds = 2;
+static int diacr = -1;
+static int npadch = 0;
+fptr key_table[];
+
+static void put_queue(int);
+void set_leds(void);
+static void applkey(int);
+static void cur(int);
+static void kb_wait(void), kb_ack(void);
+static unsigned int handle_diacr(unsigned int);
+
+void do_keyboard(void)
+{
+ unsigned char scancode, x;
+
+ scancode=inb_p(0x60);
+ x=inb_p(0x61);
+ outb_p(x|0x80, 0x61);
+ outb_p(x&0x7f, 0x61);
+ outb(0x20, 0x20);
+ sti();
+
+ if (kraw) {
+ put_queue(scancode);
+ do_keyboard_interrupt();
+ } else if (scancode == 0xe0)
+ ke0 = 1;
+ else if (scancode == 0xe1)
+ ke0 = 2;
+ else {
+ key_table[scancode](scancode);
+ do_keyboard_interrupt();
+ ke0 = 0;
+ }
+}
+
+static void put_queue(int ch)
+{
+ register struct tty_queue *qp = table_list[0];
+ unsigned long new_head;
+
+ qp->buf[qp->head]=ch;
+ if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1)) != qp->tail)
+ qp->head=new_head;
+ if (qp->proc_list != NULL)
+ qp->proc_list->state=0;
+}
+
+static void puts_queue(char *cp)
+{
+ register struct tty_queue *qp = table_list[0];
+ unsigned long new_head;
+ char ch;
+
+ while (ch=*cp++) {
+ qp->buf[qp->head]=ch;
+ if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1))
+ != qp->tail)
+ qp->head=new_head;
+ }
+ if (qp->proc_list != NULL)
+ qp->proc_list->state=0;
+}
+
+static void ctrl(int sc)
+{
+ if (ke0)
+ kmode|=RCTRL;
+ else
+ kmode|=LCTRL;
+}
+
+static void alt(int sc)
+{
+ if (ke0)
+ kmode|=ALTGR;
+ else
+ kmode|=ALT;
+}
+
+static void unctrl(int sc)
+{
+ if (ke0)
+ kmode&=(~RCTRL);
+ else
+ kmode&=(~LCTRL);
+}
+
+static void unalt(int sc)
+{
+ if (ke0)
+ kmode&=(~ALTGR);
+ else {
+ kmode&=(~ALT);
+ if (npadch != 0) {
+ put_queue(npadch);
+ npadch=0;
+ }
+ }
+}
+
+static void lshift(int sc)
+{
+ kmode|=LSHIFT;
+}
+
+static void unlshift(int sc)
+{
+ kmode&=(~LSHIFT);
+}
+
+static void rshift(int sc)
+{
+ kmode|=RSHIFT;
+}
+
+static void unrshift(int sc)
+{
+ kmode&=(~RSHIFT);
+}
+
+static void caps(int sc)
+{
+ if (!(kmode&CAPSDOWN)) {
+ kleds^=CAPSLED;
+ kmode^=CAPS;
+ kmode|=CAPSDOWN;
+ set_leds();
+ }
+}
+
+void set_leds(void)
+{
+ if (kleds != old_leds) {
+ old_leds=kleds;
+ kb_wait();
+ outb(0xed, 0x60); /* set leds command */
+ kb_ack();
+ kb_wait();
+ outb(kleds, 0x60);
+ kb_ack();
+ }
+}
+
+static void uncaps(int sc)
+{
+ kmode&=(~CAPSDOWN);
+}
+
+static void scroll(int sc)
+{
+ if (kmode&(LSHIFT|RSHIFT))
+ show_mem();
+ else
+ show_state();
+ kleds^=SCRLED;
+ set_leds();
+}
+
+static void num(int sc)
+{
+ if (kapplic)
+ applkey(0x50);
+ else {
+ kleds^=NUMLED;
+ set_leds();
+ }
+}
+
+static void applkey(int key)
+{
+ char buf[] = { 0x1b, 0x4f, 0x00, 0x00 };
+
+ buf[2]=key;
+ puts_queue(buf);
+}
+
+
+#if defined KBD_FINNISH
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '+', '\'', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '}', 0, 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|',
+ '{', 0, 0, '\'', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '\"', '#', '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', ']', '^', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\',
+ '[', 0, 0, '*', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 163, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_FINNISH_LATIN1
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '+', 180, 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', 229, 168, 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246,
+ 228, 167, 0, '\'', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '"', '#', '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', 197, '^', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214,
+ 196, 189, 0, '*', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 163, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_US
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '/', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '@', '#', '$', '%', '^',
+ '&', '*', '(', ')', '_', '+', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '{', '}', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+ '"', '~', '0', '|', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', '<', '>', '?', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 0, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_UK
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', 0, '#', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '/', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '\\', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '"', 163, '$', '%', '^',
+ '&', '*', '(', ')', '_', '+', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '{', '}', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+ '@', '~', '0', '~', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', '<', '>', '?', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 0, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_GR
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '\\', '\'', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
+ 'o', 'p', '@', '+', 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', '[',
+ ']', '^', 0, '#', 'y', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '"', '#', '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
+ 'O', 'P', '\\', '*', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', '{',
+ '}', '~', 0, '\'', 'Y', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 0, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ '@', 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_GR_LATIN1
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', 223, 180, 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',
+ 'o', 'p', 252, '+', 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246,
+ 228, 94, 0, '#', 'y', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '"', 167, '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',
+ 'O', 'P', 220, '*', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214,
+ 196, 176, 0, '\'', 'Y', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, 178, 179, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ '@', 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 181, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_FR
+
+static unsigned char key_map[] = {
+ 0, 27, '&', '{', '"', '\'', '(', '-',
+ '}', '_', '/', '@', ')', '=', 127, 9,
+ 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '^', '$', 13, 0, 'q', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
+ '|', '`', 0, 42, 'w', 'x', 'c', 'v',
+ 'b', 'n', ',', ';', ':', '!', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', ']', '+', 127, 9,
+ 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', '<', '>', 13, 0, 'Q', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
+ '%', '~', 0, '#', 'W', 'X', 'C', 'V',
+ 'B', 'N', '?', '.', '/', '\\', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '~', '#', '{', '[', '|',
+ '`', '\\', '^', '@', ']', '}', 0, 0,
+ '@', 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_FR_LATIN1
+
+static unsigned char key_map[] = {
+ 0, 27, '&', 233, '"', '\'', '(', '-',
+ 232, '_', 231, 224, ')', '=', 127, 9,
+ 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '^', '$', 13, 0, 'q', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
+ 249, 178, 0, 42, 'w', 'x', 'c', 'v',
+ 'b', 'n', ',', ';', ':', '!', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', 176, '+', 127, 9,
+ 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', 168, 163, 13, 0, 'Q', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
+ '%', 0, 0, 181, 'W', 'X', 'C', 'V',
+ 'B', 'N', '?', '.', '/', 167, 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '~', '#', '{', '[', '|',
+ '`', '\\', '^', '@', ']', '}', 0, 0,
+ '@', 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 164, 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_DK
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '+', '\'', 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', 229, 0, 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230,
+ 162, 0, 0, '\'', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '\"', '#', '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', 197, '^', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198,
+ 165, 0, 0, '*', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 163, '$', 0, 0,
+ '{', '[', ']', '}', 0, '|', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_DK_LATIN1
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '+', 180, 127, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', 229, 168, 13, 0, 'a', 's',
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230,
+ 162, 189, 0, '\'', 'z', 'x', 'c', 'v',
+ 'b', 'n', 'm', ',', '.', '-', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '\"', '#', '$', '%', '&',
+ '/', '(', ')', '=', '?', '`', 127, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+ 'O', 'P', 197, '^', 13, 0, 'A', 'S',
+ 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198,
+ 165, 167, 0, '*', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', ';', ':', '_', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '>', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 163, '$', 0, 0,
+ '{', '[', ']', '}', 0, '|', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '\\', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#elif defined KBD_DVORAK
+
+static unsigned char key_map[] = {
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '\\', '=', 127, 9,
+ '\'', ',', '.', 'p', 'y', 'f', 'g', 'c',
+ 'r', 'l', '/', ']', 13, 0, 'a', 'o',
+ 'e', 'u', 'i', 'd', 'h', 't', 'n', 's',
+ '-', '`', 0, '[', ';', 'q', 'j', 'k',
+ 'x', 'b', 'm', 'w', 'v', 'z', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char shift_map[] = {
+ 0, 27, '!', '@', '#', '$', '%', '^',
+ '&', '*', '(', ')', '|', '+', 127, 9,
+ '"', '<', '>', 'P', 'Y', 'F', 'G', 'C',
+ 'R', 'L', '?', '}', 13, 0, 'A', 'O',
+ 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S',
+ '_', '~', 0, '{', ':', 'Q', 'J', 'K',
+ 'X', 'B', 'M', 'W', 'V', 'Z', 0, '*',
+ 0, 32, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, '-', 0, 0, 0, '+', 0,
+ 0, 0, 0, 0, 0, 0, '<', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+static unsigned char alt_map[] = {
+ 0, 0, 0, '@', 0, '$', 0, 0,
+ '{', '[', ']', '}', '\\', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, '~', 13, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, '|', 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0 };
+
+#else
+#error "KBD-type not defined"
+#endif
+
+static void do_self(int sc)
+{
+ unsigned char ch;
+
+ if (kmode&ALTGR)
+ ch=alt_map[sc];
+ else if (kmode&(LSHIFT|RSHIFT|LCTRL|RCTRL))
+ ch=shift_map[sc];
+ else
+ ch=key_map[sc];
+
+ if (ch == 0)
+ return;
+
+ if ((ch=handle_diacr(ch)) == 0)
+ return;
+
+ if (kmode&(LCTRL|RCTRL|CAPS)) /* ctrl or caps */
+ if ((ch>='a' && ch <='z') || (ch>=224 && ch<=254))
+ ch -= 32;
+ if (kmode&(LCTRL|RCTRL)) /* ctrl */
+ ch &= 0x1f;
+
+ if (kmode&ALT)
+ if (kbd_flags&NO_META_BIT) {
+ put_queue('\033');
+ put_queue(ch);
+ } else
+ put_queue(ch|0x80);
+ else
+ put_queue(ch);
+}
+
+unsigned char accent_table[5][64] = {
+ " \300BCD\310FGH\314JKLMN\322PQRST\331VWXYZ[\\]^_"
+ "`\340bcd\350fgh\354jklmn\362pqrst\371vwxyz{|}~", /* accent grave */
+
+ " \301BCD\311FGH\315JKLMN\323PQRST\332VWX\335Z[\\]^_"
+ "`\341bcd\351fgh\355jklmn\363pqrst\372vwxyz{|}~", /* accent acute */
+
+ " \302BCD\312FGH\316JKLMN\324PQRST\333VWXYZ[\\]^_"
+ "`\342bcd\352fgh\356jklmn\364pqrst\373vwxyz{|}~", /* circumflex */
+
+ " \303BCDEFGHIJKLMN\325PQRSTUVWXYZ[\\]^_"
+ "`\343bcdefghijklm\361\365pqrstuvwxyz{|}~", /* tilde */
+
+ " \304BCD\313FGH\316JKLMN\326PQRST\334VWXYZ[\\]^_"
+ "`\344bcd\353fgh\357jklmn\366pqrst\374vwx\377z{|}~" /* dieresis */
+};
+
+
+/*
+ * Check if dead key pressed. If so, check if same key pressed twice;
+ * in that case return the char, otherwise store char and return 0.
+ * If dead key not pressed, check if accented character pending. If
+ * not: return the char, otherwise check if char is a space. If it is
+ * a space return the diacritical. Else combine char with diacritical
+ * mark and return.
+ */
+
+unsigned int handle_diacr(unsigned int ch)
+{
+ static unsigned char diacr_table[] =
+ {'`', 180, '^', '~', 168, 0}; /* Must end with 0 */
+ int i;
+
+ for(i=0; diacr_table[i]; i++)
+ if (ch==diacr_table[i] && ((1<<i)&kbd_flags)) {
+ if (diacr == i) {
+ diacr=-1;
+ return ch; /* pressed twice */
+ } else {
+ diacr=i; /* key is dead */
+ return 0;
+ }
+ }
+ if (diacr == -1)
+ return ch;
+ else if (ch == ' ') {
+ ch=diacr_table[diacr];
+ diacr=-1;
+ return ch;
+ } else if (ch<64 || ch>122) {
+ diacr=-1;
+ return ch;
+ } else {
+ ch=accent_table[diacr][ch-64];
+ diacr=-1;
+ return ch;
+ }
+}
+
+
+#if defined KBD_FR
+static unsigned char num_table[] = "789-456+1230.";
+#else
+static unsigned char num_table[] = "789-456+1230,";
+#endif
+
+static unsigned char cur_table[] = "HA5-DGC+YB623";
+static unsigned int pad_table[] = { 7,8,9,0,4,5,6,0,1,2,3,0,0 };
+
+/*
+ Keypad / 35 B7 Q
+ Keypad * (PrtSc) 37 B7 R
+ Keypad NumLock 45 ?? P
+ Keypad 7 (Home) 47 C7 w
+ Keypad 8 (Up arrow) 48 C8 x
+ Keypad 9 (PgUp) 49 C9 y
+ Keypad - 4A CA S
+ Keypad 4 (Left arrow) 4B CB t
+ Keypad 5 4C CC u
+ Keypad 6 (Right arrow) 4D CD v
+ Keypad + 4E CE l
+ Keypad 1 (End) 4F CF q
+ Keypad 2 (Down arrow) 50 D0 r
+ Keypad 3 (PgDn) 51 D1 s
+ Keypad 0 (Ins) 52 D2 p
+ Keypad . (Del) 53 D3 n
+*/
+
+static unsigned char appl_table[] = "wxyStuvlqrspn";
+
+static char *func_table[] = {
+ "\033[[A", "\033[[B", "\033[[C", "\033[[D",
+ "\033[[E", "\033[[F", "\033[[G", "\033[[H",
+ "\033[[I", "\033[[J", "\033[[K", "\033[[L"
+};
+
+
+static void cursor(int sc)
+{
+ if (sc < 0x47 || sc > 0x53)
+ return;
+ sc-=0x47;
+ if (sc == 12 && (kmode&(LCTRL|RCTRL)) && (kmode&(ALT|ALTGR))) {
+ ctrl_alt_del();
+ return;
+ }
+ if (ke0 == 1) {
+ cur(sc);
+ return;
+ }
+
+ if ((kmode&ALT) && sc!=12) { /* Alt-numpad */
+ npadch=npadch*10+pad_table[sc];
+ return;
+ }
+
+ if (kapplic && !(kmode&(LSHIFT|RSHIFT))) { /* shift forces cursor */
+ applkey(appl_table[sc]);
+ return;
+ }
+
+ if (kleds&NUMLED) {
+ put_queue(num_table[sc]);
+ } else
+ cur(sc);
+}
+
+static void cur(int sc)
+{
+ char buf[] = { 0x1b, '[', 0, 0, 0 }; /* must not be static */
+
+ buf[2]=cur_table[sc];
+ if (buf[2] < '9')
+ buf[3]='~';
+ if (kapplic)
+ buf[1]='O';
+ puts_queue(buf);
+}
+
+static void func(int sc)
+{
+ if (sc < 0x3b)
+ return;
+ sc-=0x3b;
+ if (sc > 9) {
+ sc-=18;
+ if (sc < 10 || sc > 11)
+ return;
+ }
+ if (kmode&ALT)
+ change_console(sc);
+ else
+ puts_queue(func_table[sc]);
+}
+
+
+static void slash(int sc)
+{
+ if (ke0 != 1)
+ do_self(sc);
+ else if (kapplic)
+ applkey('Q');
+ else
+ put_queue('/');
+}
+
+static void star(int sc)
+{
+ if (kapplic)
+ applkey('R');
+ else
+ do_self(sc);
+}
+
+static void enter(int sc)
+{
+ if (ke0 != 1)
+ do_self(sc);
+ else if (kapplic)
+ applkey('M');
+ else
+ do_self(sc);
+}
+
+static void minus(int sc)
+{
+ if (kapplic)
+ applkey('S');
+ else
+ do_self(sc);
+}
+
+static void plus(int sc)
+{
+ if (kapplic)
+ applkey('l');
+ else
+ do_self(sc);
+}
+
+
+static void none(int sc)
+{
+}
+
+
+/*
+ * kb_wait waits for the keyboard controller buffer to empty.
+ */
+
+static void kb_wait(void)
+{
+ int i;
+
+ for (i=0; i<0x10000; i++)
+ if ((inb(0x64)&0x02) == 0)
+ break;
+}
+
+/*
+ * kb_ack waits for 0xfa to appear in port 0x60
+ *
+ * Suggested by Bruce Evans
+ * Added by Niels Skou Olsen [NSO]
+ * April 21, 1992
+ *
+ * Heavily inspired by kb_wait :-)
+ * I don't know how much waiting actually is required,
+ * but this seems to work
+ */
+
+void kb_ack(void)
+{
+ int i;
+
+ for(i=0; i<0x10000; i++)
+ if (inb(0x64) == 0xfa)
+ break;
+}
+
+long no_idt[2] = {0, 0};
+
+/*
+ * This routine reboots the machine by asking the keyboard
+ * controller to pulse the reset-line low. We try that for a while,
+ * and if it doesn't work, we do some other stupid things.
+ */
+void hard_reset_now(void)
+{
+ int i;
+
+ sti();
+ for (;;) {
+ for (i=0; i<100; i++) {
+ kb_wait();
+ *((unsigned short *)0x472)=0x1234;
+ outb(0xfe,0x64); /* pulse reset low */
+ }
+ __asm__("\tlidt _no_idt"::);
+ }
+}
+
+
+static fptr key_table[] = {
+ none,do_self,do_self,do_self, /* 00-03 s0 esc 1 2 */
+ do_self,do_self,do_self,do_self, /* 04-07 3 4 5 6 */
+ do_self,do_self,do_self,do_self, /* 08-0B 7 8 9 0 */
+ do_self,do_self,do_self,do_self, /* 0C-0F + ' bs tab */
+ do_self,do_self,do_self,do_self, /* 10-13 q w e r */
+ do_self,do_self,do_self,do_self, /* 14-17 t y u i */
+ do_self,do_self,do_self,do_self, /* 18-1B o p } ^ */
+ enter,ctrl,do_self,do_self, /* 1C-1F enter ctrl a s */
+ do_self,do_self,do_self,do_self, /* 20-23 d f g h */
+ do_self,do_self,do_self,do_self, /* 24-27 j k l | */
+ do_self,do_self,lshift,do_self, /* 28-2B { para lshift , */
+ do_self,do_self,do_self,do_self, /* 2C-2F z x c v */
+ do_self,do_self,do_self,do_self, /* 30-33 b n m , */
+ do_self,slash,rshift,star, /* 34-37 . - rshift * */
+ alt,do_self,caps,func, /* 38-3B alt sp caps f1 */
+ func,func,func,func, /* 3C-3F f2 f3 f4 f5 */
+ func,func,func,func, /* 40-43 f6 f7 f8 f9 */
+ func,num,scroll,cursor, /* 44-47 f10 num scr home */
+ cursor,cursor,minus,cursor, /* 48-4B up pgup - left */
+ cursor,cursor,plus,cursor, /* 4C-4F n5 right + end */
+ cursor,cursor,cursor,cursor, /* 50-53 dn pgdn ins del */
+ none,none,do_self,func, /* 54-57 sysreq ? < f11 */
+ func,none,none,none, /* 58-5B f12 ? ? ? */
+ none,none,none,none, /* 5C-5F ? ? ? ? */
+ none,none,none,none, /* 60-63 ? ? ? ? */
+ none,none,none,none, /* 64-67 ? ? ? ? */
+ none,none,none,none, /* 68-6B ? ? ? ? */
+ none,none,none,none, /* 6C-6F ? ? ? ? */
+ none,none,none,none, /* 70-73 ? ? ? ? */
+ none,none,none,none, /* 74-77 ? ? ? ? */
+ none,none,none,none, /* 78-7B ? ? ? ? */
+ none,none,none,none, /* 7C-7F ? ? ? ? */
+ none,none,none,none, /* 80-83 ? br br br */
+ none,none,none,none, /* 84-87 br br br br */
+ none,none,none,none, /* 88-8B br br br br */
+ none,none,none,none, /* 8C-8F br br br br */
+ none,none,none,none, /* 90-93 br br br br */
+ none,none,none,none, /* 94-97 br br br br */
+ none,none,none,none, /* 98-9B br br br br */
+ none,unctrl,none,none, /* 9C-9F br unctrl br br */
+ none,none,none,none, /* A0-A3 br br br br */
+ none,none,none,none, /* A4-A7 br br br br */
+ none,none,unlshift,none, /* A8-AB br br unlshift br */
+ none,none,none,none, /* AC-AF br br br br */
+ none,none,none,none, /* B0-B3 br br br br */
+ none,none,unrshift,none, /* B4-B7 br br unrshift br */
+ unalt,none,uncaps,none, /* B8-BB unalt br uncaps br */
+ none,none,none,none, /* BC-BF br br br br */
+ none,none,none,none, /* C0-C3 br br br br */
+ none,none,none,none, /* C4-C7 br br br br */
+ none,none,none,none, /* C8-CB br br br br */
+ none,none,none,none, /* CC-CF br br br br */
+ none,none,none,none, /* D0-D3 br br br br */
+ none,none,none,none, /* D4-D7 br br br br */
+ none,none,none,none, /* D8-DB br ? ? ? */
+ none,none,none,none, /* DC-DF ? ? ? ? */
+ none,none,none,none, /* E0-E3 e0 e1 ? ? */
+ none,none,none,none, /* E4-E7 ? ? ? ? */
+ none,none,none,none, /* E8-EB ? ? ? ? */
+ none,none,none,none, /* EC-EF ? ? ? ? */
+ none,none,none,none, /* F0-F3 ? ? ? ? */
+ none,none,none,none, /* F4-F7 ? ? ? ? */
+ none,none,none,none, /* F8-FB ? ? ? ? */
+ none,none,none,none /* FC-FF ? ? ? ? */
+};
diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c
index 53d4c9b..ed56ce1 100644
--- a/kernel/chr_drv/lp.c
+++ b/kernel/chr_drv/lp.c
@@ -53,13 +53,6 @@ static int lp_write(struct inode * inode, struct file * file, char * buf, int co
unsigned int minor = MINOR(inode->i_rdev);
char c, *temp = buf;
- if (minor >= LP_NO)
- return -ENODEV;
- if ((LP_F(minor) & LP_EXIST) == 0)
- return -ENODEV;
- LP_T(minor) = current->pid;
- LP_F(minor) |= LP_BUSY;
- LP_R(minor) = count;
temp = buf;
while (count > 0) {
c = get_fs_byte(temp++);
@@ -97,19 +90,36 @@ static int lp_lseek(struct inode * inode, struct file * file, off_t offset, int
return -EINVAL;
}
-static int lp_readdir(struct inode * inode, struct file * file, struct dirent * de, int count)
+static int lp_open(struct inode * inode, struct file * file)
{
- return -ENOTDIR;
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ if (minor >= LP_NO)
+ return -ENODEV;
+ if ((LP_F(minor) & LP_EXIST) == 0)
+ return -ENODEV;
+ if (LP_F(minor) & LP_BUSY)
+ return -EBUSY;
+ LP_F(minor) |= LP_BUSY;
+ return 0;
+}
+
+static void lp_release(struct inode * inode, struct file * file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ LP_F(minor) &= ~LP_BUSY;
}
static struct file_operations lp_fops = {
lp_lseek,
lp_read,
lp_write,
- lp_readdir,
- NULL, /* lp_close */
+ NULL, /* lp_readdir */
NULL, /* lp_select */
- NULL /* lp_ioctl */
+ NULL, /* lp_ioctl */
+ lp_open,
+ lp_release
};
void lp_init(void)
diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c
index db3138f..73758ee 100644
--- a/kernel/chr_drv/mem.c
+++ b/kernel/chr_drv/mem.c
@@ -215,19 +215,15 @@ static int mem_write(struct inode * inode, struct file * file, char * buf, int c
}
}
-static int mem_readdir(struct inode * inode, struct file * file, struct dirent * de, int count)
-{
- return -ENOTDIR;
-}
-
static struct file_operations mem_fops = {
mem_lseek,
mem_read,
mem_write,
- mem_readdir,
- NULL, /* mem_close */
+ NULL, /* mem_readdir */
NULL, /* mem_select */
- NULL /* mem_ioctl */
+ NULL, /* mem_ioctl */
+ NULL, /* no special open code */
+ NULL /* no special release code */
};
void chr_dev_init(void)
diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c
index 0647ce8..7d2eca6 100644
--- a/kernel/chr_drv/pty.c
+++ b/kernel/chr_drv/pty.c
@@ -20,7 +20,7 @@
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
- char c;
+ int c;
while (!from->stopped && !EMPTY(from->write_q)) {
if (FULL(to->read_q)) {
@@ -29,7 +29,7 @@ static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
TTY_READ_FLUSH(to);
continue;
}
- GETCH(from->write_q,c);
+ c = GETCH(from->write_q);
PUTCH(c,to->read_q);
if (current->signal & ~current->blocked)
break;
diff --git a/kernel/chr_drv/rs_io.s b/kernel/chr_drv/rs_io.s
deleted file mode 100644
index e74dcd3..0000000
--- a/kernel/chr_drv/rs_io.s
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * linux/kernel/rs_io.s
- *
- * (C) 1991 Linus Torvalds
- */
-
-/*
- * rs_io.s
- *
- * This module implements the rs232 io interrupts.
- */
-
-.text
-.globl _rs1_interrupt,_rs2_interrupt
-
-size = 2048 /* must be power of two !
- and must match the value
- in tty_io.c!!! */
-
-/* these are the offsets into the read/write buffer structures */
-rs_addr = 0
-head = 4
-tail = 8
-proc_list = 12
-buf = 16
-
-startup = 256 /* chars left in write queue when we restart it */
-
-/*
- * These are the actual interrupt routines. They look where
- * the interrupt is coming from, and take appropriate action.
- *
- * rs1_interrupt (IRQ 4) takes care of com1 and com3
- * rs2_interrupt (IRQ 3) takes care of com2 and com4
- */
-.align 2
-_rs1_interrupt:
- pushl $_table_list+8
- pushl $_table_list+24
- jmp rs_int
-
-.align 2
-_rs2_interrupt:
- pushl $_table_list+16
- pushl $_table_list+32
-rs_int: cld
- pushl %edx
- pushl %ecx
- pushl %ebx
- pushl %eax
- push %es
- push %ds /* as this is an interrupt, we cannot */
- pushl $0x10 /* know that bs is ok. Load it */
- pop %ds
- pushl $0x10
- pop %es
- movl 24(%esp),%edx
- call do_rs_intr
- movl 28(%esp),%edx
- call do_rs_intr
- movb $0x20,%al
- outb %al,$0x20 /* EOI */
- pop %ds
- pop %es
- popl %eax
- popl %ebx
- popl %ecx
- popl %edx
- addl $8,%esp # jump over the _table_list entries
- iret
-
-do_rs_intr:
- pushl %edx
- movl (%edx),%edx
- movl rs_addr(%edx),%edx
- addl $2,%edx /* interrupt ident. reg */
-1: xorl %eax,%eax
- inb %dx,%al
- testb $1,%al
- jne 2f
- cmpb $6,%al /* this shouldn't happen, but ... */
- ja 2f
- movl (%esp),%ecx
- pushl %edx
- subl $2,%edx
- call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */
- popl %edx
- jmp 1b
-2: popl %edx
- ret
-
-jmp_table:
- .long modem_status,write_char,read_char,line_status
-
-.align 2
-modem_status:
- addl $6,%edx /* clear intr by reading modem status reg */
- inb %dx,%al
- ret
-
-.align 2
-line_status:
- addl $5,%edx /* clear intr by reading line status reg. */
- inb %dx,%al
- ret
-
-.align 2
-read_char:
- inb %dx,%al
- movl %ecx,%edx
- subl $_table_list,%edx
- shrl $3,%edx
- movl (%ecx),%ecx # read-queue
- movl head(%ecx),%ebx
- movb %al,buf(%ecx,%ebx)
- incl %ebx
- andl $size-1,%ebx
- cmpl tail(%ecx),%ebx
- je 1f
- movl %ebx,head(%ecx)
-1: movl mask_table(,%edx,4),%edx
- orl %edx,_timer_active
- ret
-
-.align 2
-mask_table:
- .long 0,4,8,16,32
-
-.align 2
-write_char:
- movl 4(%ecx),%ecx # write-queue
- movl head(%ecx),%ebx
- subl tail(%ecx),%ebx
- andl $size-1,%ebx # nr chars in queue
- je write_buffer_empty
- cmpl $startup,%ebx
- ja 1f
- movl proc_list(%ecx),%ebx # wake up sleeping process
- testl %ebx,%ebx # is there any?
- je 1f
- movl $0,(%ebx)
-1: movl tail(%ecx),%ebx
- movb buf(%ecx,%ebx),%al
- outb %al,%dx
- incl %ebx
- andl $size-1,%ebx
- movl %ebx,tail(%ecx)
- cmpl head(%ecx),%ebx
- je write_buffer_empty
- ret
-
-.align 2
-write_buffer_empty:
- movl proc_list(%ecx),%ebx # wake up sleeping process
- testl %ebx,%ebx # is there any?
- je 1f
- movl $0,(%ebx)
-1: incl %edx
- inb %dx,%al
- jmp 1f
-1: jmp 1f
-1: andb $0xd,%al # disable transmit interrupt
- outb %al,%dx
- ret
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index f626071..60e704b 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -13,6 +13,8 @@
* and all interrupts pertaining to serial IO.
*/
+#include <signal.h>
+
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/tty.h>
@@ -20,10 +22,101 @@
#include <asm/system.h>
#include <asm/io.h>
-#define WAKEUP_CHARS (TTY_BUF_SIZE/4)
+#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
-extern void rs1_interrupt(void);
-extern void rs2_interrupt(void);
+extern void IRQ3_interrupt(void);
+extern void IRQ4_interrupt(void);
+
+static void modem_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
+{
+ unsigned char status = inb(port+6);
+
+ if ((status & 0x88) == 0x08 && tty->pgrp > 0)
+ kill_pg(tty->pgrp,SIGHUP,1);
+}
+
+/*
+ * There are several races here: we avoid most of them by disabling timer_active
+ * for the crucial part of the process.. That's a good idea anyway.
+ *
+ * The problem is that we have to output characters /both/ from interrupts
+ * and from the normal write: the latter to be sure the interrupts start up
+ * again. With serial lines, the interrupts can happen so often that the
+ * races actually are noticeable.
+ */
+static void send_intr(unsigned line, unsigned port, struct tty_struct * tty)
+{
+ int c;
+
+#define TIMER ((SER1_TIMEOUT-1)+line)
+ timer_active &= ~(1 << TIMER);
+ if ((c = GETCH(tty->write_q)) < 0)
+ return;
+ outb(c,port);
+ timer_table[TIMER].expires = jiffies + 10;
+ timer_active |= 1 << TIMER;
+ if (LEFT(tty->write_q) > WAKEUP_CHARS)
+ wake_up(&tty->write_q->proc_list);
+#undef TIMER
+}
+
+static void receive_intr(unsigned line, unsigned port, struct tty_struct * tty)
+{
+ if (FULL(tty->read_q))
+ return;
+ PUTCH(inb(port),tty->read_q);
+ timer_active |= (1<<(SER1_TIMER-1))<<line;
+}
+
+static void line_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
+{
+ unsigned char status = inb(port+5);
+
+/* printk("line status: %02x\n",status); */
+}
+
+static void (*jmp_table[4])(unsigned,unsigned,struct tty_struct *) = {
+ modem_status_intr,
+ send_intr,
+ receive_intr,
+ line_status_intr
+};
+
+static void check_tty(unsigned line,struct tty_struct * tty)
+{
+ unsigned short port;
+ unsigned char ident;
+
+ if (!(port = tty->read_q->data))
+ return;
+ while (1) {
+ ident = inb(port+2);
+ if (ident & 1)
+ return;
+ ident >>= 1;
+ if (ident > 3)
+ return;
+ jmp_table[ident](line,port,tty);
+ }
+}
+
+/*
+ * IRQ3 normally handles com2 and com4
+ */
+void do_IRQ3(void)
+{
+ check_tty(2,tty_table+65);
+ check_tty(4,tty_table+67);
+}
+
+/*
+ * IRQ4 normally handles com1 and com2
+ */
+void do_IRQ4(void)
+{
+ check_tty(1,tty_table+64);
+ check_tty(3,tty_table+66);
+}
static void com1_timer(void)
{
@@ -45,46 +138,48 @@ static void com4_timer(void)
TTY_READ_FLUSH(tty_table+67);
}
-static inline void do_rs_write(unsigned int port)
+/*
+ * Again, we disable interrupts to be sure there aren't any races:
+ * see send_intr for details.
+ */
+static inline void do_rs_write(unsigned line, struct tty_struct * tty)
{
- char c;
+ int port;
-#define TTY (tty_table[64+port].write_q)
-#define TIMER (SER1_TIMEOUT+port)
+#define TIMER ((SER1_TIMEOUT-1)+line)
+ if (!tty || !tty->write_q || EMPTY(tty->write_q))
+ return;
+ if (!(port = tty->write_q->data))
+ return;
cli();
- if (!EMPTY(TTY)) {
- outb_p(inb_p(TTY->data+1)|0x02,TTY->data+1);
- if (inb(TTY->data+5) & 0x20) {
- GETCH(TTY,c);
- outb(c,TTY->data);
- }
- timer_table[TIMER].expires = jiffies + 50;
+ if (inb_p(port+5) & 0x20)
+ send_intr(line,port,tty);
+ else {
+ timer_table[TIMER].expires = jiffies + 10;
timer_active |= 1 << TIMER;
- } else
- timer_active &= ~(1 << TIMER);
+ }
sti();
#undef TIMER
-#undef TTY
}
static void com1_timeout(void)
{
- do_rs_write(0);
+ do_rs_write(1,tty_table+64);
}
static void com2_timeout(void)
{
- do_rs_write(1);
+ do_rs_write(2,tty_table+65);
}
static void com3_timeout(void)
{
- do_rs_write(2);
+ do_rs_write(3,tty_table+66);
}
static void com4_timeout(void)
{
- do_rs_write(3);
+ do_rs_write(4,tty_table+67);
}
static void init(int port)
@@ -94,7 +189,7 @@ static void init(int port)
outb_p(0x00,port+1); /* MS of divisor */
outb_p(0x03,port+3); /* reset DLAB */
outb_p(0x00,port+4); /* reset DTR,RTS, OUT_2 */
- outb_p(0x0d,port+1); /* enable all intrs but writes */
+ outb_p(0x0f,port+1); /* enable all intrs */
(void)inb(port); /* read data port to reset things (?) */
}
@@ -102,7 +197,7 @@ static void init(int port)
* this routine enables interrupts on 'line', and disables them on
* 'line ^ 2', as they share the same IRQ. Braindamaged AT hardware.
*/
-void serial_open(unsigned int line)
+void serial_open(unsigned line)
{
unsigned short port;
unsigned short port2;
@@ -118,10 +213,10 @@ void serial_open(unsigned int line)
outb_p(0x00,port2+4);
outb_p(0x03,port+3); /* reset DLAB */
outb_p(0x0f,port+4); /* set DTR,RTS, OUT_2 */
- outb_p(0x0d,port+1); /* enable all intrs but writes */
+ outb_p(0x0f,port+1); /* enable all intrs */
inb_p(port+5);
inb_p(port+0);
- inb(port+6);
+ inb_p(port+6);
inb(port+2);
sti();
}
@@ -146,8 +241,8 @@ void rs_init(void)
timer_table[SER3_TIMEOUT].expires = 0;
timer_table[SER4_TIMEOUT].fn = com4_timeout;
timer_table[SER4_TIMEOUT].expires = 0;
- set_intr_gate(0x24,rs1_interrupt);
- set_intr_gate(0x23,rs2_interrupt);
+ set_intr_gate(0x23,IRQ3_interrupt);
+ set_intr_gate(0x24,IRQ4_interrupt);
init(tty_table[64].read_q->data);
init(tty_table[65].read_q->data);
init(tty_table[66].read_q->data);
@@ -164,9 +259,7 @@ void rs_init(void)
*/
void rs_write(struct tty_struct * tty)
{
- cli();
- if (!EMPTY(tty->write_q))
- outb_p(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1);
- timer_active |= 15 << SER1_TIMEOUT;
- sti();
+ int line = tty - tty_table - 63;
+
+ do_rs_write(line,tty);
}
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index 8e376bb..413ee86 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -21,44 +21,10 @@
#include <linux/sched.h>
#include <linux/tty.h>
+#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
-int kill_pg(int pgrp, int sig, int priv);
-int is_orphaned_pgrp(int pgrp);
-
-extern void lp_init(void);
-
-#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
-#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
-#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)
-
-#define L_CANON(tty) _L_FLAG((tty),ICANON)
-#define L_ISIG(tty) _L_FLAG((tty),ISIG)
-#define L_ECHO(tty) _L_FLAG((tty),ECHO)
-#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
-#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
-#define L_ECHONL(tty) _L_FLAG((tty),ECHONL)
-#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
-#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
-#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
-
-#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
-#define I_NLCR(tty) _I_FLAG((tty),INLCR)
-#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
-#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
-#define I_IXON(tty) _I_FLAG((tty),IXON)
-#define I_STRP(tty) _I_FLAG((tty),ISTRIP)
-
-#define O_POST(tty) _O_FLAG((tty),OPOST)
-#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
-#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
-#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
-#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
-
-#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD)
-#define C_HUP(tty) (C_SPEED((tty)) == B0)
-
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
@@ -77,7 +43,13 @@ struct tty_struct tty_table[256];
#define mpty_table (128+tty_table)
#define spty_table (192+tty_table)
+/*
+ * fg_console is the current virtual console,
+ * redirect is the pseudo-tty that console output
+ * is redirected to if asked by TIOCCONS.
+ */
int fg_console = 0;
+struct tty_struct * redirect = NULL;
/*
* these are the tables used by the machine code handlers.
@@ -89,7 +61,7 @@ struct tty_queue * table_list[]={
rs_queues + 3, rs_queues + 4,
rs_queues + 6, rs_queues + 7,
rs_queues + 9, rs_queues + 10
- };
+};
void change_console(unsigned int new_console)
{
@@ -108,16 +80,6 @@ static void sleep_if_empty(struct tty_queue * queue)
sti();
}
-static void sleep_if_full(struct tty_queue * queue)
-{
- if (!FULL(queue))
- return;
- cli();
- while (!(current->signal & ~current->blocked) && LEFT(queue)<128)
- interruptible_sleep_on(&queue->proc_list);
- sti();
-}
-
void wait_for_keypress(void)
{
sleep_if_empty(tty_table[fg_console].secondary);
@@ -125,7 +87,7 @@ void wait_for_keypress(void)
void copy_to_cooked(struct tty_struct * tty)
{
- unsigned char c;
+ int c;
if (!(tty && tty->write && tty->read_q &&
tty->write_q && tty->secondary)) {
@@ -141,7 +103,7 @@ void copy_to_cooked(struct tty_struct * tty)
current->counter = 0;
break;
}
- GETCH(tty->read_q,c);
+ c = GETCH(tty->read_q);
if (I_STRP(tty))
c &= 0x7f;
if (c==13) {
@@ -242,6 +204,12 @@ void copy_to_cooked(struct tty_struct * tty)
wake_up(&tty->write_q->proc_list);
}
+int is_ignored(int sig)
+{
+ return ((current->blocked & (1<<(sig-1))) ||
+ (current->sigaction[sig-1].sa_handler == SIG_IGN));
+}
+
/*
* Called when we need to send a SIGTTIN or SIGTTOU to our process
* group
@@ -259,13 +227,8 @@ void copy_to_cooked(struct tty_struct * tty)
*/
int tty_signal(int sig, struct tty_struct *tty)
{
- if (is_orphaned_pgrp(current->pgrp))
- return -EIO; /* don't stop an orphaned pgrp */
(void) kill_pg(current->pgrp,sig,1);
- if ((current->blocked & (1<<(sig-1))) ||
- ((int) current->sigaction[sig-1].sa_handler == 1))
- return -EIO; /* Our signal will be ignored */
- else if (current->sigaction[sig-1].sa_handler)
+ if (current->sigaction[sig-1].sa_handler)
return -EINTR; /* We _will_ be interrupted :-) */
else
return -ERESTARTSYS; /* We _will_ be interrupted :-) */
@@ -276,7 +239,7 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
{
struct tty_struct * tty;
struct tty_struct * other_tty = NULL;
- unsigned char c;
+ int c;
char * b=buf;
int minimum,time;
@@ -285,9 +248,13 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
tty = TTY_TABLE(channel);
if (!(tty->read_q && tty->secondary))
return -EIO;
- if ((tty->pgrp > 0) && (current->tty == channel) &&
+ if ((tty->pgrp > 0) &&
+ (current->tty == channel) &&
(tty->pgrp != current->pgrp))
- return(tty_signal(SIGTTIN, tty));
+ if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
+ return -EIO;
+ else
+ return(tty_signal(SIGTTIN, tty));
if (channel & 0x80)
other_tty = tty_table + (channel ^ 0x40);
time = 10L*tty->termios.c_cc[VTIME];
@@ -328,7 +295,7 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
}
sti();
do {
- GETCH(tty->secondary,c);
+ c = GETCH(tty->secondary);
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)) || c==10)
tty->secondary->data--;
@@ -350,6 +317,9 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
current->timeout = time+jiffies;
}
sti();
+ TTY_READ_FLUSH(tty);
+ if (other_tty && other_tty->write)
+ TTY_WRITE_FLUSH(other_tty);
current->timeout = 0;
if (b-buf)
return b-buf;
@@ -369,19 +339,32 @@ static int write_chan(unsigned int channel, struct file * file, char * buf, int
if (channel > 255)
return -EIO;
tty = TTY_TABLE(channel);
- if (!(tty->write_q && tty->write))
- return -EIO;
- if (L_TOSTOP(tty) && (tty->pgrp > 0) &&
- (current->tty == channel) && (tty->pgrp != current->pgrp))
- return(tty_signal(SIGTTOU, tty));
+ if (L_TOSTOP(tty) && (tty->pgrp > 0) &&
+ (current->tty == channel) && (tty->pgrp != current->pgrp)) {
+ if (is_orphaned_pgrp(tty->pgrp))
+ return -EIO;
+ if (!is_ignored(SIGTTOU))
+ return tty_signal(SIGTTOU, tty);
+ }
if (nr < 0)
return -EINVAL;
if (!nr)
return 0;
+ if (redirect && tty == TTY_TABLE(0))
+ tty = redirect;
+ if (!(tty->write_q && tty->write))
+ return -EIO;
while (nr>0) {
- sleep_if_full(tty->write_q);
if (current->signal & ~current->blocked)
break;
+ if (FULL(tty->write_q)) {
+ TTY_WRITE_FLUSH(tty);
+ cli();
+ if (FULL(tty->write_q))
+ interruptible_sleep_on(&tty->write_q->proc_list);
+ sti();
+ continue;
+ }
while (nr>0 && !FULL(tty->write_q)) {
c=get_fs_byte(b);
if (O_POST(tty)) {
@@ -401,10 +384,10 @@ static int write_chan(unsigned int channel, struct file * file, char * buf, int
cr_flag = 0;
PUTCH(c,tty->write_q);
}
- TTY_WRITE_FLUSH(tty);
if (nr>0)
schedule();
}
+ TTY_WRITE_FLUSH(tty);
if (b-buf)
return b-buf;
if (current->signal & ~current->blocked)
@@ -414,22 +397,42 @@ static int write_chan(unsigned int channel, struct file * file, char * buf, int
static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
{
- return read_chan(current->tty,file,buf,count);
+ int i;
+
+ i = read_chan(current->tty,file,buf,count);
+ if (i > 0)
+ inode->i_atime = CURRENT_TIME;
+ return i;
}
static int ttyx_read(struct inode * inode, struct file * file, char * buf, int count)
{
- return read_chan(MINOR(inode->i_rdev),file,buf,count);
+ int i;
+
+ i = read_chan(MINOR(inode->i_rdev),file,buf,count);
+ if (i > 0)
+ inode->i_atime = CURRENT_TIME;
+ return i;
}
static int tty_write(struct inode * inode, struct file * file, char * buf, int count)
{
- return write_chan(current->tty,file,buf,count);
+ int i;
+
+ i = write_chan(current->tty,file,buf,count);
+ if (i > 0)
+ inode->i_mtime = CURRENT_TIME;
+ return i;
}
static int ttyx_write(struct inode * inode, struct file * file, char * buf, int count)
{
- return write_chan(MINOR(inode->i_rdev),file,buf,count);
+ int i;
+
+ i = write_chan(MINOR(inode->i_rdev),file,buf,count);
+ if (i > 0)
+ inode->i_mtime = CURRENT_TIME;
+ return i;
}
static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
@@ -437,29 +440,92 @@ static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int
return -EBADF;
}
-static int tty_readdir(struct inode * inode, struct file * file, struct dirent * de, int count)
+/*
+ * tty_open and tty_release keep up the tty count that contains the
+ * number of opens done on a tty. We cannot use the inode-count, as
+ * different inodes might point to the same tty.
+ *
+ * Open-counting is needed for pty masters, as well as for keeping
+ * track of serial lines: DTR is dropped when the last close happens.
+ */
+static int tty_open(struct inode * inode, struct file * filp)
+{
+ struct tty_struct *tty;
+ int dev;
+
+ dev = inode->i_rdev;
+ if (MAJOR(dev) == 5)
+ dev = current->tty;
+ else
+ dev = MINOR(dev);
+ if (dev < 0)
+ return -ENODEV;
+ tty = TTY_TABLE(dev);
+ if (IS_A_PTY_MASTER(dev)) {
+ if (tty->count)
+ return -EAGAIN;
+ }
+ tty->count++;
+ if (!(filp->f_flags & O_NOCTTY) &&
+ current->leader &&
+ current->tty<0 &&
+ tty->session==0) {
+ current->tty = dev;
+ tty->session = current->session;
+ tty->pgrp = current->pgrp;
+ }
+ if (IS_A_SERIAL(dev))
+ serial_open(dev-64);
+ return 0;
+}
+
+static void tty_release(struct inode * inode, struct file * filp)
{
- return -ENOTDIR;
+ int dev;
+ unsigned short port;
+ struct tty_struct * tty, * slave;
+
+ dev = inode->i_rdev;
+ if (MAJOR(dev) == 5)
+ dev = current->tty;
+ else
+ dev = MINOR(dev);
+ if (dev < 0)
+ return;
+ tty = TTY_TABLE(dev);
+ if (--tty->count)
+ return;
+ if (tty == redirect)
+ redirect = NULL;
+ if (port = tty->read_q->data)
+ outb(0x0c,port+4); /* reset DTR, RTS, */
+ if (IS_A_PTY_MASTER(dev)) {
+ slave = tty_table + PTY_OTHER(dev);
+ if (slave->pgrp > 0)
+ kill_pg(slave->pgrp,SIGHUP,1);
+ }
}
static struct file_operations tty_fops = {
tty_lseek,
tty_read,
tty_write,
- tty_readdir,
- NULL, /* tty_close */
+ NULL, /* tty_readdir */
NULL, /* tty_select */
- tty_ioctl /* tty_ioctl */
+ tty_ioctl,
+ tty_open,
+ tty_release
};
static struct file_operations ttyx_fops = {
tty_lseek,
ttyx_read,
ttyx_write,
- tty_readdir,
- NULL, /* ttyx_close */
+ NULL, /* ttyx_readdir */
NULL, /* ttyx_select */
- tty_ioctl /* ttyx_ioctl */
+ tty_ioctl, /* ttyx_ioctl */
+ tty_open,
+ tty_release
};
void tty_init(void)
@@ -481,7 +547,7 @@ void tty_init(void)
for (i=0 ; i<256 ; i++) {
tty_table[i] = (struct tty_struct) {
{0, 0, 0, 0, 0, INIT_C_CC},
- -1, 0, 0, 0, {0,0,0,0},
+ -1, 0, 0, 0, 0, {0,0,0,0},
NULL, NULL, NULL, NULL
};
}
@@ -498,6 +564,7 @@ void tty_init(void)
0, /* initial session */
0, /* initial stopped */
0, /* initial busy */
+ 0, /* initial count */
{video_num_lines,video_num_columns,0,0},
con_write,
con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
@@ -515,6 +582,7 @@ void tty_init(void)
0,
0,
0,
+ 0,
{25,80,0,0},
rs_write,
rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
@@ -532,6 +600,7 @@ void tty_init(void)
0,
0,
0,
+ 0,
{25,80,0,0},
mpty_write,
mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
@@ -547,6 +616,7 @@ void tty_init(void)
0,
0,
0,
+ 0,
{25,80,0,0},
spty_write,
spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c
index 9601268..21818e5 100644
--- a/kernel/chr_drv/tty_ioctl.c
+++ b/kernel/chr_drv/tty_ioctl.c
@@ -19,7 +19,6 @@
extern int session_of_pgrp(int pgrp);
extern int do_screendump(int arg);
extern int kill_pg(int pgrp, int sig, int priv);
-extern int tty_signal(int sig, struct tty_struct *tty);
extern int vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg);
static unsigned short quotient[] = {
@@ -43,7 +42,7 @@ static void change_speed(struct tty_struct * tty)
sti();
}
-static void flush(struct tty_queue * queue)
+void flush(struct tty_queue * queue)
{
if (queue) {
cli();
@@ -55,17 +54,31 @@ static void flush(struct tty_queue * queue)
static void wait_until_sent(struct tty_struct * tty)
{
- cli();
while (!(current->signal & ~current->blocked) && !EMPTY(tty->write_q)) {
+ TTY_WRITE_FLUSH(tty);
current->counter = 0;
- interruptible_sleep_on(&tty->write_q->proc_list);
+ cli();
+ if (EMPTY(tty->write_q))
+ break;
+ else
+ interruptible_sleep_on(&tty->write_q->proc_list);
+ sti();
}
sti();
}
static void send_break(struct tty_struct * tty)
{
- /* do nothing - not implemented */
+ unsigned short port;
+
+ if (!(port = tty->read_q->data))
+ return;
+ port += 3;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 25;
+ outb_p(inb_p(port) | 0x40,port);
+ schedule();
+ outb_p(inb_p(port) & 0xbf,port);
}
static int do_get_ps_info(int arg)
@@ -109,15 +122,17 @@ static int get_termios(struct tty_struct * tty, struct termios * termios)
static int set_termios(struct tty_struct * tty, struct termios * termios,
int channel)
{
- int i, retsig;
+ int i;
/* If we try to set the state of terminal and we're not in the
foreground, send a SIGTTOU. If the signal is blocked or
ignored, go ahead and perform the operation. POSIX 7.2) */
- if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
- retsig = tty_signal(SIGTTOU, tty);
- if (retsig == -ERESTARTSYS || retsig == -EINTR)
- return retsig;
+ if ((current->tty == channel) &&
+ (tty->pgrp != current->pgrp)) {
+ if (is_orphaned_pgrp(current->pgrp))
+ return -EIO;
+ if (!is_ignored(SIGTTOU))
+ return tty_signal(SIGTTOU, tty);
}
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
@@ -149,13 +164,16 @@ static int get_termio(struct tty_struct * tty, struct termio * termio)
static int set_termio(struct tty_struct * tty, struct termio * termio,
int channel)
{
- int i, retsig;
+ int i;
struct termio tmp_termio;
- if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
- retsig = tty_signal(SIGTTOU, tty);
- if (retsig == -ERESTARTSYS || retsig == -EINTR)
- return retsig;
+ if ((current->tty == channel) &&
+ (tty->pgrp > 0) &&
+ (tty->pgrp != current->pgrp)) {
+ if (is_orphaned_pgrp(current->pgrp))
+ return -EIO;
+ if (!is_ignored(SIGTTOU))
+ return tty_signal(SIGTTOU, tty);
}
for (i=0 ; i< (sizeof (*termio)) ; i++)
((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
@@ -255,10 +273,9 @@ int tty_ioctl(struct inode * inode, struct file * file,
case TCSETA:
return set_termio(tty,(struct termio *) arg, dev);
case TCSBRK:
- if (!arg) {
- wait_until_sent(tty);
+ wait_until_sent(tty);
+ if (!arg)
send_break(tty);
- }
return 0;
case TCXONC:
switch (arg) {
@@ -325,15 +342,18 @@ int tty_ioctl(struct inode * inode, struct file * file,
return 0;
case TIOCINQ:
verify_area((void *) arg,4);
- put_fs_long(CHARS(tty->secondary),
- (unsigned long *) arg);
+ if (L_CANON(tty) && !tty->secondary->data)
+ put_fs_long(0, (unsigned long *) arg);
+ else
+ put_fs_long(CHARS(tty->secondary),
+ (unsigned long *) arg);
return 0;
case TIOCSTI:
return -EINVAL; /* not implemented */
case TIOCGWINSZ:
return get_window_size(tty,(struct winsize *) arg);
case TIOCSWINSZ:
- if (other_tty)
+ if (IS_A_PTY_MASTER(dev))
set_window_size(other_tty,(struct winsize *) arg);
return set_window_size(tty,(struct winsize *) arg);
case TIOCMGET:
@@ -358,6 +378,18 @@ int tty_ioctl(struct inode * inode, struct file * file,
default:
return -EINVAL;
}
+ case TIOCCONS:
+ if (!IS_A_PTY(dev))
+ return -EINVAL;
+ if (redirect)
+ return -EBUSY;
+ if (!suser())
+ return -EPERM;
+ if (IS_A_PTY_MASTER(dev))
+ redirect = other_tty;
+ else
+ redirect = tty;
+ return 0;
default:
return vt_ioctl(tty, dev, cmd, arg);
}
diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c
index 549cb93..8bac0fe 100644
--- a/kernel/chr_drv/vt.c
+++ b/kernel/chr_drv/vt.c
@@ -23,7 +23,7 @@
* console (vt and kd) routines, as defined by usl svr4 manual
*/
-struct vt_info vt_info[MAX_CONSOLES];
+struct vt_cons vt_cons[MAX_CONSOLES];
extern int NR_CONSOLES;
extern unsigned char kleds;
@@ -64,14 +64,27 @@ kiocsound(unsigned int freq)
return 0;
}
+/*
+ * all the vt ioctls affect only consoles, so we reject all other ttys.
+ * we also have the capability to modify any console, not just the fg_console.
+ */
int
vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg)
{
+ int console = dev ? dev - 1 : fg_console;
+ unsigned char ucval;
+
+ if (!IS_A_CONSOLE(dev) || console < 0 || console >= NR_CONSOLES)
+ return -EINVAL;
+
switch (cmd) {
case KIOCSOUND:
return kiocsound((unsigned int)arg);
case KDGKBTYPE:
+ /*
+ * this is naive.
+ */
verify_area((void *) arg, sizeof(unsigned char));
put_fs_byte(KB_101, (unsigned char *) arg);
return 0;
@@ -88,7 +101,8 @@ vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg)
case KDENABIO:
case KDDISABIO:
- return sys_ioperm(GPFIRST, GPNUM, (cmd == KDENABIO)) ? -ENXIO : 0;
+ return sys_ioperm(GPFIRST, GPNUM,
+ (cmd == KDENABIO)) ? -ENXIO : 0;
case KDSETMODE:
/*
@@ -107,11 +121,11 @@ vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg)
default:
return -EINVAL;
}
- vt_info[fg_console].mode = arg;
+ vt_cons[console].vt_mode = arg;
return 0;
case KDGETMODE:
verify_area((void *) arg, sizeof(unsigned long));
- put_fs_long(vt_info[fg_console].mode, (unsigned long *) arg);
+ put_fs_long(vt_cons[console].vt_mode, (unsigned long *) arg);
return 0;
case KDMAPDISP:
@@ -124,47 +138,56 @@ vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg)
case KDSKBMODE:
if (arg == K_RAW) {
- kraw = 1;
- ke0 = 0;
+ if (console == fg_console) {
+ kraw = 1;
+ ke0 = 0;
+ } else {
+ vt_cons[console].vc_kbdraw = 1;
+ vt_cons[console].vc_kbde0 = 0;
+ }
}
else if (arg == K_XLATE) {
- kraw = 0;
+ if (console == fg_console)
+ kraw = 0;
+ else
+ vt_cons[console].vc_kbdraw = 0;
}
else
return -EINVAL;
+ flush(tty->read_q);
+ flush(tty->secondary);
return 0;
case KDGKBMODE:
verify_area((void *) arg, sizeof(unsigned long));
- put_fs_long(kraw ? K_RAW : K_XLATE, (unsigned long *) arg);
+ ucval = (console == fg_console) ? kraw :
+ vt_cons[console].vc_kbdraw;
+ put_fs_long(ucval ? K_RAW : K_XLATE, (unsigned long *) arg);
return 0;
case KDGETLED:
verify_area((void *) arg, sizeof(unsigned char));
- put_fs_byte((((kleds & 1) ? LED_SCR : 0) |
- ((kleds & 2) ? LED_NUM : 0) |
- ((kleds & 4) ? LED_CAP : 0)),
+ ucval = (console == fg_console) ? kleds :
+ vt_cons[console].vc_kbdleds;
+ put_fs_byte((((ucval & 1) ? LED_SCR : 0) |
+ ((ucval & 2) ? LED_NUM : 0) |
+ ((ucval & 4) ? LED_CAP : 0)),
(unsigned char *) arg);
return 0;
case KDSETLED:
if (arg & ~7)
return -EINVAL;
- kleds = (((arg & LED_SCR) ? 1 : 0) |
+ ucval = (((arg & LED_SCR) ? 1 : 0) |
((arg & LED_NUM) ? 2 : 0) |
((arg & LED_CAP) ? 4 : 0));
- set_leds();
+ if (console == fg_console) {
+ kleds = ucval;
+ set_leds();
+ }
+ else
+ vt_cons[console].vc_kbdleds = ucval;
return 0;
default:
return -EINVAL;
}
}
-
-void
-vt_init(void)
-{
- int i;
-
- for (i = 0; i < NR_CONSOLES; ++i) {
- vt_info[i].mode = KD_TEXT;
- }
-}
diff --git a/kernel/chr_drv/vt_kern.h b/kernel/chr_drv/vt_kern.h
index d90bf82..2c7116c 100644
--- a/kernel/chr_drv/vt_kern.h
+++ b/kernel/chr_drv/vt_kern.h
@@ -1,8 +1,15 @@
#ifndef _VT_KERN_H
#define _VT_KERN_H
-extern struct vt_info {
- int mode; /* KD_TEXT, ... */
-} vt_info[MAX_CONSOLES];
+/*
+ * this really is an extension of the vc_cons structure in console.c, but
+ * with information needed by the vt package
+ */
+extern struct vt_cons {
+ int vt_mode; /* KD_TEXT, ... */
+ unsigned char vc_kbdraw;
+ unsigned char vc_kbde0;
+ unsigned char vc_kbdleds;
+} vt_cons[MAX_CONSOLES];
#endif /* _VT_KERN_H */
diff --git a/kernel/exit.c b/kernel/exit.c
index 5253ab2..1e6ec04 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -17,11 +17,12 @@
int sys_close(int fd);
-inline int send_sig(long sig,struct task_struct * p,int priv)
+int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || (sig < 0) || (sig > 32))
return -EINVAL;
- if (!priv && (current->euid!=p->euid) && !suser())
+ if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
+ (current->euid != p->euid) && !suser())
return -EPERM;
if (!sig)
return 0;
@@ -167,14 +168,26 @@ void audit_ptree()
}
#endif /* DEBUG_PROC_TREE */
+/*
+ * This checks not only the pgrp, but falls back on the pid if no
+ * satisfactory prgp is found. I dunno - gdb doesn't work correctly
+ * without this...
+ */
int session_of_pgrp(int pgrp)
{
struct task_struct **p;
+ int fallback;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+ fallback = -1;
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ if (!*p || (*p)->session <= 0)
+ continue;
if ((*p)->pgrp == pgrp)
- return((*p)->session);
- return -1;
+ return (*p)->session;
+ if ((*p)->pid == pgrp)
+ fallback = (*p)->session;
+ }
+ return fallback;
}
int kill_pg(int pgrp, int sig, int priv)
@@ -186,7 +199,7 @@ int kill_pg(int pgrp, int sig, int priv)
if (sig<0 || sig>32 || pgrp<=0)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if ((*p)->pgrp == pgrp) {
+ if (*p && (*p)->pgrp == pgrp) {
if (sig && (err = send_sig(sig,*p,priv)))
retval = err;
else
@@ -202,7 +215,7 @@ int kill_proc(int pid, int sig, int priv)
if (sig<0 || sig>32)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if ((*p)->pid == pid)
+ if (*p && (*p)->pid == pid)
return(sig ? send_sig(sig,*p,priv) : 0);
return(-ESRCH);
}
@@ -220,7 +233,7 @@ int sys_kill(int pid,int sig)
return(kill_pg(current->pgrp,sig,0));
if (pid == -1) {
while (--p > &FIRST_TASK)
- if ((*p)->pid > 1 && *p != current) {
+ if (*p && (*p)->pid > 1 && *p != current) {
++count;
if ((err = send_sig(sig,*p,0)) != -EPERM)
retval = err;
@@ -263,7 +276,7 @@ static int has_stopped_jobs(int pgrp)
struct task_struct ** p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if ((*p)->pgrp != pgrp)
+ if (!*p || (*p)->pgrp != pgrp)
continue;
if ((*p)->state == TASK_STOPPED)
return(1);
@@ -287,7 +300,7 @@ volatile void do_exit(long code)
current->root = NULL;
iput(current->executable);
current->executable = NULL;
- for (i=0; i<current->numlibraries; i++) {
+ for (i=0; i < current->numlibraries; i++) {
iput(current->libraries[i].library);
current->libraries[i].library = NULL;
}
@@ -357,7 +370,7 @@ volatile void do_exit(long code)
tty->session = 0;
}
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
- if ((*p)->session == current->session)
+ if (*p && (*p)->session == current->session)
(*p)->tty = -1;
}
if (last_task_used_math == current)
@@ -396,8 +409,9 @@ repeat:
}
switch (p->state) {
case TASK_STOPPED:
- if (!(options & WUNTRACED) ||
- !p->exit_code)
+ if (!p->exit_code)
+ continue;
+ if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
continue;
if (stat_addr)
put_fs_long((p->exit_code << 8) | 0x7f,
diff --git a/kernel/fork.c b/kernel/fork.c
index 89277ab..8a0dbe8 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -70,7 +70,8 @@ static int find_empty_process(void)
int i;
repeat:
- if ((++last_pid)<0) last_pid=1;
+ if ((++last_pid) & 0xffff0000)
+ last_pid=1;
for(i=0 ; i<NR_TASKS ; i++)
if (task[i] && ((task[i]->pid == last_pid) ||
(task[i]->pgrp == last_pid)))
diff --git a/kernel/printk.c b/kernel/printk.c
index ebce88c..c461698 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -4,28 +4,101 @@
* (C) 1991 Linus Torvalds
*/
-/*
- * When in kernel-mode, we cannot use printf, as fs is liable to
- * point to 'interesting' things. Make a printf with fs-saving, and
- * all is well.
- */
#include <stdarg.h>
#include <stddef.h>
+#include <errno.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
#include <linux/kernel.h>
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
+extern void console_print(const char *);
+
+static unsigned long log_page = 0;
+static unsigned long log_start = 0;
+static unsigned long log_size = 0;
+static struct task_struct * log_wait = NULL;
+
+int sys_syslog(int type, char * buf, int len)
+{
+ unsigned long i;
+ char c;
+
+ if (!suser())
+ return -EPERM;
+ switch (type) {
+ case 0:
+ i = log_page;
+ log_page = 0;
+ free_page(i);
+ wake_up(&log_wait);
+ return 0;
+ case 1:
+ i = get_free_page();
+ if (log_page) {
+ free_page(i);
+ return 0;
+ } else if (log_page = i) {
+ log_start = log_size = 0;
+ return 0;
+ }
+ return -ENOMEM;
+ case 2:
+ if (!buf || len < 0)
+ return -EINVAL;
+ if (!len)
+ return 0;
+ verify_area(buf,len);
+ while (!log_size) {
+ if (!log_page)
+ return -EIO;
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ cli();
+ if (!log_size)
+ interruptible_sleep_on(&log_wait);
+ sti();
+ }
+ i = 0;
+ while (log_size && len) {
+ c = *((char *) log_page+log_start);
+ log_start++;
+ log_size--;
+ log_start &= 4095;
+ put_fs_byte(c,buf);
+ buf++;
+ i++;
+ }
+ return i;
+ }
+ return -EINVAL;
+}
+
int printk(const char *fmt, ...)
{
va_list args;
- int i;
+ int i,j;
+ char * p;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
+ for (j = 0; j < i && log_page ; j++) {
+ p = (char *) log_page + (4095 & (log_start+log_size));
+ *p = buf[j];
+ if (log_size < 4096)
+ log_size++;
+ else
+ log_start++;
+ }
+ if (log_page)
+ wake_up(&log_wait);
console_print(buf);
return i;
}
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4602fc2..60e3d6d 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -29,7 +29,7 @@
*/
#define MAGICNUMBER 68
-void do_no_page(unsigned long, unsigned long, struct task_struct *);
+void do_no_page(unsigned long, unsigned long, struct task_struct *, unsigned long);
void write_verify(unsigned long);
/* change a pid into a task struct. */
@@ -100,7 +100,7 @@ repeat:
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
- do_no_page(0,addr,tsk);
+ do_no_page(0,addr,tsk,0);
goto repeat;
}
page &= 0xfffff000;
@@ -129,7 +129,7 @@ repeat:
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
- do_no_page(0,addr,tsk);
+ do_no_page(0,addr,tsk,0);
goto repeat;
}
if (!(page & PAGE_RW)) {
diff --git a/kernel/sched.c b/kernel/sched.c
index 098d096..01d04a9 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -22,6 +22,8 @@
#include <signal.h>
#include <errno.h>
+int need_resched = 0;
+
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
@@ -124,6 +126,7 @@ void schedule(void)
/* check alarm, wake up any interruptible tasks that have got a signal */
+ need_resched = 0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->timeout && (*p)->timeout < jiffies)
@@ -178,6 +181,10 @@ int sys_pause(void)
return -EINTR;
}
+/*
+ * wake_up doesn't wake up stopped processes - they have to be awakened
+ * with signals or similar.
+ */
void wake_up(struct task_struct **p)
{
struct task_struct * wakeup_ptr, * tmp;
@@ -186,12 +193,13 @@ void wake_up(struct task_struct **p)
wakeup_ptr = *p;
*p = NULL;
while (wakeup_ptr && wakeup_ptr != task[0]) {
- if (wakeup_ptr->state == TASK_STOPPED)
- printk("wake_up: TASK_STOPPED\n");
- else if (wakeup_ptr->state == TASK_ZOMBIE)
+ if (wakeup_ptr->state == TASK_ZOMBIE)
printk("wake_up: TASK_ZOMBIE\n");
- else
+ else if (wakeup_ptr->state != TASK_STOPPED) {
wakeup_ptr->state = TASK_RUNNING;
+ if (wakeup_ptr->counter > current->counter)
+ need_resched = 1;
+ }
tmp = wakeup_ptr->next_wait;
wakeup_ptr->next_wait = task[0];
wakeup_ptr = tmp;
@@ -333,6 +341,34 @@ void add_timer(long jiffies, void (*fn)(void))
sti();
}
+#define FSHIFT 11
+#define FSCALE (1<<FSHIFT)
+/*
+ * Constants for averages over 1, 5, and 15 minutes
+ * when sampling at 5 second intervals.
+ */
+static unsigned long cexp[3] = {
+ 1884, /* 0.9200444146293232 * FSCALE, exp(-1/12) */
+ 2014, /* 0.9834714538216174 * FSCALE, exp(-1/60) */
+ 2037, /* 0.9944598480048967 * FSCALE, exp(-1/180) */
+};
+unsigned long averunnable[3]; /* fixed point numbers */
+
+void update_avg(void)
+{
+ int i, n=0;
+ struct task_struct **p;
+
+ for(p = &LAST_TASK; p > &FIRST_TASK; --p)
+ if (*p && ((*p)->state == TASK_RUNNING ||
+ (*p)->state == TASK_UNINTERRUPTIBLE))
+ ++n;
+
+ for (i = 0; i < 3; ++i)
+ averunnable[i] = (cexp[i] * averunnable[i] +
+ n * FSCALE * (FSCALE - cexp[i])) >> FSHIFT;
+}
+
unsigned long timer_active = 0;
struct timer_struct timer_table[32];
@@ -340,6 +376,7 @@ void do_timer(long cpl)
{
unsigned long mask;
struct timer_struct *tp = timer_table+0;
+ static int avg_cnt;
for (mask = 1 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
@@ -350,6 +387,7 @@ void do_timer(long cpl)
continue;
timer_active &= ~mask;
tp->fn();
+ sti();
}
if (cpl)
@@ -370,10 +408,14 @@ void do_timer(long cpl)
}
if (current_DOR & 0xf0)
do_floppy_timer();
- if ((--current->counter)>0) return;
- current->counter=0;
- if (!cpl) return;
- schedule();
+ if (--avg_cnt < 0) {
+ avg_cnt = 500;
+ update_avg();
+ }
+ if ((--current->counter)<=0) {
+ current->counter=0;
+ need_resched = 1;
+ }
}
int sys_alarm(long seconds)
@@ -420,7 +462,7 @@ int sys_nice(long increment)
{
if (increment < 0 && !suser())
return -EPERM;
- if (increment > current->priority)
+ if (increment >= current->priority)
increment = current->priority-1;
current->priority -= increment;
return 0;
diff --git a/kernel/signal.c b/kernel/signal.c
index cf0aa01..d1d5afd 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -11,9 +11,7 @@
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
-
-int send_sig (int, struct task_struct *, int);
-
+
int sys_sgetmask()
{
return current->blocked;
diff --git a/kernel/sys.c b/kernel/sys.c
index a919028..2318a7e 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -30,14 +30,72 @@ struct timezone sys_tz = { 0, 0};
extern int session_of_pgrp(int pgrp);
-int sys_getpriority()
+#define PZERO 15
+
+static int proc_sel(struct task_struct *p, int which, int who)
+{
+ switch (which) {
+ case PRIO_PROCESS:
+ if (!who && p == current)
+ return 1;
+ return(p->pid == who);
+ case PRIO_PGRP:
+ if (!who)
+ who = current->pgrp;
+ return(p->pgrp == who);
+ case PRIO_USER:
+ if (!who)
+ who = current->uid;
+ return(p->uid == who);
+ }
+ return 0;
+}
+
+int sys_setpriority(int which, int who, int niceval)
{
- return -ENOSYS;
+ struct task_struct **p;
+ int error = ESRCH;
+ int priority;
+
+ if (which > 2 || which < 0)
+ return -EINVAL;
+
+ if ((priority = PZERO - niceval) <= 0)
+ priority = 1;
+
+ for(p = &LAST_TASK; p > &FIRST_TASK; --p) {
+ if (!*p || !proc_sel(*p, which, who))
+ continue;
+ if ((*p)->uid != current->euid &&
+ (*p)->uid != current->uid && !suser()) {
+ error = EPERM;
+ continue;
+ }
+ if (error == ESRCH)
+ error = 0;
+ if (priority > (*p)->priority && !suser())
+ error = EACCES;
+ else
+ (*p)->priority = priority;
+ }
+ return -error;
}
-int sys_setpriority()
+int sys_getpriority(int which, int who)
{
- return -ENOSYS;
+ struct task_struct **p;
+ int max_prio = 0;
+
+ if (which > 2 || which < 0)
+ return -EINVAL;
+
+ for(p = &LAST_TASK; p > &FIRST_TASK; --p) {
+ if (!*p || !proc_sel(*p, which, who))
+ continue;
+ if ((*p)->priority > max_prio)
+ max_prio = (*p)->priority;
+ }
+ return(max_prio ? max_prio : -ESRCH);
}
int sys_profil()
diff --git a/kernel/sys_call.S b/kernel/sys_call.S
index e6f7766..ad70318 100644
--- a/kernel/sys_call.S
+++ b/kernel/sys_call.S
@@ -88,7 +88,8 @@ ENOSYS = 38
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_irq13,_reserved
.globl _alignment_check,_page_fault
-.globl _keyboard_interrupt
+.globl _keyboard_interrupt,_hd_interrupt
+.globl _IRQ3_interrupt,_IRQ4_interrupt
#define SAVE_ALL \
cld; \
@@ -124,15 +125,15 @@ ENOSYS = 38
inb $0xA1,%al; \
jmp 1f; \
1: jmp 1f; \
-1: orb $mask,%al; \
+1: orb $(mask),%al; \
outb %al,$0xA1; \
jmp 1f; \
1: jmp 1f; \
1: movb $0x20,%al; \
- outb %al,$0x20
+ outb %al,$0xA0 \
jmp 1f; \
1: jmp 1f; \
-1: outb %al,$0xA0
+1: outb %al,$0x20
#define UNBLK_FIRST(mask) \
inb $0x21,%al; \
@@ -172,6 +173,8 @@ ret_from_sys_call:
1: movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 2f
+ cmpl $0,_need_resched
+ jne reschedule
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
@@ -239,25 +242,65 @@ _device_not_available:
_keyboard_interrupt:
pushl $-1
SAVE_ALL
- ACK_FIRST(2)
+ ACK_FIRST(0x02)
sti
call _do_keyboard
cli
- UNBLK_FIRST(2)
+ UNBLK_FIRST(0x02)
+ jmp ret_from_sys_call
+
+.align 2
+_IRQ3_interrupt:
+ pushl $-1
+ SAVE_ALL
+ ACK_FIRST(0x08)
+ sti
+ call _do_IRQ3
+ cli
+ UNBLK_FIRST(0x08)
+ jmp ret_from_sys_call
+
+.align 2
+_IRQ4_interrupt:
+ pushl $-1
+ SAVE_ALL
+ ACK_FIRST(0x10)
+ sti
+ call _do_IRQ4
+ cli
+ UNBLK_FIRST(0x10)
jmp ret_from_sys_call
.align 2
_timer_interrupt:
pushl $-1 # mark this as an int
SAVE_ALL
+ ACK_FIRST(0x01)
+ sti
incl _jiffies
- movb $0x20,%al # EOI to interrupt controller #1
- outb %al,$0x20
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call _do_timer # 'do_timer(long CPL)' does everything from
addl $4,%esp # task switching to accounting ...
+ cli
+ UNBLK_FIRST(0x01)
+ jmp ret_from_sys_call
+
+.align 2
+_hd_interrupt:
+ pushl $-1
+ SAVE_ALL
+ ACK_SECOND(0x40)
+ andl $0xfffeffff,_timer_active
+ xorl %edx,%edx
+ xchgl _do_hd,%edx
+ testl %edx,%edx
+ jne 1f
+ movl $_unexpected_hd_interrupt,%edx
+1: call *%edx # "interesting" way of handling intr.
+ cli
+ UNBLK_SECOND(0x40)
jmp ret_from_sys_call
.align 2
diff --git a/kernel/traps.c b/kernel/traps.c
index 89b65f3..c05b549 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -60,7 +60,6 @@ void reserved(void);
void parallel_interrupt(void);
void irq13(void);
void alignment_check(void);
-int send_sig(long, struct task_struct *, int);
static void die(char * str,long esp_ptr,long nr)
{
@@ -84,7 +83,10 @@ static void die(char * str,long esp_ptr,long nr)
for(i=0;i<10;i++)
printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
printk("\n\r");
- do_exit(11); /* play segment exception */
+ if ((0xffff & esp[1]) == 0xf)
+ send_sig(SIGSEGV, current, 0);
+ else
+ do_exit(SIGSEGV);
}
void do_double_fault(long esp, long error_code)
@@ -99,7 +101,7 @@ void do_general_protection(long esp, long error_code)
void do_alignment_check(long esp, long error_code)
{
- die("alignment check",esp,error_code);
+ die("alignment check",esp,error_code);
}
void do_divide_error(long esp, long error_code)
@@ -119,7 +121,7 @@ void do_nmi(long esp, long error_code)
void do_debug(long esp, long error_code)
{
- send_sig(SIGTRAP, current, 0);
+ send_sig(SIGTRAP, current, 0);
}
void do_overflow(long esp, long error_code)
diff --git a/lib/Makefile b/lib/Makefile
index 78afc1e..94b649f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -42,31 +42,33 @@ dep:
### Dependencies:
_exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
ctype.s ctype.o : ctype.c ../include/linux/ctype.h
dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
errno.s errno.o : errno.c
execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
malloc.s malloc.o : malloc.c ../include/linux/kernel.h ../include/linux/mm.h ../include/linux/fs.h \
../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/signal.h \
../include/asm/system.h
open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/stdarg.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h \
+ ../include/stdarg.h
setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
string.s string.o : string.c ../include/linux/string.h
wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/sys/wait.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h \
+ ../include/sys/wait.h
write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \
../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \
- ../include/sys/param.h ../include/sys/resource.h ../include/utime.h
+ ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/linux/unistd.h
diff --git a/lib/close.c b/lib/close.c
index afd8364..3ef965d 100644
--- a/lib/close.c
+++ b/lib/close.c
@@ -8,3 +8,4 @@
#include <unistd.h>
_syscall1(int,close,int,fd)
+
diff --git a/lib/dup.c b/lib/dup.c
index dd13414..495c6e3 100644
--- a/lib/dup.c
+++ b/lib/dup.c
@@ -8,3 +8,4 @@
#include <unistd.h>
_syscall1(int,dup,int,fd)
+
diff --git a/lib/execve.c b/lib/execve.c
index a89726d..7669b50 100644
--- a/lib/execve.c
+++ b/lib/execve.c
@@ -8,3 +8,4 @@
#include <unistd.h>
_syscall3(int,execve,const char *,file,char **,argv,char **,envp)
+
diff --git a/lib/setsid.c b/lib/setsid.c
index 68516c7..601fcfc 100644
--- a/lib/setsid.c
+++ b/lib/setsid.c
@@ -8,3 +8,4 @@
#include <unistd.h>
_syscall0(pid_t,setsid)
+
diff --git a/lib/wait.c b/lib/wait.c
index 2815c16..e02dfdf 100644
--- a/lib/wait.c
+++ b/lib/wait.c
@@ -14,3 +14,4 @@ pid_t wait(int * wait_stat)
{
return waitpid(-1,wait_stat,0);
}
+
diff --git a/lib/write.c b/lib/write.c
index df52e74..69d4ab3 100644
--- a/lib/write.c
+++ b/lib/write.c
@@ -8,3 +8,4 @@
#include <unistd.h>
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
+
diff --git a/mm/memory.c b/mm/memory.c
index 866a5cb..1417bc9 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -355,7 +355,7 @@ static unsigned long put_page(unsigned long page,unsigned long address)
return 0;
}
if (mem_map[(page-LOW_MEM)>>12] != 1) {
- printk("mem_map disagrees with %p at %p\n",page,address);
+ printk("put_page: mem_map disagrees with %p at %p\n",page,address);
return 0;
}
page_table = (unsigned long *) ((address>>20) & 0xffc);
@@ -588,14 +588,39 @@ static int share_page(struct inode * inode, unsigned long address)
return 0;
}
+/*
+ * fill in an empty page or directory if none exists
+ */
+static unsigned long get_empty(unsigned long * p)
+{
+ unsigned long page = 0;
+
+repeat:
+ if (1 & *p) {
+ free_page(page);
+ return *p;
+ }
+ if (*p) {
+ printk("get_empty: bad page entry \n");
+ *p = 0;
+ }
+ if (page) {
+ *p = page | 7;
+ return *p;
+ }
+ if (!(page = get_free_page()))
+ oom();
+ goto repeat;
+}
+
void do_no_page(unsigned long error_code, unsigned long address,
- struct task_struct *tsk)
+ struct task_struct *tsk, unsigned long user_esp)
{
static unsigned int last_checked = 0;
int nr[4];
unsigned long tmp;
unsigned long page;
- int block,i;
+ unsigned int block,i;
struct inode * inode;
/* Thrashing ? Make it interruptible, but don't penalize otherwise */
@@ -616,54 +641,50 @@ void do_no_page(unsigned long error_code, unsigned long address,
printk("Bad things happen: nonexistent page error in do_no_page\n\r");
do_exit(SIGSEGV);
}
+ page = get_empty((unsigned long *) ((address >> 20) & 0xffc));
+ page &= 0xfffff000;
+ page += (address >> 10) & 0xffc;
+ tmp = *(unsigned long *) page;
+ if (tmp & 1) {
+ printk("bogus do_no_page\n");
+ return;
+ }
++tsk->rss;
- page = *(unsigned long *) ((address >> 20) & 0xffc);
-/* check the page directory: make a page dir entry if no such exists */
- if (page & 1) {
- page &= 0xfffff000;
- page += (address >> 10) & 0xffc;
- tmp = *(unsigned long *) page;
- if (tmp && !(1 & tmp)) {
- ++tsk->maj_flt;
- swap_in((unsigned long *) page);
- return;
- }
- } else {
- if (page)
- printk("do_no_page: bad page directory\n");
- if (!(page = get_free_page()))
- oom();
- page |= 7;
- *(unsigned long *) ((address >> 20) & 0xffc) = page;
+ if (tmp) {
+ ++tsk->maj_flt;
+ swap_in((unsigned long *) page);
+ return;
}
address &= 0xfffff000;
tmp = address - tsk->start_code;
- if (tmp >= LIBRARY_OFFSET ) {
- inode = NULL;
- block = 1;
- i = tsk->numlibraries;
- while (i-- > 0) {
- if (tmp < (tsk->libraries[i].start +
- tsk->libraries[i].length)) {
- inode = tsk->libraries[i].library;
- block = 1 + (tmp - tsk->libraries[i].start) /
- BLOCK_SIZE;
- break;
- }
- }
- } else if (tmp < tsk->end_data) {
+ inode = NULL;
+ block = 0;
+ if (tmp < tsk->end_data) {
inode = tsk->executable;
block = 1 + tmp / BLOCK_SIZE;
} else {
- inode = NULL;
- block = 0;
+ i = tsk->numlibraries;
+ while (i-- > 0) {
+ if (tmp < tsk->libraries[i].start)
+ continue;
+ block = tmp - tsk->libraries[i].start;
+ if (block >= tsk->libraries[i].length)
+ continue;
+ inode = tsk->libraries[i].library;
+ block = 1 + block / BLOCK_SIZE;
+ break;
+ }
}
if (!inode) {
++tsk->min_flt;
- if (tmp < LIBRARY_OFFSET && tmp > tsk->brk && tsk == current &&
- LIBRARY_OFFSET - tmp > tsk->rlim[RLIMIT_STACK].rlim_max)
- do_exit(SIGSEGV);
get_empty_page(address);
+ if (tsk != current)
+ return;
+ if (tmp >= LIBRARY_OFFSET || tmp < tsk->brk)
+ return;
+ if (tmp+8192 >= (user_esp & 0xfffff000))
+ return;
+ send_sig(SIGSEGV,tsk,1);
return;
}
if (tsk == current)
@@ -674,7 +695,6 @@ void do_no_page(unsigned long error_code, unsigned long address,
++tsk->maj_flt;
if (!(page = get_free_page()))
oom();
-/* remember that 1 block is used for header */
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(inode,block);
bread_page(page,inode->i_dev,nr);
@@ -682,7 +702,7 @@ void do_no_page(unsigned long error_code, unsigned long address,
if (i>4095)
i = 0;
tmp = page + 4096;
- while (i-- > 0) {
+ while (i--) {
tmp--;
*(char *)tmp = 0;
}
@@ -763,11 +783,16 @@ void show_mem(void)
void do_page_fault(unsigned long *esp, unsigned long error_code)
{
unsigned long address;
- /* get the address */
+ unsigned long user_esp;
+ if ((0xffff & esp[1]) == 0xf)
+ user_esp = esp[3];
+ else
+ user_esp = 0;
+ /* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
if (!(error_code & 1)) {
- do_no_page(error_code, address, current);
+ do_no_page(error_code, address, current, user_esp);
return;
} else {
do_wp_page(error_code, address);
diff --git a/mm/swap.c b/mm/swap.c
index ba41399..fa933e3 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -245,10 +245,10 @@ repeat:
*
* The swapon system call
*/
-
int sys_swapon(const char * specialfile)
{
struct inode * swap_inode;
+ char * tmp;
int i,j;
if (!suser())
@@ -268,38 +268,39 @@ int sys_swapon(const char * specialfile)
iput(swap_inode);
return -EINVAL;
}
- swap_bitmap = (char *) get_free_page();
- if (!swap_bitmap) {
+ tmp = (char *) get_free_page();
+ if (!tmp) {
iput(swap_file);
swap_device = 0;
swap_file = NULL;
printk("Unable to start swapping: out of memory :-)\n");
return -ENOMEM;
}
- read_swap_page(0,swap_bitmap);
- if (strncmp("SWAP-SPACE",swap_bitmap+4086,10)) {
+ read_swap_page(0,tmp);
+ if (strncmp("SWAP-SPACE",tmp+4086,10)) {
printk("Unable to find swap-space signature\n\r");
- free_page((long) swap_bitmap);
+ free_page((long) tmp);
iput(swap_file);
swap_device = 0;
swap_file = NULL;
swap_bitmap = NULL;
return -EINVAL;
}
- memset(swap_bitmap+4086,0,10);
+ memset(tmp+4086,0,10);
j = 0;
for (i = 1 ; i < SWAP_BITS ; i++)
- if (bit(swap_bitmap,i))
+ if (bit(tmp,i))
j++;
if (!j) {
printk("Empty swap-file\n");
- free_page((long) swap_bitmap);
+ free_page((long) tmp);
iput(swap_file);
swap_device = 0;
swap_file = NULL;
swap_bitmap = NULL;
return -EINVAL;
}
+ swap_bitmap = tmp;
printk("Adding Swap: %d pages (%d bytes) swap-space\n\r",j,j*4096);
return 0;
}
diff --git a/net/Makefile b/net/Makefile
new file mode 100644
index 0000000..1080839
--- /dev/null
+++ b/net/Makefile
@@ -0,0 +1,51 @@
+#
+# Makefile for the linux networking.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+AS =as
+AR =ar
+LD =ld
+CC =gcc -nostdinc -I../include -Wall # -DSOCK_DEBUG
+CPP =cpp -nostdinc -I../include
+
+.c.o:
+ $(CC) $(CFLAGS) \
+ -c -o $*.o $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.s:
+ $(CC) $(CFLAGS) \
+ -S -o $*.s $<
+
+OBJS = socket.o unix.o
+
+net.o: $(OBJS)
+ $(LD) -r -o net.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a tmp_make
+ for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+ sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+ (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make
+ cp tmp_make Makefile
+
+### Dependencies:
+socket.o : socket.c ../include/signal.h ../include/sys/types.h ../include/errno.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h \
+ ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/sys/param.h \
+ ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/system.h \
+ ../include/asm/segment.h ../include/sys/socket.h ../include/sys/stat.h ../include/fcntl.h \
+ ../include/termios.h kern_sock.h socketcall.h
+unix.o : unix.c ../include/signal.h ../include/sys/types.h ../include/errno.h \
+ ../include/linux/string.h ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+ ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
+ ../include/asm/system.h ../include/asm/segment.h ../include/sys/socket.h ../include/sys/un.h \
+ ../include/sys/stat.h ../include/fcntl.h ../include/termios.h kern_sock.h
diff --git a/net/kern_sock.h b/net/kern_sock.h
new file mode 100644
index 0000000..6ec8d1a
--- /dev/null
+++ b/net/kern_sock.h
@@ -0,0 +1,67 @@
+#ifndef _KERN_SOCK_H
+#define _KERN_SOCK_H
+
+#define NSOCKETS 128 /* should be dynamic, later... */
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING, /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+
+/*
+ * internel representation of a socket. not all the fields are used by
+ * all configurations:
+ *
+ * server client
+ * conn client connected to server connected to
+ * iconn list of clients -unused-
+ * awaiting connections
+ * wait sleep for clients, sleep for connection,
+ * sleep for i/o sleep for i/o
+ */
+struct socket {
+ short type; /* SOCK_STREAM, ... */
+ socket_state state;
+ long flags;
+ struct proto_ops *ops; /* protocols do most everything */
+ char *data; /* protocol data */
+ struct socket *conn; /* server socket connected to */
+ struct socket *iconn; /* incomplete client connections */
+ struct socket *next;
+ struct task_struct **wait; /* ptr to place to wait on */
+ void *dummy;
+};
+
+struct proto_ops {
+ int (*init)(void);
+ int (*create)(struct socket *sock, int protocol);
+ int (*dup)(struct socket *newsock, struct socket *oldsock);
+ int (*release)(struct socket *sock, struct socket *peer);
+ int (*bind)(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+ int (*connect)(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len);
+ int (*socketpair)(struct socket *sock1, struct socket *sock2);
+ int (*accept)(struct socket *sock, struct socket *newsock);
+ int (*getname)(struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer);
+ int (*read)(struct socket *sock, char *ubuf, int size, int nonblock);
+ int (*write)(struct socket *sock, char *ubuf, int size, int nonblock);
+ int (*select)(struct socket *sock, int which);
+ int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg);
+};
+
+extern int sock_awaitconn(struct socket *mysock, struct socket *servsock);
+
+#ifdef SOCK_DEBUG
+#define PRINTK printk
+#else
+#define PRINTK (void)
+#endif
+
+#endif /* _KERN_SOCK_H */
diff --git a/net/socket.c b/net/socket.c
new file mode 100644
index 0000000..70472b5
--- /dev/null
+++ b/net/socket.c
@@ -0,0 +1,761 @@
+#include <signal.h>
+#include <errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include "kern_sock.h"
+#include "socketcall.h"
+
+extern int sys_close(int fd);
+
+extern struct proto_ops unix_proto_ops;
+
+static struct {
+ short family;
+ char *name;
+ struct proto_ops *ops;
+} proto_table[] = {
+ AF_UNIX, "AF_UNIX", &unix_proto_ops
+};
+#define NPROTO (sizeof(proto_table) / sizeof(proto_table[0]))
+
+static char *
+family_name(int family)
+{
+ int i;
+
+ for (i = 0; i < NPROTO; ++i)
+ if (proto_table[i].family == family)
+ return proto_table[i].name;
+ return "UNKNOWN";
+}
+
+static int sock_lseek(struct inode *inode, struct file *file, off_t offset,
+ int whence);
+static int sock_read(struct inode *inode, struct file *file, char *buf,
+ int size);
+static int sock_write(struct inode *inode, struct file *file, char *buf,
+ int size);
+static int sock_readdir(struct inode *inode, struct file *file,
+ struct dirent *dirent, int count);
+static void sock_close(struct inode *inode, struct file *file);
+/*static*/ int sock_select(struct inode *inode, struct file *file, int which,
+ select_table *seltable);
+static int sock_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned int arg);
+
+static struct file_operations socket_file_ops = {
+ sock_lseek,
+ sock_read,
+ sock_write,
+ sock_readdir,
+ sock_select, /* not in vfs yet */
+ sock_ioctl,
+ NULL, /* no special open code... */
+ sock_close
+};
+
+#define SOCK_INODE(S) ((struct inode *)(S)->dummy)
+
+static struct socket sockets[NSOCKETS];
+#define last_socket (sockets + NSOCKETS - 1)
+static struct task_struct *socket_wait_free = NULL;
+
+/*
+ * obtains the first available file descriptor and sets it up for use
+ */
+static int
+get_fd(struct inode *inode)
+{
+ int fd, i;
+ struct file *file;
+
+ /*
+ * find a file descriptor suitable for return to the user.
+ */
+ for (fd = 0; fd < NR_OPEN; ++fd)
+ if (!current->filp[fd])
+ break;
+ if (fd == NR_OPEN)
+ return -1;
+ current->close_on_exec &= ~(1 << fd);
+ for (file = file_table, i = 0; i < NR_FILE; ++i, ++file)
+ if (!file->f_count)
+ break;
+ if (i == NR_FILE)
+ return -1;
+ current->filp[fd] = file;
+ file->f_op = &socket_file_ops;
+ file->f_mode = 3;
+ file->f_flags = 0;
+ file->f_count = 1;
+ file->f_inode = inode;
+ file->f_pos = 0;
+ return fd;
+}
+
+/*
+ * reverses the action of get_fd() by releasing the file. it closes the
+ * descriptor, but makes sure it does nothing more. called when an incomplete
+ * socket must be closed, along with sock_release().
+ */
+static inline void
+toss_fd(int fd)
+{
+ current->filp[fd]->f_inode = NULL; /* safe from iput */
+ sys_close(fd);
+}
+
+static inline struct socket *
+socki_lookup(struct inode *inode)
+{
+ struct socket *sock;
+
+ for (sock = sockets; sock <= last_socket; ++sock)
+ if (sock->state != SS_FREE && SOCK_INODE(sock) == inode)
+ return sock;
+ return NULL;
+}
+
+static inline struct socket *
+sockfd_lookup(int fd, struct file **pfile)
+{
+ struct file *file;
+
+ if (fd < 0 || fd >= NR_OPEN || !(file = current->filp[fd]))
+ return NULL;
+ if (pfile)
+ *pfile = file;
+ return socki_lookup(file->f_inode);
+}
+
+static struct socket *
+sock_alloc(int wait)
+{
+ struct socket *sock;
+
+ while (1) {
+ cli();
+ for (sock = sockets; sock <= last_socket; ++sock)
+ if (sock->state == SS_FREE) {
+ sock->state = SS_UNCONNECTED;
+ sti();
+ sock->flags = 0;
+ sock->ops = NULL;
+ sock->data = NULL;
+ sock->conn = NULL;
+ sock->iconn = NULL;
+ /*
+ * this really shouldn't be necessary, but
+ * everything else depends on inodes, so we
+ * grab it.
+ * sleeps are also done on the i_wait member
+ * of this inode.
+ * the close system call will iput this inode
+ * for us.
+ */
+ if (!(SOCK_INODE(sock) = get_empty_inode())) {
+ printk("sock_alloc: no more inodes\n");
+ sock->state = SS_FREE;
+ return NULL;
+ }
+ SOCK_INODE(sock)->i_mode = S_IFSOCK;
+ sock->wait = &SOCK_INODE(sock)->i_wait;
+ PRINTK("sock_alloc: socket 0x%x, inode 0x%x\n",
+ sock, SOCK_INODE(sock));
+ return sock;
+ }
+ sti();
+ if (!wait)
+ return NULL;
+ PRINTK("sock_alloc: no free sockets, sleeping...\n");
+ interruptible_sleep_on(&socket_wait_free);
+ if (current->signal & ~current->blocked) {
+ PRINTK("sock_alloc: sleep was interrupted\n");
+ return NULL;
+ }
+ PRINTK("sock_alloc: wakeup... trying again...\n");
+ }
+}
+
+static inline void
+sock_release_peer(struct socket *peer)
+{
+ peer->state = SS_DISCONNECTING;
+ wake_up(peer->wait);
+}
+
+static void
+sock_release(struct socket *sock)
+{
+ int oldstate;
+ struct socket *peersock, *nextsock;
+
+ PRINTK("sock_release: socket 0x%x, inode 0x%x\n", sock,
+ SOCK_INODE(sock));
+ if ((oldstate = sock->state) != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+ /*
+ * wake up anyone waiting for connections
+ */
+ for (peersock = sock->iconn; peersock; peersock = nextsock) {
+ nextsock = peersock->next;
+ sock_release_peer(peersock);
+ }
+ /*
+ * wake up anyone we're connected to. first, we release the
+ * protocol, to give it a chance to flush data, etc.
+ */
+ peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
+ if (sock->ops)
+ sock->ops->release(sock, peersock);
+ if (peersock)
+ sock_release_peer(peersock);
+ sock->state = SS_FREE; /* this really releases us */
+ wake_up(&socket_wait_free);
+}
+
+static int
+sock_lseek(struct inode *inode, struct file *file, off_t offset, int whence)
+{
+ PRINTK("sock_lseek: huh?\n");
+ return -EBADF;
+}
+
+static int
+sock_read(struct inode *inode, struct file *file, char *ubuf, int size)
+{
+ struct socket *sock;
+
+ PRINTK("sock_read: buf=0x%x, size=%d\n", ubuf, size);
+ if (!(sock = socki_lookup(inode))) {
+ printk("sock_read: can't find socket for inode!\n");
+ return -EBADF;
+ }
+ if (sock->flags & SO_ACCEPTCON)
+ return -EINVAL;
+ return sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK));
+}
+
+static int
+sock_write(struct inode *inode, struct file *file, char *ubuf, int size)
+{
+ struct socket *sock;
+
+ PRINTK("sock_write: buf=0x%x, size=%d\n", ubuf, size);
+ if (!(sock = socki_lookup(inode))) {
+ printk("sock_write: can't find socket for inode!\n");
+ return -EBADF;
+ }
+ if (sock->flags & SO_ACCEPTCON)
+ return -EINVAL;
+ return sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK));
+}
+
+static int
+sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent,
+ int count)
+{
+ PRINTK("sock_readdir: huh?\n");
+ return -EBADF;
+}
+
+int
+sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned int arg)
+{
+ struct socket *sock;
+
+ PRINTK("sock_ioctl: inode=0x%x cmd=0x%x arg=%d\n", inode, cmd, arg);
+ if (!(sock = socki_lookup(inode))) {
+ printk("sock_ioctl: can't find socket for inode!\n");
+ return -EBADF;
+ }
+ switch (cmd) {
+ case TIOCINQ:
+ case TIOCOUTQ:
+ if (sock->flags & SO_ACCEPTCON)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return sock->ops->ioctl(sock, cmd, arg);
+}
+
+/*static*/ int
+sock_select(struct inode *inode, struct file *file, int which,
+ select_table *seltable)
+{
+ struct socket *sock;
+
+ PRINTK("sock_select: inode = 0x%x, kind = %s\n", inode,
+ (which == SEL_IN) ? "in" :
+ (which == SEL_OUT) ? "out" : "ex");
+ if (!(sock = socki_lookup(inode))) {
+ printk("sock_write: can't find socket for inode!\n");
+ return -EBADF;
+ }
+
+ /*
+ * handle server sockets specially
+ */
+ if (sock->flags & SO_ACCEPTCON) {
+ if (which == SEL_IN) {
+ PRINTK("sock_select: %sconnections pending\n",
+ sock->iconn ? "" : "no ");
+ return sock->iconn ? 1 : 0;
+ }
+ PRINTK("sock_select: nothing else for server socket\n");
+ return 0;
+ }
+
+ /*
+ * we can't return errors to select, so its either yes or no.
+ */
+ return sock->ops->select(sock, which) ? 1 : 0;
+}
+
+void
+sock_close(struct inode *inode, struct file *file)
+{
+ struct socket *sock;
+
+ PRINTK("sock_close: inode=0x%x (cnt=%d)\n", inode, inode->i_count);
+ /*
+ * it's possible the inode is NULL if we're closing an unfinished
+ * socket.
+ */
+ if (!inode)
+ return;
+ if (!(sock = socki_lookup(inode))) {
+ printk("sock_close: can't find socket for inode!\n");
+ return;
+ }
+ sock_release(sock);
+}
+
+int
+sock_awaitconn(struct socket *mysock, struct socket *servsock)
+{
+ struct socket *last;
+
+ PRINTK("sock_awaitconn: trying to connect socket 0x%x to 0x%x\n",
+ mysock, servsock);
+ if (!(servsock->flags & SO_ACCEPTCON)) {
+ PRINTK("sock_awaitconn: server not accepting connections\n");
+ return -EINVAL;
+ }
+
+ /*
+ * put ourselves on the server's incomplete connection queue.
+ */
+ mysock->next = NULL;
+ cli();
+ if (!(last = servsock->iconn))
+ servsock->iconn = mysock;
+ else {
+ while (last->next)
+ last = last->next;
+ last->next = mysock;
+ }
+ mysock->state = SS_CONNECTING;
+ mysock->conn = servsock;
+ sti();
+
+ /*
+ * wake up server, then await connection. server will set state to
+ * SS_CONNECTED if we're connected.
+ */
+ wake_up(servsock->wait);
+ if (mysock->state != SS_CONNECTED) {
+ interruptible_sleep_on(mysock->wait);
+ if (mysock->state != SS_CONNECTED) {
+ /*
+ * if we're not connected we could have been
+ * 1) interrupted, so we need to remove ourselves
+ * from the server list
+ * 2) rejected (mysock->conn == NULL), and have
+ * already been removed from the list
+ */
+ if (mysock->conn == servsock) {
+ cli();
+ if ((last = servsock->iconn) == mysock)
+ servsock->iconn = mysock->next;
+ else {
+ while (last->next != mysock)
+ last = last->next;
+ last->next = mysock->next;
+ }
+ sti();
+ }
+ return mysock->conn ? -EINTR : -EACCES;
+ }
+ }
+ return 0;
+}
+
+/*
+ * perform the socket system call. we locate the appropriate family, then
+ * create a fresh socket.
+ */
+static int
+sock_socket(int family, int type, int protocol)
+{
+ int i, fd;
+ struct socket *sock;
+ struct proto_ops *ops;
+
+ PRINTK("sys_socket: family = %d (%s), type = %d, protocol = %d\n",
+ family, family_name(family), type, protocol);
+
+ /*
+ * locate the correct protocol family
+ */
+ for (i = 0; i < NPROTO; ++i)
+ if (proto_table[i].family == family)
+ break;
+ if (i == NPROTO) {
+ PRINTK("sys_socket: family not found\n");
+ return -EINVAL;
+ }
+ ops = proto_table[i].ops;
+
+ /*
+ * check that this is a type that we know how to manipulate and
+ * the protocol makes sense here. the family can still reject the
+ * protocol later.
+ */
+ if ((type != SOCK_STREAM &&
+ type != SOCK_DGRAM &&
+ type != SOCK_SEQPACKET &&
+ type != SOCK_RAW) ||
+ protocol < 0)
+ return -EINVAL;
+
+ /*
+ * allocate the socket and allow the family to set things up. if
+ * the protocol is 0, the family is instructed to select an appropriate
+ * default.
+ */
+ if (!(sock = sock_alloc(1))) {
+ printk("sys_socket: no more sockets\n");
+ return -EAGAIN;
+ }
+ sock->type = type;
+ sock->ops = ops;
+ if ((i = sock->ops->create(sock, protocol)) < 0) {
+ sock_release(sock);
+ return i;
+ }
+
+ if ((fd = get_fd(SOCK_INODE(sock))) < 0) {
+ sock_release(sock);
+ return -EINVAL;
+ }
+
+ return fd;
+}
+
+static int
+sock_socketpair(int family, int type, int protocol, int usockvec[2])
+{
+ int fd1, fd2, i;
+ struct socket *sock1, *sock2;
+
+ PRINTK("sys_socketpair: family = %d, type = %d, protocol = %d\n",
+ family, type, protocol);
+
+ /*
+ * obtain the first socket and check if the underlying protocol
+ * supports the socketpair call
+ */
+ if ((fd1 = sock_socket(family, type, protocol)) < 0)
+ return fd1;
+ sock1 = sockfd_lookup(fd1, NULL);
+ if (!sock1->ops->socketpair) {
+ sys_close(fd1);
+ return -EINVAL;
+ }
+
+ /*
+ * now grab another socket and try to connect the two together
+ */
+ if ((fd2 = sock_socket(family, type, protocol)) < 0) {
+ sys_close(fd1);
+ return -EINVAL;
+ }
+ sock2 = sockfd_lookup(fd2, NULL);
+ if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) {
+ sys_close(fd1);
+ sys_close(fd2);
+ return i;
+ }
+ sock1->conn = sock2;
+ sock2->conn = sock1;
+ sock1->state = SS_CONNECTED;
+ sock2->state = SS_CONNECTED;
+
+ verify_area(usockvec, 2 * sizeof(int));
+ put_fs_long(fd1, &usockvec[0]);
+ put_fs_long(fd2, &usockvec[1]);
+
+ return 0;
+}
+
+/*
+ * binds a name to a socket. nothing much to do here since its the
+ * protocol's responsibility to handle the local address
+ */
+static int
+sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)
+{
+ struct socket *sock;
+ int i;
+
+ PRINTK("sys_bind: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, NULL)))
+ return -EBADF;
+ if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) {
+ PRINTK("sys_bind: bind failed\n");
+ return i;
+ }
+ return 0;
+}
+
+/*
+ * perform a listen. basically, we allow the protocol to do anything
+ * necessary for a listen, and if that works, we mark the socket as
+ * ready for listening.
+ */
+static int
+sock_listen(int fd, int backlog)
+{
+ struct socket *sock;
+
+ PRINTK("sys_listen: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, NULL)))
+ return -EBADF;
+ if (sock->state != SS_UNCONNECTED) {
+ PRINTK("sys_listen: socket isn't unconnected\n");
+ return -EINVAL;
+ }
+ if (sock->flags & SO_ACCEPTCON) {
+ PRINTK("sys_listen: socket already accepting connections!\n");
+ return -EINVAL;
+ }
+ sock->flags |= SO_ACCEPTCON;
+ return 0;
+}
+
+/*
+ * for accept, we attempt to create a new socket, set up the link with the
+ * client, wake up the client, then return the new connected fd.
+ */
+static int
+sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
+{
+ struct file *file;
+ struct socket *sock, *clientsock, *newsock;
+ int i;
+
+ PRINTK("sys_accept: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, &file)))
+ return -EBADF;
+ if (sock->state != SS_UNCONNECTED) {
+ PRINTK("sys_accept: socket isn't unconnected\n");
+ return -EINVAL;
+ }
+ if (!(sock->flags & SO_ACCEPTCON)) {
+ PRINTK("sys_accept: socket not accepting connections!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * if there aren't any sockets awaiting connection, then wait for
+ * one, unless nonblocking
+ */
+ while (!(clientsock = sock->iconn)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ PRINTK("sys_accept: sleep was interrupted\n");
+ return -EINTR;
+ }
+ }
+
+ if (!(newsock = sock_alloc(0))) {
+ printk("sys_accept: no more sockets\n");
+ return -EINVAL;
+ }
+ newsock->type = sock->type;
+ newsock->ops = sock->ops;
+ if ((i = sock->ops->dup(newsock, sock)) < 0) {
+ sock_release(newsock);
+ return i;
+ }
+
+ if ((fd = get_fd(SOCK_INODE(newsock))) < 0) {
+ sock_release(newsock);
+ return -EINVAL;
+ }
+
+ /*
+ * great. finish the connection relative to server and client,
+ * wake up the client and return the new fd to the server
+ */
+ sock->iconn = clientsock->next;
+ clientsock->next = NULL;
+ newsock->conn = clientsock;
+ clientsock->conn = newsock;
+ clientsock->state = SS_CONNECTED;
+ newsock->state = SS_CONNECTED;
+ newsock->ops->accept(sock, newsock);
+ PRINTK("sys_accept: connected socket 0x%x via 0x%x to 0x%x\n",
+ sock, newsock, clientsock);
+ if (upeer_sockaddr)
+ newsock->ops->getname(newsock, upeer_sockaddr,
+ upeer_addrlen, 1);
+ wake_up(clientsock->wait);
+
+ return fd;
+}
+
+/*
+ * attempt to connect to a socket with the server address.
+ */
+static int
+sock_connect(int fd, struct sockaddr *uservaddr, int addrlen)
+{
+ struct socket *sock;
+ int i;
+
+ PRINTK("sys_connect: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, NULL)))
+ return -EBADF;
+ if (sock->state != SS_UNCONNECTED) {
+ PRINTK("sys_connect: socket not unconnected\n");
+ return -EINVAL;
+ }
+ if ((i = sock->ops->connect(sock, uservaddr, addrlen)) < 0) {
+ PRINTK("sys_connect: connect failed\n");
+ return i;
+ }
+ return 0;
+}
+
+static int
+sock_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
+{
+ struct socket *sock;
+
+ PRINTK("sys_getsockname: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, NULL)))
+ return -EBADF;
+ return sock->ops->getname(sock, usockaddr, usockaddr_len, 0);
+}
+
+static int
+sock_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len)
+{
+ struct socket *sock;
+
+ PRINTK("sys_getpeername: fd = %d\n", fd);
+ if (!(sock = sockfd_lookup(fd, NULL)))
+ return -EBADF;
+ return sock->ops->getname(sock, usockaddr, usockaddr_len, 1);
+}
+
+/*
+ * system call vectors. since i want to rewrite sockets as streams, we have
+ * this level of indirection. not a lot of overhead, since more of the work is
+ * done via read/write/select directly
+ */
+int
+sys_socketcall(int call, unsigned long *args)
+{
+ switch (call) {
+ case SYS_SOCKET:
+ verify_area(args, 3 * sizeof(long));
+ return sock_socket(get_fs_long(args+0),
+ get_fs_long(args+1),
+ get_fs_long(args+2));
+
+ case SYS_BIND:
+ verify_area(args, 3 * sizeof(long));
+ return sock_bind(get_fs_long(args+0),
+ (struct sockaddr *)get_fs_long(args+1),
+ get_fs_long(args+2));
+
+ case SYS_CONNECT:
+ verify_area(args, 3 * sizeof(long));
+ return sock_connect(get_fs_long(args+0),
+ (struct sockaddr *)get_fs_long(args+1),
+ get_fs_long(args+2));
+
+ case SYS_LISTEN:
+ verify_area(args, 2 * sizeof(long));
+ return sock_listen(get_fs_long(args+0),
+ get_fs_long(args+1));
+
+ case SYS_ACCEPT:
+ verify_area(args, 3 * sizeof(long));
+ return sock_accept(get_fs_long(args+0),
+ (struct sockaddr *)get_fs_long(args+1),
+ (int *)get_fs_long(args+2));
+
+ case SYS_GETSOCKNAME:
+ verify_area(args, 3 * sizeof(long));
+ return sock_getsockname(get_fs_long(args+0),
+ (struct sockaddr *)get_fs_long(args+1),
+ (int *)get_fs_long(args+2));
+
+ case SYS_GETPEERNAME:
+ verify_area(args, 3 * sizeof(long));
+ return sock_getpeername(get_fs_long(args+0),
+ (struct sockaddr *)get_fs_long(args+1),
+ (int *)get_fs_long(args+2));
+
+ case SYS_SOCKETPAIR:
+ verify_area(args, 4 * sizeof(long));
+ return sock_socketpair(get_fs_long(args+0),
+ get_fs_long(args+1),
+ get_fs_long(args+2),
+ (int *)get_fs_long(args+3));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+void
+sock_init(void)
+{
+ struct socket *sock;
+ int i, ok;
+
+ for (sock = sockets; sock <= last_socket; ++sock)
+ sock->state = SS_FREE;
+ for (i = ok = 0; i < NPROTO; ++i) {
+ printk("sock_init: initializing family %d (%s)\n",
+ proto_table[i].family, proto_table[i].name);
+ if ((*proto_table[i].ops->init)() < 0) {
+ printk("sock_init: init failed.\n",
+ proto_table[i].family);
+ proto_table[i].family = -1;
+ }
+ else
+ ++ok;
+ }
+ if (!ok)
+ printk("sock_init: warning: no protocols initialized\n");
+ return;
+}
+
diff --git a/net/socketcall.h b/net/socketcall.h
new file mode 100644
index 0000000..4b48562
--- /dev/null
+++ b/net/socketcall.h
@@ -0,0 +1,13 @@
+#ifndef _SOCKETCALL_
+#define _SOCKETCALL_
+
+#define SYS_SOCKET 1
+#define SYS_BIND 2
+#define SYS_CONNECT 3
+#define SYS_LISTEN 4
+#define SYS_ACCEPT 5
+#define SYS_GETSOCKNAME 6
+#define SYS_GETPEERNAME 7
+#define SYS_SOCKETPAIR 8
+
+#endif _SOCKETCALL_
diff --git a/net/unix.c b/net/unix.c
new file mode 100644
index 0000000..5a03b91
--- /dev/null
+++ b/net/unix.c
@@ -0,0 +1,606 @@
+#include <signal.h>
+#include <errno.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include "kern_sock.h"
+
+static struct unix_proto_data {
+ int refcnt; /* cnt of reference 0=free */
+ struct socket *socket; /* socket we're bound to */
+ int protocol;
+ struct sockaddr_un sockaddr_un;
+ short sockaddr_len; /* >0 if name bound */
+ char *buf;
+ int bp_head, bp_tail;
+ struct inode *inode;
+ struct unix_proto_data *peerupd;
+} unix_datas[NSOCKETS];
+#define last_unix_data (unix_datas + NSOCKETS - 1)
+
+#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data)
+#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0)->sun_path)
+
+/*
+ * buffer size must be power of 2. buffer mgmt inspired by pipe code.
+ * note that buffer contents can wraparound, and we can write one byte less
+ * than full size to discern full vs empty.
+ */
+#define BUF_SIZE PAGE_SIZE
+#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & (BUF_SIZE-1))
+#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
+
+static int unix_proto_init(void);
+static int unix_proto_create(struct socket *sock, int protocol);
+static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);
+static int unix_proto_release(struct socket *sock, struct socket *peer);
+static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len);
+static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2);
+static int unix_proto_accept(struct socket *sock, struct socket *newsock);
+static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+ int *usockaddr_len, int peer);
+static int unix_proto_read(struct socket *sock, char *ubuf, int size,
+ int nonblock);
+static int unix_proto_write(struct socket *sock, char *ubuf, int size,
+ int nonblock);
+static int unix_proto_select(struct socket *sock, int which);
+static int unix_proto_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+
+struct proto_ops unix_proto_ops = {
+ unix_proto_init,
+ unix_proto_create,
+ unix_proto_dup,
+ unix_proto_release,
+ unix_proto_bind,
+ unix_proto_connect,
+ unix_proto_socketpair,
+ unix_proto_accept,
+ unix_proto_getname,
+ unix_proto_read,
+ unix_proto_write,
+ unix_proto_select,
+ unix_proto_ioctl
+};
+
+#ifdef SOCK_DEBUG
+void
+sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
+{
+ char buf[sizeof(sockun->sun_path) + 1];
+
+ sockaddr_len -= UN_PATH_OFFSET;
+ if (sockun->sun_family != AF_UNIX)
+ printk("sockaddr_un: <BAD FAMILY: %d>\n", sockun->sun_family);
+ else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf)-1)
+ printk("sockaddr_un: <BAD LENGTH: %d>\n", sockaddr_len);
+ else {
+ memcpy(buf, sockun->sun_path, sockaddr_len);
+ buf[sockaddr_len] = '\0';
+ printk("sockaddr_un: '%s'[%d]\n", buf,
+ sockaddr_len + UN_PATH_OFFSET);
+ }
+}
+#endif
+
+static struct unix_proto_data *
+unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len)
+{
+ struct unix_proto_data *upd;
+
+ for (upd = unix_datas; upd <= last_unix_data; ++upd) {
+ if (upd->refcnt && upd->socket &&
+ upd->sockaddr_len == sockaddr_len &&
+ memcmp(&upd->sockaddr_un, sockun, sockaddr_len) == 0)
+ return upd;
+ }
+ return NULL;
+}
+
+static struct unix_proto_data *
+unix_data_alloc(void)
+{
+ struct unix_proto_data *upd;
+
+ cli();
+ for (upd = unix_datas; upd <= last_unix_data; ++upd) {
+ if (!upd->refcnt) {
+ upd->refcnt = 1;
+ sti();
+ upd->socket = NULL;
+ upd->sockaddr_len = 0;
+ upd->buf = NULL;
+ upd->bp_head = upd->bp_tail = 0;
+ upd->inode = NULL;
+ upd->peerupd = NULL;
+ return upd;
+ }
+ }
+ sti();
+ return NULL;
+}
+
+static inline void
+unix_data_ref(struct unix_proto_data *upd)
+{
+ ++upd->refcnt;
+ PRINTK("unix_data_ref: refing data 0x%x (%d)\n", upd, upd->refcnt);
+}
+
+static void
+unix_data_deref(struct unix_proto_data *upd)
+{
+ if (upd->refcnt == 1) {
+ PRINTK("unix_data_deref: releasing data 0x%x\n", upd);
+ if (upd->buf) {
+ free_page((unsigned long)upd->buf);
+ upd->buf = NULL;
+ upd->bp_head = upd->bp_tail = 0;
+ }
+ }
+ --upd->refcnt;
+}
+
+/*
+ * upon a create, we allocate an empty protocol data, and grab a page to
+ * buffer writes
+ */
+static int
+unix_proto_create(struct socket *sock, int protocol)
+{
+ struct unix_proto_data *upd;
+
+ PRINTK("unix_proto_create: socket 0x%x, proto %d\n", sock, protocol);
+ if (protocol != 0) {
+ PRINTK("unix_proto_create: protocol != 0\n");
+ return -EINVAL;
+ }
+ if (!(upd = unix_data_alloc())) {
+ printk("unix_proto_create: can't allocate buffer\n");
+ return -ENOMEM;
+ }
+ if (!(upd->buf = (char *)get_free_page())) {
+ printk("unix_proto_create: can't get page!\n");
+ unix_data_deref(upd);
+ return -ENOMEM;
+ }
+ upd->protocol = protocol;
+ upd->socket = sock;
+ UN_DATA(sock) = upd;
+ PRINTK("unix_proto_create: allocated data 0x%x\n", upd);
+ return 0;
+}
+
+static int
+unix_proto_dup(struct socket *newsock, struct socket *oldsock)
+{
+ struct unix_proto_data *upd = UN_DATA(oldsock);
+
+ return unix_proto_create(newsock, upd->protocol);
+}
+
+static int
+unix_proto_release(struct socket *sock, struct socket *peer)
+{
+ struct unix_proto_data *upd = UN_DATA(sock);
+
+ PRINTK("unix_proto_release: socket 0x%x, unix_data 0x%x\n",
+ sock, upd);
+ if (!upd)
+ return 0;
+ if (upd->socket != sock) {
+ printk("unix_proto_release: socket link mismatch!\n");
+ return -EINVAL;
+ }
+ if (upd->inode) {
+ PRINTK("unix_proto_release: releasing inode 0x%x\n",
+ upd->inode);
+ iput(upd->inode);
+ upd->inode = NULL;
+ }
+ UN_DATA(sock) = NULL;
+ upd->socket = NULL;
+ if (upd->peerupd)
+ unix_data_deref(upd->peerupd);
+ unix_data_deref(upd);
+ return 0;
+}
+
+/*
+ * bind a name to a socket. this is where much of the work is done. we
+ * allocate a fresh page for the buffer, grab the appropriate inode and
+ * set things up.
+ *
+ * XXX what should we do if an address is already bound? here we return
+ * EINVAL, but it may be necessary to re-bind. i think thats what bsd does
+ * in the case of datagram sockets
+ */
+static int
+unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len)
+{
+ struct unix_proto_data *upd = UN_DATA(sock);
+ char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
+ int i;
+ unsigned long old_fs;
+ unsigned short old_euid;
+
+ PRINTK("unix_proto_bind: socket 0x%x, len=%d\n", sock,
+ sockaddr_len);
+ if (sockaddr_len <= UN_PATH_OFFSET ||
+ sockaddr_len >= sizeof(struct sockaddr_un)) {
+ PRINTK("unix_proto_bind: bad length %d\n", sockaddr_len);
+ return -EINVAL;
+ }
+ if (upd->sockaddr_len || upd->inode) {
+ printk("unix_proto_bind: already bound!\n");
+ return -EINVAL;
+ }
+ verify_area(umyaddr, sockaddr_len);
+ memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
+ if (upd->sockaddr_un.sun_family != AF_UNIX) {
+ PRINTK("unix_proto_bind: family is %d, not AF_UNIX (%d)\n",
+ upd->sockaddr_un.sun_family, AF_UNIX);
+ return -EINVAL;
+ }
+
+ /*
+ * W A R N I N G
+ * this is a terrible hack. i want to create a socket in the
+ * filesystem and get its inode. sys_mknod() can create one for
+ * me, but it needs superuser privs and doesn't give me the inode.
+ * we fake suser here and get the file created... ugh.
+ */
+ memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
+ fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
+ old_fs = get_fs();
+ set_fs(get_ds());
+ old_euid = current->euid;
+ current->euid = 0;
+ i = sys_mknod(fname, S_IFSOCK, 0);
+ current->euid = old_euid;
+ if (i == 0)
+ i = open_namei(fname, 0, S_IFSOCK, &upd->inode);
+ set_fs(old_fs);
+ if (i < 0) {
+ printk("unix_proto_bind: can't open socket %s\n", fname);
+ return i;
+ }
+
+ upd->sockaddr_len = sockaddr_len; /* now its legal */
+ PRINTK("unix_proto_bind: bound socket address: ");
+#ifdef SOCK_DEBUG
+ sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
+#endif
+ return 0;
+}
+
+/*
+ * perform a connection. we can only connect to unix sockets (i can't for
+ * the life of me find an application where that wouldn't be the case!)
+ */
+static int
+unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len)
+{
+ int i;
+ struct unix_proto_data *serv_upd;
+ struct sockaddr_un sockun;
+
+ PRINTK("unix_proto_connect: socket 0x%x, servlen=%d\n", sock,
+ sockaddr_len);
+ if (sockaddr_len <= UN_PATH_OFFSET ||
+ sockaddr_len >= sizeof(struct sockaddr_un)) {
+ PRINTK("unix_proto_connect: bad length %d\n", sockaddr_len);
+ return -EINVAL;
+ }
+ verify_area(uservaddr, sockaddr_len);
+ memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
+ if (sockun.sun_family != AF_UNIX) {
+ PRINTK("unix_proto_connect: family is %d, not AF_UNIX (%d)\n",
+ sockun.sun_family, AF_UNIX);
+ return -EINVAL;
+ }
+ if (!(serv_upd = unix_data_lookup(&sockun, sockaddr_len))) {
+ PRINTK("unix_proto_connect: can't locate peer\n");
+ return -EINVAL;
+ }
+ if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
+ PRINTK("unix_proto_connect: can't await connection\n");
+ return i;
+ }
+ unix_data_ref(UN_DATA(sock->conn));
+ UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
+ return 0;
+}
+
+/*
+ * to do a socketpair, we make just connect the two datas, easy! since we
+ * always wait on the socket inode, they're no contention for a wait area,
+ * and deadlock prevention in the case of a process writing to itself is,
+ * ignored, in true unix fashion!
+ */
+static int
+unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
+
+ unix_data_ref(upd1);
+ unix_data_ref(upd2);
+ upd1->peerupd = upd2;
+ upd2->peerupd = upd1;
+ return 0;
+}
+
+/*
+ * on accept, we ref the peer's data for safe writes
+ */
+static int
+unix_proto_accept(struct socket *sock, struct socket *newsock)
+{
+ PRINTK("unix_proto_accept: socket 0x%x accepted via socket 0x%x\n",
+ sock, newsock);
+ unix_data_ref(UN_DATA(newsock->conn));
+ UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn);
+ return 0;
+}
+
+/*
+ * gets the current name or the name of the connected socket.
+ */
+static int
+unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
+ int *usockaddr_len, int peer)
+{
+ struct unix_proto_data *upd;
+ int len;
+
+ PRINTK("unix_proto_getname: socket 0x%x for %s\n", sock,
+ peer ? "peer" : "self");
+ if (peer) {
+ if (sock->state != SS_CONNECTED) {
+ PRINTK("unix_proto_getname: socket not connected\n");
+ return -EINVAL;
+ }
+ upd = UN_DATA(sock->conn);
+ }
+ else
+ upd = UN_DATA(sock);
+ verify_area(usockaddr_len, sizeof(*usockaddr_len));
+ if ((len = get_fs_long(usockaddr_len)) <= 0)
+ return -EINVAL;
+ if (len > upd->sockaddr_len)
+ len = upd->sockaddr_len;
+ if (len) {
+ verify_area(usockaddr, len);
+ memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
+ }
+ put_fs_long(len, usockaddr_len);
+ return 0;
+}
+
+/*
+ * we read from our own buf.
+ */
+static int
+unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
+{
+ struct unix_proto_data *upd;
+ int todo, avail;
+
+ if ((todo = size) <= 0)
+ return 0;
+ upd = UN_DATA(sock);
+ while (!(avail = UN_BUF_AVAIL(upd))) {
+ if (sock->state != SS_CONNECTED) {
+ PRINTK("unix_proto_read: socket not connected\n");
+ return (sock->state == SS_DISCONNECTING) ? 0 : -EINVAL;
+ }
+ PRINTK("unix_proto_read: no data available...\n");
+ if (nonblock)
+ return -EAGAIN;
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ PRINTK("unix_proto_read: interrupted\n");
+ return -ERESTARTSYS;
+ }
+ if (sock->state == SS_DISCONNECTING) {
+ PRINTK("unix_proto_read: disconnected\n");
+ return 0;
+ }
+ }
+
+ /*
+ * copy from the read buffer into the user's buffer, watching for
+ * wraparound. then we wake up the writer
+ */
+ do {
+ int part, cando;
+
+ if (avail <= 0) {
+ PRINTK("unix_proto_read: AVAIL IS NEGATIVE!!!\n");
+ current->signal |= (1 << (SIGKILL-1));
+ return -EINTR;
+ }
+
+ if ((cando = todo) > avail)
+ cando = avail;
+ if (cando > (part = BUF_SIZE - upd->bp_tail))
+ cando = part;
+ PRINTK("unix_proto_read: avail=%d, todo=%d, cando=%d\n",
+ avail, todo, cando);
+ verify_area(ubuf, cando);
+ memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
+ upd->bp_tail = (upd->bp_tail + cando) & (BUF_SIZE-1);
+ ubuf += cando;
+ todo -= cando;
+ if (sock->state == SS_CONNECTED)
+ wake_up(sock->conn->wait);
+ avail = UN_BUF_AVAIL(upd);
+ } while (todo && avail);
+ return size - todo;
+}
+
+/*
+ * we write to our peer's buf. when we connected we ref'd this peer so we
+ * are safe that the buffer remains, even after the peer has disconnected,
+ * which we check other ways.
+ */
+static int
+unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
+{
+ struct unix_proto_data *pupd;
+ int todo, space;
+
+ if ((todo = size) <= 0)
+ return 0;
+ if (sock->state != SS_CONNECTED) {
+ PRINTK("unix_proto_write: socket not connected\n");
+ if (sock->state == SS_DISCONNECTING) {
+ current->signal |= (1 << (SIGPIPE-1));
+ return -EINTR;
+ }
+ return -EINVAL;
+ }
+ pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */
+
+ while (!(space = UN_BUF_SPACE(pupd))) {
+ PRINTK("unix_proto_write: no space left...\n");
+ if (nonblock)
+ return 0;
+ interruptible_sleep_on(sock->wait);
+ if (current->signal & ~current->blocked) {
+ PRINTK("unix_proto_write: interrupted\n");
+ return -EINTR;
+ }
+ if (sock->state == SS_DISCONNECTING) {
+ PRINTK("unix_proto_write: disconnected (SIGPIPE)\n");
+ current->signal |= (1 << (SIGPIPE-1));
+ return -EINTR;
+ }
+ }
+
+ /*
+ * copy from the user's buffer to the write buffer, watching for
+ * wraparound. then we wake up the reader
+ */
+ do {
+ int part, cando;
+
+ if (space <= 0) {
+ PRINTK("unix_proto_write: SPACE IS NEGATIVE!!!\n");
+ current->signal |= (1 << (SIGKILL-1));
+ return -EINTR;
+ }
+
+ /*
+ * we may become disconnected inside this loop, so watch
+ * for it (peerupd is safe until we close)
+ */
+ if (sock->state == SS_DISCONNECTING) {
+ current->signal |= (1 << (SIGPIPE-1));
+ return -EINTR;
+ }
+ if ((cando = todo) > space)
+ cando = space;
+ if (cando > (part = BUF_SIZE - pupd->bp_head))
+ cando = part;
+ PRINTK("unix_proto_write: space=%d, todo=%d, cando=%d\n",
+ space, todo, cando);
+ verify_area(ubuf, cando);
+ memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
+ pupd->bp_head = (pupd->bp_head + cando) & (BUF_SIZE-1);
+ ubuf += cando;
+ todo -= cando;
+ if (sock->state == SS_CONNECTED)
+ wake_up(sock->conn->wait);
+ space = UN_BUF_SPACE(pupd);
+ } while (todo && space);
+ return size - todo;
+}
+
+static int
+unix_proto_select(struct socket *sock, int which)
+{
+ struct unix_proto_data *upd, *peerupd;
+
+ if (which == SEL_IN) {
+ upd = UN_DATA(sock);
+ PRINTK("unix_proto_select: there is%s data available\n",
+ UN_BUF_AVAIL(upd) ? "" : " no");
+ if (UN_BUF_AVAIL(upd)) /* even if disconnected */
+ return 1;
+ else if (sock->state != SS_CONNECTED) {
+ PRINTK("unix_proto_select: socket not connected (read EOF)\n");
+ return 1;
+ }
+ else
+ return 0;
+ }
+ if (which == SEL_OUT) {
+ if (sock->state != SS_CONNECTED) {
+ PRINTK("unix_proto_select: socket not connected (write EOF)\n");
+ return 1;
+ }
+ peerupd = UN_DATA(sock->conn);
+ PRINTK("unix_proto_select: there is%s space available\n",
+ UN_BUF_SPACE(peerupd) ? "" : " no");
+ return (UN_BUF_SPACE(peerupd) > 0);
+ }
+ /* SEL_EX */
+ PRINTK("unix_proto_select: there are no exceptions here?!\n");
+ return 0;
+}
+
+static int
+unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct unix_proto_data *upd, *peerupd;
+
+ upd = UN_DATA(sock);
+ peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
+
+ switch (cmd) {
+ case TIOCINQ:
+ verify_area((void *)arg, sizeof(unsigned long));
+ if (UN_BUF_AVAIL(upd) || peerupd)
+ put_fs_long(UN_BUF_AVAIL(upd), (unsigned long *)arg);
+ else
+ put_fs_long(1, (unsigned long *)arg); /* read EOF */
+ break;
+
+ case TIOCOUTQ:
+ verify_area((void *)arg, sizeof(unsigned long));
+ if (peerupd)
+ put_fs_long(UN_BUF_SPACE(peerupd),
+ (unsigned long *)arg);
+ else
+ put_fs_long(0, (unsigned long *)arg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
+unix_proto_init(void)
+{
+ struct unix_proto_data *upd;
+
+ PRINTK("unix_proto_init: initializing...\n");
+ for (upd = unix_datas; upd <= last_unix_data; ++upd)
+ upd->refcnt = 0;
+ return 0;
+}